Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List

MineField.cpp

00001 /*
00002 Copyright 2003 - 2005 Elliott Kleinrock, Dan Neely, Kurt W. Over, Damon Domjan
00003 
00004 This file is part of FreeStars, a free clone of the Stars! game.
00005 
00006 FreeStars is free software; you can redistribute it and/or modify
00007 it under the terms of the GNU General Public License as published by
00008 the Free Software Foundation; either version 2 of the License, or
00009 (at your option) any later version.
00010 
00011 FreeStars is distributed in the hope that it will be useful,
00012 but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 GNU General Public License for more details.
00015 
00016 You should have received a copy of the GNU General Public License
00017 along with FreeStars; if not, write to the Free Software
00018 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019 
00020 The full GPL Copyright notice should be in the file COPYING.txt
00021 
00022 Contact:
00023 Email Elliott at 9jm0tjj02@sneakemail.com
00024 */
00025 
00026 //dan Neely
00027 //2-9-03
00028 #include "FSServer.h"
00029 #include "MineField.h"
00030 #include <math.h>
00031 #include <stdlib.h>
00032 #include <iostream>
00033 
00034 #ifdef _DEBUG
00035 #define new DEBUG_NEW
00036 #endif
00037 
00038 int Midpoint(int a, int b)
00039 {
00040         if (a < b)
00041                 return  a + ((b - a +1)>>1);
00042         return  b + ((a - b +1)>>1);
00043 }
00044 
00045 MineField::~MineField(void)
00046 {}
00047 
00048 MineField::MineField(int mineType, const Location & L, int numMines, Player * owner)
00049 {
00050         posX = L.GetPosX();
00051         posY = L.GetPosY();
00052         mNumMines = numMines;
00053         mMineType = mineType;
00054         mOwner = owner;
00055         mID = mOwner->GetMineFieldID();
00056 }
00057 
00058 long MineField::GetScanSpace() const
00059 {
00060         return GetOwner()->MineFieldScanning() ? long(GetRadius()) : -1;
00061 }
00062 
00063 long MineField::GetCloak(const Player * p, bool pen) const
00064 {
00065         return (pen || mHadSeen[p->GetID()-1]) ? 0 : Rules::GetConstant("WormholeCloak", 75);
00066 }
00067 
00068 void MineField::AddMines(const Location & L, int n)
00069 {
00070         // TODO add code to shift center if needed
00071         mNumMines += n;
00072 }
00073 
00074 void MineField::Sweep(const Fleet * F)
00075 {
00076         double dist = Distance(*F);
00077         if (dist > GetRadius())
00078                 return;
00079 
00080         long swept = F->GetSweeping() / Rules::GetArrayValue("MineSweepDivisor", mMineType);
00081         if (mNumMines - swept < dist * dist)
00082                 swept = mNumMines - long(dist * dist);
00083 
00084         mNumMines -= swept;
00085 
00086         // add messages
00087         Message * mess;
00088         mess = mOwner->AddMessage("Your minefield swept", this);
00089         mess->AddLong("Mines swept", swept);
00090 
00091         mess = F->NCGetOwner()->AddMessage("You've swept a minefield", this);
00092         mess->AddItem("Sweeper", F);
00093         mess->AddLong("Mines swept", swept);
00094 }
00095 
00096 void MineField::Decay(int numPlanets)
00097 {
00098         double d = Rules::GetFloat("MineFieldBaseDecayRate") 
00099                    + Rules::GetFloat("MineFieldPlanetDecayRate") * numPlanets;
00100         d = min(d, Rules::GetFloat("MineFieldMaxDecayRate"));
00101 
00102         long decay = long(mNumMines * d * GetOwner()->MineDecayFactor() + .5);
00103         decay = max(decay, long(10 * GetOwner()->MineDecayFactor() + .5));
00104         mNumMines -= decay;
00105 }
00106 
00107 void MineField::ReduceFieldCollision()
00108 {
00109         if (mNumMines <= 10)
00110                 mNumMines = 0;
00111         else if (mNumMines <= 200)
00112                 mNumMines -=10;
00113         else if (mNumMines <= 1000)
00114                 mNumMines = static_cast<int>(mNumMines * 0.95);
00115         else if (mNumMines <= 5000)
00116                 mNumMines -= 50;
00117         else mNumMines = static_cast<int>(mNumMines * 0.95);
00118 }
00119 
00120 bool MineField::TestCollide(const Location & L, int warp)
00121 {
00122         long radius = long(GetRadius() + .5);
00123         int negRadius = -radius;
00124         int dx = L.GetPosX() - posX;
00125         if (dx > radius || dx < negRadius) return false; // no collision
00126         int dy = L.GetPosY() - posY;
00127         if (dy > radius || dy < negRadius) return false; // no collision
00128         
00129         int dr2 = dx*dx+dy*dy; //r^2
00130         int r2 = radius*radius;
00131         if (r2 >= dr2)
00132         {
00133                 if (warp <= Rules::GetArrayValue("MineSafeSpeed", mMineType))
00134                         return false;
00135 
00136                 double hitChance = double(Rules::GetArrayValue("MineHitChancePerWarp", mMineType)) / 10000 * 
00137                                              (warp - Rules::GetArrayValue("MineSafeSpeed", mMineType));
00138                 if (Randodd(hitChance))
00139                         return true;
00140         }
00141         return false;
00142 }
00143 
00144 bool MineField::CollisionPossible (const Location & L1, const Location & L2)
00145 {
00146         // do the easy test before the complex one
00147         if (!RectFilter(L1.GetPosX(),L1.GetPosY(),L2.GetPosX(),L2.GetPosY()))
00148                 return false;
00149 
00150         //substituions for speed and readability of following code
00151         long x1= L1.GetPosX();
00152         long y1= L1.GetPosY();
00153         long x2= L2.GetPosX();
00154         long y2= L2.GetPosY();
00155 
00156 //http://www.exaflop.org/docs/cgafaq/cga1.html#Subject%201.02:%20How%20do%20I%20find%20the%20distance%20from%20a%20point%20to%20a%20line?
00157 //Comp.Graphics.Algorithms Frequently Asked Questions
00158 //Section 1. 2D Computations: Points, Segments, Circles, Etc.
00159 //Subject 1.02: How do I find the distance from a point to a line?
00160 //Let the point be C (Cx,Cy) and the line be AB (Ax,Ay) to (Bx,By).    The 
00161 //      length of the line segment AB is L:
00162 //
00163 //        L= sqrt( (Bx-Ax)^2 + (By-Ay)^2 ) .
00164 
00165         int L = static_cast<int>(sqrt((double)((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2))));
00166 
00167 //Let P be the point of perpendicular projection of C onto AB. Let r be a 
00168 //      parameter to indicate P's location along the line containing AB, with 
00169 //      the following meaning:
00170 //
00171 //          r=0      P = A
00172 //          r=1      P = B
00173 //          r<0      P is on the backward extension of AB
00174 //          r>1      P is on the forward extension of AB
00175 //          0<r<1    P is interior to AB
00176 //
00177 //Compute r with this:
00178 //
00179 //            (Ay-Cy)(Ay-By)-(Ax-Cx)(Bx-Ax)
00180 //        r = -----------------------------
00181 //                        L^2
00182 
00183         int r=  ( (y1-posY)*(y1-y2)-(y1-posY)*(y2-y1) ) / (L * L) ;
00184 
00185         // closest point before x1,y1 is x1,y1 in circle?
00186         if (r<0)
00187         {       // r^2 > distance from x1,y1 to center of field
00188                 return InField(x1,y1);
00189         }
00190         if (r>1)
00191         {
00192                 return InField(x2,y2);
00193         }
00194 
00195 //The point P can then be found:
00196 //
00197 //        Px = Ax + r(Bx-Ax)
00198 //        Py = Ay + r(By-Ay)
00199 
00200 //  don't actaully need this (I think)
00201 //      int Px = x1 + r*(x2-x1);
00202 //      int Py = y1 + r*(y2-y1);
00203 
00204 //And the distance from A to P = r*L.
00205 //Use another parameter s to indicate the location along PC, with the 
00206 //following meaning:
00207 //
00208 //           s<0      C is left of AB
00209 //           s>0      C is right of AB
00210 //           s=0      C is on AB
00211 //
00212 //Compute s as follows:
00213 //    (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)
00214 //        s = -----------------------------
00215 //                        L^2
00216 
00217         int s = ((y1-posY)*(x2-x1)-(x1-posX)*(y2-y1)) / (L*L);
00218 
00219 //Then the distance from C to P = s*L.
00220 
00221         return (GetRadius() >= s*L);
00222 }
00223 
00224 /*
00225 void MineField::CollisionPath(int &x1, int &y1, int &x2, int &y2)
00226 {       
00227         if (InField(x1,y1) && InField(x2,y2))
00228                 return;  // both points are in minefield
00229         int mx = Midpoint(x1,x2);
00230         int my = Midpoint(y1,y2); 
00231 
00232         if(InField(x1,y1)) // x1y1 in
00233         {
00234                 CollisionPathFindEdge(x2,y2,x1,y1);
00235         } // x1y1 in
00236         else if(InField(x2,y2)) 
00237         {
00238                 CollisionPathFindEdge(x1,y1,x2,y2);
00239         } // x2y2 in
00240         else //niether in
00241         {
00242                 if (!InField(mx,my)) 
00243                 {
00244                         //since we need a point on the fleet path, drop center of mine 
00245                         //up/down to lie on path we need to convert the fleet path into 
00246                         //the form y = mx + b
00247 
00248                         // first need to test special case of vertical path.  in so 
00249                         // shift center right/left instead
00250                         if (x1==x2)
00251                         {       //since line is vertical mx doesn't need moved
00252                                 my =posY;
00253                         }
00254                         else
00255                         {
00256                                 int m = (y1-y2)/(x1-x2);
00257                                 int b = (-m*x1+y1);
00258                                 mx = posX;
00259                                 my = m*mx + b;
00260                         }
00261                 }
00262                 CollisionPathFindEdge(x1,y1,mx,my);
00263                 CollisionPathFindEdge(x2,y2,mx,my);
00264         }
00265 }
00266 */
00270 
00271 bool MineField::InField(int x, int y)
00272 {
00273         // really radius ^ 2, but that's just mNumMines
00274         return (mNumMines >= ((x-posX)*(x-posX) + 
00275                                                    (y - posY)*(y - posY)));
00276 }
00277 
00278 bool MineField::InField(const Location & L)
00279 {
00280         return InField(L.GetPosX(), L.GetPosY());
00281 }
00282 
00283 
00284 bool MineField::RectFilter(int x1, int y1, int x2, int y2)
00285 {
00286         // reorder so x1,y1 is upper left corner
00287         // since we're making a box it doesn't matter if we swap x's but not y's
00288         if (x1 > x2)
00289         {
00290                 int temp = x1;
00291                 x1 = x2;
00292                 x2= temp;
00293         }
00294         if (y1 > y2)
00295         {
00296                 int temp = y1;
00297                 y1 = y2;
00298                 y2= temp;
00299         }
00300         int X1, Y1, X2, Y2;
00301         long radius = long(GetRadius() + .5);
00302         X1 = posX - radius;
00303         X2 = posX + radius;
00304         Y1 = posY - radius;
00305         Y2 = posY + radius;
00306 
00307         return !((x2 < X1) || (x1 > X2) || (y2 < Y1) || (y1 > Y2));
00308 }
00309 
00310 /*
00311 void  MineField::CollisionPathFindEdge(int &x1, int &y1, int x2, int y2)
00312 {
00313         // midpoints
00314         int mx = Midpoint(x1,x2);
00315         int my = Midpoint(y1,y2); 
00316         
00317 
00318         if(InField(x2,y2)) // x2y2 in should be the case, just an error check
00319         {
00320                 bool abort =false;
00321                 while (!InField(mx,my) &&  !abort)
00322                 {
00323                         x1= mx;
00324                         y1= my;
00325                         mx = Midpoint(x1,x2);
00326                         my = Midpoint(y1,y2);
00327                         abort = ((x1-1 == x2) || (y1-1 == y2));
00328                 }
00329                 // at this point boundary is between mx,my and X2,Y2
00330 
00331                 // this special case is only needed when the fleet just grazes the
00332                 //edge of the field  eg r=30 center 100,100 fleet moving due north
00333                 // on x = 70 in that case without this check a 2 ly path is returned 
00334                 //instead of the correct result of only at 70,100
00335                 if ( ((x1-1) != x2) && ((y1-1) != y2) )
00336                 {
00337                         x2= mx;
00338                         y2= my;
00339                 }
00340 
00341                 bool p1ismid = false;
00342                 bool p2ismid = false;
00343 
00344                 while  ( !p1ismid && !p2ismid )
00345                 {
00346                         mx = Midpoint(x1,x2);
00347                         my = Midpoint(y1,y2); 
00348                         
00349                         p1ismid = ((x1 == mx)&&(y1 == my));
00350                         p2ismid = ((x2 == mx)&&(y2 == my));
00351 
00352 
00353                         if (InField(mx,my))
00354                         {
00355                                 x2 = mx;
00356                                 y2 = my;
00357                         }
00358                         else
00359                         {
00360                                 x1 = mx;
00361                                 y1 = my;
00362                         }
00363                         
00364                 }
00365                 //loop runs once more than needed, and when going accross right/bottom
00366                 //edge of field generates a fencepost error.  in this case, mx,my
00367                 //is one unit outside field, while x2,y2 is on the edge exactly
00368                 //since x2,y2 always is in that position and sicne I can't figure out
00369                 //how to make the loop terminate one iteration sooner I'm just dumping
00370                 //x2,y2 into x1,y1
00371 
00372                 //x1 = mx
00373                 //y1 = my
00374                 if (InField(x2,y2))
00375                 {
00376                         x1 = x2;
00377                         y1 = y2;
00378                 }
00379         }
00380         else
00381         {
00382                 std::cerr << "error in MineField::CollisionPathFindEdge x2,y2 not in"
00383                                   << " field\n";
00384                 abort();
00385         }
00386 }
00387 
00388 */

Generated on Mon Aug 8 21:33:43 2005 for Freestars by  doxygen 1.4.2-20050421