home *** CD-ROM | disk | FTP | other *** search
/ Amiga ACS 1998 #6 / amigaacscoverdisc1998-061998.iso / games / descent / source / main / fireball.c < prev    next >
Text File  |  1998-06-08  |  42KB  |  1,286 lines

  1. /*
  2. THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
  3. SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
  4. END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
  5. ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
  6. IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
  7. SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
  8. FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
  9. CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
  10. AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
  11. COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
  12. */
  13. /*
  14.  * $Source: f:/miner/source/main/rcs/fireball.c $
  15.  * $Revision: 2.2 $
  16.  * $Author: john $
  17.  * $Date: 1995/03/21 14:39:57 $
  18.  * 
  19.  * Code for rendering & otherwise dealing with explosions
  20.  * 
  21.  * $Log: fireball.c $
  22.  * Revision 2.2  1995/03/21  14:39:57  john
  23.  * Ifdef'd out the NETWORK code.
  24.  * 
  25.  * Revision 2.1  1995/03/20  18:15:47  john
  26.  * Added code to not store the normals in the segment structure.
  27.  * 
  28.  * Revision 2.0  1995/02/27  11:30:34  john
  29.  * New version 2.0, which has no anonymous unions, builds with
  30.  * Watcom 10.0, and doesn't require parsing BITMAPS.TBL.
  31.  * 
  32.  * Revision 1.200  1995/02/22  13:18:41  allender
  33.  * remove anonymous unions from object structure
  34.  * 
  35.  * Revision 1.199  1995/02/14  19:58:32  mike
  36.  * comment out "something bad has happened" int3.
  37.  * 
  38.  * Revision 1.198  1995/02/09  13:11:01  mike
  39.  * remove an annoying mprintf and Int3().
  40.  * 
  41.  * Revision 1.197  1995/02/08  17:10:14  mike
  42.  * don't drop cloaks if one nearby.
  43.  * 
  44.  * Revision 1.196  1995/02/08  13:27:14  rob
  45.  * Give keys dropped by robots 0 velocity in coop game.
  46.  * 
  47.  * Revision 1.195  1995/02/08  11:57:40  mike
  48.  * determine whether debris object failed to create because buffer was
  49.  * exhausted or because limit was hit.
  50.  * 
  51.  * Revision 1.194  1995/02/08  11:37:58  mike
  52.  * Check for failures in call to obj_create.
  53.  * 
  54.  * Revision 1.193  1995/02/07  21:09:41  mike
  55.  * only replace weapon with energy 1/2 time.
  56.  * 
  57.  * Revision 1.192  1995/01/30  18:21:52  rob
  58.  * Replace extra life powerups in multiplayer to invul when
  59.  * dropped by robots.
  60.  * 
  61.  * Revision 1.191  1995/01/28  17:40:59  mike
  62.  * fix stupidity in converting quad lasers to energy.
  63.  * 
  64.  * Revision 1.190  1995/01/27  15:05:59  rob
  65.  * Trying to fix a bug with damaging robots with player badass explosions.
  66.  * 
  67.  * Revision 1.189  1995/01/26  18:59:04  rob
  68.  * Powerups were flying too far in robot-cooperative games.
  69.  * 
  70.  * Revision 1.188  1995/01/25  10:53:35  mike
  71.  * make badass damage go through grates.
  72.  * 
  73.  * Revision 1.187  1995/01/25  09:37:23  mike
  74.  * fix objects containing robots, worked for powerups, bad {} placement.
  75.  * 
  76.  * Revision 1.186  1995/01/23  22:51:20  mike
  77.  * drop energy instead of primary weapon if you already have primary weapon.
  78.  * 
  79.  * Revision 1.185  1995/01/20  16:56:37  mike
  80.  * Cut damage done by badass weapons.
  81.  * 
  82.  * Revision 1.184  1995/01/19  17:44:57  mike
  83.  * damage_force removed, that information coming from strength field.
  84.  * 
  85.  * Revision 1.183  1995/01/16  21:06:54  mike
  86.  * Move function pick_random_point_in_segment from fireball.c to gameseg.c.
  87.  * 
  88.  * Revision 1.182  1995/01/16  19:24:04  mike
  89.  * If a gated-in robot and going to drop energy powerup, don't!
  90.  * 
  91.  * Revision 1.181  1995/01/15  20:48:03  mike
  92.  * drop energy in place of quad lasers if player already has quad lasers.
  93.  * 
  94.  * Revision 1.180  1995/01/14  19:32:19  rob
  95.  * Fixed an error.
  96.  * 
  97.  * Revision 1.179  1995/01/14  18:50:55  rob
  98.  * Make robot egg creation suitable for mutliplayer situations.
  99.  * 
  100.  * Revision 1.178  1995/01/14  14:55:07  rob
  101.  * Make weapons/keys/etc never disappear in network mode.
  102.  */
  103.  
  104.  
  105. #pragma off (unreferenced)
  106. static char rcsid[] = "$Id: fireball.c 2.2 1995/03/21 14:39:57 john Exp $";
  107. #pragma on (unreferenced)
  108.  
  109. #include <stdlib.h>
  110. #include <string.h>
  111.  
  112. #include "error.h"
  113. #include "3d.h"
  114.  
  115. #include "inferno.h"
  116. #include "object.h"
  117. #include "vclip.h"
  118. #include "game.h"
  119. #include "mono.h"
  120. #include "polyobj.h"
  121. #include "sounds.h"
  122. #include "player.h"
  123. #include "gauges.h"
  124. #include "powerup.h"
  125. #include "bm.h"
  126. #include "ai.h"
  127. #include "weapon.h"
  128. #include "fireball.h"
  129. #include "collide.h"
  130. #include "newmenu.h"
  131. #include "network.h"
  132. #include "gameseq.h"
  133. #include "physics.h"
  134. #include "scores.h"
  135. #include "laser.h"
  136. #include "wall.h"
  137. #include "multi.h"
  138. #include "endlevel.h"
  139. #include "timer.h"
  140. #include "fuelcen.h"
  141. #include "gameseg.h"
  142.  
  143. #define EXPLOSION_SCALE fl2f(2.5)        //explosion is the obj size times this  
  144.  
  145. //--unused-- ubyte    Frame_processed[MAX_OBJECTS];
  146.  
  147. object *object_create_explosion_sub(object *objp, short segnum, vms_vector * position, fix size, int vclip_type, fix maxdamage, fix maxdistance, fix maxforce, int parent )
  148. {
  149.     int objnum;
  150.     object *obj;
  151.  
  152.     objnum = obj_create( OBJ_FIREBALL,vclip_type,segnum,position,&vmd_identity_matrix,size,
  153.                     CT_EXPLOSION,MT_NONE,RT_FIREBALL);
  154.  
  155.     if (objnum < 0 ) {
  156.         mprintf((1, "Can't create object in object_create_explosion_sub.\n"));
  157.         return NULL;
  158.     }
  159.  
  160.     obj = &Objects[objnum];
  161.  
  162.     //mprintf( 0, "Fireball created at %d, %d, %d\n", obj->pos.x, obj->pos.y, obj->pos.z );
  163.  
  164.     //now set explosion-specific data
  165.  
  166.     obj->lifeleft = Vclip[vclip_type ].play_time;
  167.     obj->ctype.expl_info.spawn_time = -1;
  168.     obj->ctype.expl_info.delete_objnum = -1;
  169.     obj->ctype.expl_info.delete_time = -1;
  170.  
  171.     if (maxdamage > 0) {
  172.         fix dist, force;
  173.         vms_vector pos_hit, vforce;
  174.         fix damage;
  175.         int i;
  176.         object * obj0p = &Objects[0];
  177.                       
  178.         // -- now legal for badass explosions on a wall. Assert(objp != NULL);
  179.  
  180.         for (i=0; i<=Highest_object_index; i++ )    {
  181.             //    Weapons used to be affected by badass explosions, but this introduces serious problems.
  182.             //    When a smart bomb blows up, if one of its children goes right towards a nearby wall, it will
  183.             //    blow up, blowing up all the children.  So I remove it.  MK, 09/11/94
  184.             if ( (obj0p->type == OBJ_CNTRLCEN) || (obj0p->type==OBJ_PLAYER) || ((obj0p->type==OBJ_ROBOT) && ((Objects[parent].type != OBJ_ROBOT) || (Objects[parent].id != obj0p->id)))) {
  185.                 dist = vm_vec_dist_quick( &obj0p->pos, &obj->pos );
  186.                 // Make damage be from 'maxdamage' to 0.0, where 0.0 is 'maxdistance' away;
  187.                 if ( dist < maxdistance ) {
  188.                     if (object_to_object_visibility(obj, obj0p, FQ_TRANSWALL)) {
  189.                         damage = maxdamage - fixmuldiv( dist, maxdamage, maxdistance );
  190.                         force = maxforce - fixmuldiv( dist, maxforce, maxdistance );
  191.                         
  192.                         // Find the force vector on the object
  193.                         vm_vec_sub( &vforce, &obj0p->pos, &obj->pos );
  194.                         vm_vec_normalize_quick(&vforce);
  195.                         vm_vec_scale(&vforce, force );
  196.     
  197.                         // Find where the point of impact is... ( pos_hit )
  198.                         vm_vec_scale(vm_vec_sub(&pos_hit, &obj->pos, &obj0p->pos), fixdiv(obj0p->size, obj0p->size + dist));
  199.     
  200.                         switch ( obj0p->type )    {
  201.                             case OBJ_ROBOT:
  202.                                 phys_apply_force(obj0p,&vforce);
  203.  
  204.                                 //    When a robot gets whacked by a badass force, he looks towards it because robots tend to get blasted from behind.
  205.                                 {
  206.                                     vms_vector neg_vforce; 
  207.                                     neg_vforce.x = vforce.x * -2 * (7 - Difficulty_level)/8;
  208.                                     neg_vforce.y = vforce.y * -2 * (7 - Difficulty_level)/8;
  209.                                     neg_vforce.z = vforce.z * -2 * (7 - Difficulty_level)/8;
  210.                                     phys_apply_rot(obj0p,&neg_vforce);
  211.                                 }
  212.                                 if ( obj0p->shields >= 0 ) {
  213.                                     if (apply_damage_to_robot(obj0p, damage, parent))
  214.                                         if ((objp != NULL) && (parent == Players[Player_num].objnum))
  215.                                             add_points_to_score(Robot_info[obj0p->id].score_value);
  216.                                 }
  217.                                 break;
  218.                             case OBJ_CNTRLCEN:
  219.                                 if ( obj0p->shields >= 0 ) {
  220.                                     apply_damage_to_controlcen(obj0p, damage, parent );
  221.                                 }
  222.                                 break;
  223.                             case OBJ_PLAYER:    {
  224.                                 object * killer=NULL; 
  225.                                 vms_vector    vforce2;
  226.                                 if ((objp != NULL) && (Game_mode & GM_MULTI) && (objp->type == OBJ_PLAYER)) {
  227. //                                    mprintf((0, "Damaged by player %d's explosion.\n", objp->id));
  228.                                     killer = objp;
  229.                                 }
  230.                                 vforce2 = vforce;
  231.                                 if (parent > -1 ) {
  232.                                     killer = &Objects[parent];
  233.                                     if (killer != ConsoleObject)        // if someone else whacks you, cut force by 2x
  234.                                         vforce2.x /= 2;    vforce2.y /= 2;    vforce2.z /= 2;
  235.                                 }
  236.                                 vforce2.x /= 2;    vforce2.y /= 2;    vforce2.z /= 2;
  237.  
  238.                                 phys_apply_force(obj0p,&vforce);
  239.                                 phys_apply_rot(obj0p,&vforce2);
  240.                                 if ( obj0p->shields >= 0 )
  241.                                     apply_damage_to_player(obj0p, killer, damage );
  242.                             }
  243.                                 break;
  244.  
  245.                             default:
  246.                                 Int3();    //    Illegal object type
  247.                         }    // end switch
  248.                     } else {
  249.                         ; // mprintf((0, "No badass: robot=%2i, dist=%7.3f, maxdistance=%7.3f .\n", i, f2fl(dist), f2fl(maxdistance)));
  250.                     }    // end if (object_to_object_visibility...
  251.                 }    // end if (dist < maxdistance)
  252.             }
  253.             obj0p++;
  254.         }    // end for
  255.     }    // end if (maxdamage...
  256.  
  257. //    mprintf(0, "\n");
  258.  
  259.     return obj;
  260.  
  261. }
  262.  
  263.  
  264. object *object_create_muzzle_flash(short segnum, vms_vector * position, fix size, int vclip_type )
  265. {
  266.     return object_create_explosion_sub(NULL, segnum, position, size, vclip_type, 0, 0, 0, -1 );
  267. }
  268.  
  269. object *object_create_explosion(short segnum, vms_vector * position, fix size, int vclip_type )
  270. {
  271.     return object_create_explosion_sub(NULL, segnum, position, size, vclip_type, 0, 0, 0, -1 );
  272. }
  273.  
  274. object *object_create_badass_explosion(object *objp, short segnum, vms_vector * position, fix size, int vclip_type, fix maxdamage, fix maxdistance, fix maxforce, int parent )
  275. {
  276.     object    *rval;
  277.  
  278.     rval = object_create_explosion_sub(objp, segnum, position, size, vclip_type, maxdamage, maxdistance, maxforce, parent );
  279.  
  280.     if (objp != NULL)
  281.         create_smart_children(objp);
  282.  
  283.     return rval;
  284. }
  285.  
  286. //blows up a badass weapon, creating the badass explosion
  287. //return the explosion object
  288. object *explode_badass_weapon(object *obj)
  289. {
  290.     weapon_info *wi = &Weapon_info[obj->id];
  291.  
  292.     Assert(wi->damage_radius);
  293.  
  294.     digi_link_sound_to_object(SOUND_BADASS_EXPLOSION, obj-Objects, 0, F1_0);
  295.  
  296.     return object_create_badass_explosion( obj, obj->segnum, &obj->pos, 
  297.                     wi->impact_size, 
  298.                     wi->robot_hit_vclip, 
  299.                     wi->strength[Difficulty_level], 
  300.                     wi->damage_radius,wi->strength[Difficulty_level],
  301.                     obj->ctype.laser_info.parent_num );
  302.  
  303. }
  304.  
  305. //blows up the player with a badass explosion
  306. //return the explosion object
  307. object *explode_badass_player(object *objp)
  308. {
  309.     object     *rval;
  310.  
  311.     rval = object_create_badass_explosion(objp, objp->segnum, &objp->pos, objp->size,
  312.                     get_explosion_vclip(objp, 0),
  313.                     F1_0*50, F1_0*40, F1_0*150, 
  314.                     objp-Objects);
  315.     if (rval)
  316.         digi_link_sound_to_object(SOUND_BADASS_EXPLOSION, rval-Objects, 0, F1_0);
  317.     return (rval);
  318. }
  319.  
  320.  
  321. #define DEBRIS_LIFE (f1_0 * 2)        //lifespan in seconds
  322.  
  323. object *object_create_debris(object *parent, int subobj_num)
  324. {
  325.     int objnum;
  326.     object *obj;
  327.     polymodel *po;
  328.  
  329.     Assert((parent->type == OBJ_ROBOT) || (parent->type == OBJ_PLAYER)  );
  330.  
  331.     objnum = obj_create(OBJ_DEBRIS,0,parent->segnum,&parent->pos,
  332.                 &parent->orient,Polygon_models[parent->rtype.pobj_info.model_num].submodel_rads[subobj_num],
  333.                 CT_DEBRIS,MT_PHYSICS,RT_POLYOBJ);
  334.  
  335.     if ((objnum < 0 ) && (Highest_object_index >= MAX_OBJECTS-1)) {
  336.         mprintf((1, "Can't create object in object_create_debris.\n"));
  337.         Int3();
  338.         return NULL;
  339.     }
  340.     if ( objnum < 0 )
  341.         return NULL;                // Not enough debris slots!
  342.     obj = &Objects[objnum];
  343.  
  344.     Assert(subobj_num < 32);
  345.  
  346.     //Set polygon-object-specific data 
  347.  
  348.     obj->rtype.pobj_info.model_num = parent->rtype.pobj_info.model_num;
  349.     obj->rtype.pobj_info.subobj_flags = 1<<subobj_num;
  350.     obj->rtype.pobj_info.tmap_override = parent->rtype.pobj_info.tmap_override;
  351.  
  352.     //Set physics data for this object
  353.  
  354.     po = &Polygon_models[obj->rtype.pobj_info.model_num];
  355.  
  356.     obj->mtype.phys_info.velocity.x = RAND_MAX/2 - rand();
  357.     obj->mtype.phys_info.velocity.y = RAND_MAX/2 - rand();
  358.     obj->mtype.phys_info.velocity.z = RAND_MAX/2 - rand();
  359.     vm_vec_normalize_quick(&obj->mtype.phys_info.velocity);
  360.     vm_vec_scale(&obj->mtype.phys_info.velocity,i2f(10 + (30 * rand() / RAND_MAX)));
  361.  
  362.     vm_vec_add2(&obj->mtype.phys_info.velocity,&parent->mtype.phys_info.velocity);
  363.  
  364.     vm_vec_make(&obj->mtype.phys_info.rotvel,10*0x2000/3,10*0x4000/3,10*0x7000/3);
  365.     vm_vec_zero(&obj->mtype.phys_info.rotthrust);
  366.  
  367.     obj->lifeleft = DEBRIS_LIFE;
  368.  
  369.     obj->mtype.phys_info.mass = fixmuldiv(parent->mtype.phys_info.mass,obj->size,parent->size);
  370.     obj->mtype.phys_info.drag = 0; //fl2f(0.2);        //parent->mtype.phys_info.drag;
  371.  
  372.     return obj;
  373.  
  374. }
  375.  
  376. void draw_fireball(object *obj)
  377. {
  378.     //mprintf( 0, "[Drawing obj %d type %d fireball size %x]\n", obj-Objects, obj->id, obj->size );
  379.  
  380.     if ( obj->lifeleft > 0 )
  381.         draw_vclip_object(obj,obj->lifeleft,0, obj->id);
  382.  
  383. }
  384.  
  385. // --------------------------------------------------------------------------------------------------------------------
  386. //    Return true if there is a door here and it is openable
  387. //    It is assumed that the player has all keys.
  388. int door_is_openable_by_player(segment *segp, int sidenum)
  389. {
  390.     int    wall_num, wall_type;
  391.  
  392.     wall_num = segp->sides[sidenum].wall_num;
  393.     wall_type = Walls[wall_num].type;
  394.  
  395.     if (wall_num == -1)
  396.         return 0;                        //    no wall here.
  397.  
  398.     //    Can't open locked doors.
  399.     if (((wall_type == WALL_DOOR) && (Walls[wall_num].flags & WALL_DOOR_LOCKED)) || (wall_type == WALL_CLOSED))
  400.         return 0;
  401.  
  402.     return 1;
  403.  
  404. }
  405.  
  406. #define    QUEUE_SIZE    64
  407.  
  408. // --------------------------------------------------------------------------------------------------------------------
  409. //    Return a segment %i segments away from initial segment.
  410. //    Returns -1 if can't find a segment that distance away.
  411. int pick_connected_segment(object *objp, int max_depth)
  412. {
  413.     int        i;
  414.     int        cur_depth;
  415.     int        start_seg;
  416.     int        head, tail;
  417.     int        seg_queue[QUEUE_SIZE*2];
  418.     byte        visited[MAX_SEGMENTS];
  419.     byte        depth[MAX_SEGMENTS];
  420.     byte        side_rand[MAX_SIDES_PER_SEGMENT];
  421.  
  422. //    mprintf((0, "Finding a segment %i segments away from segment %i: ", max_depth, objp->segnum));
  423.  
  424.     start_seg = objp->segnum;
  425.     head = 0;
  426.     tail = 0;
  427.     seg_queue[head++] = start_seg;
  428.  
  429.     memset(visited, 0, Highest_segment_index+1);
  430.     memset(depth, 0, Highest_segment_index+1);
  431.     cur_depth = 0;
  432.  
  433.     for (i=0; i<MAX_SIDES_PER_SEGMENT; i++)
  434.         side_rand[i] = i;
  435.  
  436.     while (tail != head) {
  437.         int        sidenum;
  438.         segment    *segp;
  439.         byte        ind1, ind2, temp;
  440.  
  441.         if (cur_depth >= max_depth) {
  442. //            mprintf((0, "selected segment %i\n", seg_queue[tail]));
  443.             return seg_queue[tail];
  444.         }
  445.  
  446.         segp = &Segments[seg_queue[tail++]];
  447.         tail &= QUEUE_SIZE-1;
  448.  
  449.         //    to make random, switch a pair of entries in side_rand.
  450.         ind1 = (rand() * MAX_SIDES_PER_SEGMENT) >> 15;
  451.         ind2 = (rand() * MAX_SIDES_PER_SEGMENT) >> 15;
  452.         temp = side_rand[ind1];
  453.         side_rand[ind1] = side_rand[ind2];
  454.         side_rand[ind2] = temp;
  455.  
  456.         for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++) {
  457.             int    snrand = side_rand[sidenum];
  458.             int    wall_num = segp->sides[snrand].wall_num;
  459.  
  460.             if (((wall_num == -1) && (segp->children[snrand] > -1)) || door_is_openable_by_player(segp, snrand)) {
  461.                 if (visited[segp->children[snrand]] == 0) {
  462.                     seg_queue[head++] = segp->children[snrand];
  463.                     visited[segp->children[snrand]] = 1;
  464.                     depth[segp->children[snrand]] = cur_depth+1;
  465.                     head &= QUEUE_SIZE-1;
  466.                     if (head > tail) {
  467.                         if (head == tail + QUEUE_SIZE-1)
  468.                             Int3();    //    queue overflow.  Make it bigger!
  469.                     } else
  470.                         if (head+QUEUE_SIZE == tail + QUEUE_SIZE-1)
  471.                             Int3();    //    queue overflow.  Make it bigger!
  472.                 }
  473.             }
  474.         }
  475.         if ((seg_queue[tail] < 0) || (seg_queue[tail] > Highest_segment_index)) {
  476.             // -- Int3();    //    Something bad has happened.  Queue is trashed.  --MK, 12/13/94
  477.             return -1;
  478.         }
  479.         cur_depth = depth[seg_queue[tail]];
  480.     }
  481.  
  482.     mprintf((0, "...failed at depth %i, returning -1\n", cur_depth));
  483.     return -1;
  484. }
  485.  
  486. #define    BASE_NET_DROP_DEPTH    10
  487.  
  488. //    ------------------------------------------------------------------------------------------------------
  489. //    Choose segment to drop a powerup in.
  490. //    For all active net players, try to create a N segment path from the player.  If possible, return that
  491. //    segment.  If not possible, try another player.  After a few tries, use a random segment.
  492. //    Don't drop if control center in segment.
  493. int choose_drop_segment(void)
  494. {
  495.     int    pnum = 0;
  496.     int    segnum = -1;
  497.     int    initial_drop_depth = BASE_NET_DROP_DEPTH + ((rand() * 10) >> 15);
  498.     int    cur_drop_depth = initial_drop_depth;
  499.     int    count;
  500.  
  501.     srand(timer_get_fixed_seconds());
  502.  
  503.     while ((segnum == -1) && (cur_drop_depth > BASE_NET_DROP_DEPTH/2)) {
  504.         pnum = (rand() * N_players) >> 15;
  505.         count = 0;
  506.         while ((Players[pnum].connected == 0) && (count < N_players)) {
  507.             pnum = (pnum+1)%N_players;
  508.             count++;
  509.         }
  510.  
  511.         if (count == N_players) {
  512.             mprintf((1, "Warning: choose_drop_segment: Couldn't find legal drop segment because no connected players.\n"));
  513.             return (rand() * Highest_segment_index) >> 15;
  514.         }
  515.  
  516.         segnum = pick_connected_segment(&Objects[Players[pnum].objnum], cur_drop_depth);
  517.         if (Segments[segnum].special == SEGMENT_IS_CONTROLCEN)
  518.             segnum = -1;
  519.         cur_drop_depth--;
  520.     }
  521.  
  522.     if (segnum == -1) {
  523.         mprintf((1, "Warning: Unable to find a connected segment.  Picking a random one.\n"));
  524.         return (rand() * Highest_segment_index) >> 15;
  525.     } else
  526.         return segnum;
  527.  
  528. }
  529.  
  530. #ifdef NETWORK
  531. //    ------------------------------------------------------------------------------------------------------
  532. //    Drop cloak powerup if in a network game.
  533. void maybe_drop_net_powerup(int powerup_type)
  534. {
  535.     if ((Game_mode & GM_MULTI) && !(Game_mode & GM_MULTI_COOP)) {
  536.         int    segnum, objnum;
  537.         vms_vector    new_pos;
  538.  
  539.         if (Fuelcen_control_center_destroyed || Endlevel_sequence)
  540.             return;
  541.  
  542.         segnum = choose_drop_segment();
  543. //--old--         segnum = (rand() * Highest_segment_index) >> 15;
  544. //--old--         Assert((segnum >= 0) && (segnum <= Highest_segment_index));
  545. //--old--         if (segnum < 0)
  546. //--old--             segnum = -segnum;
  547. //--old--         while (segnum > Highest_segment_index)
  548. //--old--             segnum /= 2;
  549.  
  550.         Net_create_loc = 0;
  551.         objnum = call_object_create_egg(&Objects[Players[Player_num].objnum], 1, OBJ_POWERUP, powerup_type);
  552.  
  553.         if (objnum < 0)
  554.             return;
  555.  
  556. #ifndef SHAREWARE
  557.         pick_random_point_in_seg(&new_pos, segnum);
  558. #else
  559.         compute_segment_center(&new_pos, &Segments[segnum]);
  560. #endif
  561.  
  562.         multi_send_create_powerup(powerup_type, segnum, objnum, &new_pos);
  563.  
  564.         Objects[objnum].pos = new_pos;
  565.         vm_vec_zero(&Objects[objnum].mtype.phys_info.velocity);
  566.         obj_relink(objnum, segnum);
  567.  
  568.         object_create_explosion(segnum, &new_pos, i2f(5), VCLIP_POWERUP_DISAPPEARANCE );
  569. //        mprintf(0, "Creating net powerup in segment %i at %7.3f %7.3f %7.3f\n", segnum, f2fl(new_pos.x), f2fl(new_pos.y), f2fl(new_pos.z));
  570.     }
  571. }
  572. #endif
  573.  
  574. //    ------------------------------------------------------------------------------------------------------
  575. //    Return true if current segment contains some object.
  576. int segment_contains_object(int obj_type, int obj_id, int segnum)
  577. {
  578.     int    objnum;
  579.  
  580.     if (segnum == -1)
  581.         return 0;
  582.  
  583.     objnum = Segments[segnum].objects;
  584.  
  585.     while (objnum != -1)
  586.         if ((Objects[objnum].type == obj_type) && (Objects[objnum].id == obj_id))
  587.             return 1;
  588.         else
  589.             objnum = Objects[objnum].next;
  590.  
  591.     return 0;
  592. }
  593.  
  594. //    ------------------------------------------------------------------------------------------------------
  595. int object_nearby_aux(int segnum, int object_type, int object_id, int depth)
  596. {
  597.     int    i;
  598.  
  599.     if (depth == 0)
  600.         return 0;
  601.  
  602.     if (segment_contains_object(object_type, object_id, segnum))
  603.         return 1;
  604.  
  605.     for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) {
  606.         int    seg2 = Segments[segnum].children[i];
  607.  
  608.         if (seg2 != -1)
  609.             if (object_nearby_aux(seg2, object_type, object_id, depth-1))
  610.                 return 1;
  611.     }
  612.  
  613.     return 0;
  614. }
  615.  
  616.  
  617. //    ------------------------------------------------------------------------------------------------------
  618. //    Return true if some powerup is nearby (within 3 segments).
  619. int weapon_nearby(object *objp, int weapon_id)
  620. {
  621.     return object_nearby_aux(objp->segnum, OBJ_POWERUP, weapon_id, 3);
  622. }
  623.  
  624. //    ------------------------------------------------------------------------------------------------------
  625. void maybe_replace_powerup_with_energy(object *del_obj)
  626. {
  627.     int    weapon_index=-1;
  628.  
  629.     if (del_obj->contains_type != OBJ_POWERUP)
  630.         return;
  631.  
  632.     if (del_obj->contains_id == POW_CLOAK) {
  633.         if (weapon_nearby(del_obj, del_obj->contains_id)) {
  634.             mprintf((0, "Bashing cloak into nothing because there's one nearby.\n"));
  635.             del_obj->contains_count = 0;
  636.         }
  637.         return;
  638.     }
  639.     switch (del_obj->contains_id) {
  640.         case POW_VULCAN_WEAPON:            weapon_index = VULCAN_INDEX;        break;
  641.         case POW_SPREADFIRE_WEAPON:    weapon_index = SPREADFIRE_INDEX;    break;
  642. #ifndef SHAREWARE
  643.         case POW_PLASMA_WEAPON:            weapon_index = PLASMA_INDEX;        break;
  644.         case POW_FUSION_WEAPON:            weapon_index = FUSION_INDEX;        break;
  645. #endif
  646.     }
  647.  
  648.     //    Don't drop vulcan ammo if player maxed out.
  649.     if (((weapon_index == VULCAN_INDEX) || (del_obj->contains_id == POW_VULCAN_AMMO)) && (Players[Player_num].primary_ammo[VULCAN_INDEX] >= VULCAN_AMMO_MAX))
  650.         del_obj->contains_count = 0;
  651.     else if (weapon_index != -1) {
  652.         if ((player_has_weapon(weapon_index, 0) & HAS_WEAPON_FLAG) || weapon_nearby(del_obj, del_obj->contains_id)) {
  653.             if (rand() > 16384) {
  654.                 del_obj->contains_count = 1;
  655.                 del_obj->contains_type = OBJ_POWERUP;
  656.                 if (weapon_index == VULCAN_INDEX) {
  657.                     del_obj->contains_id = POW_VULCAN_AMMO;
  658.                 } else {
  659.                     del_obj->contains_id = POW_ENERGY;
  660.                 }
  661.             } else
  662.                 del_obj->contains_count = 0;
  663.         }
  664.     } else if (del_obj->contains_id == POW_QUAD_FIRE)
  665.         if ((Players[Player_num].flags & PLAYER_FLAGS_QUAD_LASERS) || weapon_nearby(del_obj, del_obj->contains_id)) {
  666.             if (rand() > 16384) {
  667.                 del_obj->contains_count = 1;
  668.                 del_obj->contains_type = OBJ_POWERUP;
  669.                 del_obj->contains_id = POW_ENERGY;
  670.             } else
  671.                 del_obj->contains_count = 0;
  672.         }
  673.  
  674.     //    If this robot was gated in by the boss and it now contains energy, make it contain nothing,
  675.     //    else the room gets full of energy.
  676.     if ( (del_obj->matcen_creator == BOSS_GATE_MATCEN_NUM) && (del_obj->contains_id == POW_ENERGY) && (del_obj->contains_type == OBJ_POWERUP) ) {
  677.         mprintf((0, "Converting energy powerup to nothing because robot %i gated in by boss.\n", del_obj-Objects));
  678.         del_obj->contains_count = 0;
  679.     }
  680.  
  681.     // Change multiplayer extra-lives into invulnerability
  682.     if ((Game_mode & GM_MULTI) && (del_obj->contains_id == POW_EXTRA_LIFE))
  683.     {
  684.         del_obj->contains_id = POW_INVULNERABILITY;
  685.     }
  686. }
  687.  
  688. //    ------------------------------------------------------------------------------------------------------
  689. //    Returns created object number.
  690. int object_create_egg(object *objp)
  691. {
  692.     int        objnum;
  693.     object    *obj;
  694.     int        powerup_type, powerup_id, count;
  695.     vms_vector    new_velocity, new_pos;
  696.     fix        old_mag;
  697.  
  698.     powerup_type = objp->contains_type;
  699.     powerup_id = objp->contains_id;
  700.  
  701. //    maybe_replace_powerup_with_energy(objp);
  702.  
  703. //    if (Game_mode & GM_NETWORK) 
  704. //    {
  705. //        char    type_str[16], id_str[16];
  706. //        if (objp->contains_type == OBJ_POWERUP) {
  707. //            if (objp->contains_count > 1)            strcpy(type_str, "powerups");        else            strcpy(type_str, "powerup");
  708. //            strcpy(id_str, Powerup_names[objp->contains_id]);
  709. //        } else if (objp->contains_type == OBJ_ROBOT) {
  710. //            if (objp->contains_count > 1)            strcpy(type_str, "robots");        else            strcpy(type_str, "robot");
  711. //                strcpy(id_str, "something-or-other");
  712. //        } else {
  713. //            strcpy(type_str, "unknown objects");
  714. //            strcpy(id_str, "mysterious");
  715. //        }
  716. //        mprintf(0, "Dropping %i %s %s.\n", objp->contains_count, id_str, type_str);
  717. //    }
  718.  
  719.     switch (objp->contains_type) {
  720.         case OBJ_POWERUP:
  721.             for (count=0; count<objp->contains_count; count++) {
  722.                 int    rand_scale;
  723.                 new_velocity = objp->mtype.phys_info.velocity;
  724.                 old_mag = vm_vec_mag_quick(&objp->mtype.phys_info.velocity);
  725.  
  726.                 //    We want powerups to move more in network mode.
  727.                 if ((Game_mode & GM_MULTI) && !(Game_mode & GM_MULTI_ROBOTS)) {
  728.                     rand_scale = 4;
  729.                     //    extra life powerups are converted to invulnerability in multiplayer, for what is an extra life, anyway?
  730.                     if (objp->contains_id == POW_EXTRA_LIFE)
  731.                         objp->contains_id = POW_INVULNERABILITY;
  732.                 } else
  733.                     rand_scale = 2;
  734.  
  735.                 new_velocity.x += fixmul(old_mag+F1_0*32, rand()*rand_scale - 16384*rand_scale);
  736.                 new_velocity.y += fixmul(old_mag+F1_0*32, rand()*rand_scale - 16384*rand_scale);
  737.                 new_velocity.z += fixmul(old_mag+F1_0*32, rand()*rand_scale - 16384*rand_scale);
  738.  
  739.                 // Give keys zero velocity so they can be tracked better in multi
  740.  
  741.                 if ((Game_mode & GM_MULTI) && (objp->contains_id >= POW_KEY_BLUE) && (objp->contains_id <= POW_KEY_GOLD))
  742.                     vm_vec_zero(&new_velocity);
  743.  
  744.                 new_pos = objp->pos;
  745. //                new_pos.x += (rand()-16384)*8;
  746. //                new_pos.y += (rand()-16384)*8;
  747. //                new_pos.z += (rand()-16384)*8;
  748.  
  749.                 #ifdef NETWORK
  750.                 if (Game_mode & GM_MULTI)
  751.                 {    
  752.                     if (Net_create_loc >= MAX_NET_CREATE_OBJECTS)
  753.                     {
  754.                          mprintf( (0, "Not enough slots to drop all powerups!\n" ));
  755.                         return (-1);
  756.                     }
  757.                 }
  758.                 #endif
  759.  
  760.                 objnum = obj_create( objp->contains_type, objp->contains_id, objp->segnum, &new_pos, &vmd_identity_matrix, Powerup_info[objp->contains_id].size, CT_POWERUP, MT_PHYSICS, RT_POWERUP);
  761.  
  762.                 if (objnum < 0 ) {
  763.                     mprintf((1, "Can't create object in object_create_egg.  Aborting.\n"));
  764.                     Int3();
  765.                     return objnum;
  766.                 }
  767.  
  768.                 #ifdef NETWORK
  769.                 if (Game_mode & GM_MULTI)
  770.                 {
  771.                     Net_create_objnums[Net_create_loc++] = objnum;
  772.                 }
  773.                 #endif
  774.  
  775.                 obj = &Objects[objnum];
  776.  
  777.                 obj->mtype.phys_info.velocity = new_velocity;
  778.  
  779. //                mprintf(0, "Created powerup, object #%i, velocity = %7.3f %7.3f %7.3f\n", objnum, f2fl(new_velocity.x), f2fl(new_velocity.y), f2fl(new_velocity.z));
  780. //                mprintf(0, "                             pos = x=%d y=%d z=%d\n", obj->pos.x, obj->pos.y, obj->pos.z);
  781.  
  782.                 obj->mtype.phys_info.drag = 512;    //1024;
  783.                 obj->mtype.phys_info.mass = F1_0;
  784.  
  785.                 obj->mtype.phys_info.flags = PF_BOUNCE;
  786.  
  787.                 obj->rtype.vclip_info.vclip_num = Powerup_info[obj->id].vclip_num;
  788.                 obj->rtype.vclip_info.frametime = Vclip[obj->rtype.vclip_info.vclip_num].frame_time;
  789.                 obj->rtype.vclip_info.framenum = 0;
  790.  
  791.                 switch (obj->id) {
  792.                     case POW_MISSILE_1:
  793.                     case POW_MISSILE_4:
  794.                     case POW_SHIELD_BOOST:
  795.                     case POW_ENERGY:
  796.                         obj->lifeleft = (rand() + F1_0*3) * 64;        //    Lives for 3 to 3.5 binary minutes (a binary minute is 64 seconds)
  797.                         if (Game_mode & GM_MULTI)
  798.                             obj->lifeleft /= 2;
  799.                         break;
  800.                     default:
  801. //                        if (Game_mode & GM_MULTI)
  802. //                            obj->lifeleft = (rand() + F1_0*3) * 64;        //    Lives for 5 to 5.5 binary minutes (a binary minute is 64 seconds)
  803.                         break;
  804.                 }
  805.             }
  806.             break;
  807.  
  808.         case OBJ_ROBOT:
  809.             for (count=0; count<objp->contains_count; count++) {
  810.                 int    rand_scale;
  811.                 new_velocity = objp->mtype.phys_info.velocity;
  812.                 old_mag = vm_vec_mag_quick(&objp->mtype.phys_info.velocity);
  813.  
  814.                 vm_vec_normalize_quick(&new_velocity);
  815.  
  816.                 //    We want powerups to move more in network mode.
  817. //                if (Game_mode & GM_MULTI)
  818. //                    rand_scale = 4;
  819. //                else
  820.                     rand_scale = 2;
  821.  
  822.                 new_velocity.x += (rand()-16384)*2;
  823.                 new_velocity.y += (rand()-16384)*2;
  824.                 new_velocity.z += (rand()-16384)*2;
  825.  
  826.                 vm_vec_normalize_quick(&new_velocity);
  827.                 vm_vec_scale(&new_velocity, (F1_0*32 + old_mag) * rand_scale);
  828.                 new_pos = objp->pos;
  829.                 //    This is dangerous, could be outside mine.
  830. //                new_pos.x += (rand()-16384)*8;
  831. //                new_pos.y += (rand()-16384)*7;
  832. //                new_pos.z += (rand()-16384)*6;
  833.  
  834.                 objnum = obj_create(OBJ_ROBOT, objp->contains_id, objp->segnum, &new_pos, &vmd_identity_matrix, Polygon_models[Robot_info[ObjId[objp->contains_type]].model_num].rad, CT_AI, MT_PHYSICS, RT_POLYOBJ);
  835.  
  836. mprintf((0, "Object %2i, Scatter vector = [%7.3f %7.3f %7.3f]\n", objnum, f2fl(new_velocity.x), f2fl(new_velocity.y), f2fl(new_velocity.z)));
  837.  
  838.                 if ( objnum < 0 ) {
  839.                     mprintf((1, "Can't create object in object_create_egg, robots.  Aborting.\n"));
  840.                     Int3();
  841.                     return objnum;
  842.                 }
  843.  
  844.                 #ifdef NETWORK
  845.                 if (Game_mode & GM_MULTI)
  846.                 {
  847.                     Net_create_objnums[Net_create_loc++] = objnum;
  848.                 }
  849.                 #endif
  850.  
  851.                 obj = &Objects[objnum];
  852.  
  853.                 //Set polygon-object-specific data 
  854.  
  855.                 obj->rtype.pobj_info.model_num = Robot_info[obj->id].model_num;
  856.                 obj->rtype.pobj_info.subobj_flags = 0;
  857.  
  858.                 //set Physics info
  859.         
  860.                 obj->mtype.phys_info.velocity = new_velocity;
  861.  
  862.                 obj->mtype.phys_info.mass = Robot_info[obj->id].mass;
  863.                 obj->mtype.phys_info.drag = Robot_info[obj->id].drag;
  864.  
  865.                 obj->mtype.phys_info.flags |= (PF_LEVELLING);
  866.  
  867.                 obj->shields = Robot_info[obj->id].strength;
  868.  
  869.                 obj->ctype.ai_info.behavior = AIB_NORMAL;
  870.                 Ai_local_info[obj-Objects].player_awareness_type = PA_WEAPON_ROBOT_COLLISION;
  871.                 Ai_local_info[obj-Objects].player_awareness_time = F1_0*3;
  872.                 obj->ctype.ai_info.CURRENT_STATE = AIS_LOCK;
  873.                 obj->ctype.ai_info.GOAL_STATE = AIS_LOCK;
  874.                 obj->ctype.ai_info.REMOTE_OWNER = -1;
  875.             }
  876.  
  877.             break;
  878.  
  879.         default:
  880.             Error("Error: Illegal type (%i) in object spawning.\n", objp->contains_type);
  881.     }
  882.  
  883.     return objnum;
  884. }
  885.  
  886. //    -------------------------------------------------------------------------------------------------------
  887. //    Put count objects of type type (eg, powerup), id = id (eg, energy) into *objp, then drop them!  Yippee!
  888. //    Returns created object number.
  889. int call_object_create_egg(object *objp, int count, int type, int id)
  890. {
  891.     if (count > 0) {
  892.         objp->contains_count = count;
  893.         objp->contains_type = type;
  894.         objp->contains_id = id;
  895.         return object_create_egg(objp);
  896.     }
  897.  
  898.     return -1;
  899. }
  900.  
  901. //what vclip does this explode with?
  902. int get_explosion_vclip(object *obj,int stage)
  903. {
  904.     if (obj->type==OBJ_ROBOT) {
  905.  
  906.         if (stage==0 && Robot_info[obj->id].exp1_vclip_num>-1)
  907.                 return Robot_info[obj->id].exp1_vclip_num;
  908.         else if (stage==1 && Robot_info[obj->id].exp2_vclip_num>-1)
  909.                 return Robot_info[obj->id].exp2_vclip_num;
  910.  
  911.     }
  912.     else if (obj->type==OBJ_PLAYER && Player_ship->expl_vclip_num>-1)
  913.             return Player_ship->expl_vclip_num;
  914.  
  915.     return VCLIP_SMALL_EXPLOSION;        //default
  916. }
  917.  
  918. //blow up a polygon model
  919. explode_model(object *obj)
  920. {
  921.     Assert(obj->render_type == RT_POLYOBJ);
  922.  
  923.     if (Dying_modelnums[obj->rtype.pobj_info.model_num] != -1)
  924.         obj->rtype.pobj_info.model_num = Dying_modelnums[obj->rtype.pobj_info.model_num];
  925.  
  926.     if (Polygon_models[obj->rtype.pobj_info.model_num].n_models > 1) {
  927.         int i;
  928.  
  929.         for (i=1;i<Polygon_models[obj->rtype.pobj_info.model_num].n_models;i++)
  930.             object_create_debris(obj,i);
  931.  
  932.         //make parent object only draw center part
  933.         obj->rtype.pobj_info.subobj_flags=1;
  934.     }
  935. }
  936.  
  937. //if the object has a destroyed model, switch to it.  Otherwise, delete it.
  938. maybe_delete_object(object *del_obj)
  939. {
  940.     if (Dead_modelnums[del_obj->rtype.pobj_info.model_num] != -1) {
  941.         del_obj->rtype.pobj_info.model_num = Dead_modelnums[del_obj->rtype.pobj_info.model_num];
  942.         del_obj->flags |= OF_DESTROYED;
  943.     }
  944.     else {        //normal, multi-stage explosion
  945.         if (del_obj->type == OBJ_PLAYER)
  946.             del_obj->render_type = RT_NONE;
  947.         else
  948.             del_obj->flags |= OF_SHOULD_BE_DEAD;
  949.     }
  950. }
  951.  
  952. //    -------------------------------------------------------------------------------------------------------
  953. //blow up an object.  Takes the object to destroy, and the point of impact
  954. void explode_object(object *hitobj,fix delay_time)
  955. {
  956.     if (hitobj->flags & OF_EXPLODING) return;
  957.  
  958.     if (delay_time) {        //wait a little while before creating explosion
  959.         int objnum;
  960.         object *obj;
  961.  
  962.         //create a placeholder object to do the delay, with id==-1
  963.  
  964.         objnum = obj_create( OBJ_FIREBALL,-1,hitobj->segnum,&hitobj->pos,&vmd_identity_matrix,0,
  965.                         CT_EXPLOSION,MT_NONE,RT_NONE);
  966.     
  967.         if (objnum < 0 ) {
  968.             maybe_delete_object(hitobj);        //no explosion, die instantly
  969.             mprintf((1,"Couldn't start explosion, deleting object now\n"));
  970.             Int3();
  971.             return;
  972.         }
  973.     
  974.         obj = &Objects[objnum];
  975.     
  976.         //now set explosion-specific data
  977.     
  978.         obj->lifeleft = delay_time;
  979.         obj->ctype.expl_info.delete_objnum = hitobj-Objects;
  980. #ifndef NDEBUG
  981.         if (obj->ctype.expl_info.delete_objnum < 0)
  982.          Int3(); // See Rob!
  983. #endif
  984.         obj->ctype.expl_info.delete_time = -1;
  985.         obj->ctype.expl_info.spawn_time = 0;
  986.  
  987.     }
  988.     else {
  989.         object *expl_obj;
  990.         int vclip_num;
  991.  
  992.         vclip_num = get_explosion_vclip(hitobj,0);
  993.  
  994.         expl_obj = object_create_explosion(hitobj->segnum, &hitobj->pos, fixmul(hitobj->size,EXPLOSION_SCALE), vclip_num );
  995.     
  996.         if (! expl_obj) {
  997.             maybe_delete_object(hitobj);        //no explosion, die instantly
  998.             mprintf((0,"Couldn't start explosion, deleting object now\n"));
  999.             return;
  1000.         }
  1001.  
  1002.         //don't make debris explosions have physics, because they often
  1003.         //happen when the debris has hit the wall, so the fireball is trying
  1004.         //to move into the wall, which shows off FVI problems.       
  1005.         if (hitobj->type!=OBJ_DEBRIS && hitobj->movement_type==MT_PHYSICS) {
  1006.             expl_obj->movement_type = MT_PHYSICS;
  1007.             expl_obj->mtype.phys_info = hitobj->mtype.phys_info;
  1008.         }
  1009.     
  1010.         if (hitobj->render_type==RT_POLYOBJ && hitobj->type!=OBJ_DEBRIS)
  1011.             explode_model(hitobj);
  1012.  
  1013.         maybe_delete_object(hitobj);
  1014.     }
  1015.  
  1016.     hitobj->flags |= OF_EXPLODING;        //say that this is blowing up
  1017.     hitobj->control_type = CT_NONE;        //become inert while exploding
  1018.  
  1019. }
  1020.  
  1021.  
  1022. //do whatever needs to be done for this piece of debris for this frame
  1023. void do_debris_frame(object *obj)
  1024. {
  1025.     Assert(obj->control_type == CT_DEBRIS);
  1026.  
  1027.     if (obj->lifeleft < 0)
  1028.         explode_object(obj,0);
  1029.  
  1030. }
  1031.  
  1032. //do whatever needs to be done for this explosion for this frame
  1033. void do_explosion_sequence(object *obj)
  1034. {
  1035.     Assert(obj->control_type == CT_EXPLOSION);
  1036.  
  1037.     //mprintf( 0, "Object %d life left is %d\n", obj-Objects, obj->lifeleft );
  1038.  
  1039.     //See if we should die of old age
  1040.     if (obj->lifeleft <= 0 )     {    // We died of old age
  1041.         obj->flags |= OF_SHOULD_BE_DEAD;
  1042.         obj->lifeleft = 0;
  1043.     }
  1044.  
  1045.     //See if we should create a secondary explosion
  1046.     if (obj->lifeleft <= obj->ctype.expl_info.spawn_time) {
  1047.         object *expl_obj,*del_obj;
  1048.         int vclip_num;
  1049.         vms_vector *spawn_pos;
  1050.  
  1051.         if ((obj->ctype.expl_info.delete_objnum < 0) || (obj->ctype.expl_info.delete_objnum > Highest_object_index)) {
  1052.             mprintf((0, "Illegal value for delete_objnum in fireball.c\n"));
  1053.             Int3(); // get Rob, please... thanks
  1054.             return;
  1055.         }
  1056.  
  1057.         del_obj = &Objects[obj->ctype.expl_info.delete_objnum];
  1058.  
  1059.         spawn_pos = &del_obj->pos;
  1060.  
  1061.         Assert(del_obj->type==OBJ_ROBOT || del_obj->type==OBJ_CLUTTER || del_obj->type==OBJ_CNTRLCEN || del_obj->type == OBJ_PLAYER);
  1062.         Assert(del_obj->segnum != -1);
  1063.  
  1064.         vclip_num = get_explosion_vclip(del_obj,1);
  1065.  
  1066.         expl_obj = object_create_explosion( del_obj->segnum, spawn_pos, fixmul(del_obj->size, EXPLOSION_SCALE), vclip_num );
  1067.  
  1068.         if ((del_obj->contains_count > 0) && !(Game_mode & GM_MULTI)) { // Multiplayer handled outside of this code!!
  1069.             //    If dropping a weapon that the player has, drop energy instead, unless it's vulcan, in which case drop vulcan ammo.
  1070.             if (del_obj->contains_type == OBJ_POWERUP)
  1071.                 maybe_replace_powerup_with_energy(del_obj);
  1072.             object_create_egg(del_obj);
  1073.         } else if ((del_obj->type == OBJ_ROBOT) && !(Game_mode & GM_MULTI)) { // Multiplayer handled outside this code!!
  1074.             robot_info    *robptr = &Robot_info[del_obj->id];
  1075.             if (robptr->contains_count) {
  1076.                 if (((rand() * 16) >> 15) < robptr->contains_prob) {
  1077.                     del_obj->contains_count = ((rand() * robptr->contains_count) >> 15) + 1;
  1078.                     del_obj->contains_type = robptr->contains_type;
  1079.                     del_obj->contains_id = robptr->contains_id;
  1080.                     maybe_replace_powerup_with_energy(del_obj);
  1081.                     object_create_egg(del_obj);
  1082.                 }
  1083.             }
  1084.         }
  1085.  
  1086.         if ( Robot_info[del_obj->id].exp2_sound_num > -1 )
  1087.             digi_link_sound_to_pos( Robot_info[del_obj->id].exp2_sound_num, del_obj->segnum, 0, spawn_pos, 0, F1_0 );
  1088.             //PLAY_SOUND_3D( Robot_info[del_obj->id].exp2_sound_num, spawn_pos, del_obj->segnum  );
  1089.  
  1090.         // mprintf( 0, "Spawned an explosion of type %d\n", Robot_info[del_obj->id].exp2_vclip_num );
  1091.  
  1092.         //mprintf( 0, "Object %d spawned.\n", obj-Objects );
  1093.         //mprintf( 0, "Explosion at %d,%d,%d\n", obj->pos.x, obj->pos.y, obj->pos.z );
  1094.         //mprintf( 0, "Explosion at %d,%d,%d\n", obj->pos.x, obj->pos.y, obj->pos.z );
  1095.         //mprintf( 0, "Spawned exp at %d,%d,%d\n", expl_obj->pos.x, expl_obj->pos.y, expl_obj->pos.z );
  1096.  
  1097.         obj->ctype.expl_info.spawn_time = -1;
  1098.  
  1099.         //make debris
  1100.         if (del_obj->render_type==RT_POLYOBJ)
  1101.             explode_model(del_obj);        //explode a polygon model
  1102.  
  1103.         //set some parm in explosion
  1104.         if (expl_obj) {
  1105.  
  1106.             if (del_obj->movement_type == MT_PHYSICS) {
  1107.                 expl_obj->movement_type = MT_PHYSICS;
  1108.                 expl_obj->mtype.phys_info = del_obj->mtype.phys_info;
  1109.             }
  1110.  
  1111.             expl_obj->ctype.expl_info.delete_time = expl_obj->lifeleft/2;
  1112.             expl_obj->ctype.expl_info.delete_objnum = del_obj-Objects;
  1113. #ifndef NDEBUG
  1114.             if (obj->ctype.expl_info.delete_objnum < 0)
  1115.                   Int3(); // See Rob!
  1116. #endif
  1117.  
  1118.         }
  1119.         else {
  1120.             maybe_delete_object(del_obj);
  1121.             mprintf((0,"Couldn't create secondary explosion, deleting object now\n"));
  1122.         }
  1123.  
  1124.     }
  1125.  
  1126.     //See if we should delete an object
  1127.     if (obj->lifeleft <= obj->ctype.expl_info.delete_time) {
  1128.         object *del_obj = &Objects[obj->ctype.expl_info.delete_objnum];
  1129.  
  1130.         obj->ctype.expl_info.delete_time = -1;
  1131.  
  1132.         maybe_delete_object(del_obj);
  1133.     }
  1134. }
  1135.  
  1136. typedef struct expl_wall {
  1137.     int segnum,sidenum;
  1138.     fix time;
  1139. } expl_wall;
  1140.  
  1141. #define MAX_EXPLODING_WALLS             10
  1142. #define EXPL_WALL_TIME                    (f1_0)
  1143. #define EXPL_WALL_TOTAL_FIREBALLS    32
  1144. #define EXPL_WALL_FIREBALL_SIZE         0x48000    //smallest size
  1145.  
  1146. expl_wall expl_wall_list[MAX_EXPLODING_WALLS];
  1147. //--unused-- int n_exploding_walls;
  1148.  
  1149. void init_exploding_walls()
  1150. {
  1151.     int i;
  1152.  
  1153.     for (i=0;i<MAX_EXPLODING_WALLS;i++)
  1154.         expl_wall_list[i].segnum = -1;
  1155. }
  1156.  
  1157. //explode the given wall
  1158. void explode_wall(int segnum,int sidenum)
  1159. {
  1160.     int i;
  1161.     vms_vector pos;
  1162.  
  1163.     //find a free slot
  1164.  
  1165.     for (i=0;i<MAX_EXPLODING_WALLS && expl_wall_list[i].segnum != -1;i++);
  1166.  
  1167.     if (i==MAX_EXPLODING_WALLS) {        //didn't find slot.
  1168.         mprintf((0,"Couldn't find free slot for exploding wall!\n"));
  1169.         Int3();
  1170.         return;
  1171.     }
  1172.  
  1173.     expl_wall_list[i].segnum    = segnum;
  1174.     expl_wall_list[i].sidenum    = sidenum;
  1175.     expl_wall_list[i].time        = 0;
  1176.  
  1177.     //play one long sound for whole door wall explosion
  1178.     compute_center_point_on_side(&pos,&Segments[segnum],sidenum);
  1179.     digi_link_sound_to_pos( SOUND_EXPLODING_WALL,segnum, sidenum, &pos, 0, F1_0 );
  1180.  
  1181. }
  1182.  
  1183. //handle walls for this frame
  1184. //note: this wall code assumes the wall is not triangulated
  1185. void do_exploding_wall_frame()
  1186. {
  1187.     int i;
  1188.  
  1189.     for (i=0;i<MAX_EXPLODING_WALLS;i++) {
  1190.         int segnum = expl_wall_list[i].segnum;
  1191.  
  1192.         if (segnum != -1) {
  1193.             int sidenum = expl_wall_list[i].sidenum;
  1194.             fix oldfrac,newfrac;
  1195.             int old_count,new_count,e;        //n,
  1196.  
  1197.             oldfrac = fixdiv(expl_wall_list[i].time,EXPL_WALL_TIME);
  1198.  
  1199.             expl_wall_list[i].time += FrameTime;
  1200.             if (expl_wall_list[i].time > EXPL_WALL_TIME)
  1201.                 expl_wall_list[i].time = EXPL_WALL_TIME;
  1202.  
  1203.             if (expl_wall_list[i].time>(EXPL_WALL_TIME*3)/4) {
  1204.                 segment *seg,*csegp;
  1205.                 int cside,a,n;
  1206.  
  1207.                 seg = &Segments[segnum];
  1208.  
  1209.                 a = Walls[seg->sides[sidenum].wall_num].clip_num;
  1210.                 n = WallAnims[a].num_frames;
  1211.  
  1212.                 csegp = &Segments[seg->children[sidenum]];
  1213.                 cside = find_connect_side(seg, csegp);
  1214.  
  1215.                 wall_set_tmap_num(seg,sidenum,csegp,cside,a,n-1);
  1216.             }
  1217.  
  1218.             newfrac = fixdiv(expl_wall_list[i].time,EXPL_WALL_TIME);
  1219.  
  1220.             old_count = f2i(EXPL_WALL_TOTAL_FIREBALLS * fixmul(oldfrac,oldfrac));
  1221.             new_count = f2i(EXPL_WALL_TOTAL_FIREBALLS * fixmul(newfrac,newfrac));
  1222.  
  1223.             //n = new_count - old_count;
  1224.  
  1225. //            mprintf(0,"Creating %d new explosions\n",new_count-old_count);
  1226.  
  1227.             //now create all the next explosions
  1228.  
  1229.             for (e=old_count;e<new_count;e++) {
  1230.                 short            vertnum_list[4];
  1231.                 vms_vector    *v0,*v1,*v2;
  1232.                 vms_vector    vv0,vv1,pos;
  1233.                 fix            size;
  1234.  
  1235.                 //calc expl position
  1236.  
  1237.                 get_side_verts(vertnum_list,segnum,sidenum);
  1238.  
  1239.                 v0 = &Vertices[vertnum_list[0]];
  1240.                 v1 = &Vertices[vertnum_list[1]];
  1241.                 v2 = &Vertices[vertnum_list[2]];
  1242.  
  1243.                 vm_vec_sub(&vv0,v0,v1);
  1244.                 vm_vec_sub(&vv1,v2,v1);
  1245.  
  1246.                 vm_vec_scale_add(&pos,v1,&vv0,rand()*2);
  1247.                 vm_vec_scale_add2(&pos,&vv1,rand()*2);
  1248.  
  1249.                 size = EXPL_WALL_FIREBALL_SIZE + (2*EXPL_WALL_FIREBALL_SIZE * e / EXPL_WALL_TOTAL_FIREBALLS);
  1250.  
  1251.                 //fireballs start away from door, with subsequent ones getting closer
  1252.                 #ifdef COMPACT_SEGS    
  1253.                     {
  1254.                     vms_vector _vn;
  1255.                     get_side_normal(&Segments[segnum], sidenum, 0, &_vn );
  1256.                     vm_vec_scale_add2(&pos,&_vn,size*(EXPL_WALL_TOTAL_FIREBALLS-e)/EXPL_WALL_TOTAL_FIREBALLS);
  1257.                     }
  1258.                 #else
  1259.                     vm_vec_scale_add2(&pos,&Segments[segnum].sides[sidenum].normals[0],size*(EXPL_WALL_TOTAL_FIREBALLS-e)/EXPL_WALL_TOTAL_FIREBALLS);
  1260.                 #endif
  1261.  
  1262.                 if (e & 3)        //3 of 4 are normal
  1263.                     object_create_explosion(expl_wall_list[i].segnum,&pos,size,VCLIP_SMALL_EXPLOSION);
  1264.                 else
  1265.                     object_create_badass_explosion( NULL, expl_wall_list[i].segnum, &pos, 
  1266.                     size, 
  1267.                     VCLIP_SMALL_EXPLOSION,
  1268.                     i2f(4),        // damage strength
  1269.                     i2f(20),        //    damage radius
  1270.                     i2f(50),        //    damage force
  1271.                     -1        //    parent id
  1272.                     );
  1273.  
  1274.  
  1275.             } 
  1276.  
  1277.             if (expl_wall_list[i].time >= EXPL_WALL_TIME)
  1278.                 expl_wall_list[i].segnum = -1;    //flag this slot as free
  1279.  
  1280.         }
  1281.     }
  1282.  
  1283. }
  1284.  
  1285. 
  1286.