/*=============================================================================
    Name    : Collision
    Purpose : Handles object collisions

    Created 9/25/1997 by gshaw
    Copyright Relic Entertainment, Inc.  All rights reserved.
=============================================================================*/

#include "types.h"
#include "fastmath.h"
#include "vector.h"
#include "matrix.h"
#include "spaceobj.h"
#include "prim3d.h"
#include "render.h"
#include "collision.h"
#include "universe.h"
#include "univupdate.h"
#include "blobs.h"
#include "SalCapCorvette.h"
#include "DefenseFighter.h"
#include "DFGFrigate.h"
#include "nis.h"
#include "tactics.h"
#include "alliance.h"
#include "singleplayer.h"

BlobProperties collBlobProperties;

#ifdef COLLISION_CHECK_STATS

udword shipsavoidingstuff;
udword shipsavoidedwalks;
udword shipsavoidedchecks;

udword shipshipwalks;
udword shipresourcewalks;
udword shipderelictwalks;
udword shipshipchecks;
udword shipresourcechecks;
udword shipderelictchecks;

udword bulletwalks;
udword bulletchecks;

udword missilewalks;
udword missilechecks;

udword minewalks;
udword minechecks;

#endif

/*-----------------------------------------------------------------------------
    Name        : RangeToTarget
    Description : Returns range from ship to target, taking into account collision
                  information of target.
    Inputs      : ship, target, trajectory (which is target->posinfo.position - ship->posinfo.position)
    Outputs     :
    Return      : range (in metres) to target
----------------------------------------------------------------------------*/
real32 RangeToTarget(Ship *ship,SpaceObjRotImpTarg *target,vector *trajectory)
{
    real32 dist;

    dist = fsqrt(vecMagnitudeSquared(*trajectory));
    dist -= target->staticinfo->staticheader.staticCollInfo.approxcollspheresize;

    if (dist > 0.0f)
    {
        return dist;
    }
    else
    {
        return 0.0f;
    }
}

/*-----------------------------------------------------------------------------
    Name        : RangeToTargetGivenDist
    Description : Returns range from ship to target, taking into account collision
                  information of target.
                  Takes the additional parameter dist if it has been pre-calculated
    Inputs      : ship, target, trajectory (which is target->posinfo.position - ship->posinfo.position), dist
    Outputs     :
    Return      : range (in metres) to target
----------------------------------------------------------------------------*/
real32 RangeToTargetGivenDist(Ship *ship,SpaceObjRotImpTarg *target,real32 dist)
{
    dist -= target->staticinfo->staticheader.staticCollInfo.approxcollspheresize;

    if (dist > 0.0f)
    {
        return dist;
    }
    else
    {
        return 0.0f;
    }
}

/*-----------------------------------------------------------------------------
    Name        : collUpdateCollRectangle
    Description : updates collision rectangle information
    Inputs      : irobj
    Outputs     :
    Return      :
----------------------------------------------------------------------------*/
void collUpdateCollRectangle(SpaceObjRotImp *irobj)
{
    StaticCollInfo *staticCollInfo = &irobj->staticinfo->staticheader.staticCollInfo;
    CollInfo *collInfo = &irobj->collInfo;
    vector upvector;
    vector rightvector;
    vector forwardvector;

    if (staticCollInfo->uplength == 0)
    {
        return;         // don't bother for all-zero rectangles
    }

    matMultiplyMatByVec(&collInfo->rectpos[0],&(irobj->rotinfo.coordsys),&staticCollInfo->collrectoffset);

    matGetVectFromMatrixCol1(upvector,irobj->rotinfo.coordsys);
    matGetVectFromMatrixCol2(rightvector,irobj->rotinfo.coordsys);
    matGetVectFromMatrixCol3(forwardvector,irobj->rotinfo.coordsys);

    vecMultiplyByScalar(upvector,staticCollInfo->uplength);
    vecMultiplyByScalar(rightvector,staticCollInfo->rightlength);
    vecMultiplyByScalar(forwardvector,staticCollInfo->forwardlength);

    vecAdd(collInfo->rectpos[1],collInfo->rectpos[0],rightvector);
    vecAdd(collInfo->rectpos[2],collInfo->rectpos[1],forwardvector);
    vecAdd(collInfo->rectpos[3],collInfo->rectpos[0],forwardvector);

    vecAdd(collInfo->rectpos[4],collInfo->rectpos[0],upvector);
    vecAdd(collInfo->rectpos[5],collInfo->rectpos[1],upvector);
    vecAdd(collInfo->rectpos[6],collInfo->rectpos[2],upvector);
    vecAdd(collInfo->rectpos[7],collInfo->rectpos[3],upvector);
}

#define LEFT    -1
#define MIDDLE  0
#define RIGHT   1

/*-----------------------------------------------------------------------------
    Name        : collCheckRectLine
    Description : checks for a collision between a rectangle and a line of length linelength
    Inputs      : obj1 (object to check collision with), univpoint (origin of line in universe coordinates),
                  univdir (unit vector direction of line in universe coordinates), linelength
    Outputs     : collSide
                  if hit on a side, up == 0 (TRANS_UP), down == 1, right == 2, left == 3, forward == 4, back == 5
                  otherwise collSide == -1
    Return      : -1.0 if line misses, 0.0 if line origin is inside rectangle, else +x if line collides where x is the distance
                  from the line origin to the collision point
----------------------------------------------------------------------------*/
real32 collCheckRectLine(SpaceObjRotImp *obj1,vector *univpoint,vector *univdir,real32 linelength,sdword *collSide)
{
    StaticCollInfo *staticCollInfo = &obj1->staticinfo->staticheader.staticCollInfo;
    vector pointInWorld;
    real32 minB[3];
    real32 maxB[3];
    real32 origin[3];
    real32 dir[3];
    sdword quadrant[3];
    real32 maxT[3];
    real32 candidatePlane[3];
    real32 coord;
    real32 largestMaxT;
    sdword i;
    sdword whichPlane;
    bool inside;

    *collSide = -1;

    if (staticCollInfo->uplength == 0)
    {
        return 0.0f;            // if no rectangle information present, assume collision
    }

    // axis aligned bounding box in object co-ordinates
    minB[0] = staticCollInfo->collrectoffset.x;
    minB[1] = staticCollInfo->collrectoffset.y;
    minB[2] = staticCollInfo->collrectoffset.z;
    maxB[0] = minB[0] + staticCollInfo->uplength;
    maxB[1] = minB[1] + staticCollInfo->rightlength;
    maxB[2] = minB[2] + staticCollInfo->forwardlength;

    // convert univpoint,univdir from universe to object co-ordinates

    vecSub(pointInWorld,*univpoint,obj1->posinfo.position);
    matMultiplyVecByMat((vector *)origin,&pointInWorld,&obj1->rotinfo.coordsys);

    // now we have minB, maxB, origin, dir in object co-ordinates
    // use a ray-box intersection algorithm as found in Graphics Gems I

    inside = TRUE;

    for (i=0;i<3;i++)
    {
        if (origin[i] < minB[i])
        {
            quadrant[i] = LEFT;
            candidatePlane[i] = minB[i];
            inside = FALSE;
        }
        else if (origin[i] > maxB[i])
        {
            quadrant[i] = RIGHT;
            candidatePlane[i] = maxB[i];
            inside = FALSE;
        }
        else
        {
            quadrant[i] = MIDDLE;
        }
    }

    if (inside)
    {
        return 0.0f;
    }

    if (vecDotProduct(pointInWorld,*univdir) > 0.0f)
    {
        return -1.0f;
    }

    matMultiplyVecByMat((vector *)dir,univdir,&obj1->rotinfo.coordsys);

    // calculate distances to candidate planes
    for (i=0;i<3;i++)
    {
        if ((quadrant[i] != MIDDLE) && (dir[i] != 0.0f))
        {
            maxT[i] = (candidatePlane[i]-origin[i]) / dir[i];
        }
        else
        {
            maxT[i] = -1.0f;
        }
    }

    // find largest maxT for final choice of intersection
    whichPlane = 0;
    for (i=1;i<3;i++)
    {
        if (maxT[i] > maxT[whichPlane])
        {
            whichPlane = i;
        }
    }

    largestMaxT = maxT[whichPlane];
    if (largestMaxT < 0.0f)
    {
        return -1.0f;
    }

    if (largestMaxT > linelength)
    {
        return -1.0f;
    }

    // Check final candidate if it is actually inside box
    for (i=0;i<3;i++)
    {
        if (whichPlane == i)
        {
            continue;
        }

        coord = origin[i] + largestMaxT * dir[i];

        if ((coord <= minB[i]) || (coord >= maxB[i]))
        {
            return -1.0f;
        }
    }

    // Set collSide baesd on where the plane intersected the box:
    // up == 0 (TRANS_UP), down == 1, right == 2, left == 3, forward == 4, back == 5
    *collSide = (whichPlane<<1) + ((quadrant[whichPlane] == LEFT) ? 1 : 0);
    dbgAssert((*collSide >= 0) && (*collSide < NUM_TRANS_DEGOFFREEDOM));
    return largestMaxT;
}

/*-----------------------------------------------------------------------------
    Name        : collCheckRectPoint
    Description : checks if collision between rectangle of obj1 and point (if point is inside obj1 rectangle)
    Inputs      : obj1, point
    Outputs     :
    Return      :
----------------------------------------------------------------------------*/
bool collCheckRectPoint(SpaceObjRotImp *obj1,vector *point)
{
    vector rect0;
    vector rect6;

    vector upvector;
    vector rightvector;
    vector forwardvector;

    vector rect0topoint;
    vector rect6topoint;

    if (obj1->staticinfo->staticheader.staticCollInfo.uplength == 0)
    {
        return TRUE;            // if no rectangle information present, assume collision
    }

    vecAdd(rect0,obj1->collInfo.rectpos[0],obj1->posinfo.position);
    vecAdd(rect6,obj1->collInfo.rectpos[6],obj1->posinfo.position);

    matGetVectFromMatrixCol1(upvector,obj1->rotinfo.coordsys);
    matGetVectFromMatrixCol2(rightvector,obj1->rotinfo.coordsys);
    matGetVectFromMatrixCol3(forwardvector,obj1->rotinfo.coordsys);

    vecSub(rect0topoint,*point,rect0);
    vecSub(rect6topoint,*point,rect6);

    // reject if point is forward of forward face
    if (vecDotProduct(forwardvector,rect6topoint) > 0.0f)
    {
        return FALSE;
    }

    // reject if point is behind behind face
    if (vecDotProduct(forwardvector,rect0topoint) < 0.0f)
    {
        return FALSE;
    }

    // reject if point is right of right face
    if (vecDotProduct(rightvector,rect6topoint) > 0.0f)
    {
        return FALSE;
    }

    // reject if point is left of left face
    if (vecDotProduct(rightvector,rect0topoint) < 0.0f)
    {
        return FALSE;
    }

    // reject if point is above up face
    if (vecDotProduct(upvector,rect6topoint) > 0.0f)
    {
        return FALSE;
    }

    // reject if point is below bottom face
    if (vecDotProduct(upvector,rect0topoint) < 0.0f)
    {
        return FALSE;
    }

    return TRUE;
}

/*-----------------------------------------------------------------------------
    Name        : collCheckRectInRect
    Description : checks if rectangle obj1 is in obj2
    Inputs      : obj1, obj2
    Outputs     :
    Return      :
----------------------------------------------------------------------------*/
bool collCheckRectInRect(SpaceObjRotImp *obj1,SpaceObjRotImp *obj2)
{
    vector rect0;
    vector rect6;

    vector upvector;
    vector rightvector;
    vector forwardvector;

    vector rect0topoint;
    vector rect6topoint;

    vector obj2point;

    udword i;
    bool pointinside = FALSE;

    matGetVectFromMatrixCol1(upvector,obj1->rotinfo.coordsys);
    matGetVectFromMatrixCol2(rightvector,obj1->rotinfo.coordsys);
    matGetVectFromMatrixCol3(forwardvector,obj1->rotinfo.coordsys);

    vecAdd(rect0,obj1->collInfo.rectpos[0],obj1->posinfo.position);
    vecAdd(rect6,obj1->collInfo.rectpos[6],obj1->posinfo.position);

    for (i=0;i<8;i++)
    {
        vecAdd(obj2point,obj2->collInfo.rectpos[i],obj2->posinfo.position);

        vecSub(rect0topoint,obj2point,rect0);
        vecSub(rect6topoint,obj2point,rect6);

        // reject if point is forward of forward face
        if (vecDotProduct(forwardvector,rect6topoint) > (real32)1e-6)
        {
            continue;
        }

        // reject if point is behind behind face
        if (vecDotProduct(forwardvector,rect0topoint) < (real32)-1e-6)
        {
            continue;
        }

        // reject if point is right of right face
        if (vecDotProduct(rightvector,rect6topoint) > (real32)1e-6)
        {
            continue;
        }

        // reject if point is left of left face
        if (vecDotProduct(rightvector,rect0topoint) < (real32)-1e-6)
        {
            continue;
        }

        // reject if point is above up face
        if (vecDotProduct(upvector,rect6topoint) > (real32)1e-6)
        {
            continue;
        }

        // reject if point is below bottom face
        if (vecDotProduct(upvector,rect0topoint) < (real32)-1e-6)
        {
            continue;
        }

        pointinside = TRUE;
        break;
    }

    return pointinside;
}

/*-----------------------------------------------------------------------------
    Name        : collCheckRectBumpCollision
    Description : checks if two rectangles are colliding
    Inputs      :
    Outputs     :
    Return      :
----------------------------------------------------------------------------*/
bool collCheckRectBumpCollision(SpaceObjRotImp *obj1,SpaceObjRotImp *obj2)
{
    sdword collSide;
    vector sourcePosition;
    vector univdir;
    real32 linelength;
    real32 temp;

    if (obj1->staticinfo->staticheader.staticCollInfo.uplength == 0)
    {
        return TRUE;            // if no rectangle information present, assume collision
    }

    if (obj2->staticinfo->staticheader.staticCollInfo.uplength == 0)
    {
        return TRUE;            // if no rectangle information present, assume collision
    }

    if (collCheckRectInRect(obj1,obj2) || collCheckRectInRect(obj2,obj1))
    {
        return TRUE;
    }

    // now check case if both rectangles are colliding, but points are not inside each other
    vecAdd(sourcePosition,obj1->collInfo.rectpos[0],obj1->posinfo.position);

    vecSub(univdir,obj1->collInfo.rectpos[6],obj1->collInfo.rectpos[0]);

    linelength = obj1->staticinfo->staticheader.staticCollInfo.diagonallength;
    vecDivideByScalar(univdir,linelength,temp);

    if (collCheckRectLine(obj2,&sourcePosition,&univdir,linelength,&collSide) >= 0.0f)
    {
        return TRUE;
    }

    return FALSE;
}

/*-----------------------------------------------------------------------------
    Name        : collZeroRectInfo
    Description : zero's out all rectangular information so it's not used
    Inputs      : irobj
    Outputs     :
    Return      :
----------------------------------------------------------------------------*/
void collZeroRectInfo(StaticCollInfo *staticCollInfo)
{
    staticCollInfo->collrectoffset.x = 0.0f;
    staticCollInfo->collrectoffset.y = 0.0f;
    staticCollInfo->collrectoffset.z = 0.0f;

    staticCollInfo->uplength = 0.0f;
    staticCollInfo->rightlength = 0.0f;
    staticCollInfo->forwardlength = 0.0f;
}

/*-----------------------------------------------------------------------------
    Name        : collDrawCollisionInfo
    Description : draws all collision info for debugging purposes
    Inputs      : irobj
    Outputs     :
    Return      :
----------------------------------------------------------------------------*/
void collDrawCollisionInfo(SpaceObjRotImp *irobj)
{
    vector position;
    vector univrectpos[8];
    udword i;

    if (irobj->staticinfo->staticheader.staticCollInfo.uplength == 0)
    {
        return;         // don't bother for all-zero rectangles
    }

    position = irobj->posinfo.position;

    for (i=0;i<8;i++)
    {
        vecAdd(univrectpos[i],position,irobj->collInfo.rectpos[i]);
    }

    rndLightingEnable(FALSE);
    rndTextureEnable(FALSE);

    primLine3(&univrectpos[0],&univrectpos[1],colRGB(255,255,255));
    primLine3(&univrectpos[1],&univrectpos[2],colRGB(255,0,0));
    primLine3(&univrectpos[2],&univrectpos[3],colRGB(0,255,0));
    primLine3(&univrectpos[3],&univrectpos[0],colRGB(0,0,255));

    primLine3(&univrectpos[4],&univrectpos[5],colRGB(200,200,200));
    primLine3(&univrectpos[5],&univrectpos[6],colRGB(255,255,0));
    primLine3(&univrectpos[6],&univrectpos[7],colRGB(0,255,255));
    primLine3(&univrectpos[7],&univrectpos[4],colRGB(255,0,255));

    primLine3(&univrectpos[0],&univrectpos[4],colRGB(50,50,50));
    primLine3(&univrectpos[1],&univrectpos[5],colRGB(50,50,50));
    primLine3(&univrectpos[2],&univrectpos[6],colRGB(50,50,50));
    primLine3(&univrectpos[3],&univrectpos[7],colRGB(50,50,50));

    rndLightingEnable(TRUE);
}

#if 0
/*-----------------------------------------------------------------------------
    Name        :
    Description :
    Inputs      :
    Outputs     :
    Return      :
----------------------------------------------------------------------------*/
void collAddSpaceObjToCollBlobs(SpaceObj *obj)
{
    bobAddObjToNearestBlob(&universe.collBlobList,obj);
}
#endif

/*-----------------------------------------------------------------------------
    Name        : collUpdateCollBlobs
    Description : Updates collision blobs
    Inputs      :
    Outputs     :
    Return      :
----------------------------------------------------------------------------*/
void collUpdateCollBlobs(void)
{
    if (universe.collUpdateAllBlobs)
    {
        universe.collUpdateAllBlobs = FALSE;
        bobListDelete(&universe.collBlobList);
    }
    bobListCreate(&collBlobProperties, &universe.collBlobList, 0);
}


/*-----------------------------------------------------------------------------
    Name        : collUpdateObjsInCollBlobs
    Description : Updates objects in collision blobs
    Inputs      :
    Outputs     :
    Return      :
----------------------------------------------------------------------------*/
void collUpdateObjsInCollBlobs(void)
{
    Node *blobnode = universe.collBlobList.head;
    blob *thisBlob;

    while (blobnode != NULL)
    {
        thisBlob = (blob *)listGetStructOfNode(blobnode);

        bobUpdateObjsInBlobCollInfo(thisBlob);

        blobnode = blobnode->next;
    }
}

//function "fixes" a ships velocity and forces such that
//a collision looks good, as opposed to physically correct...

void kamikazeVelocityFix(SpaceObjRotImpTarg *obj1, SpaceObjRotImpTarg *obj2)
{

}

bool kamikazeCheck(SpaceObjRotImpTarg *obj1, SpaceObjRotImpTarg *obj2)
{
    real32 damage1,damage2;
    sdword ret = FALSE;
    sdword killer1type,killer1index;
    sdword killer2type,killer2index;
    Ship *ship1,*ship2;
    ship1 = (Ship *)obj1;
    ship2 = (Ship *)obj2;

    killer1type = killer2type = DEATH_Killed_By_Collision;
    killer1index = killer2index = 99;
    damage1=0.0f;
    damage2=0.0f;

    if(ship1->objtype == OBJ_ShipType)
    {
        if(bitTest(ship1->specialFlags,SPECIAL_Kamikaze))
        {
            //vector einsteinVel;
            //real32 shipvel,damFactor;
            //vecSub(einsteinVel,obj1->posinfo.velocity,obj2->posinfo.velocity);
            //shipvel = fsqrt(vecMagnitudeSquared(einsteinVel));
            //damFactor = shipvel/ship1->staticinfo->staticheader.maxvelocity;
            if(ship1->attackvars.attacktarget == obj2)
            {
                //ship was kamikazeeing and has collided with it's target
                if(obj2->objtype == OBJ_ShipType)
                {
                    damage1 = ((Ship *)obj2)->staticinfo->maxhealth;//*damFactor;
                }
                else
                {
                    damage1 = ((Ship *)obj1)->staticinfo->maxhealth+1;   //set very big so kamikaze ship dies
                }
                damage2 = kamikazeDamage[((Ship *)obj1)->shiptype];//*damFactor;
                if (ship2->staticinfo->shipclass == CLASS_Fighter || ship2->staticinfo->shipclass == CLASS_Corvette)
                {                                           //if target was a strike craft
                    damage2 = min(damage2, ship1->staticinfo->maxhealth);//make sure strike craft cannot destroy targets more powerful than themselves
                }
                killer1type = DEATH_Killed_By_Kamikaze;
                killer1index = ship1->playerowner->playerIndex;
                ret = TRUE;
            }
        }
    }

    //if ship 2 was kamikazing
    if(ship2->objtype == OBJ_ShipType)
    {
        if(bitTest(ship2->specialFlags,SPECIAL_Kamikaze))
        {
            //vector einsteinVel;
            //real32 shipvel,damFactor;
            //vecSub(einsteinVel,obj1->posinfo.velocity,obj2->posinfo.velocity);
            //shipvel = fsqrt(vecMagnitudeSquared(einsteinVel));
            //vecSub(einsteinVel,obj1->posinfo.velocity,obj2->posinfo.velocity);
            //shipvel = fsqrt(vecMagnitudeSquared(einsteinVel));
            //damFactor = shipvel/ship2->staticinfo->staticheader.maxvelocity;
            if(ship2->attackvars.attacktarget == obj1)
            {
                //ship was kamikazeeing and has collided with it's target
                if(obj1->objtype == OBJ_ShipType)
                {
                    damage2 += ((Ship *)obj1)->staticinfo->maxhealth;//*damFactor;
                }
                else
                {
                    damage2 += ((Ship *)obj2)->staticinfo->maxhealth+1;   //set very big so kamikaze ship dies
                }
                damage1 += kamikazeDamage[((Ship *)obj2)->shiptype];//*damFactor;
                if (ship1->staticinfo->shipclass == CLASS_Fighter || ship1->staticinfo->shipclass == CLASS_Corvette)
                {                                           //if target was a strike craft
                    damage1 = min(damage1, ship2->staticinfo->maxhealth);//make sure strike craft cannot destroy targets more powerful than themselves
                }

                killer2type = DEATH_Killed_By_Kamikaze;
                killer2index = ship2->playerowner->playerIndex;
                ret = TRUE;
            }
        }
    }
    if(!ret)
        return FALSE;

    ApplyDamageToTarget(obj1,damage1,0,killer2type,killer2index); //99 so as to register as invalid if it tries to get used (cause it shouldn't)
    ApplyDamageToTarget(obj2,damage2,0,killer1type,killer1index);
    return TRUE;
}
/*-----------------------------------------------------------------------------
    Name        : collCheckShipShipColl
    Description : checks collisions between ships and ships, and impacts a force
                  on each one (equal and opposite forces)
    Inputs      : thisBlob
    Outputs     :
    Return      :
----------------------------------------------------------------------------*/
void collCheckShipShipColl(blob *thisBlob,bool checkSmallShips)
{
    sdword obj1index = 0;
    Ship *obj1;

    sdword obj2index;
    Ship *obj2;

    real32 dist;
    real32 distsquared;
    real32 colldist;
    vector distvector;

    real32 obj1collspheresize;
    real32 maxdistCollPossible;

    real32 checkdist;

    SelectCommand *selection;
    sdword numShips;

    real32 maxShipCollSphereSize;

    if (checkSmallShips)
    {
        selection = thisBlob->blobSmallShips;
        maxShipCollSphereSize = thisBlob->blobMaxSmallShipCollSphereSize;
    }
    else
    {
        selection = thisBlob->blobBigShips;
        maxShipCollSphereSize = thisBlob->blobMaxBigShipCollSphereSize;
    }
    numShips = selection->numShips;

    while (obj1index < numShips)
    {
        obj1 = selection->ShipPtr[obj1index];
        obj1collspheresize = obj1->staticinfo->staticheader.staticCollInfo.collspheresize;
        maxdistCollPossible = obj1collspheresize + maxShipCollSphereSize;

        obj2index = obj1index + 1;
        while (obj2index < numShips)
        {
            obj2 = selection->ShipPtr[obj2index];

#ifdef COLLISION_CHECK_STATS
            shipshipwalks++;
#endif

            checkdist = obj2->collOptimizeDist - obj1->collOptimizeDist;

            if (checkdist > maxdistCollPossible)
            {
                goto nextobj1;
            }

            if ((obj1->posinfo.isMoving | obj2->posinfo.isMoving) == FALSE)     // assume objects not moving can't collide
            {
                goto nocollision;
            }

            colldist = obj1collspheresize + obj2->staticinfo->staticheader.staticCollInfo.collspheresize;

            if (checkdist > colldist)
            {
                goto nocollision;
            }

            //old docking rejection code

            if ((obj1->dockingship == obj2) || (obj2->dockingship == obj1))
            {
                goto nocollision;       // docking ships shouldn't collide
            }

            if (obj1->dockingship != NULL && obj2->dockingship != NULL)
            {   //special for slaved ships in DOCKING process...silly I know
                goto nocollision;
            }

            if(bitTest(obj1->flags, SOF_Slaveable) && bitTest(obj2->flags,SOF_Slaveable))
            {    //both objects are slaveable...see if commonly linked.
                if(obj1->slaveinfo->Master == obj2->slaveinfo->Master)
                {
                    goto nocollision;
                }
            }

            // Check for collisions between obj1 and obj2 here:

#ifdef COLLISION_CHECK_STATS
            shipshipchecks++;
#endif

            vecSub(distvector,obj2->collInfo.collPosition,obj1->collInfo.collPosition);

            if (!isBetweenInclusive(distvector.x,-colldist,colldist))
            {
                goto nocollision;
            }

            if (!isBetweenInclusive(distvector.y,-colldist,colldist))
            {
                goto nocollision;
            }

            if (!isBetweenInclusive(distvector.z,-colldist,colldist))
            {
                goto nocollision;
            }

            if(obj1->specialFlags & SPECIAL_IsASalvager)
            {
                if( ((SalCapCorvetteSpec *)obj1->ShipSpecifics)->target == (SpaceObjRotImpTargGuidanceShipDerelict *)obj2)
                {
                    if( ((SalCapCorvetteSpec *)obj1->ShipSpecifics)->noAvoid)
                    {
                        goto nocollision;
                    }
                }
            }
            if(obj2->specialFlags & SPECIAL_IsASalvager)
            {
                if( ((SalCapCorvetteSpec *)obj2->ShipSpecifics)->target == (SpaceObjRotImpTargGuidanceShipDerelict *)obj1)
                {
                    if( ((SalCapCorvetteSpec *)obj2->ShipSpecifics)->noAvoid)
                    {
                        goto nocollision;
                    }
                }
            }

            if ((obj1->flags|obj2->flags) & SOF_Hide)
            {
                goto nocollision;
            }

            if(obj1->clampInfo != NULL)
            {
                //fix later...maybe can assume obj ios a ship?
                //no a big speed concern...not A LOT of clamping going on
                if(obj1->clampInfo->host->objtype == OBJ_ShipType)
                {
                    if(((Ship *)obj1->clampInfo->host)->shiptype == SalCapCorvette)
                    {
                        if(((SalCapCorvetteSpec *)((Ship *)obj1->clampInfo->host)->ShipSpecifics)->dockwith == obj2)
                        {
                            //ship clamped to salcap is colliding with the ship the salcap is docking with,
                            //don't really collide
                            goto nocollision;
                        }
                    }
                }
                if(obj2->clampInfo != NULL)
                {
                    if(((Ship *)obj2->clampInfo->host)->dockingship != NULL)
                    {
                        if(((Ship *)obj2->clampInfo->host)->dockingship == obj1->dockingship)
                        {
                            //docking with same ships...
                            goto nocollision;
                        }
                    }
                    if(obj2->clampInfo->host == obj1->clampInfo->host)
                    {
                        //same host, so don't do collision detection
                        goto nocollision;
                    }
                }
                else if(((Ship *)obj1->clampInfo->host)->dockingship != NULL)
                {
                    if (((Ship *)obj1->clampInfo->host)->dockingship == obj2)
                    {
                        goto nocollision;
                    }
                    if (((Ship *)obj1->clampInfo->host)->dockingship == obj2->dockingship)
                    {
                        goto nocollision;
                    }
                }
            }
            if(obj2->clampInfo != NULL)
            {
                if(obj2->clampInfo->host->objtype == OBJ_ShipType)
                {
                    if(((Ship *)obj2->clampInfo->host)->shiptype == SalCapCorvette)
                    {
                        if(((SalCapCorvetteSpec *)((Ship *)obj2->clampInfo->host)->ShipSpecifics)->dockwith == obj1)
                        {
                            //ship clamped to salcap is colliding with the ship the salcap is docking with,
                            //don't really collide
                            goto nocollision;
                        }
                    }
                    if(obj1->clampInfo != NULL)
                    {
                        if(((Ship *)obj1->clampInfo->host)->dockingship != NULL)
                        {
                            if(((Ship *)obj1->clampInfo->host)->dockingship == obj2->dockingship)
                            {
                                //docking with same ships...
                                goto nocollision;
                            }
                        }
                    }
                    else if(((Ship *)obj2->clampInfo->host)->dockingship != NULL)
                    {
                        if (((Ship *)obj2->clampInfo->host)->dockingship == obj1)
                        {
                            goto nocollision;
                        }
                        if (((Ship *)obj2->clampInfo->host)->dockingship == obj1->dockingship)
                        {
                            goto nocollision;
                        }
                    }
                }
            }
            distsquared = vecMagnitudeSquared(distvector);
            dist = (real32)fsqrt(distsquared);

            if (dist < colldist)
            {
                if (collCheckRectBumpCollision((SpaceObjRotImp *)obj1,(SpaceObjRotImp *)obj2))
                {
                    if (distsquared < 1.0f)
                    {
                        distvector.x = 10.0f;   // special case, if objects are too close together
                        distsquared = 100.0f;
                        dist = 10.0f;
                    }

                    if (!bitTest(obj1->flags, SOF_NotBumpable) && !bitTest(obj2->flags, SOF_NotBumpable))
                    {
                        if(!kamikazeCheck((SpaceObjRotImpTarg *)obj1,(SpaceObjRotImpTarg *)obj2))    //return true if kamikaze collision is true
                        {
                            if (obj1->playerowner == obj2->playerowner)
                            {
                                goto dontapplydamage;      // don't apply bonk damage for own ships
                            }

                            if ((obj1->flags & SOF_Disabled) || (obj2->flags & SOF_Disabled))
                            {
                                goto dontapplydamage;
                            }

                            if(obj1->shiptype == SalCapCorvette || obj1->shiptype == JunkYardDawg)
                            {
                                SalCapCorvetteSpec *spec = (SalCapCorvetteSpec *)obj1->ShipSpecifics;
                                if(((SpaceObjRotImpTarg *)spec->noDamageTarget) == ((SpaceObjRotImpTarg *)obj2))
                                {
                                    goto dontapplydamage;
                                }
                            }
                            else if(obj2->shiptype == SalCapCorvette || obj2->shiptype == JunkYardDawg)
                            {
                                SalCapCorvetteSpec *spec = (SalCapCorvetteSpec *)obj2->ShipSpecifics;
                                if(((SpaceObjRotImpTarg *)spec->noDamageTarget) == ((SpaceObjRotImpTarg *)obj1))
                                {
                                    goto dontapplydamage;
                                }
                            }

                            ApplyDamageToCollidingObjects((SpaceObjRotImpTarg *)obj1,(SpaceObjRotImpTarg *)obj2,&distvector,dist);
dontapplydamage:
                            ObjectsCollided((SpaceObjRotImpTarg *)obj1,(SpaceObjRotImpTarg *)obj2,colldist,&distvector,dist,distsquared);
                        }
                        else
                        {
                            //fix collided ships velocity....
                            ObjectsCollided((SpaceObjRotImpTarg *)obj1,(SpaceObjRotImpTarg *)obj2,colldist,&distvector,dist,distsquared);
                            kamikazeVelocityFix((SpaceObjRotImpTarg *)obj1,(SpaceObjRotImpTarg *)obj2);

                        }

                    }

                }
            }
nocollision:
            obj2index++;
        }
nextobj1:
        obj1index++;
    }
}

/*-----------------------------------------------------------------------------
    Name        : collCheckBigShipSmallShipColl
    Description : checks collisions between bigships and smallships, and impacts a force
                  on each one (equal and opposite forces)
    Inputs      : thisBlob
    Outputs     :
    Return      :
----------------------------------------------------------------------------*/
void collCheckBigShipSmallShipColl(blob *thisBlob)
{
    sdword obj1index = 0;
    Ship *obj1;

    sdword obj2index;
    Ship *obj2;

    real32 dist;
    real32 distsquared;
    real32 colldist;
    vector distvector;

    real32 obj1collspheresize;
    real32 maxdistCollPossible;

    real32 checkdist;

    SelectCommand *selection1 = thisBlob->blobBigShips;
    sdword numShips1 = selection1->numShips;

    SelectCommand *selection2 = thisBlob->blobSmallShips;
    sdword numShips2 = selection2->numShips;

    real32 maxShipCollSphereSize = thisBlob->blobMaxSmallShipCollSphereSize;

    sdword bottom,top;

    if (numShips2 == 0)
    {
        return;
    }

    while (obj1index < numShips1)
    {
        obj1 = selection1->ShipPtr[obj1index];
        obj1collspheresize = obj1->staticinfo->staticheader.staticCollInfo.collspheresize;
        maxdistCollPossible = obj1collspheresize + maxShipCollSphereSize;

        // binary search targetindex such that
        // (obj1->collOptimizeDist - obj2->collOptimizeDist) <= maxdistCollPossible
        bottom = 0;
        top = numShips2-1;
        while (top >= bottom)
        {
            obj2index = (bottom+top)>>1;
            if ((obj1->collOptimizeDist - selection2->ShipPtr[obj2index]->collOptimizeDist) > maxdistCollPossible)
            {
                // targetindex is too small
                bottom = obj2index+1;
            }
            else
            {
                top = obj2index-1;
            }
        }

        dbgAssert(obj2index >= 0);
        dbgAssert(obj2index < numShips2);

        while (obj2index < numShips2)
        {
            obj2 = selection2->ShipPtr[obj2index];

#ifdef COLLISION_CHECK_STATS
            shipshipwalks++;
#endif

            checkdist = obj2->collOptimizeDist - obj1->collOptimizeDist;

            if (checkdist > maxdistCollPossible)
            {
                goto nextobj1;
            }

            if ((obj1->posinfo.isMoving | obj2->posinfo.isMoving) == FALSE)     // assume objects not moving can't collide
            {
                goto nocollision;
            }

            colldist = obj1collspheresize + obj2->staticinfo->staticheader.staticCollInfo.collspheresize;

            if (checkdist > colldist)
            {
                goto nocollision;
            }

            //old docking rejection code

            if ((obj1->dockingship == obj2) || (obj2->dockingship == obj1))
            {
                goto nocollision;       // docking ships shouldn't collide
            }

            if (obj1->dockingship != NULL && obj2->dockingship != NULL)
            {   //special for slaved ships in DOCKING process...silly I know
                goto nocollision;
            }

            if(bitTest(obj1->flags, SOF_Slaveable) && bitTest(obj2->flags,SOF_Slaveable))
            {    //both objects are slaveable...see if commonly linked.
                if(obj1->slaveinfo->Master == obj2->slaveinfo->Master)
                {
                    goto nocollision;
                }
            }

            // Check for collisions between obj1 and obj2 here:

#ifdef COLLISION_CHECK_STATS
            shipshipchecks++;
#endif

            vecSub(distvector,obj2->collInfo.collPosition,obj1->collInfo.collPosition);

            if (!isBetweenInclusive(distvector.x,-colldist,colldist))
            {
                goto nocollision;
            }

            if (!isBetweenInclusive(distvector.y,-colldist,colldist))
            {
                goto nocollision;
            }

            if (!isBetweenInclusive(distvector.z,-colldist,colldist))
            {
                goto nocollision;
            }

            if(obj1->specialFlags & SPECIAL_IsASalvager)
            {
                if( ((SalCapCorvetteSpec *)obj1->ShipSpecifics)->target == (SpaceObjRotImpTargGuidanceShipDerelict *)obj2)
                {
                    if( ((SalCapCorvetteSpec *)obj1->ShipSpecifics)->noAvoid)
                    {
                        goto nocollision;
                    }
                }
            }
            if(obj2->specialFlags & SPECIAL_IsASalvager)
            {
                if( ((SalCapCorvetteSpec *)obj2->ShipSpecifics)->target == (SpaceObjRotImpTargGuidanceShipDerelict *)obj1)
                {
                    if( ((SalCapCorvetteSpec *)obj2->ShipSpecifics)->noAvoid)
                    {
                        goto nocollision;
                    }
                }
            }

            if ((obj1->flags|obj2->flags) & SOF_Hide)
            {
                goto nocollision;
            }

            if(obj1->clampInfo != NULL)
            {
                if(obj1->clampInfo->host->objtype == OBJ_ShipType)
                {
                    if(((Ship *)obj1->clampInfo->host)->shiptype == SalCapCorvette)
                    {
                        if(((SalCapCorvetteSpec *)((Ship *)obj1->clampInfo->host)->ShipSpecifics)->dockwith == obj2)
                        {
                            //ship clamped to salcap is colliding with the ship the salcap is docking with,
                            //don't really collide
                            goto nocollision;
                        }
                    }
                }
                if(obj2->clampInfo != NULL)
                {
                    if(((Ship *)obj2->clampInfo->host)->dockingship != NULL)
                    {
                        if(((Ship *)obj2->clampInfo->host)->dockingship == obj1->dockingship)
                        {
                            //docking with same ships...
                            goto nocollision;
                        }
                    }
                    if(obj2->clampInfo->host == obj1->clampInfo->host)
                    {
                        //same host, so don't do collision detection
                        goto nocollision;
                    }
                }
                else if(((Ship *)obj1->clampInfo->host)->dockingship != NULL)
                {
                    if (((Ship *)obj1->clampInfo->host)->dockingship == obj2)
                    {
                        goto nocollision;
                    }
                    if (((Ship *)obj1->clampInfo->host)->dockingship == obj2->dockingship)
                    {
                        goto nocollision;
                    }
                }
            }
            if(obj2->clampInfo != NULL)
            {
                if(obj2->clampInfo->host->objtype == OBJ_ShipType)
                {
                    if(((Ship *)obj2->clampInfo->host)->shiptype == SalCapCorvette)
                    {
                        if(((SalCapCorvetteSpec *)((Ship *)obj2->clampInfo->host)->ShipSpecifics)->dockwith == obj1)
                        {
                            //ship clamped to salcap is colliding with the ship the salcap is docking with,
                            //don't really collide
                            goto nocollision;
                        }
                    }
                }
                if(obj1->clampInfo != NULL)
                {
                    if(((Ship *)obj1->clampInfo->host)->dockingship != NULL)
                    {
                        if(((Ship *)obj1->clampInfo->host)->dockingship == obj2->dockingship)
                        {
                            //docking with same ships...
                            goto nocollision;
                        }
                    }
                }
                else if(((Ship *)obj2->clampInfo->host)->dockingship != NULL)
                {
                    if (((Ship *)obj2->clampInfo->host)->dockingship == obj1)
                    {
                        goto nocollision;
                    }

                    if (((Ship *)obj2->clampInfo->host)->dockingship == obj1->dockingship)
                    {
                        goto nocollision;
                    }
                }
            }

            distsquared = vecMagnitudeSquared(distvector);
            dist = (real32)fsqrt(distsquared);

            if (dist < colldist)
            {
                if (collCheckRectBumpCollision((SpaceObjRotImp *)obj1,(SpaceObjRotImp *)obj2))
                {
                    if (distsquared < 1.0f)
                    {
                        distvector.x = 10.0f;   // special case, if objects are too close together
                        distsquared = 100.0f;
                        dist = 10.0f;
                    }

                    if (!bitTest(obj1->flags, SOF_NotBumpable) && !bitTest(obj2->flags, SOF_NotBumpable))
                    {
                        if(!kamikazeCheck((SpaceObjRotImpTarg *)obj1,(SpaceObjRotImpTarg *)obj2))    //return true if kamikaze collision is true
                        {
                            if (obj1->playerowner == obj2->playerowner)
                            {
                                goto dontapplydamage;
                            }

                            if ((obj1->flags & SOF_Disabled) || (obj2->flags & SOF_Disabled))
                            {
                                goto dontapplydamage;
                            }

                            if(obj1->shiptype == SalCapCorvette || obj1->shiptype == JunkYardDawg)
                            {
                                SalCapCorvetteSpec *spec = (SalCapCorvetteSpec *)obj1->ShipSpecifics;
                                if(((SpaceObjRotImpTarg *)spec->noDamageTarget) == ((SpaceObjRotImpTarg *)obj2))
                                {
                                    goto dontapplydamage;
                                }
                            }
                            else if(obj2->shiptype == SalCapCorvette || obj2->shiptype == JunkYardDawg)
                            {
                                SalCapCorvetteSpec *spec = (SalCapCorvetteSpec *)obj2->ShipSpecifics;
                                if(((SpaceObjRotImpTarg *)spec->noDamageTarget) == ((SpaceObjRotImpTarg *)obj1))
                                {
                                    goto dontapplydamage;
                                }
                            }

                            ApplyDamageToCollidingObjects((SpaceObjRotImpTarg *)obj1,(SpaceObjRotImpTarg *)obj2,&distvector,dist);
dontapplydamage:
                            ObjectsCollided((SpaceObjRotImpTarg *)obj1,(SpaceObjRotImpTarg *)obj2,colldist,&distvector,dist,distsquared);
                        }
                        else
                        {
                            //fix collided ships velocity....
                            ObjectsCollided((SpaceObjRotImpTarg *)obj1,(SpaceObjRotImpTarg *)obj2,colldist,&distvector,dist,distsquared);
                            kamikazeVelocityFix((SpaceObjRotImpTarg *)obj1,(SpaceObjRotImpTarg *)obj2);
                        }
                    }

                }
            }
nocollision:
            obj2index++;
        }
nextobj1:
        obj1index++;
    }
}

/*-----------------------------------------------------------------------------
    Name        : collCheckShipResourceColl
    Description : checks collisions between ships and resources
    Inputs      : thisBlob
    Outputs     :
    Return      :
----------------------------------------------------------------------------*/
void collCheckShipResourceColl(blob *thisBlob)
{
    sdword obj1index = 0;
    Ship *obj1;

    sdword obj2index;
    Resource *obj2;

    real32 dist;
    real32 distsquared;
    real32 colldist;
    vector distvector;

    real32 obj1collspheresize;
    real32 maxdistCollPossible;

    real32 distcheck;
    real32 absdistcheck;

    SelectCommand *selection = thisBlob->blobShips;
    sdword numShips = selection->numShips;

    ResourceSelection *resselection = thisBlob->blobResources;
    sdword numResources = resselection->numResources;

    real32 maxResourceCollSphereSize;

    sdword bottom,top;

    if (numResources == 0)
    {
        return;
    }

    maxResourceCollSphereSize = thisBlob->blobMaxResourceCollSphereSize;

    while (obj1index < numShips)
    {
        obj1 = selection->ShipPtr[obj1index];
        obj1collspheresize = obj1->staticinfo->staticheader.staticCollInfo.collspheresize;
        maxdistCollPossible = obj1collspheresize + maxResourceCollSphereSize;

        // binary search targetindex such that
        // (obj1->collOptimizeDist - obj2->collOptimizeDist) <= maxdistCollPossible
        bottom = 0;
        top = numResources-1;
        while (top >= bottom)
        {
            obj2index = (bottom+top)>>1;
            if ((obj1->collOptimizeDist - resselection->ResourcePtr[obj2index]->collOptimizeDist) > maxdistCollPossible)
            {
                // targetindex is too small
                bottom = obj2index+1;
            }
            else
            {
                top = obj2index-1;
            }
        }

        dbgAssert(obj2index >= 0);
        dbgAssert(obj2index < numResources);

        while (obj2index < numResources)
        {
            obj2 = resselection->ResourcePtr[obj2index];

#ifdef COLLISION_CHECK_STATS
            shipresourcewalks++;
#endif
            if ((distcheck = obj2->collOptimizeDist - obj1->collOptimizeDist) > maxdistCollPossible)
            {
                goto nextobj1;
            }

            if ((absdistcheck = abs(distcheck)) > maxdistCollPossible)
            {
                goto nocollision;
            }

            if ((obj1->posinfo.isMoving | obj2->posinfo.isMoving) == FALSE)     // assume objects not moving can't collide
            {
                goto nocollision;
            }

            colldist = obj1collspheresize + obj2->staticinfo->staticheader.staticCollInfo.collspheresize;

            if (absdistcheck > colldist)
            {
                goto nocollision;
            }

            // Check for collisions between obj1 and obj2 here:

#ifdef COLLISION_CHECK_STATS
            shipresourcechecks++;
#endif

            vecSub(distvector,obj2->collInfo.collPosition,obj1->collInfo.collPosition);

            if (!isBetweenInclusive(distvector.x,-colldist,colldist))
            {
                goto nocollision;
            }

            if (!isBetweenInclusive(distvector.y,-colldist,colldist))
            {
                goto nocollision;
            }

            if (!isBetweenInclusive(distvector.z,-colldist,colldist))
            {
                goto nocollision;
            }

            distsquared = vecMagnitudeSquared(distvector);
            dist = (real32)fsqrt(distsquared);

            if (dist < colldist)
            {
                if (collCheckRectBumpCollision((SpaceObjRotImp *)obj1,(SpaceObjRotImp *)obj2))
                {
                    if (distsquared < 1.0f)
                    {
                        distvector.x = 10.0f;   // special case, if objects are too close together
                        distsquared = 100.0f;
                        dist = 10.0f;
                    }

                    if (!bitTest(obj1->flags, SOF_NotBumpable) && !bitTest(obj2->flags, SOF_NotBumpable))
                    {
                        if(!kamikazeCheck((SpaceObjRotImpTarg *)obj1,(SpaceObjRotImpTarg *)obj2))    //return true if kamikaze collision is true
                        {
                            if (obj1->shiptype != ResourceCollector)   // ResourceCollector's have special armor because they always collect resources
                            {
                                ApplyDamageToCollidingObjects((SpaceObjRotImpTarg *)obj1,(SpaceObjRotImpTarg *)obj2,&distvector,dist);
                            }

                            ObjectsCollided((SpaceObjRotImpTarg *)obj1,(SpaceObjRotImpTarg *)obj2,colldist,&distvector,dist,distsquared);
                        }
                        else
                        {
                             //fix collided ships velocity....
                            ObjectsCollided((SpaceObjRotImpTarg *)obj1,(SpaceObjRotImpTarg *)obj2,colldist,&distvector,dist,distsquared);
                            kamikazeVelocityFix((SpaceObjRotImpTarg *)obj1,(SpaceObjRotImpTarg *)obj2);
                        }
                    }
                }
            }
nocollision:
            obj2index++;
        }
nextobj1:
        obj1index++;
    }
}

/*-----------------------------------------------------------------------------
    Name        : collCheckShipDerelictColl
    Description : checks collisions between ships and derelicts
    Inputs      : thisBlob
    Outputs     :
    Return      :
----------------------------------------------------------------------------*/
void collCheckShipDerelictColl(blob *thisBlob)
{
    sdword obj1index = 0;
    Ship *obj1;

    sdword obj2index;
    Derelict *obj2;

    real32 dist;
    real32 distsquared;
    real32 colldist;
    vector distvector;

    real32 obj1collspheresize;
    real32 maxdistCollPossible;

    real32 distcheck;
    real32 absdistcheck;

    SelectCommand *selection = thisBlob->blobShips;
    sdword numShips = selection->numShips;

    DerelictSelection *derselection = thisBlob->blobDerelicts;
    sdword numDerelicts = derselection->numDerelicts;

    real32 maxDerelictCollSphereSize;

    sdword bottom,top;

    if (numDerelicts == 0)
    {
        return;
    }

    maxDerelictCollSphereSize = thisBlob->blobMaxDerelictCollSphereSize;

    while (obj1index < numShips)
    {
        obj1 = selection->ShipPtr[obj1index];
        obj1collspheresize = obj1->staticinfo->staticheader.staticCollInfo.collspheresize;
        maxdistCollPossible = obj1collspheresize + maxDerelictCollSphereSize;

        // binary search targetindex such that
        // (obj1->collOptimizeDist - obj2->collOptimizeDist) <= maxdistCollPossible
        bottom = 0;
        top = numDerelicts-1;
        while (top >= bottom)
        {
            obj2index = (bottom+top)>>1;
            if ((obj1->collOptimizeDist - derselection->DerelictPtr[obj2index]->collOptimizeDist) > maxdistCollPossible)
            {
                // targetindex is too small
                bottom = obj2index+1;
            }
            else
            {
                top = obj2index-1;
            }
        }

        dbgAssert(obj2index >= 0);
        dbgAssert(obj2index < numDerelicts);

        while (obj2index < numDerelicts)
        {
            obj2 = derselection->DerelictPtr[obj2index];

#ifdef COLLISION_CHECK_STATS
            shipderelictwalks++;
#endif
            if ((distcheck = obj2->collOptimizeDist - obj1->collOptimizeDist) > maxdistCollPossible)
            {
                goto nextobj1;
            }

            if ((absdistcheck = abs(distcheck)) > maxdistCollPossible)
            {
                goto nocollision;
            }

            if ((obj1->posinfo.isMoving | obj2->posinfo.isMoving) == FALSE)     // assume objects not moving can't collide
            {
                goto nocollision;
            }

            colldist = obj1collspheresize + obj2->staticinfo->staticheader.staticCollInfo.collspheresize;

            if (absdistcheck > colldist)
            {
                goto nocollision;
            }

            if(obj2->dockingship == obj1)
            {    //derelict is 'docking' with obj1 so don't do collision detection
                goto nocollision;
            }
            // Check for collisions between obj1 and obj2 here:

#ifdef COLLISION_CHECK_STATS
            shipderelictchecks++;
#endif

            vecSub(distvector,obj2->collInfo.collPosition,obj1->collInfo.collPosition);

            if (!isBetweenInclusive(distvector.x,-colldist,colldist))
            {
                goto nocollision;
            }

            if (!isBetweenInclusive(distvector.y,-colldist,colldist))
            {
                goto nocollision;
            }

            if (!isBetweenInclusive(distvector.z,-colldist,colldist))
            {
                goto nocollision;
            }

            if(obj1->specialFlags & SPECIAL_IsASalvager)
            {
                if( ((SalCapCorvetteSpec *)obj1->ShipSpecifics)->target == (SpaceObjRotImpTargGuidanceShipDerelict *)obj2)
                {
                    if( ((SalCapCorvetteSpec *)obj1->ShipSpecifics)->noAvoid)
                    {
                        goto nocollision;
                    }
                }
            }

            if ((obj1->flags|obj2->flags) & SOF_Hide)
            {
                goto nocollision;
            }

            distsquared = vecMagnitudeSquared(distvector);
            dist = (real32)fsqrt(distsquared);

            if (dist < colldist)
            {
                if (collCheckRectBumpCollision((SpaceObjRotImp *)obj1,(SpaceObjRotImp *)obj2))
                {
                    if (distsquared < 1.0f)
                    {
                        distvector.x = 10.0f;   // special case, if objects are too close together
                        distsquared = 100.0f;
                        dist = 10.0f;
                    }

                    if (!bitTest(obj1->flags, SOF_NotBumpable) && !bitTest(obj2->flags, SOF_NotBumpable))
                    {
                        if(!kamikazeCheck((SpaceObjRotImpTarg *)obj1,(SpaceObjRotImpTarg *)obj2))    //return true if kamikaze collision is true
                        {
                            // deliberately do not apply damage for ship-derelict collisions
                            ObjectsCollided((SpaceObjRotImpTarg *)obj1,(SpaceObjRotImpTarg *)obj2,colldist,&distvector,dist,distsquared);
                        }
                        else
                        {
                             //fix collided ships velocity....
                            ObjectsCollided((SpaceObjRotImpTarg *)obj1,(SpaceObjRotImpTarg *)obj2,colldist,&distvector,dist,distsquared);
                            kamikazeVelocityFix((SpaceObjRotImpTarg *)obj1,(SpaceObjRotImpTarg *)obj2);
                        }
                    }
                }
            }
nocollision:
            obj2index++;
        }
nextobj1:
        obj1index++;
    }
}

/*-----------------------------------------------------------------------------
    Name        : collCheckLineOfSight
    Description :
    Inputs      : source - ship that's firing
                  target - potential destination (target)
                  sourcePosition - origin of the los test in worldspace
                  direction - worldspace unit direction
    Outputs     :
    Return      : TRUE if path is clear, FALSE if obscured
    todo: must check if target is closer than a "hit"
----------------------------------------------------------------------------*/
bool collCheckLineOfSight(Ship* source, Ship* target, vector* sourcePosition, vector* direction)
{
    sdword i, collSide;
    real32 length;
    blob* thisBlob;
    Ship* ship;
    vector distvec;

    if (target == NULL)
    {
        length = REALlyBig;
    }
    else
    {
        vecSub(distvec, target->posinfo.position, *sourcePosition);
        length = fsqrt(vecMagnitudeSquared(distvec));
    }

    if (target == NULL || target->collMyBlob == source->collMyBlob)
    {
        //los in current blob
        thisBlob = source->collMyBlob;
        for (i = 0; i < thisBlob->blobShips->numShips; i++)
        {
            ship = thisBlob->blobShips->ShipPtr[i];
            if (ship == source || ship == target)
            {
                //ignore source or target
                continue;
            }

            if (!allianceIsShipAlly(ship, source->playerowner))
            {
                //only test with allied ships, self included
                continue;
            }

            if (collCheckRectLine((SpaceObjRotImp*)ship, sourcePosition, direction, length, &collSide) != -1.0f)
            {
                //hit something of ours
                return FALSE;
            }
        }

        //path is clear
        return TRUE;
    }
    else
    {
        //los in universe
        Node* blobnode = universe.collBlobList.head;
        vector distvector;
        real32 distsquared, v, dsqr;

        while (blobnode != NULL)
        {
            thisBlob = (blob*)listGetStructOfNode(blobnode);
            blobnode = blobnode->next;

            vecSub(distvector, thisBlob->centre, *sourcePosition);
            distsquared = vecMagnitudeSquared(distvector);
            v = vecDotProduct(distvector, *direction);
			if (v > 0)
			{
				dsqr = (thisBlob->radius * thisBlob->radius) - (distsquared - (v*v));
				if (dsqr > 0)
				{
					//line intersects blob, check all ships
					for (i = 0; i < thisBlob->blobShips->numShips; i++)
					{
						ship = thisBlob->blobShips->ShipPtr[i];
						if (ship == source || ship == target)
						{
							//ignore source or target
							continue;
						}

						if (!allianceIsShipAlly(ship, source->playerowner))
						{
							//only test with allied ships, self included
							continue;
						}

						if (collCheckRectLine((SpaceObjRotImp*)ship, sourcePosition, direction, length, &collSide) != -1.0f)
						{
							//hit something of ours
							return FALSE;
						}
					}
				}
			}
        }

        //path is clear
        return TRUE;
    }
}

void collCheckBeamCollWithTargetsInBlob(Bullet *bullet,blob *thisBlob,real32 *minbeamCollideLineDist,SpaceObjRotImpTarg **minbeamTarget,sdword *minbeamCollSide)
{
    SelectAnyCommand *targetselection;
    sdword numTargets;

    SpaceObjRotImpTarg *target;
    StaticHeader *targetstaticheader;

    real32 distsquared;
    vector distvector;
    real32 v,d,dsqr;
    sdword collSide;

    real32 collideLineDist;

    sdword targetindex;

    sdword pass = 0;

passagain:

    if (pass == 0)
    {
        targetselection = thisBlob->blobSmallTargets;
    }
    else
    {
        dbgAssert(pass == 1);
        targetselection = thisBlob->blobBigTargets;
    }

    targetindex = 0;
    numTargets = targetselection->numTargets;

    while (targetindex < numTargets)
    {
        target = targetselection->TargetPtr[targetindex];
        targetstaticheader = &target->staticinfo->staticheader;

#ifdef COLLISION_CHECK_STATS
bulletwalks++;
#endif

        if (bullet->owner == (Ship *)target)
        {
            goto beamnexttarget;
        }

        // Check for collisions between bullet and target here:

#ifdef COLLISION_CHECK_STATS
bulletchecks++;
#endif

        vecSub(distvector,target->collInfo.collPosition,bullet->posinfo.position);
        distsquared = vecMagnitudeSquared(distvector);
        v = vecDotProduct(distvector,bullet->bulletheading);
		if (v > 0)
        {
			dsqr = targetstaticheader->staticCollInfo.collspheresizeSqr - (distsquared - (v*v));
			if (dsqr > 0)
			{
				d = fsqrt(dsqr);
				if ((v-d) < bullet->traveldist)
				{
					if ((collideLineDist = collCheckRectLine((SpaceObjRotImp *)target,&bullet->posinfo.position,&bullet->bulletheading,bullet->traveldist,&collSide)) >= 0.0f)
					{
						if (collideLineDist == 0.0f)
						{
							collideLineDist = v-d;
							if (collideLineDist < 0.0f)
							{
								collideLineDist = 0.0f;
							}
						}
						if (collideLineDist < *minbeamCollideLineDist)
						{
							*minbeamCollideLineDist = collideLineDist;
							*minbeamTarget = target;
							*minbeamCollSide = collSide;
						}
					}
				}
			}
		}

beamnexttarget:
        targetindex++;
    }

nextpass:
    pass++;
    if (pass < 2)
    {
        goto passagain;
    }
}

void collCheckBeamColl(blob *thisBlob,Bullet *bullet)
{
    real32 minbeamCollideLineDist = REALlyBig;
    SpaceObjRotImpTarg *minbeamTarget = NULL;
    sdword minbeamCollSide = -1;
    Node *blobnode = universe.collBlobList.head;
    blob *checkBlob;

    real32 collideLineDist;
    real32 distsquared;
    vector distvector;
    real32 v,d,dsqr;
    sdword collSide;

    if(bullet->target != NULL)
    {
        switch(bullet->target->objtype)
        {
        case OBJ_MissileType:
            //do bullet/missle target collision with its target only!
            vecSub(distvector,bullet->target->collInfo.collPosition,bullet->posinfo.position);
            distsquared = vecMagnitudeSquared(distvector);
            v = vecDotProduct(distvector,bullet->bulletheading);
			//if (v > 0)
			{
				dsqr = bullet->target->staticinfo->staticheader.staticCollInfo.collspheresizeSqr - (distsquared - (v*v));
				if (dsqr > 0)
				{
					d = fsqrt(dsqr);
					if ((v-d) < bullet->traveldist)
					{
						if ((collideLineDist = collCheckRectLine((SpaceObjRotImp *)bullet->target,&bullet->posinfo.position,&bullet->bulletheading,bullet->traveldist,&collSide)) >= 0.0f)
						{
							if (collideLineDist == 0.0f)
							{
								collideLineDist = v-d;
								if (collideLineDist < 0.0f)
								{
									collideLineDist = 0.0f;
								}
							}
							if (collideLineDist < minbeamCollideLineDist)
							{
								minbeamCollideLineDist = collideLineDist;
								minbeamTarget = bullet->target;
								minbeamCollSide = collSide;
							}
						}
					}
				}
			}
            break;
        }
    }

    collCheckBeamCollWithTargetsInBlob(bullet,thisBlob,&minbeamCollideLineDist,&minbeamTarget,&minbeamCollSide);

    // do line-blob collision test for all other blobs in universe:
    while (blobnode != NULL)
    {
        checkBlob = (blob *)listGetStructOfNode(blobnode);
        blobnode = blobnode->next;

        if (checkBlob == thisBlob)
        {
            continue;
        }

        vecSub(distvector,checkBlob->centre,bullet->posinfo.position);
        distsquared = vecMagnitudeSquared(distvector);
        v = vecDotProduct(distvector,bullet->bulletheading);
		if (v > 0)
		{
			dsqr = (checkBlob->radius*checkBlob->radius) - (distsquared - (v*v));
			if (dsqr > 0)
			{
				d = fsqrt(dsqr);
				if ((v-d) < bullet->traveldist)
				{
					collCheckBeamCollWithTargetsInBlob(bullet,checkBlob,&minbeamCollideLineDist,&minbeamTarget,&minbeamCollSide);
				}
			}
		}
    }

    if (minbeamTarget != NULL)        // beam only collides with closest target
    {
        dbgAssert(minbeamCollideLineDist >= 0.0f);
        univBulletCollidedWithTarget(minbeamTarget,&minbeamTarget->staticinfo->staticheader,bullet,minbeamCollideLineDist,minbeamCollSide);
        bullet->beamtraveldist = minbeamCollideLineDist;
    }
    else
    {
        bullet->beamtraveldist = bullet->lengthmag;
    }
    //adjust length of effect associated with bullet, if applicable
    if (bullet->effect != NULL)
    {
        ((real32 *)bullet->effect->variable)[ETG_LengthVariable] = bullet->beamtraveldist;
    }
}

/*-----------------------------------------------------------------------------
    Name        : collCheckBulletTargetColl
    Description : checks collisions between bullets and targets.  When a bullet
                  collides with a target, it should disappear and do damage to
                  the enemy ship.
    Inputs      :
    Outputs     :
    Return      :
    Notes       : This routine uses line-sphere intersection for collision detection
----------------------------------------------------------------------------*/
void collCheckBulletTargetColl(blob *thisBlob)
{
    sdword bulletindex = 0;
    Bullet *bullet;

    sdword targetindex;
    SpaceObjRotImpTarg *target;
    StaticHeader *targetstaticheader;

    real32 distsquared;
    vector distvector;
    real32 v,d,dsqr;
    sdword collSide;

    real32 distcheck;

    bool deletebulletflag;

    real32 collideLineDist;

    real32 testcollsizesqr;

    BulletSelection *bulletselection = thisBlob->blobBullets;

    SelectAnyCommand *targetselection;
    sdword numTargets;

    sdword bottom,top;

    real32 maxTargetCollSphereSizePlusBulletTravelDist;

    sdword pass;

#ifndef HW_Release
//    thisBlob->debugFlag = 1;
#endif

    while (bulletindex < bulletselection->numBullets)
    {
        bullet = bulletselection->BulletPtr[bulletindex];
        dbgAssert(bullet->objtype == OBJ_BulletType);

        deletebulletflag = FALSE;

        switch (bullet->bulletType)
        {
            case BULLET_Beam:
                collCheckBeamColl(thisBlob,bullet);
                break;
            case BULLET_Laser:
                break;              //don't do collision detection for lasers
            default:
                pass = 0;


                if(bullet->target != NULL)
                {
                    switch(bullet->target->objtype)
                    {
                    case OBJ_MissileType:
                        //do bullet/missle target collision with its target only!
                         distcheck = bullet->target->collOptimizeDist - bullet->collOptimizeDist;

                        if (abs(distcheck) > (bullet->target->staticinfo->staticheader.staticCollInfo.collspheresize+bullet->traveldist))
                        {
                            //trivially reject...
                            goto nominecollision;
                        }
                        vecSub(distvector,bullet->target->collInfo.collPosition,bullet->posinfo.position);
                        distsquared = vecMagnitudeSquared(distvector);
                        v = vecDotProduct(distvector,bullet->bulletheading);
						//if (v > 0)
						{

							testcollsizesqr = bullet->target->staticinfo->staticheader.staticCollInfo.collspheresizeSqr;

							dsqr = testcollsizesqr - (distsquared - (v*v));
							if (dsqr > 0)
							{
								d = fsqrt(dsqr);
								if ((v-d) < bullet->traveldist)
								{
									if ((collideLineDist = collCheckRectLine((SpaceObjRotImp *)bullet->target,&bullet->posinfo.position,&bullet->bulletheading,bullet->traveldist,&collSide)) >= 0.0f)
									{
										univBulletCollidedWithTarget(bullet->target,&bullet->target->staticinfo->staticheader,bullet,collideLineDist,collSide);
										deletebulletflag = TRUE;
										goto nextbullet;
									}
								}
							}
						}

                        break;
                    }
                }
nominecollision:
passagain:
                if (pass == 0)
                {
                    targetselection = thisBlob->blobSmallTargets;
                    numTargets = targetselection->numTargets;

                    if (numTargets == 0)
                    {
                        goto nextpass;         // go directly to next pass
                    }

                    maxTargetCollSphereSizePlusBulletTravelDist = thisBlob->blobMaxSTargetCollSphereSize + bullet->traveldist;

                    // binary search targetindex such that
                    // (bullet->collOptimizeDist - target->collOptimizeDist) <= maxTargetCollSphereSize
                    bottom = 0;
                    top = numTargets-1;
                    while (top >= bottom)
                    {
                        targetindex = (bottom+top)>>1;
                        if ((bullet->collOptimizeDist - targetselection->TargetPtr[targetindex]->collOptimizeDist) > maxTargetCollSphereSizePlusBulletTravelDist)
                        {
                            // targetindex is too small
                            bottom = targetindex+1;
                        }
                        else
                        {
                            top = targetindex-1;
                        }
                    }

                    dbgAssert(targetindex >= 0);
                    dbgAssert(targetindex < numTargets);
                }
                else
                {
                    dbgAssert(pass == 1);
                    targetselection = thisBlob->blobBigTargets;
                    numTargets = targetselection->numTargets;
                    targetindex = 0;
                    maxTargetCollSphereSizePlusBulletTravelDist = thisBlob->blobMaxBTargetCollSphereSize + bullet->traveldist;
                }

                while (targetindex < numTargets)
                {
                    target = targetselection->TargetPtr[targetindex];
                    targetstaticheader = &target->staticinfo->staticheader;

#ifdef COLLISION_CHECK_STATS
            bulletwalks++;
#endif

                    distcheck = target->collOptimizeDist - bullet->collOptimizeDist;

                    if (distcheck > maxTargetCollSphereSizePlusBulletTravelDist)
                    {
                        goto nextpass;
                    }

                    if ((target->objtype != OBJ_ShipType) || (((Ship *)target)->shiptype != DFGFrigate && ((Ship *)target)->shiptype != DefenseFighter))
                    {
                        if (abs(distcheck) > (targetstaticheader->staticCollInfo.collspheresize+bullet->traveldist))
                        {
                            goto nexttarget;
                        }
                    }

                    if (bullet->owner == (Ship *)target)
                    {
                        goto nexttarget;
                    }


                    if ((target->objtype == OBJ_DustType) || (target->objtype == OBJ_NebulaType))
                    {
                        goto nexttarget;
                    }

                    // Check for collisions between bullet and target here:

#ifdef COLLISION_CHECK_STATS
            bulletchecks++;
#endif

                    vecSub(distvector,target->collInfo.collPosition,bullet->posinfo.position);
                    distsquared = vecMagnitudeSquared(distvector);
                    v = vecDotProduct(distvector,bullet->bulletheading);

                    if (target->objtype == OBJ_ShipType)
                    {
                        if(((Ship *)target)->shiptype == DFGFrigate)
                        {       //if target is a dfgf,
                            DFGFrigateStatics *DFGFstatics;

                            DFGFstatics = (DFGFrigateStatics *) ((ShipStaticInfo *)(((Ship *)target)->staticinfo))->custstatinfo;
                            if(distsquared < DFGFstatics->DFGFrigateFieldRadiusSqr)
                            {   //and bullet is within its field radius...affect the bullet
                                univDFGFieldEffect((Ship *)target,bullet,universe.totaltimeelapsed);
                            }
                        }
                        if(((Ship *)target)->shiptype == DefenseFighter)
                        {    //if target is a DefenseFighter
                            DefenseFighterStatics *defensestatics;
                            defensestatics = (DefenseFighterStatics *) ((ShipStaticInfo *)(((Ship *)target)->staticinfo))->custstatinfo;
                            if(distsquared < ((Ship *)target)->staticinfo->bulletRangeSquared[((Ship *)target)->tacticstype])
                            {
                                DefenseFighterReportBullet((Ship *)target, bullet);
                            }
                        }
                    }

					//if (v > 0)
					{

						testcollsizesqr = targetstaticheader->staticCollInfo.collspheresizeSqr;

						dsqr = testcollsizesqr - (distsquared - (v*v));
						if (dsqr > 0)
						{
							d = fsqrt(dsqr);
							if ((v-d) < bullet->traveldist)
							{
								if ((collideLineDist = collCheckRectLine((SpaceObjRotImp *)target,&bullet->posinfo.position,&bullet->bulletheading,bullet->traveldist,&collSide)) >= 0.0f)
								{
									univBulletCollidedWithTarget(target,targetstaticheader,bullet,collideLineDist,collSide);
									deletebulletflag = TRUE;
									goto nextbullet;
								}
							}
						}
					}

        nexttarget:
                    targetindex++;
                }

nextpass:
                pass++;
                if (pass < 2)
                {
                    goto passagain;
                }
                break;
        }

nextbullet:
        if (deletebulletflag)
        {
            // we are deleting this bullet's index, so don't update bulletindex

            if(bitTest(bullet->SpecialEffectFlag, 0x0002))
            {    //bullet is possibly being targetted by a defense fighter...remove that reference
                DefenseFighterBulletRemoval(bullet);
            }

            bobObjectDied((SpaceObj *)bullet,&universe.collBlobList);
            listRemoveNode(&bullet->bulletlink);
            univRemoveObjFromRenderList((SpaceObj *)bullet);
            listDeleteNode(&bullet->objlink);
#if 0
            univDeleteBullet(bullet);
#endif
        }
        else
        {
            bulletindex++;
        }
    }
#ifndef HW_Release
//    thisBlob->debugFlag = 0;
#endif
}

/*-----------------------------------------------------------------------------
    Name        : collCheckMissileShipColl
    Description : checks collisions between missiles and ships
    Inputs      :
    Outputs     :
    Return      :
----------------------------------------------------------------------------*/
void collCheckMissileShipColl(blob *thisBlob,MissileType missileType)
{
    sdword missileindex = 0;
    Missile *missile;

    sdword targetindex;
    Ship *target;
    StaticHeader *targetstaticheader;

    real32 distsquared;
    vector distvector;
    real32 v,d,dsqr;
    sdword collSide;

    real32 distcheck;

    real32 collideLineDist;

    vector missileheading;

    MissileSelection *missileselection;
    sdword numMissiles;

    SelectCommand *targetselection;
    sdword numTargets;

    sdword bottom,top;

    real32 maxCollSphereSizePlusMissileTravelDist;

    sdword pass;

    if (missileType == MISSILE_Mine)
    {
        missileselection = thisBlob->blobMissileMines;
    }
    else
    {
        missileselection = thisBlob->blobMissileMissiles;
    }
    numMissiles = missileselection->numMissiles;

#ifndef HW_Release
//    thisBlob->debugFlag = 1;
#endif

    while (missileindex < numMissiles)
    {
        missile = missileselection->MissilePtr[missileindex];
        dbgAssert(missile->objtype == OBJ_MissileType);

        if (missile->flags & SOF_Dead)
        {
            goto nextmissile;
        }

        pass = 0;

passagain:

        if (pass == 0)
        {
            targetselection = thisBlob->blobSmallShips;
            numTargets = targetselection->numShips;

            if (numTargets == 0)
            {
                goto nextpass;
            }

            maxCollSphereSizePlusMissileTravelDist = thisBlob->blobMaxSmallShipCollSphereSize+missile->maxtraveldist;

            // binary search targetindex such that
            // (missile->collOptimizeDist - target->collOptimizeDist) <= maxCollSphereSize
            bottom = 0;
            top = numTargets-1;
            while (top >= bottom)
            {
                targetindex = (bottom+top)>>1;
                if ((missile->collOptimizeDist - targetselection->ShipPtr[targetindex]->collOptimizeDist) > maxCollSphereSizePlusMissileTravelDist)
                {
                    // targetindex is too small
                    bottom = targetindex+1;
                }
                else
                {
                    top = targetindex-1;
                }
            }

            dbgAssert(targetindex >= 0);
            dbgAssert(targetindex < numTargets);
        }
        else
        {
            dbgAssert(pass == 1);
            targetselection = thisBlob->blobBigShips;
            numTargets = targetselection->numShips;
            maxCollSphereSizePlusMissileTravelDist = thisBlob->blobMaxBigShipCollSphereSize+missile->maxtraveldist;
            targetindex = 0;
        }

        while (targetindex < numTargets)
        {
            target = targetselection->ShipPtr[targetindex];

#ifdef COLLISION_CHECK_STATS
    missilewalks++;
#endif

            distcheck = target->collOptimizeDist - missile->collOptimizeDist;

            if (distcheck > maxCollSphereSizePlusMissileTravelDist)
            {
                goto nextpass;
            }

            targetstaticheader = &target->staticinfo->staticheader;

            if (abs(distcheck) > (targetstaticheader->staticCollInfo.collspheresize+missile->maxtraveldist))
            {
                goto nexttarget;
            }

            if (missile->owner == (Ship *)target)
            {
                goto nexttarget;
            }

            // Check for collisions between missile and target here:

#ifdef COLLISION_CHECK_STATS
    missilechecks++;
#endif
            vecSub(distvector,target->collInfo.collPosition,missile->posinfo.position);
            distsquared = vecMagnitudeSquared(distvector);
            matGetVectFromMatrixCol3(missileheading,missile->rotinfo.coordsys);
            v = vecDotProduct(distvector,missileheading);
			//if (v > 0)
			{
				dsqr = targetstaticheader->staticCollInfo.collspheresizeSqr - (distsquared - (v*v));
				if (dsqr > 0)
				{
					d = fsqrt(dsqr);
					if ((v-d) < missile->maxtraveldist)
					{
						if ((collideLineDist = collCheckRectLine((SpaceObjRotImp *)target,&missile->posinfo.position,&missileheading,missile->maxtraveldist,&collSide)) >= 0.0f)
						{
							univMissileCollidedWithTarget((SpaceObjRotImpTarg *)target,targetstaticheader,missile,collideLineDist,collSide);
							AddMissileToDeleteMissileList(missile, -1);
							goto nextmissile;
						}
					}
				}
			}

nexttarget:
            targetindex++;
        }

nextpass:
        pass++;
        if (pass < 2)
        {
            goto passagain;
        }

nextmissile:
        missileindex++;
    }

#ifndef HW_Release
//    thisBlob->debugFlag = 0;
#endif

}

/*-----------------------------------------------------------------------------
    Name        : collCheckMissileResourceColl
    Description : checks collisions between missiles and resources
    Inputs      :
    Outputs     :
    Return      :
----------------------------------------------------------------------------*/
void collCheckMissileResourceColl(blob *thisBlob,MissileType missileType)
{
    sdword missileindex = 0;
    Missile *missile;

    sdword targetindex;
    Resource *target;
    StaticHeader *targetstaticheader;

    real32 distsquared;
    vector distvector;
    real32 v,d,dsqr;
    sdword collSide;

    real32 distcheck;

    real32 collideLineDist;

    vector missileheading;

    MissileSelection *missileselection;
    sdword numMissiles;

    ResourceSelection *targetselection = thisBlob->blobResources;
    sdword numTargets = targetselection->numResources;

    sdword bottom,top;

    real32 maxCollSphereSize = thisBlob->blobMaxResourceCollSphereSize;
    real32 maxCollSphereSizePlusMissileTravelDist;

    if (numTargets == 0)
    {
        return;
    }

    if (missileType == MISSILE_Mine)
    {
        missileselection = thisBlob->blobMissileMines;
    }
    else
    {
        missileselection = thisBlob->blobMissileMissiles;
    }
    numMissiles = missileselection->numMissiles;

#ifndef HW_Release
//    thisBlob->debugFlag = 1;
#endif

    while (missileindex < numMissiles)
    {
        missile = missileselection->MissilePtr[missileindex];
        dbgAssert(missile->objtype == OBJ_MissileType);

        if (missile->flags & SOF_Dead)
        {
            goto nextmissile;
        }

        maxCollSphereSizePlusMissileTravelDist = maxCollSphereSize + missile->maxtraveldist;

        // binary search targetindex such that
        // (missile->collOptimizeDist - target->collOptimizeDist) <= maxCollSphereSizePlusMissileTravelDist
        bottom = 0;
        top = numTargets-1;
        while (top >= bottom)
        {
            targetindex = (bottom+top)>>1;
            if ((missile->collOptimizeDist - targetselection->ResourcePtr[targetindex]->collOptimizeDist) > maxCollSphereSizePlusMissileTravelDist)
            {
                // targetindex is too small
                bottom = targetindex+1;
            }
            else
            {
                top = targetindex-1;
            }
        }

        dbgAssert(targetindex >= 0);
        dbgAssert(targetindex < numTargets);

        while (targetindex < numTargets)
        {
            target = targetselection->ResourcePtr[targetindex];

#ifdef COLLISION_CHECK_STATS
    missilewalks++;
#endif

            distcheck = target->collOptimizeDist - missile->collOptimizeDist;

            if (distcheck > maxCollSphereSizePlusMissileTravelDist)
            {
                goto nextmissile;
            }

            targetstaticheader = &target->staticinfo->staticheader;

            if (abs(distcheck) > (targetstaticheader->staticCollInfo.collspheresize+missile->maxtraveldist))
            {
                goto nexttarget;
            }

            if (target->flags & SOF_NotBumpable)
            {
                goto nexttarget;
            }

            // Check for collisions between missile and target here:

#ifdef COLLISION_CHECK_STATS
    missilechecks++;
#endif
            vecSub(distvector,target->collInfo.collPosition,missile->posinfo.position);
            distsquared = vecMagnitudeSquared(distvector);
            matGetVectFromMatrixCol3(missileheading,missile->rotinfo.coordsys);
            v = vecDotProduct(distvector,missileheading);
			//if (v > 0)
			{
				dsqr = targetstaticheader->staticCollInfo.collspheresizeSqr - (distsquared - (v*v));
				if (dsqr > 0)
				{
					d = fsqrt(dsqr);
					if ((v-d) < missile->maxtraveldist)
					{
						if ((collideLineDist = collCheckRectLine((SpaceObjRotImp *)target,&missile->posinfo.position,&missileheading,missile->maxtraveldist,&collSide)) >= 0.0f)
						{
							univMissileCollidedWithTarget((SpaceObjRotImpTarg *)target,targetstaticheader,missile,collideLineDist,collSide);
							AddMissileToDeleteMissileList(missile, -1);
							goto nextmissile;
						}
					}
				}
			}

nexttarget:
            targetindex++;
        }

nextmissile:
        missileindex++;
    }

#ifndef HW_Release
//    thisBlob->debugFlag = 0;
#endif

}


/*-----------------------------------------------------------------------------
    Name        : collCheckMissileDerelictColl
    Description : checks collisions between missiles and derelicts
    Inputs      :
    Outputs     :
    Return      :
----------------------------------------------------------------------------*/
void collCheckMissileDerelictColl(blob *thisBlob,MissileType missileType)
{
    sdword missileindex = 0;
    Missile *missile;

    sdword targetindex;
    Derelict *target;
    StaticHeader *targetstaticheader;

    real32 distsquared;
    vector distvector;
    real32 v,d,dsqr;
    sdword collSide;

    real32 distcheck;

    real32 collideLineDist;

    vector missileheading;

    MissileSelection *missileselection;
    sdword numMissiles;

    DerelictSelection *targetselection = thisBlob->blobDerelicts;
    sdword numTargets = targetselection->numDerelicts;

    real32 maxCollSphereSize = thisBlob->blobMaxDerelictCollSphereSize;
    real32 maxCollSphereSizePlusMissileTravelDist;

    sdword bottom,top;

    if (numTargets == 0)
    {
        return;
    }

    if (missileType == MISSILE_Mine)
    {
        missileselection = thisBlob->blobMissileMines;
    }
    else
    {
        missileselection = thisBlob->blobMissileMissiles;
    }
    numMissiles = missileselection->numMissiles;

#ifndef HW_Release
//    thisBlob->debugFlag = 1;
#endif

    while (missileindex < numMissiles)
    {
        missile = missileselection->MissilePtr[missileindex];
        dbgAssert(missile->objtype == OBJ_MissileType);

        if (missile->flags & SOF_Dead)
        {
            goto nextmissile;
        }

        maxCollSphereSizePlusMissileTravelDist = maxCollSphereSize + missile->maxtraveldist;

        // binary search targetindex such that
        // (missile->collOptimizeDist - target->collOptimizeDist) <= maxCollSphereSizePlusMissileTravelDist
        bottom = 0;
        top = numTargets-1;
        while (top >= bottom)
        {
            targetindex = (bottom+top)>>1;
            if ((missile->collOptimizeDist - targetselection->DerelictPtr[targetindex]->collOptimizeDist) > maxCollSphereSizePlusMissileTravelDist)
            {
                // targetindex is too small
                bottom = targetindex+1;
            }
            else
            {
                top = targetindex-1;
            }
        }

        dbgAssert(targetindex >= 0);
        dbgAssert(targetindex < numTargets);

        while (targetindex < numTargets)
        {
            target = targetselection->DerelictPtr[targetindex];

#ifdef COLLISION_CHECK_STATS
    missilewalks++;
#endif

            distcheck = target->collOptimizeDist - missile->collOptimizeDist;

            if (distcheck > maxCollSphereSizePlusMissileTravelDist)
            {
                goto nextmissile;
            }

            targetstaticheader = &target->staticinfo->staticheader;

            if (abs(distcheck) > (targetstaticheader->staticCollInfo.collspheresize+missile->maxtraveldist))
            {
                goto nexttarget;
            }

            // Check for collisions between missile and target here:

#ifdef COLLISION_CHECK_STATS
    missilechecks++;
#endif
            vecSub(distvector,target->collInfo.collPosition,missile->posinfo.position);
            distsquared = vecMagnitudeSquared(distvector);
            matGetVectFromMatrixCol3(missileheading,missile->rotinfo.coordsys);
            v = vecDotProduct(distvector,missileheading);
			//if (v > 0)
			{
				dsqr = targetstaticheader->staticCollInfo.collspheresizeSqr - (distsquared - (v*v));
				if (dsqr > 0)
				{
					d = fsqrt(dsqr);
					if ((v-d) < missile->maxtraveldist)
					{
						if ((collideLineDist = collCheckRectLine((SpaceObjRotImp *)target,&missile->posinfo.position,&missileheading,missile->maxtraveldist,&collSide)) >= 0.0f)
						{
							univMissileCollidedWithTarget((SpaceObjRotImpTarg *)target,targetstaticheader,missile,collideLineDist,collSide);
							AddMissileToDeleteMissileList(missile, -1);
							goto nextmissile;
						}
					}
				}
			}

nexttarget:
            targetindex++;
        }

nextmissile:
        missileindex++;
    }

#ifndef HW_Release
//    thisBlob->debugFlag = 0;
#endif

}

/*-----------------------------------------------------------------------------
    Name        : collCheckMissileMineColl
    Description : checks collisions between missiles and mines
    Inputs      :
    Outputs     :
    Return      :
----------------------------------------------------------------------------*/
void collCheckMissileMineColl(blob *thisBlob)
{
    sdword missileindex = 0;
    Missile *missile;

    sdword targetindex;
    Missile *target;
    StaticHeader *targetstaticheader;

    real32 distsquared;
    vector distvector;
    real32 v,d,dsqr;
    sdword collSide;

    real32 distcheck;

    real32 collideLineDist;

    vector missileheading;

    MissileSelection *missileselection = thisBlob->blobMissileMissiles;
    sdword numMissiles = missileselection->numMissiles;

    MissileSelection *targetselection = thisBlob->blobMissileMines;
    sdword numTargets = targetselection->numMissiles;

    sdword bottom,top;

    real32 maxMineCollSphereSizePlusMissileTravelDist;

    if (numTargets == 0)
    {
        return;
    }

#ifndef HW_Release
//    thisBlob->debugFlag = 1;
#endif

    while (missileindex < numMissiles)
    {
        missile = missileselection->MissilePtr[missileindex];
        dbgAssert(missile->objtype == OBJ_MissileType);
        dbgAssert(missile->missileType == MISSILE_Regular);

        if (missile->flags & SOF_Dead)
        {
            goto nextmissile;
        }

        maxMineCollSphereSizePlusMissileTravelDist = maxMineCollSphereSize + missile->maxtraveldist;

        // binary search targetindex such that
        // (missile->collOptimizeDist - target->collOptimizeDist) <= maxMineCollSphereSizePlusMissileTravelDist
        bottom = 0;
        top = numTargets-1;
        while (top >= bottom)
        {
            targetindex = (bottom+top)>>1;
            if ((missile->collOptimizeDist - targetselection->MissilePtr[targetindex]->collOptimizeDist) > maxMineCollSphereSizePlusMissileTravelDist)
            {
                // targetindex is too small
                bottom = targetindex+1;
            }
            else
            {
                top = targetindex-1;
            }
        }

        dbgAssert(targetindex >= 0);
        dbgAssert(targetindex < numTargets);

        while (targetindex < numTargets)
        {
            target = targetselection->MissilePtr[targetindex];
            dbgAssert(target->objtype == OBJ_MissileType);
            dbgAssert(target->missileType == MISSILE_Mine);

#ifdef COLLISION_CHECK_STATS
    missilewalks++;
#endif

            distcheck = target->collOptimizeDist - missile->collOptimizeDist;

            if (distcheck > maxMineCollSphereSizePlusMissileTravelDist)
            {
                goto nextmissile;
            }

            targetstaticheader = &target->staticinfo->staticheader;

            if (abs(distcheck) > (targetstaticheader->staticCollInfo.collspheresize+missile->maxtraveldist))
            {
                goto nexttarget;
            }

            // Check for collisions between missile and target here:

#ifdef COLLISION_CHECK_STATS
    missilechecks++;
#endif
            vecSub(distvector,target->collInfo.collPosition,missile->posinfo.position);
            distsquared = vecMagnitudeSquared(distvector);
            matGetVectFromMatrixCol3(missileheading,missile->rotinfo.coordsys);
            v = vecDotProduct(distvector,missileheading);
			//if (v > 0)
			{
				dsqr = targetstaticheader->staticCollInfo.collspheresizeSqr - (distsquared - (v*v));
				if (dsqr > 0)
				{
					d = fsqrt(dsqr);
					if ((v-d) < missile->maxtraveldist)
					{
						if ((collideLineDist = collCheckRectLine((SpaceObjRotImp *)target,&missile->posinfo.position,&missileheading,missile->maxtraveldist,&collSide)) >= 0.0f)
						{
							univMissileCollidedWithTarget((SpaceObjRotImpTarg *)target,targetstaticheader,missile,collideLineDist,collSide);
							AddMissileToDeleteMissileList(missile, -1);
							goto nextmissile;
						}
					}
				}
			}

nexttarget:
            targetindex++;
        }

nextmissile:
        missileindex++;
    }

#ifndef HW_Release
//    thisBlob->debugFlag = 0;
#endif

}

/*-----------------------------------------------------------------------------
    Name        : collCheckAllBumpCollisions
    Description : checks all bump collisions in universe (ship-ship, ship-resource, etc)
    Inputs      :
    Outputs     :
    Return      :
----------------------------------------------------------------------------*/
void collCheckAllBumpCollisions(void)
{
    Node *blobnode = universe.collBlobList.head;
    blob *thisBlob;

#ifdef COLLISION_CHECK_STATS
    shipshipwalks = 0;
    shipshipchecks = 0;
    shipresourcewalks = 0;
    shipresourcechecks = 0;
    shipderelictwalks = 0;
    shipderelictchecks = 0;
#endif

    if (!nisIsRunning)
    {
        // for each blob, check all collisions within that blob
        while (blobnode != NULL)
        {
            thisBlob = (blob *)listGetStructOfNode(blobnode);
            blobnode = blobnode->next;

            collCheckShipShipColl(thisBlob,TRUE);       // check smallship-smallship collisions
            collCheckShipShipColl(thisBlob,FALSE);      // check bigship-bigship collisions
            collCheckBigShipSmallShipColl(thisBlob);    // check bigship-smallship collisions
            collCheckShipResourceColl(thisBlob);
            collCheckShipDerelictColl(thisBlob);
        }
    }
    else if ((singlePlayerGame) && (singlePlayerGameInfo.currentMission == 4))
    {
        // for each blob, check all collisions within that blob
        while (blobnode != NULL)
        {
            thisBlob = (blob *)listGetStructOfNode(blobnode);
            blobnode = blobnode->next;

            // in case Bentusi traders run into some ships...
            collCheckShipShipColl(thisBlob,FALSE);      // check bigship-bigship collisions
            collCheckBigShipSmallShipColl(thisBlob);    // check bigship-smallship collisions
        }
    }
}

/*-----------------------------------------------------------------------------
    Name        : collCheckAllBulletMissileCollisions
    Description : checks all bullet, missile, and mine collisions in universe
    Inputs      :
    Outputs     :
    Return      :
----------------------------------------------------------------------------*/
void collCheckAllBulletMissileCollisions(void)
{
    Node *blobnode = universe.collBlobList.head;
    blob *thisBlob;
    udword savemissilewalks;
    udword savemissilechecks;

#ifdef COLLISION_CHECK_STATS
    bulletwalks = 0;
    bulletchecks = 0;
    missilewalks = 0;
    missilechecks = 0;
    minewalks = 0;
    minechecks = 0;
#endif

    // for each blob, check all collisions within that blob
    while (blobnode != NULL)
    {
        thisBlob = (blob *)listGetStructOfNode(blobnode);
        blobnode = blobnode->next;

        collCheckBulletTargetColl(thisBlob);
        //collCheckBulletShipColl(thisBlob);
        //collCheckBulletResourceColl(thisBlob);
        //collCheckBulletDerelictColl(thisBlob);
        //collCheckBulletMissileColl(thisBlob);

        //collCheckMissileTargetColl(thisBlob);

        collCheckMissileShipColl(thisBlob,MISSILE_Regular);
        collCheckMissileResourceColl(thisBlob,MISSILE_Regular);
        collCheckMissileDerelictColl(thisBlob,MISSILE_Regular);

#ifdef COLLISION_CHECK_STATS
        savemissilewalks = missilewalks;
        savemissilechecks = missilechecks;
        missilewalks = 0;
        missilechecks = 0;
#endif

        collCheckMissileShipColl(thisBlob,MISSILE_Mine);
        //collCheckMissileResourceColl(thisBlob,MISSILE_Mine);
        collCheckMissileDerelictColl(thisBlob,MISSILE_Mine);

#ifdef COLLISION_CHECK_STATS
        minewalks += missilewalks;
        minechecks += missilechecks;
        missilewalks = savemissilewalks;
        missilechecks = savemissilechecks;
#endif

        collCheckMissileMineColl(thisBlob);
    }
}


