Ejemplo n.º 1
0
        /* Overload this so as not to change
         *  velocity as I normally want my walls to be stable and not drift after being dragged. */

        /// <summary>
        /// There's a collision if pcritter has crossed the wall or if the radius of pcritter is more than
        /// the pcritter's distance to the wall.  Has code to prevent the pcritter from going through the wall.
        /// Returns true if a collision was detected and false otherwise.
        /// </summary>
        /// <param name="pcritter">The critter to test for a collision with the wall.</param>
        /// <returns></returns>
        public override bool collide(cCritter pcritter)
        {
            cVector3 oldlocalpos, newlocalpos;
            float    newdistance;
            int      oldoutcode, newoutcode;
            bool     crossedwall;

            oldlocalpos = globalToLocalPosition(pcritter.OldPosition);
            oldoutcode  = _pskeleton.outcode(oldlocalpos);
            newlocalpos = globalToLocalPosition(pcritter.Position);
            newdistance = _pskeleton.distanceToOutcode(newlocalpos,
                                                       out newoutcode); //Sets the newoutcode as well.
            crossedwall = crossed(oldoutcode, newoutcode);

            if (newdistance >= pcritter.Radius && !crossedwall) //No collision
            {
                return(false);                                  /*See if there's a collision at all. We
                                                                 * say there's a collision if crossedwall or if the
                                                                 * cCritterWall.distance is less than radius.  Remember that
                                                                 * cCritterWall.distance measures the distance to the OUTSIDE
                                                                 * PERIMETER of the box, not the distance to the box's center. */
            }

            /* I collided, so I need to move back further into the last good
             * zone I was in outside the wall.  I want to set newlocalpos so
             * the rim of its critter is touching the wall. The idea is to back
             * up in the direction of oldlocalpos.  To allow the possibility
             * of skidding along the wall, we plan to back up from the
             * the face (or edge or corner) facing oldlocalpos.  This works
             * only if oldlocalpos was a good one, not inside the box.  In
             * principle this should always be true, but some rare weird circumstance
             * (like a triple collsion) might mess this up, so we check for the
             * bad case before starting. */

            if (oldoutcode == cRealBox3.BOX_INSIDE) //Note that this almost never happens.
            {
                cVector3 insidepos = new cVector3();
                insidepos.copy(oldlocalpos);
                oldlocalpos -= pcritter.Tangent * _pskeleton.MaxSize;
                //Do a brutally large backup to get out of the box for sure.
                oldoutcode = _pskeleton.outcode(oldlocalpos);
                //Recalculate outcode at this new position.
                oldlocalpos = _pskeleton.closestSurfacePoint(oldlocalpos, oldoutcode,
                                                             insidepos, cRealBox3.BOX_INSIDE, false);
                //Go to the closest surface point from there.
                oldoutcode = _pskeleton.outcode(oldlocalpos);
                //Recalculate outcode one more time to be safe.
                crossedwall = crossed(oldoutcode, newoutcode);
                //Recalculate crossedwall
            }

            /* I find that with this code, the mouse can drag things through walls,
             * so I do a kludge to block it by setting crossedwall to TRUE, this
             * affects the action of cRealBox.closestSurfacePoint, as modified
             * in build 34_4. */
            if (pcritter.Listener is cListenerCursor)
            {
                crossedwall = true; //Don't trust the mouse listener.
            }
            newlocalpos = _pskeleton.closestSurfacePoint(oldlocalpos, oldoutcode,
                                                         newlocalpos, newoutcode, crossedwall);

            /* This call to closestSurfacePoint will move the newlocal pos
             * from the far new side (or inside, or overlapping) of the box back to
             * the surface, usually on the old near side, edge, or corner given by
             * oldoutcode. This prevents going through the	wall.
             * If oldoutcode is a corner position and you are in fact heading
             * towards a face near the corner, we used to bounce off the corner
             * even though visually you can see you should bounce off the
             * face.  This had the effect of making a scooter player get hung up on
             * a corner sometimes. As of build 34_3, I'm moving the
             * newlocalpos to the newoutocode side in the case where oldlocalpos
             * is an edge or a corner, and where crossedwall isn't TRUE.  I
             * have to force in a TRUE for the cCursorLIstener case.  The USEJIGGLE
             * code below also helps keep non-player critters from getting stuck
             * on corners. */
            //Now back away from the box.
            newoutcode = _pskeleton.outcode(newlocalpos);
            cVector3 avoidbox = _pskeleton.escapeVector(newlocalpos, newoutcode);

            newlocalpos += avoidbox * pcritter.Radius;
            newoutcode   = _pskeleton.outcode(newlocalpos);
            pcritter.moveTo(localToGlobalPosition(newlocalpos), true);
            //TRUE means continuous motion, means adjust tangent etc.
            //Done with position, now change the velocity
            cVector3 localvelocity    = globalToLocalDirection(pcritter.Velocity);
            cVector3 oldlocalvelocity = new cVector3();

            oldlocalvelocity.copy(localvelocity);
            _pskeleton.reflect(localvelocity, newoutcode);

            /* I rewrote the reflect code on Feb 22, 2004 for VErsion 34_3, changing
             * it so that when you reflect off an edge or corner, you only bounce
             * the smallest of your three velocity components. Balls stll seem to
             * get hung up on the corner once is awhile. */
            /* Now decide, depending on the pcritter's absorberflag and bounciness,
             * how much you want to use the new localvelocity vs. the
             * oldlocalvelocity. We decompose the new localvelocity into the
             * tangentvelocity parallel to the wall and the normalvelocity
             * away from the wall. Some pencil and paper drawings convince
             * me that the tangent is half the sum of the oldlocalvelocity
             * and the reflected new localvelocity. */
            cVector3 tangentvelocity = (localvelocity + oldlocalvelocity) * 0.5f;
            cVector3 normalvelocity  = localvelocity - tangentvelocity;
            float    bouncefactor    = 1.0f;

            if (pcritter.AbsorberFlag)
            {
                bouncefactor = 0.0f;
            }
            else
            {
                bouncefactor = pcritter.Bounciness;
            }
            localvelocity = tangentvelocity + normalvelocity * bouncefactor;

            /* Maybe the rotation should depend on the kind of edge or corner.
             * Right now let's just use critter's binormal. Don't to it
             * to the player or viewer as it's confusing.  */
            if (!(cRealBox3.isFaceOutcode(newoutcode)) && //edge or corner
                !(pcritter is cCritterViewer) &&          //not viewer
                !(pcritter is cCritterArmedPlayer))
            //Not player.  Note that cPlayer inherits from cCritterArmedPlayer,
            //so don't use cCritterPlayer as the base class here.
            {
                localvelocity.rotate(new cSpin(
                                         Framework.randomOb.randomReal(
                                             -cCritterWall.CORNERJIGGLETURN,
                                             cCritterWall.CORNERJIGGLETURN), //A random turn
                                         pcritter.Binormal));                //Around the critter's binormal
                localvelocity *= cCritterWall.CORNERJIGGLEKICK;              //Goose it a little
            }
            pcritter.Velocity = localToGlobalDirection(localvelocity);
            return(true);
        }