// Add an attack Animation here // TODO: maybe put the thing that actually applies the damage here, just to consolodate logical code public void AddAttack(Derp from, Derp to) { // This must be located two radius distance from the derp, in the direction of the delivering attacker myVector u = new myVector(from.x - to.x, from.y - to.y); u.toUnit(); // opposite direction for nearest cardinal dir calculation myVector u2 = new myVector(-u.x, -u.y); DerpAttack attack = new DerpAttack(to.x + (u.x * to.stats.radius * 2), to.y + (u.y * to.stats.radius * 2), Geometry.GetNearestCardinalDir(u2)); derpAttacks.Add(attack.key, attack); }
// Set a derp to move out of the way of another derp. Also returns the slow derp. (even if it actually isn't slower?) // The fastDerp has run into the slow derp, it must tell it to move out of the way. Give the other derp the unit vector to move along public Derp SetToMoveOutOfWay(int slowDerpID, Derp fastDerp) { List <Derp> checkList = (fastDerp.team == TEAM.HOME ? homeDerps : awayDerps); Derp slowDerp = checkList[slowDerpID]; // only actually do this if the slow derp is slower or equal if (slowDerp.stats.spd > fastDerp.stats.spd) { return(slowDerp); } // The Move Vector should be directly away from the fast derp myVector awayVector = new myVector(slowDerp.x - fastDerp.x, slowDerp.y - fastDerp.y); awayVector.toUnit(); // Tell 'em to move slowDerp.moveOutOfWay = true; slowDerp.moveOutOfWayVector = awayVector; return(slowDerp); }
// This is the function that tries to make a step, it can recurse once. public void attemptStep(myVector v, bool recurse) { // Optimistic no collision double t = 1.0; myVector newV = null; // Check for team derp collisions double derpT; myVector derpV = null; int colID = manager.CheckDerpCollision(this, v, out derpT, enemyDetected); // note: this returns -1 if either there is no collision, or if we are colliding with the other team isColliding = (colID != -1); // We have collided with another derp // Tell the other to move out of the way, and have us try to move perpendicular to them if (isColliding && !recurse) { Derp slowDerp = manager.SetToMoveOutOfWay(colID, this); if (slowDerp != null) { // Get the Unit Vector towards the slow Derp myVector u = new myVector(slowDerp.x - x, slowDerp.y - y); u.toUnit(); // Make sure that the slow derp is actually in front of where you want to go if (u.dot(v) > 0.0) { // Get a perpendicular unit vector to follow myVector uPerp = new myVector(-u.y, u.x); // Make sure it is the correct way double dir = uPerp.dot(v); // Wrong way if (dir < -1e-3) { uPerp.x *= -1; uPerp.y *= -1; } // Special Zero case, pick a random direction // this should... almost never happen // I'm concerned this could mess up random synch in multiplayer. Just pick a constant direction. else if (dir < 1e-3) { /* * if (MyRandom.Next(team, 2) % 2 == 0) * { * uPerp.x *= -1; * uPerp.y *= -1; * } */ } // Try moving along the new path double remainingT = (1.0 - derpT); derpV = new myVector(uPerp.x * remainingT * v.mag(), uPerp.y * remainingT * v.mag()); } } } // Check for field collisions double fieldT; myVector fieldV = null; myLineSegment collisionVect; if (Field.field.CheckFieldCollision(this, v, out fieldT, out collisionVect) && !recurse) { // try to move parallel to the wall with the extra t collisionVect.v.toUnit(); // make sure this unit vector is in the right direction double dir = collisionVect.v.dot(v); // Wrong way if (dir < -1e-3) { collisionVect.v.x *= -1; collisionVect.v.y *= -1; } // ignore zero case if (Math.Abs(dir) > 1e-3) { // Try moving along the new path double remainingT = (1.0 - fieldT); fieldV = new myVector(collisionVect.v.x * remainingT * v.mag(), collisionVect.v.y * remainingT * v.mag()); } } // See if we have a collision // If either of the recalculated V values are not null, we hit something along the way if (derpT < 1.0 - 1e-6 || fieldT < 1.0 - 1e-6) { if (fieldT < derpT) { t = fieldT; newV = fieldV; } else { t = derpT; newV = derpV; } } // Only step as far as we can x += v.x * t; y += v.y * t; if (newV != null) { attemptStep(newV, true); } }
// Check for a Derp's Circle-Cast across the field // This will work like a BFS, starting from the initial position // then check all 4 corners around the position to build on the bfs public bool CheckFieldCollision(Derp d, myVector v, out double t, out myLineSegment col) { // initial optimistic setup that there will not be a collision bool ret = false; t = 1.0; col = null; // calculate starting point int startX = (int)(d.x / BLOCK_WIDTH); int startY = (int)(d.y / BLOCK_HEIGHT); // Unit Vector in desired direction myVector vUnit = new myVector(v.x, v.y); vUnit.toUnit(); // set up the bfs Queue<SimpleNode> q = new Queue<SimpleNode>(); bool[,] vis = new bool[height+1, width+1]; q.Enqueue(new SimpleNode(startX, startY, 0)); vis[startY, startX] = true; // Create the 4 line segments so we don't have to do quiiite as much object creation in this loop myLineSegment[] segs = new myLineSegment[4]; for (int i = 0; i < 4; ++i) segs[i] = new myLineSegment(null, null); // BFS int[] dx = { 0, 1, 0, -1 }; int[] dy = { -1, 0, 1, 0 }; int cur_step = 0; while (q.Count > 0) { SimpleNode cur = q.Dequeue(); // end early if we had a hit already in a previous step if (ret && cur_step != cur.step) break; // checking 4 nodes around us myPoint p1 = new myPoint(cur.x * BLOCK_WIDTH, cur.y * BLOCK_HEIGHT); myPoint p2 = new myPoint((cur.x + 1) * BLOCK_WIDTH, cur.y * BLOCK_HEIGHT); myPoint p3 = new myPoint((cur.x + 1) * BLOCK_WIDTH, (cur.y + 1) * BLOCK_HEIGHT); myPoint p4 = new myPoint(cur.x * BLOCK_WIDTH, (cur.y + 1) * BLOCK_HEIGHT); segs[0].Update(p1, p2); segs[1].Update(p2, p3); segs[2].Update(p4, p3); segs[3].Update(p1, p4); for (int i = 0; i < 4; ++i) { int nx = cur.x + dx[i]; int ny = cur.y + dy[i]; if (nx < 0 || nx > width || ny < 0 || ny >= height || vis[ny, nx]) continue; double possible_t; if (Geometry.DerpLineSegmentCast(d, v, segs[i], out possible_t)) { // We have a hit! If the next zone is safe to move in, then continue the bfs if (gameGrid[ny, nx] != '0') { q.Enqueue(new SimpleNode(nx, ny, cur.step + 1)); vis[ny, nx] = true; } // We hit an unnavigable space. Stop the BFS, this is as far as we go else { ret = true; if (Math.Abs(possible_t - t) < 1e-5 && col != null) { // break ties by taking the furthest behind the direction we wish to go // Calculate the center point on the wall, and get the dot product of the vector to that point. // The most negative value is the furthest behind myPoint segMidPoint1 = new myPoint((segs[i].p1.x + segs[i].p2.x) / 2.0, (segs[i].p1.y + segs[i].p2.y) / 2.0); myVector toMidPoint1 = new myVector(segMidPoint1.x - d.x, segMidPoint1.y - d.y); myPoint segMidPoint2 = new myPoint((col.p1.x + col.p2.x) / 2.0, (col.p1.y + col.p2.y) / 2.0); myVector toMidPoint2 = new myVector(segMidPoint2.x - d.x, segMidPoint2.y - d.y); if (vUnit.dot(toMidPoint1) < vUnit.dot(toMidPoint2)) { t = possible_t; col = new myLineSegment(segs[i].p1.x, segs[i].p1.y, segs[i].p2.x, segs[i].p2.y); // careful... memory bugs } } else if (possible_t < t) { t = possible_t; col = new myLineSegment(segs[i].p1.x, segs[i].p1.y, segs[i].p2.x, segs[i].p2.y); // careful... memory bugs } } } } // if we are a special diagonal case, then check the cross hit as well myLineSegment diag = null; char c = gameGrid[cur.y, cur.x]; if (c == '1' || c == '3') diag = new myLineSegment(p2, p4); if (c == '2' || c == '4') diag = new myLineSegment(p1, p3); if (diag != null) { double possible_t; if (Geometry.DerpLineSegmentCast(d, v, diag, out possible_t)) { ret = true; if (Math.Abs(possible_t - t) < 1e-5 && col != null) { // break ties by taking the furthest behind the direction we wish to go // Calculate the center point on the wall, and get the dot product of the vector to that point. // The most negative value is the furthest behind myPoint segMidPoint1 = new myPoint((diag.p1.x + diag.p2.x) / 2.0, (diag.p1.y + diag.p2.y) / 2.0); myVector toMidPoint1 = new myVector(segMidPoint1.x - d.x, segMidPoint1.y - d.y); myPoint segMidPoint2 = new myPoint((col.p1.x + col.p2.x) / 2.0, (col.p1.y + col.p2.y) / 2.0); myVector toMidPoint2 = new myVector(segMidPoint2.x - d.x, segMidPoint2.y - d.y); if (vUnit.dot(toMidPoint1) < vUnit.dot(toMidPoint2)) { t = possible_t; col = new myLineSegment(diag.p1.x, diag.p1.y, diag.p2.x, diag.p2.y); // careful... memory bugs } } else if (possible_t < t) { t = possible_t; col = new myLineSegment(diag.p1.x,diag.p1.y, diag.p2.x, diag.p2.y); // careful... memory bugs } } } cur_step = cur.step; } return ret; }