Example #1
0
 public void addToPath(MeshNode target, List<MeshNode> ignore)
 {
     if (path.Count == 0) {
         Console.WriteLine("setting path");
         setPathTo(target, ignore);
     }
     else if (path[path.Count - 1] == target) {
         Console.WriteLine("nevermind");
         return;
     }
     else if (path.Contains(target)) {
         Console.WriteLine("truncating path");
         int index = path.IndexOf(target);
         path.RemoveRange(index + 1, path.Count - index - 1);
     }
     else {
         Console.WriteLine("adding path");
         List<MeshNode> pathAdd = pathFrom(path[path.Count - 1], target, ignore);
         if (pathAdd.Count > 0) {
             Console.WriteLine("added");
             pathAdd.RemoveAt(0);
             path.AddRange(pathAdd);
         }
     }
 }
Example #2
0
        public void generateAIMesh()
        {
            mesh = new List<MeshNode>();
            //generate priliminary list of meshNodes from faces that point vaguely up
            float floorDot = (float)Math.Cos(MathHelper.ToRadians(floorAngle));

            foreach(Brush brush in core.mapEngine.brushes)   {
                foreach (Face f in brush.faces) {
                    //if plane is valid "floor"
                    if (Vector3.Dot(f.plane.getNormal(), Vector3.Up) > floorDot) {
                        //we move along the "plane" that the face sits on
                        //and then for each valid point on the face, we put a node
                        Vector3 x = f.plane.meshThird - f.plane.meshSecond,
                                y = f.plane.meshFirst - f.plane.meshSecond;
                        float width = (float)Math.Sqrt(Vector3.Dot(x, x)),
                              height = (float)Math.Sqrt(Vector3.Dot(y, y));
                        x.Normalize();
                        y.Normalize();

                        //we need the face normal for node lift
                        Vector3 N = Vector3.Normalize(f.plane.getNormal());

                        for(float i = nodeRadius; i < height; i += nodeRadius)
                            for (float j = nodeRadius; j < width; j += nodeRadius) {
                                //get the point represented by (j,i)
                                //Vector3 p = f.plane.meshSecond + j * x + i * y + N * nodeLift;
                                Vector3 p = f.plane.meshSecond + j * x + i * y + new Vector3(0, nodeLift, 0);

                                //now find if this point is on the face
                                if (MapEngine.pointOnFace(p, f) && !core.mapEngine.pointInBrush(p) && core.physicsEngine.hitscan(p, Vector3.Up, null) != null)
                                {
                                    mesh.Add(new MeshNode(p, f));
                                }
                            }
                    } // if "floor"
                } // for face
            } // for brush

            //add meshnodes for the pickups
            Vector3 heightVec = new Vector3(0, nodeHeight, 0);
            foreach (PickUpGen g in core.pickupEngine.gens) {
                Vector3 gh = g.pos + heightVec;
                //cast a ray down
                PhysicsEngine.HitScan h = core.physicsEngine.hitscan(gh, -Vector3.Up, null);
                if (h != null && h.collisionFace != null && h.Distance() < 60+nodeHeight) {
                    gh = h.collisionPoint + new Vector3(0, nodeLift, 0);
                    MeshNode m = new MeshNode(gh, h.collisionFace);
                    m.linkedPickupGen = g;
                    mesh.Add(m);
                    pickupNodes.Add(m);
                }
            }

            //connect vibes
            List<MeshNode> meshAdd = new List<MeshNode>();
            heightVec = new Vector3(0, nodeHeight-nodeLift, 0);
            foreach(MeshNode m1 in mesh)   {
                foreach (MeshNode m2 in mesh)
                {
                    if (m1 == m2 || m1.neighbours.Contains(m2))
                        continue;
                    //now check if they're ok to connect
                    if (Vector3.Distance(m1.position, m2.position) > nodeRadius * 2.2)
                        continue;
                    //now check if they are line-of-sight
                    PhysicsEngine.HitScan hs = core.physicsEngine.hitscan(m1.position + heightVec, m2.position - m1.position, null);
                    if (hs != null && hs.Distance() < Vector3.Distance(m1.position, m2.position) - 0.005)
                        continue;

                    //now check if they're connecteable using the y-project intersection test
                    double ad = m1.face.plane.getD(),
                          bd = m2.face.plane.getD();
                    Vector3 a = m1.face.plane.getNormal(),
                            b = m2.face.plane.getNormal(),
                            ap = m1.position,
                            bp = m2.position;
                    if (Vector3.Distance(a, b) > 0.005)
                    {
                        double denom = (bp.X - ap.X) * (a.X * b.Y - b.X * a.Y) + (bp.Z - ap.Z) * (a.Z * b.Y - b.Z * a.Y);
                        if (denom == 0)
                            continue;
                        double t = (a.Y * (bd + b.X * ap.X + b.Z * ap.Z) - b.Y * (ad + a.X * ap.X + a.Z * ap.Z)) / denom;
                        if (0 < t && t < 1)   {
                            //create the intermediate mesh node
                            Vector3 w = bp - ap;
                            Vector3 interPoint = ap + (float)t * w;
                            interPoint.Y = -((float)ad + a.X*interPoint.X + a.Z*interPoint.Z)/a.Y + nodeLift;
                            MeshNode mi = new MeshNode(interPoint, m1.face);
                            meshAdd.Add(mi);
                            m1.neighbours.Add(mi);
                            m2.neighbours.Add(mi);
                            mi.neighbours.Add(m1);
                            mi.neighbours.Add(m2);
                        }
                        else if(t == 0 || t == 1) {
                            m1.neighbours.Add(m2);
                            m2.neighbours.Add(m1);
                        }
                    }
                    else if(Math.Abs(ad-bd) < 0.5)  //if the planes are "parallel" then just check their D's
                    {
                        m1.neighbours.Add(m2);
                        m2.neighbours.Add(m1);
                    }
                }
            }
            foreach (MeshNode mi in meshAdd)
                mesh.Add(mi);
        }
Example #3
0
        //perform A* from the closest node to the target node
        public void setPathTo(MeshNode target, List<MeshNode> ignore)
        {
            //previousTarget = null;
            targetAquisitionDuration = 0;
            path.Clear();
            MeshNode start = findClosestMeshNode(ignore);
            if (start == null) {
                Console.WriteLine("start is null");
                return;
            }

            path = pathFrom(start, target, ignore);
        }
Example #4
0
 public AStarHeapNode(MeshNode m, float distSoFar, float hDist)
 {
     node = m;
     pathLength = distSoFar;
     cost = hDist;
 }
Example #5
0
 protected void popFromPath()
 {
     previousTarget = null;
     if(path.Count > 0)  {
         previousTarget = path[0];
         path.RemoveAt(0);
     }
 }
Example #6
0
        protected List<MeshNode> pathFrom(MeshNode start, MeshNode target, List<MeshNode> ignore)
        {
            if (ignore == null) ignore = new List<MeshNode>();
            List<MeshNode> path = new List<MeshNode>();
            AStarHeap q = new AStarHeap();
            //{node: (path length, previous node)}
            Dictionary<MeshNode, KeyValuePair<float, MeshNode>> distances = new Dictionary<MeshNode, KeyValuePair<float, MeshNode>>();
            q.add(new AStarHeapNode(start, 0, Vector3.Distance(start.position, target.position)));
            distances.Add(start, new KeyValuePair<float, MeshNode>(0, null));
            while (!q.empty()) {
                AStarHeapNode n = q.pop();
                if (n.node == target)
                    break;

                //try and expand from this node to shortest paths
                foreach (MeshNode m in n.node.neighbours) {
                    if (ignore.Contains(m))
                        continue;
                    float pathLength = n.pathLength + Vector3.Distance(m.position, n.node.position);
                    //see if we don't already have a shorter path to this node
                    if (distances.ContainsKey(m) && distances[m].Key <= pathLength)
                        continue;
                    //otherwise this is promising path, so add it to the queue
                    q.add(new AStarHeapNode(m, pathLength, pathLength + Vector3.Distance(m.position, target.position)));
                    distances.Remove(m);
                    distances.Add(m, new KeyValuePair<float, MeshNode>(pathLength, n.node));
                }
            }

            //if we've found a path
            if (distances.ContainsKey(target)) {
                //backtrack the entire path and add it to the path list
                MeshNode m = target;
                while (m != null) {
                    path.Insert(0, m);
                    m = distances[m].Value;
                }
            }
            return path;
        }
Example #7
0
        public override void Update(GameTime gameTime)
        {
            /* If we're dead then wait until we can spawn ourselves
             * again.
             */
            if (spawnTime > 0) {
                spawnTime -= (float)gameTime.ElapsedGameTime.TotalSeconds;
                if (spawnTime <= 0) {
                    spawnTime = 0;
                    health = 100;
                    ammo = 200;
                    equipped = new Pistol();
                    core.spawnPlayer(this);
                }
                return;
            }
            else if (health < 0) {
                spawnTime = spawnDelay;
                return;
            }

            timeSinceDamageDealt += (float)gameTime.ElapsedGameTime.TotalSeconds;

            //update the weapon
            equipped.Update(gameTime);

            //check if we can look for and find an enemy agent
            //if we aren't already targetting an agent, or it's time to give up, find a new target, if we can
            if (timeSinceLastShot >= lastShotTimeLimit) {
                agentTarget = null;
                path.Clear();
                timeSinceLastShot = 0;
                Console.WriteLine("giving up");
            }
            else if(agentTarget != null)
                timeSinceLastShot += (float)gameTime.ElapsedGameTime.TotalSeconds;

            curReevaluationTime -= (float)gameTime.ElapsedGameTime.TotalSeconds;

            if (curReevaluationTime <= 0) {
                curReevaluationTime = reevaulationTime;
                agentTarget = null;

                /* depending on the current state of this agent we want to do something
                 * different. if we are low on health, then we move towards a health pickup
                 * if we are low on ammo, then towards ammo, otherwise we look for an opponent.
                 * Now there are also overriding circumstances: if an enemy is too close, then we
                 * attack them (unless we have no ammo). also, if we have a low-level weapon then
                 * we want to upgrade our weapon. to do this we calculate all relevant data
                 */

                //find a new target, but only if ammo is sufficient
                float closestFeasibleTargetDist = float.MaxValue;
                Agent closestAgent = null;
                //we need to at least be able to shoot our gun at the enemy for a second
                if (ammo >= equipped.ammoUsed * 2 && ammo >= equipped.ammoUsed / equipped.cooldown) {
                    Vector3 eye = getEyePosition(),
                            dir = getDirectionVector();
                    //first try and go after the guy that just shot us
                    if (damageSource != null
                            && damageSource != this
                            && damageSource.spawnTime == 0
                            && timeSinceDamageDealt <= timeLimitSinceDamageDealt) {
                        timeSinceDamageDealt = timeLimitSinceDamageDealt + 1;
                        Vector3 aCent = damageSource.getCenter();
                        float dist = Vector3.Distance(eye, aCent);
                        PhysicsEngine.HitScan hs = core.physicsEngine.hitscan(eye, aCent - eye, null);
                        if (hs == null || hs.Distance() > dist) {
                            closestAgent = damageSource;
                            closestFeasibleTargetDist = Vector3.Distance(eye, aCent);
                        }
                    }
                    if(closestAgent == null) {
                        foreach (Agent a in core.allAgents()) {
                            if (a.spawnTime > 0 || a == this) continue;  //if the agent is not in the level, then you can't see them
                            Vector3 aCent = a.getCenter();
                            float dist = Vector3.Distance(eye, aCent);
                            if (dist < closestFeasibleTargetDist    //we're only concerned in closer enemies
                                    && Vector3.Dot(dir, Vector3.Normalize(aCent - eye)) > -0.2) {   //if they're infront of us
                                PhysicsEngine.HitScan hs = core.physicsEngine.hitscan(eye, aCent - eye, null);
                                if (hs == null || hs.Distance() > dist) {
                                    closestAgent = a;
                                    closestFeasibleTargetDist = Vector3.Distance(eye, aCent);
                                }
                            }
                        }
                    }
                }

                //calculate the tier of a weapon
                int weaponTier = 2;
                if (equipped is Rifle || equipped is Shotgun)
                    weaponTier = 1;
                else if (equipped is Pistol)
                    weaponTier = 0;

                //calculate the nearest ammo, upgrade and health pickups
                float closestHealthDist = float.MaxValue, closestAmmoDist = float.MaxValue, closestUpgradeDist = float.MaxValue;
                MeshNode closestHealth = null, closestAmmo = null, closestUpgrade = null;
                foreach (MeshNode m in core.aiEngine.pickupNodes) {
                    if (m.linkedPickupGen.held != null) {
                        float dist = Vector3.Distance(m.position, position);
                        if (m.linkedPickupGen.itemType == PickUp.PickUpType.HEALTH) {
                            if (dist < closestHealthDist) {
                                closestHealthDist = dist;
                                closestHealth = m;
                            }
                        }
                        else if (m.linkedPickupGen.itemType == PickUp.PickUpType.AMMO) {
                            if (dist < closestAmmoDist) {
                                closestAmmoDist = dist;
                                closestAmmo = m;
                            }
                        }
                        else if (dist < closestUpgradeDist) {
                            closestUpgradeDist = dist;
                            closestUpgrade = m;
                        }
                    }
                }

                //now decide what to do, but how? we calculate a score for each value between 0 and 1
                double agentScore = Math.Min(1, Math.Max(0, (closestAgent == null || closestFeasibleTargetDist >= sightRadius ?
                                        0 : Math.Pow((sightRadius - closestFeasibleTargetDist) / sightRadius, 0.4)))),
                       healthScore = Math.Min(1, Math.Max(0, (closestHealth == null ?
                                        0 : Math.Pow((double)(70 - health)/maxHealth, 2)))),
                       ammoScore = Math.Min(1, Math.Max(0, (closestAmmo == null ?
                                        0 : Math.Pow((double)(maxAmmo - ammo)/maxAmmo, 3)))),
                       upgradeScore = Math.Min(1, Math.Max(0, (closestUpgrade == null ?
                                        0 : Math.Pow((double)(2 - weaponTier) / 2, 4))));

                //find the max
                double maxScore = Math.Max(agentScore, Math.Max(healthScore, Math.Max(ammoScore, upgradeScore)));

                //do the work
                if (maxScore > 0) {
                    ignore.Clear();
                    if (agentScore == maxScore) {
                        Console.WriteLine("going for agent: " + closestAgent);
                        agentTarget = closestAgent;
                        setPathTo(core.aiEngine.findClosestMeshNode(agentTarget.getPosition(), 100, ignore), ignore);
                    }
                    else if (healthScore == maxScore) {
                        Console.WriteLine("going for health: " + closestHealth);
                        setPathTo(closestHealth, null);
                    }
                    else if (ammoScore == maxScore) {
                        Console.WriteLine("going for ammo: " + closestAmmo);
                        setPathTo(closestAmmo, null);
                    }
                    else {
                        Console.WriteLine("going for upgrade: " + closestUpgrade);
                        setPathTo(closestUpgrade, null);
                    }
                }
            }

            if (agentTarget != null && (agentTarget == this || agentTarget.spawnTime > 0))
                agentTarget = null;

            if(agentTarget != null) {
                //walking towards the target
                if (path.Count == 0)
                    setPathTo(core.aiEngine.findClosestMeshNode(agentTarget.getPosition(), 100, ignore), ignore);
                Vector3 aCent = agentTarget.getCenter();
                Vector3 cent = position + new Vector3(0, size.Y / 2, 0);
                direction = getDirectionFromVector(Vector3.Normalize(aCent - cent));
                //shooting the target
                curAgentShootTime -= (float)gameTime.ElapsedGameTime.TotalSeconds;
                if (curAgentShootTime <= 0 && equipped.curCooldown <= 0) {
                    curAgentShootTime = agentShootTime;
                    //try and shoot the player
                    PhysicsEngine.HitScan hs = core.physicsEngine.hitscan(cent, aCent - cent, null);
                    if (hs == null || hs.Distance() > Vector3.Distance(cent, aCent)) {
                        timeSinceLastShot = 0;
                        //actually shoot now
                        //we want to adjust the direction based on the relative motion of the target to this agent
                        Vector3 right = Vector3.Normalize(Vector3.Cross(aCent - cent, Vector3.Up));
                        //now get the target's previous move in terms of right and up
                        Vector3 move = agentTarget.getPosition() - agentTarget.getOldPosition() + position - oldPosition;
                        double x = Math.Abs(Vector3.Dot(move, right)),
                            y = Math.Abs(Vector3.Dot(move, Vector3.Up));
                        //clamp x and y
                        //what's the max speed that they could've moved?
                        double max = agentTarget.speed * gameTime.ElapsedGameTime.TotalSeconds;
                        x = Math.Min(x/max, 1);
                        y = Math.Min(y/max, 1);

                        //y determines the inaccuracy of up-down aiming and x for left-right
                        //the "range" of this inaccuracy is determined by the distance the agent is from the me
                        double inaccuracy = Math.Pow(Math.Min(Vector3.Distance(cent, aCent) / sightRadius, 1), 0.02);

                        Console.WriteLine(inaccuracy * x);
                        direction.X += (float)((core.aiEngine.random.NextDouble() * 2 - 1) * Math.PI * inaccuracy * x / 2);
                        direction.Y += (float)((core.aiEngine.random.NextDouble() * 2 - 1) * Math.PI * inaccuracy * y / 2);

                        equipped.fire(this, core.physicsEngine);
                    }
                }
            }

            //if we're not moving towards something then stop ignoring nodes and find a pickup to go to
            if (path.Count == 0) {
                Console.WriteLine("here");
                ignore.Clear();     //should we clear ignore here?
                setPathTo(core.aiEngine.pickupNodes[core.aiEngine.random.Next(core.aiEngine.pickupNodes.Count)], ignore);
                if (path.Count == 0) {
                    Console.WriteLine("no path");
                    core.spawnPlayer(this);
                    return;
                }
            }

            //walk along the current path
            if (path.Count > 0) {

                //try move to the latest point in the path
                MeshNode target = path[0];
                /* if we're sufficiently close to the target switch it
                 * find the target's position relative to the position
                 */
                Vector2 tpos = new Vector2(Math.Abs(target.position.X - position.X),
                                            Math.Abs(target.position.Z - position.Z));
                if (tpos.X < size.X / 6 && tpos.Y < size.Y / 6) {
                    ignore.Clear();
                    popFromPath();
                    targetAquisitionDuration = 0;
                    //if (path.Count == 0 && agentTarget == null)setPathTo(core.aiEngine.pickupNodes[core.aiEngine.random.Next(core.aiEngine.pickupNodes.Count)], ignore);
                    if (path.Count == 0)
                        return;
                    target = path[0];
                }

                if (agentVelocities.persistentVelocity != Vector3.Zero)
                    previousTarget = null;

                //calculate direction to target -- we need this for the model, this will probably change, but the principles are right
                Vector3 velocity = target.position - position;
                direction = getDirectionFromVector(velocity);

                /* now calculate the move and actually move
                 * depending on whether we're on the mesh or not, we don't need collision detection
                 * if we've gotten here and there's a previous target then we're on the path
                 */
                if (true || previousTarget != null) {
                    velocity.Normalize();
                    core.physicsEngine.applySimpleMovement(gameTime, this, velocity * speed);
                }
                //otherwise we need to take care of things the expensive way
                else {
                    velocity.Y = 0;
                    velocity.Normalize();
                    core.physicsEngine.applyMovement((float)gameTime.ElapsedGameTime.TotalSeconds, this, speed * velocity);
                }

                /* the targetAquisitionDuration variable helps us keep track of the time taken
                 * to move between two nodes. if it's taking too long the we give up on that node
                 * and try a different path to our target node (path[-1])
                 */
                targetAquisitionDuration += gameTime.ElapsedGameTime.TotalSeconds;
                if (targetAquisitionDuration >= timeout) {
                    ignore.Add(target);
                    targetAquisitionDuration = 0;
                    if (path.Count > 0)
                        setPathTo(path[path.Count - 1], ignore);
                }

                core.physicsEngine.updateCollisionCellsFor(this);
            }
        }
Example #8
0
 public AStarHeapNode(MeshNode m, float distSoFar, float hDist)
 {
     node       = m;
     pathLength = distSoFar;
     cost       = hDist;
 }
Example #9
0
        public override void Update(GameTime gameTime)
        {
            /* If we're dead then wait until we can spawn ourselves
             * again.
             */
            if (spawnTime > 0)
            {
                spawnTime -= (float)gameTime.ElapsedGameTime.TotalSeconds;
                if (spawnTime <= 0)
                {
                    spawnTime = 0;
                    health    = 100;
                    ammo      = 200;
                    equipped  = new Pistol();
                    core.spawnPlayer(this);
                }
                return;
            }
            else if (health < 0)
            {
                spawnTime = spawnDelay;
                return;
            }

            timeSinceDamageDealt += (float)gameTime.ElapsedGameTime.TotalSeconds;

            //update the weapon
            equipped.Update(gameTime);

            //check if we can look for and find an enemy agent
            //if we aren't already targetting an agent, or it's time to give up, find a new target, if we can
            if (timeSinceLastShot >= lastShotTimeLimit)
            {
                agentTarget = null;
                path.Clear();
                timeSinceLastShot = 0;
                Console.WriteLine("giving up");
            }
            else if (agentTarget != null)
            {
                timeSinceLastShot += (float)gameTime.ElapsedGameTime.TotalSeconds;
            }

            curReevaluationTime -= (float)gameTime.ElapsedGameTime.TotalSeconds;

            if (curReevaluationTime <= 0)
            {
                curReevaluationTime = reevaulationTime;
                agentTarget         = null;

                /* depending on the current state of this agent we want to do something
                 * different. if we are low on health, then we move towards a health pickup
                 * if we are low on ammo, then towards ammo, otherwise we look for an opponent.
                 * Now there are also overriding circumstances: if an enemy is too close, then we
                 * attack them (unless we have no ammo). also, if we have a low-level weapon then
                 * we want to upgrade our weapon. to do this we calculate all relevant data
                 */

                //find a new target, but only if ammo is sufficient
                float closestFeasibleTargetDist = float.MaxValue;
                Agent closestAgent = null;
                //we need to at least be able to shoot our gun at the enemy for a second
                if (ammo >= equipped.ammoUsed * 2 && ammo >= equipped.ammoUsed / equipped.cooldown)
                {
                    Vector3 eye = getEyePosition(),
                            dir = getDirectionVector();
                    //first try and go after the guy that just shot us
                    if (damageSource != null &&
                        damageSource != this &&
                        damageSource.spawnTime == 0 &&
                        timeSinceDamageDealt <= timeLimitSinceDamageDealt)
                    {
                        timeSinceDamageDealt = timeLimitSinceDamageDealt + 1;
                        Vector3 aCent            = damageSource.getCenter();
                        float   dist             = Vector3.Distance(eye, aCent);
                        PhysicsEngine.HitScan hs = core.physicsEngine.hitscan(eye, aCent - eye, null);
                        if (hs == null || hs.Distance() > dist)
                        {
                            closestAgent = damageSource;
                            closestFeasibleTargetDist = Vector3.Distance(eye, aCent);
                        }
                    }
                    if (closestAgent == null)
                    {
                        foreach (Agent a in core.allAgents())
                        {
                            if (a.spawnTime > 0 || a == this)
                            {
                                continue;                                //if the agent is not in the level, then you can't see them
                            }
                            Vector3 aCent = a.getCenter();
                            float   dist  = Vector3.Distance(eye, aCent);
                            if (dist < closestFeasibleTargetDist &&                      //we're only concerned in closer enemies
                                Vector3.Dot(dir, Vector3.Normalize(aCent - eye)) > -0.2) //if they're infront of us
                            {
                                PhysicsEngine.HitScan hs = core.physicsEngine.hitscan(eye, aCent - eye, null);
                                if (hs == null || hs.Distance() > dist)
                                {
                                    closestAgent = a;
                                    closestFeasibleTargetDist = Vector3.Distance(eye, aCent);
                                }
                            }
                        }
                    }
                }

                //calculate the tier of a weapon
                int weaponTier = 2;
                if (equipped is Rifle || equipped is Shotgun)
                {
                    weaponTier = 1;
                }
                else if (equipped is Pistol)
                {
                    weaponTier = 0;
                }

                //calculate the nearest ammo, upgrade and health pickups
                float    closestHealthDist = float.MaxValue, closestAmmoDist = float.MaxValue, closestUpgradeDist = float.MaxValue;
                MeshNode closestHealth = null, closestAmmo = null, closestUpgrade = null;
                foreach (MeshNode m in core.aiEngine.pickupNodes)
                {
                    if (m.linkedPickupGen.held != null)
                    {
                        float dist = Vector3.Distance(m.position, position);
                        if (m.linkedPickupGen.itemType == PickUp.PickUpType.HEALTH)
                        {
                            if (dist < closestHealthDist)
                            {
                                closestHealthDist = dist;
                                closestHealth     = m;
                            }
                        }
                        else if (m.linkedPickupGen.itemType == PickUp.PickUpType.AMMO)
                        {
                            if (dist < closestAmmoDist)
                            {
                                closestAmmoDist = dist;
                                closestAmmo     = m;
                            }
                        }
                        else if (dist < closestUpgradeDist)
                        {
                            closestUpgradeDist = dist;
                            closestUpgrade     = m;
                        }
                    }
                }

                //now decide what to do, but how? we calculate a score for each value between 0 and 1
                double agentScore = Math.Min(1, Math.Max(0, (closestAgent == null || closestFeasibleTargetDist >= sightRadius ?
                                                             0 : Math.Pow((sightRadius - closestFeasibleTargetDist) / sightRadius, 0.4)))),
                       healthScore = Math.Min(1, Math.Max(0, (closestHealth == null ?
                                                              0 : Math.Pow((double)(70 - health) / maxHealth, 2)))),
                       ammoScore = Math.Min(1, Math.Max(0, (closestAmmo == null ?
                                                            0 : Math.Pow((double)(maxAmmo - ammo) / maxAmmo, 3)))),
                       upgradeScore = Math.Min(1, Math.Max(0, (closestUpgrade == null ?
                                                               0 : Math.Pow((double)(2 - weaponTier) / 2, 4))));

                //find the max
                double maxScore = Math.Max(agentScore, Math.Max(healthScore, Math.Max(ammoScore, upgradeScore)));

                //do the work
                if (maxScore > 0)
                {
                    ignore.Clear();
                    if (agentScore == maxScore)
                    {
                        Console.WriteLine("going for agent: " + closestAgent);
                        agentTarget = closestAgent;
                        setPathTo(core.aiEngine.findClosestMeshNode(agentTarget.getPosition(), 100, ignore), ignore);
                    }
                    else if (healthScore == maxScore)
                    {
                        Console.WriteLine("going for health: " + closestHealth);
                        setPathTo(closestHealth, null);
                    }
                    else if (ammoScore == maxScore)
                    {
                        Console.WriteLine("going for ammo: " + closestAmmo);
                        setPathTo(closestAmmo, null);
                    }
                    else
                    {
                        Console.WriteLine("going for upgrade: " + closestUpgrade);
                        setPathTo(closestUpgrade, null);
                    }
                }
            }

            if (agentTarget != null && (agentTarget == this || agentTarget.spawnTime > 0))
            {
                agentTarget = null;
            }

            if (agentTarget != null)
            {
                //walking towards the target
                if (path.Count == 0)
                {
                    setPathTo(core.aiEngine.findClosestMeshNode(agentTarget.getPosition(), 100, ignore), ignore);
                }
                Vector3 aCent = agentTarget.getCenter();
                Vector3 cent  = position + new Vector3(0, size.Y / 2, 0);
                direction = getDirectionFromVector(Vector3.Normalize(aCent - cent));
                //shooting the target
                curAgentShootTime -= (float)gameTime.ElapsedGameTime.TotalSeconds;
                if (curAgentShootTime <= 0 && equipped.curCooldown <= 0)
                {
                    curAgentShootTime = agentShootTime;
                    //try and shoot the player
                    PhysicsEngine.HitScan hs = core.physicsEngine.hitscan(cent, aCent - cent, null);
                    if (hs == null || hs.Distance() > Vector3.Distance(cent, aCent))
                    {
                        timeSinceLastShot = 0;
                        //actually shoot now
                        //we want to adjust the direction based on the relative motion of the target to this agent
                        Vector3 right = Vector3.Normalize(Vector3.Cross(aCent - cent, Vector3.Up));
                        //now get the target's previous move in terms of right and up
                        Vector3 move = agentTarget.getPosition() - agentTarget.getOldPosition() + position - oldPosition;
                        double  x    = Math.Abs(Vector3.Dot(move, right)),
                                y    = Math.Abs(Vector3.Dot(move, Vector3.Up));
                        //clamp x and y
                        //what's the max speed that they could've moved?
                        double max = agentTarget.speed * gameTime.ElapsedGameTime.TotalSeconds;
                        x = Math.Min(x / max, 1);
                        y = Math.Min(y / max, 1);

                        //y determines the inaccuracy of up-down aiming and x for left-right
                        //the "range" of this inaccuracy is determined by the distance the agent is from the me
                        double inaccuracy = Math.Pow(Math.Min(Vector3.Distance(cent, aCent) / sightRadius, 1), 0.02);

                        Console.WriteLine(inaccuracy * x);
                        direction.X += (float)((core.aiEngine.random.NextDouble() * 2 - 1) * Math.PI * inaccuracy * x / 2);
                        direction.Y += (float)((core.aiEngine.random.NextDouble() * 2 - 1) * Math.PI * inaccuracy * y / 2);

                        equipped.fire(this, core.physicsEngine);
                    }
                }
            }


            //if we're not moving towards something then stop ignoring nodes and find a pickup to go to
            if (path.Count == 0)
            {
                Console.WriteLine("here");
                ignore.Clear();     //should we clear ignore here?
                setPathTo(core.aiEngine.pickupNodes[core.aiEngine.random.Next(core.aiEngine.pickupNodes.Count)], ignore);
                if (path.Count == 0)
                {
                    Console.WriteLine("no path");
                    core.spawnPlayer(this);
                    return;
                }
            }

            //walk along the current path
            if (path.Count > 0)
            {
                //try move to the latest point in the path
                MeshNode target = path[0];

                /* if we're sufficiently close to the target switch it
                 * find the target's position relative to the position
                 */
                Vector2 tpos = new Vector2(Math.Abs(target.position.X - position.X),
                                           Math.Abs(target.position.Z - position.Z));
                if (tpos.X < size.X / 6 && tpos.Y < size.Y / 6)
                {
                    ignore.Clear();
                    popFromPath();
                    targetAquisitionDuration = 0;
                    //if (path.Count == 0 && agentTarget == null)setPathTo(core.aiEngine.pickupNodes[core.aiEngine.random.Next(core.aiEngine.pickupNodes.Count)], ignore);
                    if (path.Count == 0)
                    {
                        return;
                    }
                    target = path[0];
                }

                if (agentVelocities.persistentVelocity != Vector3.Zero)
                {
                    previousTarget = null;
                }

                //calculate direction to target -- we need this for the model, this will probably change, but the principles are right
                Vector3 velocity = target.position - position;
                direction = getDirectionFromVector(velocity);

                /* now calculate the move and actually move
                 * depending on whether we're on the mesh or not, we don't need collision detection
                 * if we've gotten here and there's a previous target then we're on the path
                 */
                if (true || previousTarget != null)
                {
                    velocity.Normalize();
                    core.physicsEngine.applySimpleMovement(gameTime, this, velocity * speed);
                }
                //otherwise we need to take care of things the expensive way
                else
                {
                    velocity.Y = 0;
                    velocity.Normalize();
                    core.physicsEngine.applyMovement((float)gameTime.ElapsedGameTime.TotalSeconds, this, speed * velocity);
                }

                /* the targetAquisitionDuration variable helps us keep track of the time taken
                 * to move between two nodes. if it's taking too long the we give up on that node
                 * and try a different path to our target node (path[-1])
                 */
                targetAquisitionDuration += gameTime.ElapsedGameTime.TotalSeconds;
                if (targetAquisitionDuration >= timeout)
                {
                    ignore.Add(target);
                    targetAquisitionDuration = 0;
                    if (path.Count > 0)
                    {
                        setPathTo(path[path.Count - 1], ignore);
                    }
                }

                core.physicsEngine.updateCollisionCellsFor(this);
            }
        }