public override void fire(Agent p, PhysicsEngine ph) { base.fire(p, ph); if (curCooldown == cooldown) { Random rand = new Random(); Vector3 dir = Vector3.Normalize(p.getDirectionVector()); Vector3 right = Vector3.Cross(dir, Vector3.Up); Vector3 up = Vector3.Cross(dir, right); up *= inaccuracy; right *= inaccuracy; dir = dir + (float)(rand.NextDouble() * 2 - 1) * up + (float)(rand.NextDouble() * 2 - 1) * right; inaccuracy = Math.Min(maxInaccuracy, inaccruacyJump + inaccuracy); inaccuracyCurCooldown = 0; List <Agent> l = new List <Agent>(); l.Add(p); PhysicsEngine.HitScan hs = ph.hitscan(p.getPosition() + new Vector3(0, 75, 0) + p.getDirectionVector() * 10, p.getDirectionVector(), null); PhysicsEngine.AgentHitScan ahs = ph.agentHitscan(p.getPosition() + new Vector3(0, 60, 0) + p.getDirectionVector() * 10, dir, l); if (hs != null && (ahs == null || hs.Distance() < ahs.Distance())) { makeLaser(p, hs.ray, Vector3.Distance(hs.ray.Position, hs.collisionPoint), 5, 5, "Rifle"); } else if (ahs != null) { ahs.agent.dealDamage(damage, p); makeLaser(p, ahs.ray, Vector3.Distance(ahs.ray.Position, ahs.collisionPoint), 5, 5, "Rifle"); } } }
public override void fire(Agent p, PhysicsEngine ph) { base.fire(p, ph); if (curCooldown == cooldown) { List <Agent> l = new List <Agent>(); l.Add(p); PhysicsEngine.HitScan hs = ph.hitscan(p.getPosition() + new Vector3(0, 75, 0) + p.getDirectionVector() * 10, p.getDirectionVector(), null); PhysicsEngine.AgentHitScan ahs = ph.agentHitscan(p.getPosition() + new Vector3(0, 60, 0) + p.getDirectionVector() * 10, p.getDirectionVector(), l); if (hs != null && (ahs == null || hs.Distance() < ahs.Distance())) { makeLaser(p, hs.ray, Vector3.Distance(hs.ray.Position, hs.collisionPoint), 5, 5, "Pistol"); } else if (ahs != null) { ahs.agent.dealDamage(damage, p); makeLaser(p, ahs.ray, Vector3.Distance(ahs.ray.Position, ahs.collisionPoint), 5, 5, "Pistol"); } } }
public override void fire(Agent p, PhysicsEngine ph) { base.fire(p, ph); if (curCooldown == cooldown) { Random rand = new Random(); Vector3 [] dirs = new Vector3[5]; dirs[0] = Vector3.Normalize(p.getDirectionVector()); Vector3 right = Vector3.Cross(dirs[0], Vector3.Up); Vector3 up = Vector3.Cross(dirs[0], right); up *= 0.15f; right *= 0.15f; dirs[1] = dirs[0] + (float)rand.NextDouble() * right + (float)rand.NextDouble() * up; dirs[2] = dirs[0] + (float)rand.NextDouble() * right - (float)rand.NextDouble() * up; dirs[3] = dirs[0] - (float)rand.NextDouble() * right - (float)rand.NextDouble() * up; dirs[4] = dirs[0] - (float)rand.NextDouble() * right + (float)rand.NextDouble() * up; foreach (Vector3 dir in dirs) { List <Agent> l = new List <Agent>(); l.Add(p); PhysicsEngine.HitScan hs = ph.hitscan(p.getPosition() + new Vector3(0, 75, 0) + p.getDirectionVector() * 10, dir, null); PhysicsEngine.AgentHitScan ahs = ph.agentHitscan(p.getPosition() + new Vector3(0, 75, 0) + p.getDirectionVector() * 10, dir, l); if (hs != null && (ahs == null || hs.Distance() < ahs.Distance())) { makeLaser(p, hs.ray, Vector3.Distance(hs.ray.Position, hs.collisionPoint), 5, 5, "Shotgun"); } else if (ahs != null) { ahs.agent.dealDamage(damage, p); makeLaser(p, ahs.ray, Vector3.Distance(ahs.ray.Position, ahs.collisionPoint), 5, 5, "Shotgun"); } } } }
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); } }
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); } }