1 /* $Id: laser.c,v 1.14 2006-07-09 03:25:10 chris Exp $ */
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.
17 * This will contain the laser code
26 char laser_rcsid[] = "$Id: laser.c,v 1.14 2006-07-09 03:25:10 chris Exp $";
73 int Laser_rapid_fire = 0;
76 object *Guided_missile[MAX_PLAYERS]={NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
77 int Guided_missile_sig[MAX_PLAYERS]={-1,-1,-1,-1,-1,-1,-1,-1};
79 int find_homing_object_complete(vms_vector *curpos, object *tracker, int track_obj_type1, int track_obj_type2);
81 extern char Multi_is_guided;
82 extern char BounceCheat;
84 extern void newdemo_record_guided_end();
85 extern void newdemo_record_guided_start();
87 int find_homing_object(vms_vector *curpos, object *tracker);
89 //---------------------------------------------------------------------------------
90 // Called by render code.... determines if the laser is from a robot or the
91 // player and calls the appropriate routine.
93 void Laser_render(object *obj)
96 // Commented out by John (sort of, typed by Mike) on 6/8/94
99 case WEAPON_TYPE_WEAK_LASER:
100 case WEAPON_TYPE_STRONG_LASER:
101 case WEAPON_TYPE_CANNON_BALL:
102 case WEAPON_TYPE_MISSILE:
105 Error( "Invalid weapon type in Laser_render\n" );
109 switch( Weapon_info[obj->id].render_type ) {
110 case WEAPON_RENDER_LASER:
111 Int3(); // Not supported anymore!
112 //Laser_draw_one(obj-Objects, Weapon_info[obj->id].bitmap );
114 case WEAPON_RENDER_BLOB:
115 draw_object_blob(obj, Weapon_info[obj->id].bitmap );
117 case WEAPON_RENDER_POLYMODEL:
119 case WEAPON_RENDER_VCLIP:
120 Int3(); // Oops, not supported, type added by mk on 09/09/94, but not for lasers...
122 Error( "Invalid weapon render type in Laser_render\n" );
127 //---------------------------------------------------------------------------------
128 // Draws a texture-mapped laser bolt
130 //void Laser_draw_one( int objnum, grs_bitmap * bmp )
135 // vms_vector start_pos,end_pos;
137 // obj = &Objects[objnum];
139 // start_pos = obj->pos;
140 // vm_vec_scale_add(&end_pos,&start_pos,&obj->orient.fvec,-Laser_length);
142 // g3_rotate_point(&p1,&start_pos);
143 // g3_rotate_point(&p2,&end_pos);
146 // t2 = Interpolation_method;
147 // t3 = Transparency_on;
150 // //Interpolation_method = 3; // Full perspective
151 // Interpolation_method = 1; // Linear
152 // Transparency_on = 1;
154 // //gr_setcolor( gr_getcolor(31,15,0));
155 // //g3_draw_line_ptrs(p1,p2);
156 // //g3_draw_rod(p1,0x2000,p2,0x2000);
157 // //g3_draw_rod(p1,Laser_width,p2,Laser_width);
158 // g3_draw_rod_tmap(bmp,&p2,Laser_width,&p1,Laser_width,0);
160 // Interpolation_method = t2;
161 // Transparency_on = t3;
165 // Changed by MK on 09/07/94
166 // I want you to be able to blow up your own bombs.
167 // AND...Your proximity bombs can blow you up if they're 2.0 seconds or more old.
168 // Changed by MK on 06/06/95: Now must be 4.0 seconds old. Much valid Net-complaining.
169 int laser_are_related( int o1, int o2 )
171 if ( (o1<0) || (o2<0) )
174 // See if o2 is the parent of o1
175 if ( Objects[o1].type == OBJ_WEAPON )
176 if ( (Objects[o1].ctype.laser_info.parent_num==o2) && (Objects[o1].ctype.laser_info.parent_signature==Objects[o2].signature) )
178 // 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
179 if ((Objects[o1].id == PHOENIX_ID && (GameTime > Objects[o1].ctype.laser_info.creation_time + F1_0/4)) ||
180 (Objects[o1].id == GUIDEDMISS_ID && (GameTime > Objects[o1].ctype.laser_info.creation_time + F1_0*2)) ||
181 (((Objects[o1].id == PROXIMITY_ID) || (Objects[o1].id == SUPERPROX_ID)) && (GameTime > Objects[o1].ctype.laser_info.creation_time + F1_0*4))) {
187 // See if o1 is the parent of o2
188 if ( Objects[o2].type == OBJ_WEAPON )
190 if ( (Objects[o2].ctype.laser_info.parent_num==o1) && (Objects[o2].ctype.laser_info.parent_signature==Objects[o1].signature) )
192 // 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
193 if ((Objects[o2].id == PHOENIX_ID && (GameTime > Objects[o2].ctype.laser_info.creation_time + F1_0/4)) ||
194 (Objects[o2].id == GUIDEDMISS_ID && (GameTime > Objects[o2].ctype.laser_info.creation_time + F1_0*2)) ||
195 (((Objects[o2].id == PROXIMITY_ID) || (Objects[o2].id == SUPERPROX_ID)) && (GameTime > Objects[o2].ctype.laser_info.creation_time + F1_0*4))) {
202 // They must both be weapons
203 if ( Objects[o1].type != OBJ_WEAPON || Objects[o2].type != OBJ_WEAPON )
206 // Here is the 09/07/94 change -- Siblings must be identical, others can hurt each other
207 // See if they're siblings...
208 // 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.
209 if ( Objects[o1].ctype.laser_info.parent_signature==Objects[o2].ctype.laser_info.parent_signature )
211 if (Objects[o1].id == PROXIMITY_ID || Objects[o2].id == PROXIMITY_ID || Objects[o1].id == SUPERPROX_ID || Objects[o2].id == SUPERPROX_ID) {
212 // If neither is older than 1/2 second, then can't blow up!
213 if ((GameTime > (Objects[o1].ctype.laser_info.creation_time + F1_0/2)) || (GameTime > (Objects[o2].ctype.laser_info.creation_time + F1_0/2)))
221 // Anything can cause a collision with a robot super prox mine.
222 if (Objects[o1].id == ROBOT_SUPERPROX_ID || Objects[o2].id == ROBOT_SUPERPROX_ID ||
223 Objects[o1].id == PROXIMITY_ID || Objects[o2].id == PROXIMITY_ID ||
224 Objects[o1].id == SUPERPROX_ID || Objects[o2].id == SUPERPROX_ID ||
225 Objects[o1].id == PMINE_ID || Objects[o2].id == PMINE_ID)
231 //--unused-- int Muzzle_scale=2;
234 void do_muzzle_stuff(int segnum, vms_vector *pos)
236 Muzzle_data[Muzzle_queue_index].create_time = timer_get_fixed_seconds();
237 Muzzle_data[Muzzle_queue_index].segnum = segnum;
238 Muzzle_data[Muzzle_queue_index].pos = *pos;
239 Muzzle_queue_index++;
240 if (Muzzle_queue_index >= MUZZLE_QUEUE_MAX)
241 Muzzle_queue_index = 0;
244 //creates a weapon object
245 int create_weapon_object(int weapon_type,int segnum,vms_vector *position)
248 fix laser_radius = -1;
252 switch( Weapon_info[weapon_type].render_type ) {
254 case WEAPON_RENDER_BLOB:
255 rtype = RT_LASER; // Render as a laser even if blob (see render code above for explanation)
256 laser_radius = Weapon_info[weapon_type].blob_size;
258 case WEAPON_RENDER_POLYMODEL:
259 laser_radius = 0; // Filled in below.
262 case WEAPON_RENDER_LASER:
263 Int3(); // Not supported anymore
265 case WEAPON_RENDER_NONE:
269 case WEAPON_RENDER_VCLIP:
270 rtype = RT_WEAPON_VCLIP;
271 laser_radius = Weapon_info[weapon_type].blob_size;
274 Error( "Invalid weapon render type in Laser_create_new\n" );
277 Assert(laser_radius != -1);
280 objnum = obj_create( OBJ_WEAPON, weapon_type, segnum, position, NULL, laser_radius, CT_WEAPON, MT_PHYSICS, rtype );
284 obj = &Objects[objnum];
286 if (Weapon_info[weapon_type].render_type == WEAPON_RENDER_POLYMODEL) {
287 obj->rtype.pobj_info.model_num = Weapon_info[obj->id].model_num;
288 obj->size = fixdiv(Polygon_models[obj->rtype.pobj_info.model_num].rad,Weapon_info[obj->id].po_len_to_width_ratio);
291 obj->mtype.phys_info.mass = Weapon_info[weapon_type].mass;
292 obj->mtype.phys_info.drag = Weapon_info[weapon_type].drag;
293 vm_vec_zero(&obj->mtype.phys_info.thrust);
295 if (Weapon_info[weapon_type].bounce==1)
296 obj->mtype.phys_info.flags |= PF_BOUNCE;
298 if (Weapon_info[weapon_type].bounce==2 || BounceCheat)
299 obj->mtype.phys_info.flags |= PF_BOUNCE+PF_BOUNCES_TWICE;
305 extern int Doing_lighting_hack_flag;
307 // -------------------------------------------------------------------------------------------------------------------------------
308 // ***** HEY ARTISTS!! *****
309 // Here are the constants you're looking for! --MK
311 // Change the following constants to affect the look of the omega cannon.
312 // Changing these constants will not affect the damage done.
313 // WARNING: If you change DESIRED_OMEGA_DIST and MAX_OMEGA_BLOBS, you don't merely change the look of the cannon,
314 // you change its range. If you decrease DESIRED_OMEGA_DIST, you decrease how far the gun can fire.
315 #define MIN_OMEGA_BLOBS 3 // No matter how close the obstruction, at this many blobs created.
316 #define MIN_OMEGA_DIST (F1_0*3) // At least this distance between blobs, unless doing so would violate MIN_OMEGA_BLOBS
317 #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.
318 #define MAX_OMEGA_BLOBS 16 // No matter how far away the obstruction, this is the maximum number of blobs.
319 #define MAX_OMEGA_DIST (MAX_OMEGA_BLOBS * DESIRED_OMEGA_DIST) // Maximum extent of lightning blobs.
321 // Additionally, several constants which apply to homing objects in general control the behavior of the Omega Cannon.
322 // They are defined in laser.h. They are copied here for reference. These values are valid on 1/10/96:
323 // If you want the Omega Cannon view cone to be different than the Homing Missile viewcone, contact MK to make the change.
324 // (Unless you are a programmer, in which case, do it yourself!)
325 #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.
326 #define OMEGA_MAX_TRACKABLE_DIST MAX_OMEGA_DIST // An object must be at least this close to be tracked.
328 // Note, you don't need to change these constants. You can control damage and energy consumption by changing the
329 // usual bitmaps.tbl parameters.
330 #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.
331 #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.
332 // -------------------------------------------------------------------------------------------------------------------------------
334 void delete_old_omega_blobs(object *parent_objp)
340 parent_num = parent_objp->ctype.laser_info.parent_num;
342 for (i=0; i<=Highest_object_index; i++)
343 if (Objects[i].type == OBJ_WEAPON)
344 if (Objects[i].id == OMEGA_ID)
345 if (Objects[i].ctype.laser_info.parent_num == parent_num) {
350 mprintf((0, "%i Omega blobs deleted in frame %i\n", count, FrameCount));
353 // ---------------------------------------------------------------------------------
354 void create_omega_blobs(int firing_segnum, vms_vector *firing_pos, vms_vector *goal_pos, object *parent_objp)
356 int i, last_segnum, last_created_objnum = -1;
357 vms_vector vec_to_goal;
361 vms_vector omega_delta_vector;
362 vms_vector blob_pos, perturb_vec;
363 fix perturb_array[MAX_OMEGA_BLOBS];
365 if (Game_mode & GM_MULTI)
366 delete_old_omega_blobs(parent_objp);
368 vm_vec_sub(&vec_to_goal, goal_pos, firing_pos);
370 dist_to_goal = vm_vec_normalize_quick(&vec_to_goal);
372 if (dist_to_goal < MIN_OMEGA_BLOBS * MIN_OMEGA_DIST) {
373 omega_blob_dist = MIN_OMEGA_DIST;
374 num_omega_blobs = dist_to_goal/omega_blob_dist;
375 if (num_omega_blobs == 0)
378 omega_blob_dist = DESIRED_OMEGA_DIST;
379 num_omega_blobs = dist_to_goal / omega_blob_dist;
380 if (num_omega_blobs > MAX_OMEGA_BLOBS) {
381 num_omega_blobs = MAX_OMEGA_BLOBS;
382 omega_blob_dist = dist_to_goal / num_omega_blobs;
383 } else if (num_omega_blobs < MIN_OMEGA_BLOBS) {
384 num_omega_blobs = MIN_OMEGA_BLOBS;
385 omega_blob_dist = dist_to_goal / num_omega_blobs;
389 omega_delta_vector = vec_to_goal;
390 vm_vec_scale(&omega_delta_vector, omega_blob_dist);
392 // Now, create all the blobs
393 blob_pos = *firing_pos;
394 last_segnum = firing_segnum;
396 // If nearby, don't perturb vector. If not nearby, start halfway out.
397 if (dist_to_goal < MIN_OMEGA_DIST*4) {
398 for (i=0; i<num_omega_blobs; i++)
399 perturb_array[i] = 0;
401 vm_vec_scale_add2(&blob_pos, &omega_delta_vector, F1_0/2); // Put first blob half way out.
402 for (i=0; i<num_omega_blobs/2; i++) {
403 perturb_array[i] = F1_0*i + F1_0/4;
404 perturb_array[num_omega_blobs-1-i] = F1_0*i;
408 // Create random perturbation vector, but favor _not_ going up in player's reference.
409 make_random_vector(&perturb_vec);
410 vm_vec_scale_add2(&perturb_vec, &parent_objp->orient.uvec, -F1_0/2);
412 Doing_lighting_hack_flag = 1; // Ugly, but prevents blobs which are probably outside the mine from killing framerate.
414 for (i=0; i<num_omega_blobs; i++) {
416 int blob_objnum, segnum;
418 // This will put the last blob right at the destination object, causing damage.
419 if (i == num_omega_blobs-1)
420 vm_vec_scale_add2(&blob_pos, &omega_delta_vector, 15*F1_0/32); // Move last blob another (almost) half section
422 // Every so often, re-perturb blobs
426 make_random_vector(&temp_vec);
427 vm_vec_scale_add2(&perturb_vec, &temp_vec, F1_0/4);
430 vm_vec_scale_add(&temp_pos, &blob_pos, &perturb_vec, perturb_array[i]);
432 segnum = find_point_seg(&temp_pos, last_segnum);
436 last_segnum = segnum;
437 blob_objnum = obj_create(OBJ_WEAPON, OMEGA_ID, segnum, &temp_pos, NULL, 0, CT_WEAPON, MT_PHYSICS, RT_WEAPON_VCLIP );
438 if (blob_objnum == -1)
441 last_created_objnum = blob_objnum;
443 objp = &Objects[blob_objnum];
445 objp->lifeleft = ONE_FRAME_TIME;
446 objp->mtype.phys_info.velocity = vec_to_goal;
448 // Only make the last one move fast, else multiple blobs might collide with target.
449 vm_vec_scale(&objp->mtype.phys_info.velocity, F1_0*4);
451 objp->size = Weapon_info[objp->id].blob_size;
453 objp->shields = fixmul(OMEGA_DAMAGE_SCALE*FrameTime, Weapon_info[objp->id].strength[Difficulty_level]);
455 objp->ctype.laser_info.parent_type = parent_objp->type;
456 objp->ctype.laser_info.parent_signature = parent_objp->signature;
457 objp->ctype.laser_info.parent_num = parent_objp-Objects;
458 objp->movement_type = MT_NONE; // Only last one moves, that will get bashed below.
462 vm_vec_add2(&blob_pos, &omega_delta_vector);
466 // Make last one move faster, but it's already moving at speed = F1_0*4.
467 if (last_created_objnum != -1) {
468 vm_vec_scale(&Objects[last_created_objnum].mtype.phys_info.velocity, Weapon_info[OMEGA_ID].speed[Difficulty_level]/4);
469 Objects[last_created_objnum].movement_type = MT_PHYSICS;
472 Doing_lighting_hack_flag = 0;
475 #define MIN_OMEGA_CHARGE (MAX_OMEGA_CHARGE/8)
476 #define OMEGA_CHARGE_SCALE 4 // FrameTime / OMEGA_CHARGE_SCALE added to Omega_charge every frame.
477 fix Omega_charge = MAX_OMEGA_CHARGE;
479 #define OMEGA_CHARGE_SCALE 4
481 int Last_omega_fire_frame=0;
483 // ---------------------------------------------------------------------------------
484 // Call this every frame to recharge the Omega Cannon.
485 void omega_charge_frame(void)
487 fix delta_charge, old_omega_charge;
489 if (Omega_charge == MAX_OMEGA_CHARGE)
492 if (!(player_has_weapon(OMEGA_INDEX, 0) & HAS_WEAPON_FLAG))
498 if ((Primary_weapon == OMEGA_INDEX) && (Omega_charge == 0) && (Players[Player_num].energy == 0)) {
500 auto_select_weapon(0);
503 // Don't charge while firing.
504 if ((Last_omega_fire_frame == FrameCount) || (Last_omega_fire_frame == FrameCount-1))
507 if (Players[Player_num].energy) {
510 old_omega_charge = Omega_charge;
511 Omega_charge += FrameTime/OMEGA_CHARGE_SCALE;
512 if (Omega_charge > MAX_OMEGA_CHARGE)
513 Omega_charge = MAX_OMEGA_CHARGE;
515 delta_charge = Omega_charge - old_omega_charge;
517 energy_used = fixmul(F1_0*190/17, delta_charge);
518 if (Difficulty_level < 2)
519 energy_used = fixmul(energy_used, i2f(Difficulty_level+2)/4);
521 Players[Player_num].energy -= energy_used;
522 if (Players[Player_num].energy < 0)
523 Players[Player_num].energy = 0;
529 // -- fix Last_omega_muzzle_flash_time;
531 // ---------------------------------------------------------------------------------
532 // *objp is the object firing the omega cannon
533 // *pos is the location from which the omega bolt starts
534 void do_omega_stuff(object *parent_objp, vms_vector *firing_pos, object *weapon_objp)
536 int lock_objnum, firing_segnum;
538 int pnum = parent_objp->id;
540 if (pnum == Player_num) {
541 // If charge >= min, or (some charge and zero energy), allow to fire.
542 if (!((Omega_charge >= MIN_OMEGA_CHARGE) || (Omega_charge && !Players[pnum].energy))) {
543 obj_delete(weapon_objp-Objects);
547 Omega_charge -= FrameTime;
548 if (Omega_charge < 0)
551 // Ensure that the lightning cannon can be fired next frame.
552 Next_laser_fire_time = GameTime+1;
554 Last_omega_fire_frame = FrameCount;
557 weapon_objp->ctype.laser_info.parent_type = OBJ_PLAYER;
558 weapon_objp->ctype.laser_info.parent_num = Players[pnum].objnum;
559 weapon_objp->ctype.laser_info.parent_signature = Objects[Players[pnum].objnum].signature;
561 lock_objnum = find_homing_object(firing_pos, weapon_objp);
563 firing_segnum = find_point_seg(firing_pos, parent_objp->segnum);
566 if ( parent_objp == Viewer )
567 digi_play_sample( Weapon_info[weapon_objp->id].flash_sound, F1_0 );
569 digi_link_sound_to_pos( Weapon_info[weapon_objp->id].flash_sound, weapon_objp->segnum, 0, &weapon_objp->pos, 0, F1_0 );
571 // -- if ((Last_omega_muzzle_flash_time + F1_0/4 < GameTime) || (Last_omega_muzzle_flash_time > GameTime)) {
572 // -- do_muzzle_stuff(firing_segnum, firing_pos);
573 // -- Last_omega_muzzle_flash_time = GameTime;
576 // Delete the original object. Its only purpose in life was to determine which object to home in on.
577 obj_delete(weapon_objp-Objects);
579 // If couldn't lock on anything, fire straight ahead.
580 if (lock_objnum == -1) {
584 vms_vector perturb_vec, perturbed_fvec;
586 make_random_vector(&perturb_vec);
587 vm_vec_scale_add(&perturbed_fvec, &parent_objp->orient.fvec, &perturb_vec, F1_0/16);
589 vm_vec_scale_add(&goal_pos, firing_pos, &perturbed_fvec, MAX_OMEGA_DIST);
590 fq.startseg = firing_segnum;
591 if (fq.startseg == -1) {
592 mprintf((1, "Trying to fire Omega Cannon, but gun is outside mine. Aborting!\n"));
598 fq.thisobjnum = parent_objp-Objects;
599 fq.ignore_obj_list = NULL;
600 fq.flags = FQ_IGNORE_POWERUPS | FQ_TRANSPOINT | FQ_CHECK_OBJS; //what about trans walls???
602 fate = find_vector_intersection(&fq, &hit_data);
603 if (fate != HIT_NONE) {
604 Assert(hit_data.hit_seg != -1); // How can this be? We went from inside the mine to outside without hitting anything?
605 goal_pos = hit_data.hit_pnt;
608 goal_pos = Objects[lock_objnum].pos;
610 // This is where we create a pile of omega blobs!
611 create_omega_blobs(firing_segnum, firing_pos, &goal_pos, parent_objp);
615 // ---------------------------------------------------------------------------------
616 // Initializes a laser after Fire is pressed
617 // Returns object number.
618 int Laser_create_new( vms_vector * direction, vms_vector * position, int segnum, int parent, int weapon_type, int make_sound )
622 fix parent_speed, weapon_speed;
626 Assert( weapon_type < N_weapon_types );
628 if ( (weapon_type<0) || (weapon_type>=N_weapon_types) )
631 // Don't let homing blobs make muzzle flash.
632 if (Objects[parent].type == OBJ_ROBOT)
633 do_muzzle_stuff(segnum, position);
635 objnum = create_weapon_object(weapon_type,segnum,position);
638 mprintf((1, "Can't create laser - Out of objects!\n" ));
642 obj = &Objects[objnum];
644 // Do the special Omega Cannon stuff. Then return on account of everything that follows does
645 // not apply to the Omega Cannon.
646 if (weapon_type == OMEGA_ID) {
647 // Create orientation matrix for tracking purposes.
648 vm_vector_2_matrix( &obj->orient, direction, &Objects[parent].orient.uvec ,NULL);
650 if (( &Objects[parent] != Viewer ) && (Objects[parent].type != OBJ_WEAPON)) {
652 if (Weapon_info[obj->id].flash_vclip > -1 )
653 object_create_muzzle_flash( obj->segnum, &obj->pos, Weapon_info[obj->id].flash_size, Weapon_info[obj->id].flash_vclip );
656 do_omega_stuff(&Objects[parent], position, obj);
661 if (Objects[parent].type == OBJ_PLAYER) {
662 if (weapon_type == FUSION_ID) {
664 if (Fusion_charge <= 0)
665 obj->ctype.laser_info.multiplier = F1_0;
666 else if (Fusion_charge <= 4*F1_0)
667 obj->ctype.laser_info.multiplier = F1_0 + Fusion_charge/2;
669 obj->ctype.laser_info.multiplier = 4*F1_0;
671 } else if ((weapon_type >= LASER_ID && weapon_type <= MAX_SUPER_LASER_LEVEL) && (Players[Objects[parent].id].flags & PLAYER_FLAGS_QUAD_LASERS))
672 obj->ctype.laser_info.multiplier = F1_0*3/4;
673 else if (weapon_type == GUIDEDMISS_ID) {
674 if (parent==Players[Player_num].objnum) {
675 Guided_missile[Player_num]= obj;
676 Guided_missile_sig[Player_num] = obj->signature;
677 if (Newdemo_state==ND_STATE_RECORDING)
678 newdemo_record_guided_start();
683 // Make children of smart bomb bounce so if they hit a wall right away, they
684 // won't detonate. The frame interval code will clear this bit after 1/2 second.
685 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))
686 obj->mtype.phys_info.flags |= PF_BOUNCE;
688 if (Weapon_info[weapon_type].render_type == WEAPON_RENDER_POLYMODEL)
689 laser_length = Polygon_models[obj->rtype.pobj_info.model_num].rad * 2;
691 if (weapon_type == FLARE_ID)
692 obj->mtype.phys_info.flags |= PF_STICK; //this obj sticks to walls
694 obj->shields = Weapon_info[obj->id].strength[Difficulty_level];
696 // Fill in laser-specific data
698 obj->lifeleft = Weapon_info[obj->id].lifetime;
699 obj->ctype.laser_info.parent_type = Objects[parent].type;
700 obj->ctype.laser_info.parent_signature = Objects[parent].signature;
701 obj->ctype.laser_info.parent_num = parent;
703 // Assign parent type to highest level creator. This propagates parent type down from
704 // the original creator through weapons which create children of their own (ie, smart missile)
705 if (Objects[parent].type == OBJ_WEAPON) {
706 int highest_parent = parent;
710 while ((count++ < 10) && (Objects[highest_parent].type == OBJ_WEAPON)) {
713 next_parent = Objects[highest_parent].ctype.laser_info.parent_num;
714 if (Objects[next_parent].signature != Objects[highest_parent].ctype.laser_info.parent_signature)
715 break; // Probably means parent was killed. Just continue.
717 if (next_parent == highest_parent) {
718 Int3(); // Hmm, object is parent of itself. This would seem to be bad, no?
722 highest_parent = next_parent;
724 obj->ctype.laser_info.parent_num = highest_parent;
725 obj->ctype.laser_info.parent_type = Objects[highest_parent].type;
726 obj->ctype.laser_info.parent_signature = Objects[highest_parent].signature;
730 // Create orientation matrix so we can look from this pov
731 // Homing missiles also need an orientation matrix so they know if they can make a turn.
732 if ((obj->render_type == RT_POLYOBJ) || (Weapon_info[obj->id].homing_flag))
733 vm_vector_2_matrix( &obj->orient,direction, &Objects[parent].orient.uvec ,NULL);
735 if (( &Objects[parent] != Viewer ) && (Objects[parent].type != OBJ_WEAPON)) {
737 if (Weapon_info[obj->id].flash_vclip > -1 )
738 object_create_muzzle_flash( obj->segnum, &obj->pos, Weapon_info[obj->id].flash_size, Weapon_info[obj->id].flash_vclip );
742 if (Weapon_info[obj->id].flash_sound > -1 ) {
744 if ( parent == (Viewer-Objects) ) {
745 if (weapon_type == VULCAN_ID) // Make your own vulcan gun 1/2 as loud.
747 digi_play_sample( Weapon_info[obj->id].flash_sound, volume );
749 digi_link_sound_to_pos( Weapon_info[obj->id].flash_sound, obj->segnum, 0, &obj->pos, 0, volume );
754 // Fire the laser from the gun tip so that the back end of the laser bolt is at the gun tip.
755 // Move 1 frame, so that the end-tip of the laser is touching the gun barrel.
756 // This also jitters the laser a bit so that it doesn't alias.
757 // Don't do for weapons created by weapons.
758 if ((Objects[parent].type == OBJ_PLAYER) && (Weapon_info[weapon_type].render_type != WEAPON_RENDER_NONE) && (weapon_type != FLARE_ID)) {
762 vm_vec_scale_add( &end_pos, &obj->pos, direction, Laser_offset+(laser_length/2) );
763 end_segnum = find_point_seg(&end_pos, obj->segnum);
764 if (end_segnum != obj->segnum) {
765 // mprintf(0, "Warning: Laser tip not in same segment as player.\n");
766 if (end_segnum != -1) {
768 obj_relink(obj-Objects, end_segnum);
770 mprintf((0, "Warning: Laser tip outside mine. Laser not being moved to end of gun.\n"));
775 // Here's where to fix the problem with objects which are moving backwards imparting higher velocity to their weaponfire.
776 // Find out if moving backwards.
777 if ((weapon_type == PROXIMITY_ID) || (weapon_type == SUPERPROX_ID)) {
778 parent_speed = vm_vec_mag_quick(&Objects[parent].mtype.phys_info.velocity);
779 if (vm_vec_dot(&Objects[parent].mtype.phys_info.velocity, &Objects[parent].orient.fvec) < 0)
780 parent_speed = -parent_speed;
784 weapon_speed = Weapon_info[obj->id].speed[Difficulty_level];
785 if (Weapon_info[obj->id].speedvar != 128) {
788 // Get a scale factor between speedvar% and 1.0.
789 randval = F1_0 - ((d_rand() * Weapon_info[obj->id].speedvar) >> 6);
790 weapon_speed = fixmul(weapon_speed, randval);
793 // Ugly hack (too bad we're on a deadline), for homing missiles dropped by smart bomb, start them out slower.
794 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))
797 if (Weapon_info[obj->id].thrust != 0)
800 vm_vec_copy_scale( &obj->mtype.phys_info.velocity, direction, weapon_speed + parent_speed );
803 if (Weapon_info[weapon_type].thrust != 0) {
804 obj->mtype.phys_info.thrust = obj->mtype.phys_info.velocity;
805 vm_vec_scale(&obj->mtype.phys_info.thrust, fixdiv(Weapon_info[obj->id].thrust, weapon_speed+parent_speed));
808 if ((obj->type == OBJ_WEAPON) && (obj->id == FLARE_ID))
809 obj->lifeleft += (d_rand()-16384) << 2; // add in -2..2 seconds
811 // mprintf( 0, "Weapon speed = %.1f (%.1f)\n", f2fl(Weapon_info[obj->id].speed[Difficulty_level] + parent_speed), f2fl(parent_speed) );
816 // -----------------------------------------------------------------------------------------------------------
817 // Calls Laser_create_new, but takes care of the segment and point computation for you.
818 int Laser_create_new_easy( vms_vector * direction, vms_vector * position, int parent, int weapon_type, int make_sound )
822 object *pobjp = &Objects[parent];
825 // Find segment containing laser fire position. If the robot is straddling a segment, the position from
826 // which it fires may be in a different segment, which is bad news for find_vector_intersection. So, cast
827 // a ray from the object center (whose segment we know) to the laser position. Then, in the call to Laser_create_new
828 // use the data returned from this call to find_vector_intersection.
829 // Note that while find_vector_intersection is pretty slow, it is not terribly slow if the destination point is
830 // in the same segment as the source point.
833 fq.startseg = pobjp->segnum;
836 fq.thisobjnum = pobjp-Objects;
837 fq.ignore_obj_list = NULL;
838 fq.flags = FQ_TRANSWALL | FQ_CHECK_OBJS; //what about trans walls???
840 fate = find_vector_intersection(&fq, &hit_data);
841 if (fate != HIT_NONE || hit_data.hit_seg==-1) {
842 mprintf((1, "Warning: Laser from parent=%i stuck in wall or object, didn't fire!\n", parent));
846 return Laser_create_new( direction, &hit_data.hit_pnt, hit_data.hit_seg, parent, weapon_type, make_sound );
850 int Muzzle_queue_index = 0;
852 muzzle_info Muzzle_data[MUZZLE_QUEUE_MAX];
854 // -----------------------------------------------------------------------------------------------------------
855 // Determine if two objects are on a line of sight. If so, return true, else return false.
857 int object_to_object_visibility(object *obj1, object *obj2, int trans_type)
864 fq.startseg = obj1->segnum;
867 fq.thisobjnum = obj1-Objects;
868 fq.ignore_obj_list = NULL;
869 fq.flags = trans_type;
871 fate = find_vector_intersection(&fq, &hit_data);
873 if (fate == HIT_WALL)
875 else if (fate == HIT_NONE)
878 Int3(); // Contact Mike: Oops, what happened? What is fate?
879 // 2 = hit object (impossible), 3 = bad starting point (bad)
884 fix Min_trackable_dot = MIN_TRACKABLE_DOT;
886 // -----------------------------------------------------------------------------------------------------------
887 // Return true if weapon *tracker is able to track object Objects[track_goal], else return false.
888 // In order for the object to be trackable, it must be within a reasonable turning radius for the missile
889 // and it must not be obstructed by a wall.
890 int object_is_trackable(int track_goal, object *tracker, fix *dot)
892 vms_vector vector_to_goal;
895 if (track_goal == -1)
898 if (Game_mode & GM_MULTI_COOP)
901 objp = &Objects[track_goal];
903 // Don't track player if he's cloaked.
904 if ((track_goal == Players[Player_num].objnum) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED))
907 // Can't track AI object if he's cloaked.
908 if (objp->type == OBJ_ROBOT) {
909 if (objp->ctype.ai_info.CLOAKED)
911 // Your missiles don't track your escort.
912 if (Robot_info[objp->id].companion)
913 if (tracker->ctype.laser_info.parent_type == OBJ_PLAYER)
916 vm_vec_sub(&vector_to_goal, &objp->pos, &tracker->pos);
917 vm_vec_normalize_quick(&vector_to_goal);
918 *dot = vm_vec_dot(&vector_to_goal, &tracker->orient.fvec);
920 if ((*dot < Min_trackable_dot) && (*dot > F1_0*9/10)) {
921 // -- mprintf((0, "."));
922 vm_vec_normalize(&vector_to_goal);
923 *dot = vm_vec_dot(&vector_to_goal, &tracker->orient.fvec);
926 //mprintf((0, " OIT: dot=%5.2f ", f2fl(dot)));
928 // mprintf((0, "object_is_trackable: [%3i] %7.3f, min = %7.3f\n", track_goal, f2fl(dot), f2fl(Min_trackable_dot)));
930 if (*dot >= Min_trackable_dot) {
932 // dot is in legal range, now see if object is visible
933 rval = object_to_object_visibility(tracker, objp, FQ_TRANSWALL);
934 //mprintf((0, " TRACK "));
937 //mprintf((0, " LOST! "));
943 extern int Robots_kill_robots_cheat;
945 // --------------------------------------------------------------------------------------------
946 int call_find_homing_object_complete(object *tracker, vms_vector *curpos)
948 if (Game_mode & GM_MULTI) {
949 if (tracker->ctype.laser_info.parent_type == OBJ_PLAYER) {
950 // It's fired by a player, so if robots present, track robot, else track player.
951 if (Game_mode & GM_MULTI_COOP)
952 return find_homing_object_complete( curpos, tracker, OBJ_ROBOT, -1);
954 return find_homing_object_complete( curpos, tracker, OBJ_PLAYER, OBJ_ROBOT);
958 if (Robots_kill_robots_cheat)
959 goal2_type = OBJ_ROBOT;
960 Assert(tracker->ctype.laser_info.parent_type == OBJ_ROBOT);
961 return find_homing_object_complete(curpos, tracker, OBJ_PLAYER, goal2_type);
964 return find_homing_object_complete( curpos, tracker, OBJ_ROBOT, -1);
967 // --------------------------------------------------------------------------------------------
968 // Find object to home in on.
969 // Scan list of objects rendered last frame, find one that satisfies function of nearness to center and distance.
970 int find_homing_object(vms_vector *curpos, object *tracker)
973 fix max_dot = -F1_0*2;
974 int best_objnum = -1;
976 // Contact Mike: This is a bad and stupid thing. Who called this routine with an illegal laser type??
977 Assert((Weapon_info[tracker->id].homing_flag) || (tracker->id == OMEGA_ID));
979 // Find an object to track based on game mode (eg, whether in network play) and who fired it.
981 if (Game_mode & GM_MULTI)
982 return call_find_homing_object_complete(tracker, curpos);
984 int cur_min_trackable_dot;
986 cur_min_trackable_dot = MIN_TRACKABLE_DOT;
987 if ((tracker->type == OBJ_WEAPON) && (tracker->id == OMEGA_ID))
988 cur_min_trackable_dot = OMEGA_MIN_TRACKABLE_DOT;
990 // Not in network mode. If not fired by player, then track player.
991 if (tracker->ctype.laser_info.parent_num != Players[Player_num].objnum) {
992 if (!(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED))
993 best_objnum = ConsoleObject - Objects;
996 fix dist, max_trackable_dist;
998 // Find the window which has the forward view.
999 for (i=0; i<MAX_RENDERED_WINDOWS; i++)
1000 if (Window_rendered_data[i].frame >= FrameCount-1)
1001 if (Window_rendered_data[i].viewer == ConsoleObject)
1002 if (!Window_rendered_data[i].rear_view) {
1007 // Couldn't find suitable view from this frame, so do complete search.
1008 if (window_num == -1) {
1009 mprintf((0, "Note: Calling find_homing_object_complete because no suitable rendered window.\n"));
1010 return call_find_homing_object_complete(tracker, curpos);
1013 max_trackable_dist = MAX_TRACKABLE_DIST;
1014 if (tracker->id == OMEGA_ID)
1015 max_trackable_dist = OMEGA_MAX_TRACKABLE_DIST;
1017 // Not in network mode and fired by player.
1018 for (i=Window_rendered_data[window_num].num_objects-1; i>=0; i--) {
1020 vms_vector vec_to_curobj;
1021 int objnum = Window_rendered_data[window_num].rendered_objects[i];
1022 object *curobjp = &Objects[objnum];
1024 if (objnum == Players[Player_num].objnum)
1027 // Can't track AI object if he's cloaked.
1028 if (curobjp->type == OBJ_ROBOT) {
1029 if (curobjp->ctype.ai_info.CLOAKED)
1032 // Your missiles don't track your escort.
1033 if (Robot_info[curobjp->id].companion)
1034 if (tracker->ctype.laser_info.parent_type == OBJ_PLAYER)
1038 vm_vec_sub(&vec_to_curobj, &curobjp->pos, curpos);
1039 dist = vm_vec_normalize_quick(&vec_to_curobj);
1040 if (dist < max_trackable_dist) {
1041 dot = vm_vec_dot(&vec_to_curobj, &tracker->orient.fvec);
1043 // mprintf(0, "Object %i: dist = %7.3f, dot = %7.3f\n", objnum, f2fl(dist), f2fl(dot));
1045 // Note: This uses the constant, not-scaled-by-frametime value, because it is only used
1046 // to determine if an object is initially trackable. find_homing_object is called on subsequent
1047 // frames to determine if the object remains trackable.
1048 // mprintf((0, "find_homing_object: [%3i] %7.3f, min = %7.3f\n", curobjp-Objects, f2fl(dot), f2fl(MIN_TRACKABLE_DOT)));
1049 if (dot > cur_min_trackable_dot) {
1050 if (dot > max_dot) {
1051 if (object_to_object_visibility(tracker, &Objects[objnum], FQ_TRANSWALL)) {
1053 best_objnum = objnum;
1056 } else if (dot > F1_0 - (F1_0 - cur_min_trackable_dot)*2) {
1057 vm_vec_normalize(&vec_to_curobj);
1058 dot = vm_vec_dot(&vec_to_curobj, &tracker->orient.fvec);
1059 if (dot > cur_min_trackable_dot) {
1060 if (dot > max_dot) {
1061 if (object_to_object_visibility(tracker, &Objects[objnum], FQ_TRANSWALL)) {
1063 best_objnum = objnum;
1073 // mprintf(0, "Selecting object #%i\n=n", best_objnum);
1078 // --------------------------------------------------------------------------------------------
1079 // Find object to home in on.
1080 // Scan list of objects rendered last frame, find one that satisfies function of nearness to center and distance.
1081 // Can track two kinds of objects. If you are only interested in one type, set track_obj_type2 to NULL
1082 // Always track proximity bombs. --MK, 06/14/95
1083 // Make homing objects not track parent's prox bombs.
1084 int find_homing_object_complete(vms_vector *curpos, object *tracker, int track_obj_type1, int track_obj_type2)
1087 fix max_dot = -F1_0*2;
1088 int best_objnum = -1;
1089 fix max_trackable_dist;
1090 fix min_trackable_dot;
1092 // Contact Mike: This is a bad and stupid thing. Who called this routine with an illegal laser type??
1093 Assert((Weapon_info[tracker->id].homing_flag) || (tracker->id == OMEGA_ID));
1095 max_trackable_dist = MAX_TRACKABLE_DIST;
1096 min_trackable_dot = MIN_TRACKABLE_DOT;
1098 if (tracker->id == OMEGA_ID) {
1099 max_trackable_dist = OMEGA_MAX_TRACKABLE_DIST;
1100 min_trackable_dot = OMEGA_MIN_TRACKABLE_DOT;
1103 for (objnum=0; objnum<=Highest_object_index; objnum++) {
1104 int is_proximity = 0;
1106 vms_vector vec_to_curobj;
1107 object *curobjp = &Objects[objnum];
1109 if ((curobjp->type != track_obj_type1) && (curobjp->type != track_obj_type2))
1111 if ((curobjp->type == OBJ_WEAPON) && ((curobjp->id == PROXIMITY_ID) || (curobjp->id == SUPERPROX_ID))) {
1112 if (curobjp->ctype.laser_info.parent_signature != tracker->ctype.laser_info.parent_signature)
1120 if (objnum == tracker->ctype.laser_info.parent_num) // Don't track shooter
1123 // Don't track cloaked players.
1124 if (curobjp->type == OBJ_PLAYER)
1126 if (Players[curobjp->id].flags & PLAYER_FLAGS_CLOAKED)
1128 // Don't track teammates in team games
1130 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)))
1135 // Can't track AI object if he's cloaked.
1136 if (curobjp->type == OBJ_ROBOT) {
1137 if (curobjp->ctype.ai_info.CLOAKED)
1140 // Your missiles don't track your escort.
1141 if (Robot_info[curobjp->id].companion)
1142 if (tracker->ctype.laser_info.parent_type == OBJ_PLAYER)
1146 vm_vec_sub(&vec_to_curobj, &curobjp->pos, curpos);
1147 dist = vm_vec_mag_quick(&vec_to_curobj);
1149 if (dist < max_trackable_dist) {
1150 vm_vec_normalize_quick(&vec_to_curobj);
1151 dot = vm_vec_dot(&vec_to_curobj, &tracker->orient.fvec);
1153 dot = ((dot << 3) + dot) >> 3; // I suspect Watcom would be too stupid to figure out the obvious...
1155 // Note: This uses the constant, not-scaled-by-frametime value, because it is only used
1156 // to determine if an object is initially trackable. find_homing_object is called on subsequent
1157 // frames to determine if the object remains trackable.
1158 // mprintf((0, "fho_complete: [%3i] %7.3f, min = %7.3f\n", curobjp-Objects, f2fl(dot), f2fl(MIN_TRACKABLE_DOT)));
1159 if (dot > min_trackable_dot) {
1160 // mprintf(0, "Object %i: dist = %7.3f, dot = %7.3f\n", objnum, f2fl(dist), f2fl(dot));
1161 if (dot > max_dot) {
1162 if (object_to_object_visibility(tracker, &Objects[objnum], FQ_TRANSWALL)) {
1164 best_objnum = objnum;
1172 // -- mprintf((0, "Selecting object #%i in find_homing_object_complete\n\n", best_objnum));
1177 // ------------------------------------------------------------------------------------------------------------
1178 // See if legal to keep tracking currently tracked object. If not, see if another object is trackable. If not, return -1,
1179 // else return object number of tracking object.
1180 // Computes and returns a fairly precise dot product.
1181 int track_track_goal(int track_goal, object *tracker, fix *dot)
1183 // Every 8 frames for each object, scan all objects.
1184 if (object_is_trackable(track_goal, tracker, dot) && ((((tracker-Objects) ^ FrameCount) % 8) != 0)) {
1185 //mprintf((0, "ttg: QO"));
1187 } else if ((((tracker-Objects) ^ FrameCount) % 4) == 0) {
1190 // If player fired missile, then search for an object, if not, then give up.
1191 if (Objects[tracker->ctype.laser_info.parent_num].type == OBJ_PLAYER) {
1194 if (track_goal == -1)
1196 if (Game_mode & GM_MULTI)
1198 if (Game_mode & GM_MULTI_COOP)
1199 rval = find_homing_object_complete( &tracker->pos, tracker, OBJ_ROBOT, -1);
1200 else if (Game_mode & GM_MULTI_ROBOTS) // Not cooperative, if robots, track either robot or player
1201 rval = find_homing_object_complete( &tracker->pos, tracker, OBJ_PLAYER, OBJ_ROBOT);
1202 else // Not cooperative and no robots, track only a player
1203 rval = find_homing_object_complete( &tracker->pos, tracker, OBJ_PLAYER, -1);
1206 rval = find_homing_object_complete(&tracker->pos, tracker, OBJ_PLAYER, OBJ_ROBOT);
1210 goal_type = Objects[tracker->ctype.laser_info.track_goal].type;
1211 if ((goal_type == OBJ_PLAYER) || (goal_type == OBJ_ROBOT))
1212 rval = find_homing_object_complete(&tracker->pos, tracker, goal_type, -1);
1218 int goal_type, goal2_type = -1;
1220 if (Robots_kill_robots_cheat)
1221 goal2_type = OBJ_ROBOT;
1223 if (track_goal == -1)
1224 rval = find_homing_object_complete(&tracker->pos, tracker, OBJ_PLAYER, goal2_type);
1226 goal_type = Objects[tracker->ctype.laser_info.track_goal].type;
1227 rval = find_homing_object_complete(&tracker->pos, tracker, goal_type, goal2_type);
1231 Assert(rval != -2); // This means it never got set which is bad! Contact Mike.
1235 //if (track_goal != -1)
1236 // mprintf((0, "Object %i not tracking anything.\n", tracker-Objects));
1241 //-------------- Initializes a laser after Fire is pressed -----------------
1243 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)
1246 vms_vector LaserPos, LaserDir;
1249 vms_vector gun_point, *pnt;
1253 create_awareness_event(obj, PA_WEAPON_WALL_COLLISION);
1255 // Find the initial position of the laser
1256 pnt = &Player_ship->gun_points[gun_num];
1258 vm_copy_transpose_matrix(&m,&obj->orient);
1259 vm_vec_rotate(&gun_point,pnt,&m);
1261 vm_vec_add(&LaserPos,&obj->pos,&gun_point);
1263 // If supposed to fire at a delayed time (delay_time), then move this point backwards.
1265 vm_vec_scale_add2(&LaserPos, &obj->orient.fvec, -fixmul(delay_time, Weapon_info[laser_type].speed[Difficulty_level]));
1267 // do_muzzle_stuff(obj, &Pos);
1269 //--------------- Find LaserPos and LaserSeg ------------------
1271 fq.startseg = obj->segnum;
1274 fq.thisobjnum = obj-Objects;
1275 fq.ignore_obj_list = NULL;
1276 fq.flags = FQ_CHECK_OBJS | FQ_IGNORE_POWERUPS;
1278 Fate = find_vector_intersection(&fq, &hit_data);
1280 LaserSeg = hit_data.hit_seg;
1282 if (LaserSeg == -1) //some sort of annoying error
1285 //SORT OF HACK... IF ABOVE WAS CORRECT THIS WOULDNT BE NECESSARY.
1286 if ( vm_vec_dist_quick(&LaserPos, &obj->pos) > 0x50000 )
1289 if (Fate==HIT_WALL) {
1291 mprintf((0, "Your DELAYED laser is stuck thru a wall!\n" ));
1293 mprintf((0, "Your laser is stuck thru a wall!\n" ));
1297 if (Fate==HIT_OBJECT) {
1298 // if ( Objects[hit_data.hit_object].type == OBJ_ROBOT )
1299 // Objects[hit_data.hit_object].flags |= OF_SHOULD_BE_DEAD;
1300 mprintf((0, "Your laser is stuck in an object!\n" ));
1301 // if ( Objects[hit_data.hit_object].type != OBJ_POWERUP )
1303 //as of 12/6/94, we don't care if the laser is stuck in an object. We
1304 //just fire away normally
1307 // Now, make laser spread out.
1308 LaserDir = obj->orient.fvec;
1309 if ((spreadr != 0) || (spreadu != 0)) {
1310 vm_vec_scale_add2(&LaserDir, &obj->orient.rvec, spreadr);
1311 vm_vec_scale_add2(&LaserDir, &obj->orient.uvec, spreadu);
1314 objnum = Laser_create_new( &LaserDir, &LaserPos, LaserSeg, obj-Objects, laser_type, make_sound );
1316 // Omega cannon is a hack, not surprisingly. Don't want to do the rest of this stuff.
1317 if (laser_type == OMEGA_ID)
1324 if (laser_type==GUIDEDMISS_ID && Multi_is_guided) {
1325 mprintf ((0,"Guided missile %s activated!\n",Players[obj->id].callsign));
1326 Guided_missile[obj->id]=&Objects[objnum];
1332 if (laser_type == CONCUSSION_ID ||
1333 laser_type == HOMING_ID ||
1334 laser_type == SMART_ID ||
1335 laser_type == MEGA_ID ||
1336 laser_type == FLASH_ID ||
1337 //laser_type == GUIDEDMISS_ID ||
1338 //laser_type == SUPERPROX_ID ||
1339 laser_type == MERCURY_ID ||
1340 laser_type == EARTHSHAKER_ID)
1341 if (Missile_viewer == NULL && obj->id==Player_num)
1342 Missile_viewer = &Objects[objnum];
1344 // If this weapon is supposed to be silent, set that bit!
1346 Objects[objnum].flags |= OF_SILENT;
1348 // If this weapon is supposed to be silent, set that bit!
1350 Objects[objnum].flags |= OF_HARMLESS;
1352 // If the object firing the laser is the player, then indicate the laser object so robots can dodge.
1353 // New by MK on 6/8/95, don't let robots evade proximity bombs, thereby decreasing uselessness of bombs.
1354 if ((obj == ConsoleObject) && ((Objects[objnum].id != PROXIMITY_ID) && (Objects[objnum].id != SUPERPROX_ID)))
1355 Player_fired_laser_this_frame = objnum;
1357 if (Weapon_info[laser_type].homing_flag) {
1358 if (obj == ConsoleObject)
1360 Objects[objnum].ctype.laser_info.track_goal = find_homing_object(&LaserPos, &Objects[objnum]);
1362 Network_laser_track = Objects[objnum].ctype.laser_info.track_goal;
1366 else // Some other player shot the homing thing
1368 Assert(Game_mode & GM_MULTI);
1369 Objects[objnum].ctype.laser_info.track_goal = Network_laser_track;
1372 // mprintf((0, "Selecting object #%i in find_homing_object_complete\n", Network_laser_track));
1376 // -----------------------------------------------------------------------------------------------------------
1377 void Laser_player_fire_spread(object *obj, int laser_type, int gun_num, fix spreadr, fix spreadu, int make_sound, int harmless)
1379 Laser_player_fire_spread_delay(obj, laser_type, gun_num, spreadr, spreadu, 0, make_sound, harmless);
1383 // -----------------------------------------------------------------------------------------------------------
1384 void Laser_player_fire(object *obj, int laser_type, int gun_num, int make_sound, int harmless)
1386 Laser_player_fire_spread(obj, laser_type, gun_num, 0, 0, make_sound, harmless);
1389 // -----------------------------------------------------------------------------------------------------------
1390 void Flare_create(object *obj)
1394 energy_usage = Weapon_info[FLARE_ID].energy_usage;
1396 if (Difficulty_level < 2)
1397 energy_usage = fixmul(energy_usage, i2f(Difficulty_level+2)/4);
1399 // MK, 11/04/95: Allowed to fire flare even if no energy.
1400 // -- if (Players[Player_num].energy >= energy_usage) {
1401 Players[Player_num].energy -= energy_usage;
1403 if (Players[Player_num].energy <= 0) {
1404 Players[Player_num].energy = 0;
1405 // -- auto_select_weapon(0);
1408 Laser_player_fire( obj, FLARE_ID, 6, 1, 0);
1411 if (Game_mode & GM_MULTI) {
1412 Network_laser_fired = 1;
1413 Network_laser_gun = FLARE_ADJUST;
1414 Network_laser_flags = 0;
1415 Network_laser_level = 0;
1422 #define HOMING_MISSILE_SCALE 16
1424 #define LIMIT_HOMERS 1
1425 #define HOMER_MAX_FPS 30
1426 #define HOMER_MIN_DELAY (1000 / HOMER_MAX_FPS)
1428 //--------------------------------------------------------------------
1429 // Set object *objp's orientation to (or towards if I'm ambitious) its velocity.
1430 void homing_missile_turn_towards_velocity(object *objp, vms_vector *norm_vel)
1432 vms_vector new_fvec;
1435 static time_t last_time = -1;
1436 static int nFrames = 1;
1437 time_t this_time, delta_time;
1440 if (last_time == -1) {
1441 last_time = clock ();
1442 frame_time = FrameTime;
1445 this_time = clock ();
1446 delta_time = this_time - last_time;
1447 if (delta_time < HOMER_MIN_DELAY) {
1450 fps = (1000 + delta_time / 2) / delta_time;
1451 frame_time = fps ? (f1_0 + fps / 2) / fps : f1_0;
1452 // frame_time /= nFrames;
1454 last_time = this_time;
1458 fix frame_time = FrameTime;
1461 new_fvec = *norm_vel;
1463 vm_vec_scale(&new_fvec, frame_time * HOMING_MISSILE_SCALE);
1464 vm_vec_add2(&new_fvec, &objp->orient.fvec);
1465 vm_vec_normalize_quick(&new_fvec);
1467 // if ((norm_vel->x == 0) && (norm_vel->y == 0) && (norm_vel->z == 0))
1470 vm_vector_2_matrix(&objp->orient, &new_fvec, NULL, NULL);
1473 //-------------------------------------------------------------------------------------------
1474 //sequence this laser object for this _frame_ (underscores added here to aid MK in his searching!)
1475 void Laser_do_weapon_sequence(object *obj)
1477 Assert(obj->control_type == CT_WEAPON);
1479 // Ok, this is a big hack by MK.
1480 // If you want an object to last for exactly one frame, then give it a lifeleft of ONE_FRAME_TIME
1481 if (obj->lifeleft == ONE_FRAME_TIME) {
1482 if (Game_mode & GM_MULTI)
1483 obj->lifeleft = OMEGA_MULTI_LIFELEFT;
1486 obj->render_type = RT_NONE;
1489 if (obj->lifeleft < 0 ) { // We died of old age
1490 obj->flags |= OF_SHOULD_BE_DEAD;
1491 if ( Weapon_info[obj->id].damage_radius )
1492 explode_badass_weapon(obj,&obj->pos);
1496 //delete weapons that are not moving
1497 if ( !((FrameCount ^ obj->signature) & 3) &&
1498 (obj->id != FLARE_ID) &&
1499 (Weapon_info[obj->id].speed[Difficulty_level] > 0) &&
1500 (vm_vec_mag_quick(&obj->mtype.phys_info.velocity) < F2_0)) {
1501 obj_delete(obj-Objects);
1505 if ( obj->id == FUSION_ID ) { //always set fusion weapon to max vel
1507 vm_vec_normalize_quick(&obj->mtype.phys_info.velocity);
1509 vm_vec_scale(&obj->mtype.phys_info.velocity, Weapon_info[obj->id].speed[Difficulty_level]);
1512 // -- // The Super Spreadfire (Helix) blobs travel in a sinusoidal path. That is accomplished
1513 // -- // by modifying velocity (direction) in the frame interval.
1514 // -- if (obj->id == SSPREADFIRE_ID) {
1515 // -- fix age, sinval, cosval;
1516 // -- vms_vector p, newp;
1519 // -- speed = vm_vec_mag_quick(&obj->phys_info.velocity);
1521 // -- age = Weapon_info[obj->id].lifetime - obj->lifeleft;
1523 // -- fix_fast_sincos(age, &sinval, &cosval);
1525 // -- // Note: Below code assumes x=1, y=0. Need to scale this for values around a circle for 5 helix positions.
1526 // -- p.x = cosval << 3;
1527 // -- p.y = sinval << 3;
1530 // -- vm_vec_rotate(&newp, &p, &obj->orient);
1532 // -- vm_vec_add(&goal_point, &obj->pos, &newp);
1534 // -- vm_vec_sub(&vec_to_goal, &goal_point, obj
1538 // For homing missiles, turn towards target. (unless it's the guided missile)
1539 if (Weapon_info[obj->id].homing_flag && !(obj->id==GUIDEDMISS_ID && obj->ctype.laser_info.parent_type==OBJ_PLAYER && obj==Guided_missile[Objects[obj->ctype.laser_info.parent_num].id] && obj->signature==Guided_missile[Objects[obj->ctype.laser_info.parent_num].id]->signature))
1541 vms_vector vector_to_object, temp_vec;
1543 fix speed, max_speed;
1545 // For first 1/2 second of life, missile flies straight.
1546 if (obj->ctype.laser_info.creation_time + HOMING_MISSILE_STRAIGHT_TIME < GameTime) {
1548 int track_goal = obj->ctype.laser_info.track_goal;
1550 //mprintf((0, "%5i: mtd=%5.2f", FrameCount, f2fl(Min_trackable_dot)));
1552 // If it's time to do tracking, then it's time to grow up, stop bouncing and start exploding!.
1553 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)) {
1554 // if (obj->mtype.phys_info.flags & PF_BOUNCE) mprintf(0, "Debouncing smart child %i\n", obj-Objects);
1555 obj->mtype.phys_info.flags &= ~PF_BOUNCE;
1558 // Make sure the object we are tracking is still trackable.
1559 track_goal = track_track_goal(track_goal, obj, &dot);
1561 //mprintf((0, " after ttg=%3i ", track_goal));
1563 if (track_goal == Players[Player_num].objnum) {
1566 dist_to_player = vm_vec_dist_quick(&obj->pos, &Objects[track_goal].pos);
1567 if ((dist_to_player < Players[Player_num].homing_object_dist) || (Players[Player_num].homing_object_dist < 0))
1568 Players[Player_num].homing_object_dist = dist_to_player;
1572 if (track_goal != -1) {
1573 vm_vec_sub(&vector_to_object, &Objects[track_goal].pos, &obj->pos);
1575 vm_vec_normalize_quick(&vector_to_object);
1576 temp_vec = obj->mtype.phys_info.velocity;
1577 speed = vm_vec_normalize_quick(&temp_vec);
1578 max_speed = Weapon_info[obj->id].speed[Difficulty_level];
1579 if (speed+F1_0 < max_speed) {
1580 speed += fixmul(max_speed, FrameTime/2);
1581 if (speed > max_speed)
1585 // -- dot = vm_vec_dot(&temp_vec, &vector_to_object);
1586 //mprintf((0, " dot=%5.2f ", f2fl(dot)));
1587 vm_vec_add2(&temp_vec, &vector_to_object);
1588 // The boss' smart children track better...
1589 if (Weapon_info[obj->id].render_type != WEAPON_RENDER_POLYMODEL)
1590 vm_vec_add2(&temp_vec, &vector_to_object);
1591 vm_vec_normalize_quick(&temp_vec);
1592 obj->mtype.phys_info.velocity = temp_vec;
1593 vm_vec_scale(&obj->mtype.phys_info.velocity, speed);
1595 // Subtract off life proportional to amount turned.
1596 // For hardest turn, it will lose 2 seconds per second.
1598 fix lifelost, absdot;
1600 absdot = abs(F1_0 - dot);
1602 lifelost = fixmul(absdot*32, FrameTime);
1603 obj->lifeleft -= lifelost;
1604 // -- 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)));
1607 // Only polygon objects have visible orientation, so only they should turn.
1608 if (Weapon_info[obj->id].render_type == WEAPON_RENDER_POLYMODEL)
1609 homing_missile_turn_towards_velocity(obj, &temp_vec); // temp_vec is normalized velocity.
1613 //mprintf((0, "\n"));
1616 // Make sure weapon is not moving faster than allowed speed.
1620 weapon_speed = vm_vec_mag_quick(&obj->mtype.phys_info.velocity);
1621 if (weapon_speed > Weapon_info[obj->id].speed[Difficulty_level]) {
1622 // Only slow down if not allowed to move. Makes sense, huh? Allows proxbombs to get moved by physics force. --MK, 2/13/96
1623 if (Weapon_info[obj->id].speed[Difficulty_level]) {
1626 scale_factor = fixdiv(Weapon_info[obj->id].speed[Difficulty_level], weapon_speed);
1627 vm_vec_scale(&obj->mtype.phys_info.velocity, scale_factor);
1633 fix Last_laser_fired_time = 0;
1635 extern int Player_fired_laser_this_frame;
1639 // --------------------------------------------------------------------------------------------------
1640 // Assumption: This is only called by the actual console player, not for network players
1642 int do_laser_firing_player(void)
1644 player *plp = &Players[Player_num];
1646 int ammo_used,primary_ammo;
1651 static int Spreadfire_toggle=0;
1652 static int Helix_orientation = 0;
1657 weapon_index = Primary_weapon_to_weapon_info[Primary_weapon];
1658 energy_used = Weapon_info[weapon_index].energy_usage;
1659 if (Primary_weapon == OMEGA_INDEX)
1660 energy_used = 0; // Omega consumes energy when recharging, not when firing.
1662 if (Difficulty_level < 2)
1663 energy_used = fixmul(energy_used, i2f(Difficulty_level+2)/4);
1665 // MK, 01/26/96, Helix use 2x energy in multiplayer. bitmaps.tbl parm should have been reduced for single player.
1666 if (weapon_index == HELIX_INDEX)
1667 if (Game_mode & GM_MULTI)
1670 ammo_used = Weapon_info[weapon_index].ammo_usage;
1672 addval = 2*FrameTime;
1676 if ((Last_laser_fired_time + 2*FrameTime < GameTime) || (GameTime < Last_laser_fired_time))
1677 Next_laser_fire_time = GameTime;
1679 Last_laser_fired_time = GameTime;
1681 primary_ammo = (Primary_weapon == GAUSS_INDEX)?(plp->primary_ammo[VULCAN_INDEX]):(plp->primary_ammo[Primary_weapon]);
1683 if (!((plp->energy >= energy_used) && (primary_ammo >= ammo_used)))
1684 auto_select_weapon(0); // Make sure the player can fire from this weapon.
1691 while (Next_laser_fire_time <= GameTime) {
1692 if ((plp->energy >= energy_used) && (primary_ammo >= ammo_used)) {
1693 int laser_level, flags;
1696 if (Laser_rapid_fire!=0xBADA55)
1697 Next_laser_fire_time += Weapon_info[weapon_index].fire_wait;
1699 Next_laser_fire_time += F1_0/25;
1701 laser_level = Players[Player_num].laser_level;
1705 if (Primary_weapon == SPREADFIRE_INDEX) {
1706 if (Spreadfire_toggle)
1707 flags |= LASER_SPREADFIRE_TOGGLED;
1708 Spreadfire_toggle = !Spreadfire_toggle;
1711 if (Primary_weapon == HELIX_INDEX) {
1712 Helix_orientation++;
1713 flags |= ((Helix_orientation & LASER_HELIX_MASK) << LASER_HELIX_SHIFT);
1716 if (Players[Player_num].flags & PLAYER_FLAGS_QUAD_LASERS)
1717 flags |= LASER_QUAD;
1719 rval += do_laser_firing(Players[Player_num].objnum, Primary_weapon, laser_level, flags, nfires);
1721 plp->energy -= (energy_used * rval) / Weapon_info[weapon_index].fire_count;
1722 if (plp->energy < 0)
1725 if ((Primary_weapon == VULCAN_INDEX) || (Primary_weapon == GAUSS_INDEX)) {
1726 if (ammo_used > plp->primary_ammo[VULCAN_INDEX])
1727 plp->primary_ammo[VULCAN_INDEX] = 0;
1729 plp->primary_ammo[VULCAN_INDEX] -= ammo_used;
1732 auto_select_weapon(0); // Make sure the player can fire from this weapon.
1735 auto_select_weapon(0); // Make sure the player can fire from this weapon.
1736 Next_laser_fire_time = GameTime; // Prevents shots-to-fire from building up.
1737 break; // Couldn't fire weapon, so abort.
1740 //mprintf(0, " fires = %i\n", rval);
1742 Global_laser_firing_count = 0;
1747 // -- #define MAX_LIGHTNING_DISTANCE (F1_0*300)
1748 // -- #define MAX_LIGHTNING_BLOBS 16
1749 // -- #define LIGHTNING_BLOB_DISTANCE (MAX_LIGHTNING_DISTANCE/MAX_LIGHTNING_BLOBS)
1751 // -- #define LIGHTNING_BLOB_ID 13
1753 // -- #define LIGHTNING_TIME (F1_0/4)
1754 // -- #define LIGHTNING_DELAY (F1_0/8)
1756 // -- int Lightning_gun_num = 1;
1758 // -- fix Lightning_start_time = -F1_0*10, Lightning_last_time;
1760 // -- // --------------------------------------------------------------------------------------------------
1761 // -- // Return -1 if failed to create at least one blob. Else return index of last blob created.
1762 // -- int create_lightning_blobs(vms_vector *direction, vms_vector *start_pos, int start_segnum, int parent)
1766 // -- fvi_info hit_data;
1767 // -- vms_vector end_pos;
1768 // -- vms_vector norm_dir;
1770 // -- int num_blobs;
1771 // -- vms_vector tvec;
1772 // -- fix dist_to_hit_point;
1773 // -- vms_vector point_pos, delta_pos;
1775 // -- vms_vector *gun_pos;
1777 // -- vms_vector gun_pos2;
1779 // -- if (Players[Player_num].energy > F1_0)
1780 // -- Players[Player_num].energy -= F1_0;
1782 // -- if (Players[Player_num].energy <= F1_0) {
1783 // -- Players[Player_num].energy = 0;
1784 // -- auto_select_weapon(0);
1788 // -- norm_dir = *direction;
1790 // -- vm_vec_normalize_quick(&norm_dir);
1791 // -- vm_vec_scale_add(&end_pos, start_pos, &norm_dir, MAX_LIGHTNING_DISTANCE);
1793 // -- fq.p0 = start_pos;
1794 // -- fq.startseg = start_segnum;
1795 // -- fq.p1 = &end_pos;
1797 // -- fq.thisobjnum = parent;
1798 // -- fq.ignore_obj_list = NULL;
1799 // -- fq.flags = FQ_TRANSWALL | FQ_CHECK_OBJS;
1801 // -- fate = find_vector_intersection(&fq, &hit_data);
1802 // -- if (hit_data.hit_seg == -1) {
1803 // -- mprintf((1, "Warning: Lightning bolt has hit seg of -1.\n"));
1807 // -- dist_to_hit_point = vm_vec_mag(vm_vec_sub(&tvec, &hit_data.hit_pnt, start_pos));
1808 // -- num_blobs = dist_to_hit_point/LIGHTNING_BLOB_DISTANCE;
1810 // -- if (num_blobs > MAX_LIGHTNING_BLOBS)
1811 // -- num_blobs = MAX_LIGHTNING_BLOBS;
1813 // -- if (num_blobs < MAX_LIGHTNING_BLOBS/4)
1814 // -- num_blobs = MAX_LIGHTNING_BLOBS/4;
1816 // -- // Find the initial position of the laser
1817 // -- gun_pos = &Player_ship->gun_points[Lightning_gun_num];
1818 // -- vm_copy_transpose_matrix(&m,&Objects[parent].orient);
1819 // -- vm_vec_rotate(&gun_pos2, gun_pos, &m);
1820 // -- vm_vec_add(&point_pos, &Objects[parent].pos, &gun_pos2);
1822 // -- delta_pos = norm_dir;
1823 // -- vm_vec_scale(&delta_pos, dist_to_hit_point/num_blobs);
1825 // -- for (i=0; i<num_blobs; i++) {
1826 // -- int point_seg;
1829 // -- vm_vec_add2(&point_pos, &delta_pos);
1830 // -- point_seg = find_point_seg(&point_pos, start_segnum);
1831 // -- if (point_seg == -1) // Hey, we thought we were creating points on a line, but we left the mine!
1834 // -- objnum = Laser_create_new( direction, &point_pos, point_seg, parent, LIGHTNING_BLOB_ID, 0 );
1836 // -- if ( objnum < 0 ) {
1837 // -- mprintf((1, "Can't create lightning blob - Out of objects!\n" ));
1842 // -- obj = &Objects[objnum];
1844 // -- digi_play_sample( Weapon_info[obj->id].flash_sound, F1_0 );
1846 // -- // -- vm_vec_scale( &obj->mtype.phys_info.velocity, F1_0/2);
1848 // -- obj->lifeleft = (LIGHTNING_TIME + LIGHTNING_DELAY)/2;
1852 // -- return objnum;
1856 // -- // --------------------------------------------------------------------------------------------------
1857 // -- // Lightning Cannon.
1858 // -- // While being fired, creates path of blobs forward from player until it hits something.
1859 // -- // Up to MAX_LIGHTNING_BLOBS blobs, spaced LIGHTNING_BLOB_DISTANCE units apart.
1860 // -- // When the player releases the firing key, the blobs move forward.
1861 // -- void lightning_frame(void)
1863 // -- if ((GameTime - Lightning_start_time < LIGHTNING_TIME) && (GameTime - Lightning_start_time > 0)) {
1864 // -- if (GameTime - Lightning_last_time > LIGHTNING_DELAY) {
1865 // -- create_lightning_blobs(&ConsoleObject->orient.fvec, &ConsoleObject->pos, ConsoleObject->segnum, ConsoleObject-Objects);
1866 // -- Lightning_last_time = GameTime;
1871 // --------------------------------------------------------------------------------------------------
1872 // Object "objnum" fires weapon "weapon_num" of level "level". (Right now (9/24/94) level is used only for type 0 laser.
1873 // Flags are the player flags. For network mode, set to 0.
1874 // It is assumed that this is a player object (as in multiplayer), and therefore the gun positions are known.
1875 // Returns number of times a weapon was fired. This is typically 1, but might be more for low frame rates.
1876 // More than one shot is fired with a pseudo-delay so that players on show machines can fire (for themselves
1877 // or other players) often enough for things like the vulcan cannon.
1878 int do_laser_firing(int objnum, int weapon_num, int level, int flags, int nfires)
1880 object *objp = &Objects[objnum];
1882 switch (weapon_num) {
1886 Laser_offset = ((F1_0*2)*(d_rand()%8))/8;
1888 if (level <= MAX_LASER_LEVEL)
1889 weapon_num = LASER_ID + level;
1891 weapon_num = SUPER_LASER_ID + (level-MAX_LASER_LEVEL-1);
1893 Laser_player_fire( objp, weapon_num, 0, 1, 0);
1894 Laser_player_fire( objp, weapon_num, 1, 0, 0);
1896 if (flags & LASER_QUAD) {
1897 // hideous system to make quad laser 1.5x powerful as normal laser, make every other quad laser bolt harmless
1898 Laser_player_fire( objp, weapon_num, 2, 0, 0);
1899 Laser_player_fire( objp, weapon_num, 3, 0, 0);
1903 case VULCAN_INDEX: {
1904 // Only make sound for 1/4 of vulcan bullets.
1906 //if (d_rand() > 24576)
1908 Laser_player_fire_spread( objp, VULCAN_ID, 6, d_rand()/8 - 32767/16, d_rand()/8 - 32767/16, make_sound, 0);
1910 Laser_player_fire_spread( objp, VULCAN_ID, 6, d_rand()/8 - 32767/16, d_rand()/8 - 32767/16, 0, 0);
1912 Laser_player_fire_spread( objp, VULCAN_ID, 6, d_rand()/8 - 32767/16, d_rand()/8 - 32767/16, 0, 0);
1917 case SPREADFIRE_INDEX:
1918 if (flags & LASER_SPREADFIRE_TOGGLED) {
1919 Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, F1_0/16, 0, 0, 0);
1920 Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, -F1_0/16, 0, 0, 0);
1921 Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, 0, 0, 1, 0);
1923 Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, 0, F1_0/16, 0, 0);
1924 Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, 0, -F1_0/16, 0, 0);
1925 Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, 0, 0, 1, 0);
1930 Laser_player_fire( objp, PLASMA_ID, 0, 1, 0);
1931 Laser_player_fire( objp, PLASMA_ID, 1, 0, 0);
1933 Laser_player_fire_spread_delay( objp, PLASMA_ID, 0, 0, 0, FrameTime/2, 1, 0);
1934 Laser_player_fire_spread_delay( objp, PLASMA_ID, 1, 0, 0, FrameTime/2, 0, 0);
1938 case FUSION_INDEX: {
1939 vms_vector force_vec;
1941 // mprintf((0, "Fusion multiplier %f.\n", f2fl(Fusion_charge)));
1943 Laser_player_fire( objp, FUSION_ID, 0, 1, 0);
1944 Laser_player_fire( objp, FUSION_ID, 1, 1, 0);
1946 flags = (sbyte)(Fusion_charge >> 12);
1950 force_vec.x = -(objp->orient.fvec.x << 7);
1951 force_vec.y = -(objp->orient.fvec.y << 7);
1952 force_vec.z = -(objp->orient.fvec.z << 7);
1953 phys_apply_force(objp, &force_vec);
1955 force_vec.x = (force_vec.x >> 4) + d_rand() - 16384;
1956 force_vec.y = (force_vec.y >> 4) + d_rand() - 16384;
1957 force_vec.z = (force_vec.z >> 4) + d_rand() - 16384;
1958 phys_apply_rot(objp, &force_vec);
1962 case SUPER_LASER_INDEX: {
1963 int super_level = 3; //make some new kind of laser eventually
1964 Laser_player_fire( objp, super_level, 0, 1, 0);
1965 Laser_player_fire( objp, super_level, 1, 0, 0);
1967 if (flags & LASER_QUAD) {
1968 // hideous system to make quad laser 1.5x powerful as normal laser, make every other quad laser bolt harmless
1969 Laser_player_fire( objp, super_level, 2, 0, 0);
1970 Laser_player_fire( objp, super_level, 3, 0, 0);
1975 // Only make sound for 1/4 of vulcan bullets.
1977 //if (d_rand() > 24576)
1980 Laser_player_fire_spread( objp, GAUSS_ID, 6, (d_rand()/8 - 32767/16)/5, (d_rand()/8 - 32767/16)/5, make_sound, 0);
1982 Laser_player_fire_spread( objp, GAUSS_ID, 6, (d_rand()/8 - 32767/16)/5, (d_rand()/8 - 32767/16)/5, 0, 0);
1984 Laser_player_fire_spread( objp, GAUSS_ID, 6, (d_rand()/8 - 32767/16)/5, (d_rand()/8 - 32767/16)/5, 0, 0);
1991 fix spreadr,spreadu;
1992 helix_orient = (flags >> LASER_HELIX_SHIFT) & LASER_HELIX_MASK;
1993 switch(helix_orient) {
1995 case 0: spreadr = F1_0/16; spreadu = 0; break; // Vertical
1996 case 1: spreadr = F1_0/17; spreadu = F1_0/42; break; // 22.5 degrees
1997 case 2: spreadr = F1_0/22; spreadu = F1_0/22; break; // 45 degrees
1998 case 3: spreadr = F1_0/42; spreadu = F1_0/17; break; // 67.5 degrees
1999 case 4: spreadr = 0; spreadu = F1_0/16; break; // 90 degrees
2000 case 5: spreadr = -F1_0/42; spreadu = F1_0/17; break; // 112.5 degrees
2001 case 6: spreadr = -F1_0/22; spreadu = F1_0/22; break; // 135 degrees
2002 case 7: spreadr = -F1_0/17; spreadu = F1_0/42; break; // 157.5 degrees
2004 Error("Invalid helix_orientation value %x\n",helix_orient);
2007 Laser_player_fire_spread( objp, HELIX_ID, 6, 0, 0, 1, 0);
2008 Laser_player_fire_spread( objp, HELIX_ID, 6, spreadr, spreadu, 0, 0);
2009 Laser_player_fire_spread( objp, HELIX_ID, 6, -spreadr, -spreadu, 0, 0);
2010 Laser_player_fire_spread( objp, HELIX_ID, 6, spreadr*2, spreadu*2, 0, 0);
2011 Laser_player_fire_spread( objp, HELIX_ID, 6, -spreadr*2, -spreadu*2, 0, 0);
2016 Laser_player_fire( objp, PHOENIX_ID, 0, 1, 0);
2017 Laser_player_fire( objp, PHOENIX_ID, 1, 0, 0);
2019 Laser_player_fire_spread_delay( objp, PHOENIX_ID, 0, 0, 0, FrameTime/2, 1, 0);
2020 Laser_player_fire_spread_delay( objp, PHOENIX_ID, 1, 0, 0, FrameTime/2, 0, 0);
2025 Laser_player_fire( objp, OMEGA_ID, 1, 1, 0);
2029 Int3(); // Contact Yuan: Unknown Primary weapon type, setting to 0.
2033 // Set values to be recognized during comunication phase, if we are the
2036 if ((Game_mode & GM_MULTI) && (objnum == Players[Player_num].objnum))
2038 // mprintf((0, "Flags on fire: %d.\n", flags));
2039 Network_laser_fired = nfires;
2040 Network_laser_gun = weapon_num;
2041 Network_laser_flags = flags;
2042 Network_laser_level = level;
2049 #define MAX_SMART_DISTANCE (F1_0*150)
2050 #define MAX_OBJDISTS 30
2052 // -------------------------------------------------------------------------------------------
2053 // if goal_obj == -1, then create random vector
2054 int create_homing_missile(object *objp, int goal_obj, int objtype, int make_sound)
2057 vms_vector vector_to_goal;
2058 vms_vector random_vector;
2059 //vms_vector goal_pos;
2061 if (goal_obj == -1) {
2062 make_random_vector(&vector_to_goal);
2064 vm_vec_normalized_dir_quick(&vector_to_goal, &Objects[goal_obj].pos, &objp->pos);
2065 make_random_vector(&random_vector);
2066 vm_vec_scale_add2(&vector_to_goal, &random_vector, F1_0/4);
2067 vm_vec_normalize_quick(&vector_to_goal);
2070 // Create a vector towards the goal, then add some noise to it.
2071 objnum = Laser_create_new(&vector_to_goal, &objp->pos, objp->segnum, objp-Objects, objtype, make_sound);
2075 // Fixed to make sure the right person gets credit for the kill
2077 // Objects[objnum].ctype.laser_info.parent_num = objp->ctype.laser_info.parent_num;
2078 // Objects[objnum].ctype.laser_info.parent_type = objp->ctype.laser_info.parent_type;
2079 // Objects[objnum].ctype.laser_info.parent_signature = objp->ctype.laser_info.parent_signature;
2081 Objects[objnum].ctype.laser_info.track_goal = goal_obj;
2086 extern void blast_nearby_glass(object *objp, fix damage);
2088 //-----------------------------------------------------------------------------
2089 // Create the children of a smart bomb, which is a bunch of homing missiles.
2090 void create_smart_children(object *objp, int num_smart_children)
2092 int parent_type, parent_num;
2095 int objlist[MAX_OBJDISTS];
2098 if (objp->type == OBJ_WEAPON) {
2099 parent_type = objp->ctype.laser_info.parent_type;
2100 parent_num = objp->ctype.laser_info.parent_num;
2101 } else if (objp->type == OBJ_ROBOT) {
2102 parent_type = OBJ_ROBOT;
2103 parent_num = objp-Objects;
2105 Int3(); // Hey, what kind of object is this!?
2110 if (objp->id == EARTHSHAKER_ID)
2111 blast_nearby_glass(objp, Weapon_info[EARTHSHAKER_ID].strength[Difficulty_level]);
2114 if ((objp->type == OBJ_WEAPON) && ((objp->id == SMART_ID) || (objp->id == SUPERPROX_ID) || (objp->id == ROBOT_SUPERPROX_ID) || (objp->id == EARTHSHAKER_ID)))
2115 Assert(Weapon_info[objp->id].children != -1);
2118 if (((objp->type == OBJ_WEAPON) && (Weapon_info[objp->id].children != -1)) || (objp->type == OBJ_ROBOT)) {
2121 if (Game_mode & GM_MULTI)
2124 for (objnum=0; objnum<=Highest_object_index; objnum++) {
2125 object *curobjp = &Objects[objnum];
2127 if ((((curobjp->type == OBJ_ROBOT) && (!curobjp->ctype.ai_info.CLOAKED)) || (curobjp->type == OBJ_PLAYER)) && (objnum != parent_num)) {
2130 if (curobjp->type == OBJ_PLAYER)
2132 if ((parent_type == OBJ_PLAYER) && (Game_mode & GM_MULTI_COOP))
2135 if ((Game_mode & GM_TEAM) && (get_team(curobjp->id) == get_team(Objects[parent_num].id)))
2139 if (Players[curobjp->id].flags & PLAYER_FLAGS_CLOAKED)
2143 // Robot blobs can't track robots.
2144 if (curobjp->type == OBJ_ROBOT) {
2145 if (parent_type == OBJ_ROBOT)
2148 // Your shots won't track the buddy.
2149 if (parent_type == OBJ_PLAYER)
2150 if (Robot_info[curobjp->id].companion)
2154 dist = vm_vec_dist_quick(&objp->pos, &curobjp->pos);
2155 if (dist < MAX_SMART_DISTANCE) {
2158 oovis = object_to_object_visibility(objp, curobjp, FQ_TRANSWALL);
2160 if (oovis) { //object_to_object_visibility(objp, curobjp, FQ_TRANSWALL)) {
2161 objlist[numobjs] = objnum;
2163 if (numobjs >= MAX_OBJDISTS) {
2164 mprintf((0, "Warning -- too many objects near smart bomb explosion. See laser.c.\n"));
2165 numobjs = MAX_OBJDISTS;
2173 // Get type of weapon for child from parent.
2174 if (objp->type == OBJ_WEAPON) {
2175 blob_id = Weapon_info[objp->id].children;
2176 Assert(blob_id != -1); // Hmm, missing data in bitmaps.tbl. Need "children=NN" parameter.
2178 Assert(objp->type == OBJ_ROBOT);
2179 blob_id = ROBOT_SMART_HOMING_ID;
2182 // -- //determine what kind of blob to drop
2183 // -- // Note: parent_type is not the type of the weapon's parent. It is actually the type of the weapon's
2184 // -- // earliest ancestor. This deals with the issue of weapons spewing weapons which spew weapons.
2185 // -- switch (parent_type) {
2186 // -- case OBJ_WEAPON:
2187 // -- Int3(); // Should this ever happen?
2188 // -- switch (objp->id) {
2189 // -- case SUPERPROX_ID: blob_id = SMART_MINE_HOMING_ID; break;
2190 // -- case ROBOT_SUPERPROX_ID: blob_id = ROBOT_SMART_MINE_HOMING_ID; break;
2191 // -- case EARTHSHAKER_ID: blob_id = EARTHSHAKER_MEGA_ID; break;
2192 // -- default: Int3(); //bogus id for weapon
2195 // -- case OBJ_PLAYER:
2196 // -- switch (objp->id) {
2197 // -- case SUPERPROX_ID: blob_id = SMART_MINE_HOMING_ID; break;
2198 // -- case ROBOT_SUPERPROX_ID: Int3(); break;
2199 // -- case EARTHSHAKER_ID: blob_id = EARTHSHAKER_MEGA_ID; break;
2200 // -- case SMART_ID: blob_id = PLAYER_SMART_HOMING_ID; break;
2201 // -- default: Int3(); //bogus id for weapon
2204 // -- case OBJ_ROBOT:
2205 // -- switch (objp->id) {
2206 // -- case ROBOT_SUPERPROX_ID: blob_id = ROBOT_SMART_MINE_HOMING_ID; break;
2207 // -- // -- case EARTHSHAKER_ID: blob_id = EARTHSHAKER_MEGA_ID; break;
2208 // -- case SMART_ID: blob_id = ROBOT_SMART_HOMING_ID; break;
2209 // -- default: blob_id = ROBOT_SMART_HOMING_ID; break;
2212 // -- default: Int3(); //bogus type for parent object
2216 for (i=0; i<num_smart_children; i++) {
2218 objnum = (numobjs==0)?-1:objlist[(d_rand() * numobjs) >> 15];
2219 create_homing_missile(objp, objnum, blob_id, make_sound);
2225 int Missile_gun = 0;
2227 //give up control of the guided missile
2228 void release_guided_missile(int player_num)
2230 if (player_num == Player_num)
2232 if (Guided_missile[player_num]==NULL)
2235 Missile_viewer = Guided_missile[player_num];
2237 if (Game_mode & GM_MULTI)
2238 multi_send_guided_info (Guided_missile[Player_num],1);
2240 if (Newdemo_state==ND_STATE_RECORDING)
2241 newdemo_record_guided_end();
2244 Guided_missile[player_num] = NULL;
2247 int Proximity_dropped=0,Smartmines_dropped=0;
2249 // -------------------------------------------------------------------------------------------
2250 //parameter determines whether or not to do autoselect if have run out of ammo
2251 //this is needed because if you drop a bomb with the B key, you don't want
2252 //want to autoselect if the bomb isn't actually selected.
2253 void do_missile_firing(int do_autoselect)
2257 Assert(Secondary_weapon < MAX_SECONDARY_WEAPONS);
2259 if (Guided_missile[Player_num] && Guided_missile[Player_num]->signature==Guided_missile_sig[Player_num]) {
2260 release_guided_missile(Player_num);
2261 Next_missile_fire_time = GameTime + Weapon_info[Secondary_weapon_to_weapon_info[Secondary_weapon]].fire_wait;
2265 if (!Player_is_dead && (Players[Player_num].secondary_ammo[Secondary_weapon] > 0)) {
2267 int weapon_id,weapon_gun;
2269 Players[Player_num].secondary_ammo[Secondary_weapon]--;
2271 weapon_id = Secondary_weapon_to_weapon_info[Secondary_weapon];
2273 if (Laser_rapid_fire!=0xBADA55)
2274 Next_missile_fire_time = GameTime + Weapon_info[weapon_id].fire_wait;
2276 Next_missile_fire_time = GameTime + F1_0/25;
2278 weapon_gun = Secondary_weapon_to_gun_num[Secondary_weapon];
2280 if (weapon_gun==4) { //alternate left/right
2281 weapon_gun += (gun_flag = (Missile_gun & 1));
2285 Laser_player_fire( ConsoleObject, weapon_id, weapon_gun, 1, 0);
2287 if (Secondary_weapon == PROXIMITY_INDEX) {
2288 if (++Proximity_dropped == 4) {
2289 Proximity_dropped = 0;
2291 maybe_drop_net_powerup(POW_PROXIMITY_WEAPON);
2295 else if (Secondary_weapon == SMART_MINE_INDEX) {
2296 if (++Smartmines_dropped == 4) {
2297 Smartmines_dropped = 0;
2299 maybe_drop_net_powerup(POW_SMART_MINE);
2304 else if (Secondary_weapon != CONCUSSION_INDEX)
2305 maybe_drop_net_powerup(Secondary_weapon_to_powerup[Secondary_weapon]);
2308 if (Secondary_weapon == MEGA_INDEX || Secondary_weapon == SMISSILE5_INDEX) {
2309 vms_vector force_vec;
2311 force_vec.x = -(ConsoleObject->orient.fvec.x << 7);
2312 force_vec.y = -(ConsoleObject->orient.fvec.y << 7);
2313 force_vec.z = -(ConsoleObject->orient.fvec.z << 7);
2314 phys_apply_force(ConsoleObject, &force_vec);
2316 force_vec.x = (force_vec.x >> 4) + d_rand() - 16384;
2317 force_vec.y = (force_vec.y >> 4) + d_rand() - 16384;
2318 force_vec.z = (force_vec.z >> 4) + d_rand() - 16384;
2319 phys_apply_rot(ConsoleObject, &force_vec);
2323 if (Game_mode & GM_MULTI)
2325 Network_laser_fired = 1; //how many
2326 Network_laser_gun = Secondary_weapon + MISSILE_ADJUST;
2327 Network_laser_flags = gun_flag;
2328 Network_laser_level = 0;
2333 auto_select_weapon(1); //select next missile, if this one out of ammo