SourceForge: atanksaiupgrade/atanksaiupgrade: changeset 43:1212983e94b1
- Fixed Threaded rendering, it's save now and no longer crashes the game when a game is aborted and then re-started atanks-3.9-experimental
authoryamakuzure <yamakuzure@users.sourceforge.net>
Sun Sep 20 08:32:58 2009 +0200 (2 months ago)
branchatanks-3.9-experimental
changeset 431212983e94b1
parent 4253df839a41b4
child 443957ea3fcb06
- Fixed Threaded rendering, it's save now and no longer crashes the game when a game is aborted and then re-started
- Fixed dependencies in Makefile
- Threw out the some old remains of the old AI
- balanced AI a bit back. I guess I overdid it a bit the last round.
- Bots should use a wider range of weapons now.
src/Makefile
src/atanks.cpp
src/environment.cpp
src/environment.h
src/player.cpp
src/player.h
     1.1 --- a/src/Makefile	Fri Sep 18 18:36:29 2009 +0200
     1.2 +++ b/src/Makefile	Sun Sep 20 08:32:58 2009 +0200
     1.3 @@ -64,7 +64,7 @@
     1.4  player.cpp: environment.h globaldata.h player.h tank.h menu.h lineseq.h files.h  floattext.h
     1.5  satellite.cpp: environment.h satellite.h  beam.h
     1.6  sky.cpp: globaldata.h  main.h
     1.7 -atanks.cpp: globals.h lineseq.h menu.h button.h files.h satellite.h  menucontent.h update.h
     1.8 +atanks.cpp: globals.h lineseq.h menu.h button.h files.h satellite.h  menucontent.h update.h skterenderer.h
     1.9  beam.cpp: environment.h globaldata.h physobj.h player.h decor.h tank.h  beam.h
    1.10  button.cpp:  button.h
    1.11  satellite.h: environment.h globaldata.h  virtobj.h
     2.1 --- a/src/atanks.cpp	Fri Sep 18 18:36:29 2009 +0200
     2.2 +++ b/src/atanks.cpp	Sun Sep 20 08:32:58 2009 +0200
     2.3 @@ -1925,7 +1925,7 @@
     2.4        int iCurrentBoostLevel =	global->players[pl]->getBoostValue();
     2.5        if (iCurrentBoostLevel > iMaxBoost)
     2.6          iMaxBoost	=	iCurrentBoostLevel;
     2.7 -      if (global->players[pl]->score > iMaxScore)
     2.8 +      if ((global->players[pl]->type == HUMAN_PLAYER) && (global->players[pl]->score > iMaxScore))
     2.9          iMaxScore = global->players[pl]->score;
    2.10      }
    2.11  
    2.12 @@ -3375,7 +3375,7 @@
    2.13  
    2.14  #ifdef LINUX
    2.15    // Be sure we hadn't triggered the rendering thread
    2.16 -  if (*pRenderState != SKR_INACTIVE)
    2.17 +  if ((*pRenderState != SKR_INACTIVE) && (*pRenderState != SKR_FINISHED))
    2.18      {
    2.19        if (! (int)global->os_mouse) show_mouse (NULL);
    2.20        draw_sprite (screen, (BITMAP *) global->gfxData.M[1].dat, global->halfWidth - 120, global->halfHeight + 195);
    2.21 @@ -3383,8 +3383,9 @@
    2.22        *pRenderState = SKR_BREAKUP;
    2.23        while (*pRenderState < SKR_FINISHED)
    2.24          _HIBERNATE; // We need to wait for the thread to finish before triggering to inactive again
    2.25 -      skr_Trigger();
    2.26 -    }
    2.27 +    }
    2.28 +  if (*pRenderState == SKR_FINISHED)
    2.29 +    skr_Trigger();
    2.30  #endif // LINUX
    2.31  
    2.32  }
     3.1 --- a/src/environment.cpp	Fri Sep 18 18:36:29 2009 +0200
     3.2 +++ b/src/environment.cpp	Sun Sep 20 08:32:58 2009 +0200
     3.3 @@ -231,32 +231,40 @@
     3.4    combineUpdates = 1;
     3.5  }
     3.6  
     3.7 -int ENVIRONMENT::isItemAvailable (int itemNum)
     3.8 +bool ENVIRONMENT::isItemAvailable (int itemNum)
     3.9  {
    3.10 -  if (itemNum < WEAPONS)
    3.11 -    {
    3.12 -      if ((weapon[itemNum].warhead) ||
    3.13 -          (weapon[itemNum].techLevel > weapontechLevel))
    3.14 -        return (FALSE);
    3.15 -    }
    3.16 -  else if (item[itemNum - WEAPONS].techLevel > itemtechLevel)
    3.17 -    {
    3.18 -      return (FALSE);
    3.19 -    }
    3.20 -  return (TRUE);
    3.21 +  bool bIsAvailable = true;
    3.22 +
    3.23 +  if ( (!itemNum)          // Small missiles can't be bought!
    3.24 +     ||(itemNum >= THINGS) // Same with naturals
    3.25 +     ||( (itemNum < WEAPONS)
    3.26 +       &&(weapon[itemNum].warhead // Warheads are contents of weapons and can't be bought (napalm jelly, deathlets...)
    3.27 +         ||(weapon[itemNum].techLevel > weapontechLevel) ) ) // too high tech level
    3.28 +     ||( (itemNum >= WEAPONS)
    3.29 +       &&(item[itemNum - WEAPONS].techLevel > itemtechLevel) ) ) // too high tech level
    3.30 +    bIsAvailable = false;
    3.31 +
    3.32 +  return (bIsAvailable);
    3.33  }
    3.34  
    3.35  void ENVIRONMENT::generateAvailableItemsList ()
    3.36  {
    3.37    int slot = 0;
    3.38 +
    3.39 +  for (int itemNum = 0; itemNum < THINGS; itemNum++)
    3.40 +    availableItems[itemNum] = 0;
    3.41 +
    3.42    for (int itemNum = 0; itemNum < THINGS; itemNum++)
    3.43      {
    3.44 -      if (!isItemAvailable (itemNum))
    3.45 -        continue;
    3.46 -      availableItems[slot] = itemNum;
    3.47 -      slot++;
    3.48 +      if (isItemAvailable (itemNum))
    3.49 +        {
    3.50 +          slot++; // Needs to be raised first, so slot 0 is kept free.
    3.51 +          // ( Slot 0 can't be used without major rewrites of the shopping fucntions in atanks.cpp,
    3.52 +          //   because of the functions relying on slot 0 being the small missile which is unavailable. )
    3.53 +          availableItems[slot] = itemNum;
    3.54 +        }
    3.55      }
    3.56 -  numAvailable = slot;
    3.57 +  numAvailable = slot + 1; // Plus 1, because the shopping methods in atanks.cpp count slot 0 but ignore it
    3.58  }
    3.59  
    3.60  int ENVIRONMENT::addObject (VIRTUAL_OBJECT *object)
     4.1 --- a/src/environment.h	Fri Sep 18 18:36:29 2009 +0200
     4.2 +++ b/src/environment.h	Sun Sep 20 08:32:58 2009 +0200
     4.3 @@ -134,7 +134,7 @@
     4.4        _global = global;
     4.5      }
     4.6      void generateAvailableItemsList ();
     4.7 -    int isItemAvailable (int itemNum);
     4.8 +    bool isItemAvailable (int itemNum);
     4.9      int addObject (VIRTUAL_OBJECT *object);
    4.10      int removeObject (VIRTUAL_OBJECT *object);
    4.11      VIRTUAL_OBJECT *getNextOfClass (int classNum, int *index);
     5.1 --- a/src/player.cpp	Fri Sep 18 18:36:29 2009 +0200
     5.2 +++ b/src/player.cpp	Sun Sep 20 08:32:58 2009 +0200
     5.3 @@ -185,22 +185,18 @@
     5.4    if (iType > (int) DEADLY_PLAYER)
     5.5      iType  =  (int) DEADLY_PLAYER;
     5.6  
     5.7 -  sAIData.iRangeFindAttempts = (int)(pow(iType + 1, 3) + 2);  // Useless: 10  , Deadly: 218 (*)
     5.8 +  sAIData.iRangeFindAttempts = (int)pow(iType + round((iType + 1) / 2), 2);  // Useless: 4  , Deadly: 64
     5.9    sAIData.iRetargetAttempts  = (int)(pow(iType, 2) + 2);      // Useless:  3  , Deadly: 27
    5.10    sAIData.dFocusRate         = ((double) iType * 2.0) / 10.0; // Useless:  0.2, Deadly:  1.0
    5.11    sAIData.iWeapPoolSize      = iType * 3;                     // Useless:  3  , Deadly: 15
    5.12    sAIData.dErrorMultiplier   = 4.0 * ((double)(DEADLY_PLAYER + 1 - iType) / ((double)sAIData.iRangeFindAttempts / (double)iType));
    5.13  
    5.14    /* Type      iType  RFA  RTA   FR   EM
    5.15 -     Useless     1     10    3  0,2  2,0000
    5.16 -     Guesser     2     29    6  0,4  1,1034
    5.17 -     Rangefind   3     66   11  0,6  0,5455
    5.18 -     Targetter   4    127   18  0,8  0,2520
    5.19 -     Deadly      5    218   27  1,0  0,0917
    5.20 -   */
    5.21 -
    5.22 -  /* (*) While this looks like alot, it only means that deadly bots can keep trying until they
    5.23 -   *     hit an end in aiming. They'll probably never get beyond 40 or fifty tries anyway.
    5.24 +     Useless     1      4    3  0,2  2,0000
    5.25 +     Guesser     2     16    6  0,4  1,1034
    5.26 +     Rangefind   3     25   11  0,6  0,5455
    5.27 +     Targetter   4     49   18  0,8  0,2520
    5.28 +     Deadly      5     64   27  1,0  0,0917
    5.29     */
    5.30  }
    5.31  
    5.32 @@ -624,8 +620,8 @@
    5.33  
    5.34  void PLAYER::initialise ()
    5.35  {
    5.36 -  long int totalPrefs;
    5.37 -  int rouletteCount;
    5.38 +//  long int totalPrefs = 0L;
    5.39 +//  int rouletteCount   = 0;
    5.40  
    5.41    nm[0] = 99;
    5.42    for (int count = 1; count < WEAPONS; count++)
    5.43 @@ -634,27 +630,26 @@
    5.44    for (int count = 0; count < ITEMS; count++)
    5.45      ni[count] = 0;
    5.46  
    5.47 -  totalPrefs = 0;
    5.48 -  for (int weapCount = 0; weapCount < THINGS; weapCount++)
    5.49 -    totalPrefs += _weaponPreference[weapCount];
    5.50 -
    5.51 -  rouletteCount = 0;
    5.52 -  for (int weapCount = 0; weapCount < THINGS; weapCount++)
    5.53 -    {
    5.54 -      int weapRSpace = (int)((double)_weaponPreference[weapCount] / totalPrefs * NUM_ROULETTE_SLOTS);
    5.55 -      int weapRCount = 0;
    5.56 -
    5.57 -      if (weapRSpace < 1)
    5.58 -        weapRSpace = 1;
    5.59 -      while (weapRCount < weapRSpace && rouletteCount + weapRCount < NUM_ROULETTE_SLOTS)
    5.60 -        {
    5.61 -          _rouletteWheel[rouletteCount + weapRCount] = weapCount;
    5.62 -          weapRCount++;
    5.63 -        }
    5.64 -      rouletteCount += weapRSpace;
    5.65 -    }
    5.66 -  while (rouletteCount < NUM_ROULETTE_SLOTS)
    5.67 -    _rouletteWheel[rouletteCount++] = rand () % THINGS;
    5.68 +/// No longer needed, bots have shopping karts, sorted by preferences.
    5.69 +//  for (int weapCount = 0; weapCount < THINGS; weapCount++)
    5.70 +//    totalPrefs += _weaponPreference[weapCount];
    5.71 +//
    5.72 +//  for (int weapCount = 0; weapCount < THINGS; weapCount++)
    5.73 +//    {
    5.74 +//      int weapRSpace = (int)((double)_weaponPreference[weapCount] / totalPrefs * NUM_ROULETTE_SLOTS);
    5.75 +//      int weapRCount = 0;
    5.76 +//
    5.77 +//      if (weapRSpace < 1)
    5.78 +//        weapRSpace = 1;
    5.79 +//      while (weapRCount < weapRSpace && rouletteCount + weapRCount < NUM_ROULETTE_SLOTS)
    5.80 +//        {
    5.81 +//          _rouletteWheel[rouletteCount + weapRCount] = weapCount;
    5.82 +//          weapRCount++;
    5.83 +//        }
    5.84 +//      rouletteCount += weapRSpace;
    5.85 +//    }
    5.86 +//  while (rouletteCount < NUM_ROULETTE_SLOTS)
    5.87 +//    _rouletteWheel[rouletteCount++] = rand () % THINGS;
    5.88  
    5.89    kills       = 0;
    5.90    killed      = 0;
    5.91 @@ -924,7 +919,15 @@
    5.92  
    5.93  int PLAYER::selectRandomItem ()
    5.94  {
    5.95 -  return (_rouletteWheel[rand () % NUM_ROULETTE_SLOTS]);
    5.96 +/// no longer needed, bots have shopping karts of varying size now. Items are sorted by preferences,
    5.97 +/// so keeping the rouletteWheel would make buying lesser wanted stuff almost impossible.
    5.98 +//  return (_rouletteWheel[rand () % NUM_ROULETTE_SLOTS]);
    5.99 +  int iItemNum = rand() % THINGS;
   5.100 +
   5.101 +  while (!_env->isItemAvailable(iItemNum))
   5.102 +    iItemNum = rand() % THINGS;
   5.103 +
   5.104 +  return(iItemNum);
   5.105  }
   5.106  
   5.107  void PLAYER::setName (char *name)
   5.108 @@ -1040,7 +1043,7 @@
   5.109        7.: if all is set, look for dimpled/slick projectiles! */
   5.110  
   5.111    // Step 1: Panicking on OHWs
   5.112 -  if (sAIData.iOHWCount > (int)round((type * 2.0) - sAIData.dDefensiveness))
   5.113 +  if (RNG.random(sAIData.iOHWCount) > (int)round((type * 2.0) - sAIData.dDefensiveness))
   5.114      {
   5.115        // PANIC!
   5.116        if (money > item[ITEM_PLASTEEL].cost)
   5.117 @@ -1062,7 +1065,7 @@
   5.118      currItem = WEAPONS + ITEM_PARACHUTE;
   5.119  
   5.120    // Step 3:
   5.121 -  // depending on intelligence, bots might (char *)"forget"
   5.122 +  // depending on intelligence, bots might "forget"
   5.123    if (!currItem && (rand() % ( (int) type + 1)))
   5.124      {
   5.125        if (nm[MED_MIS] < weapon[MED_MIS].amt)
   5.126 @@ -1079,7 +1082,7 @@
   5.127        int iLimit = aMaxBoostValue - getBoostValue(); // > 0 means: Someone has more than we have!
   5.128  
   5.129  #ifdef DEBUG_SHOPPING
   5.130 -      printf( (char *)"%10s: Boost: %4d, Max: %4d, Limit: %4d\n", getName(), getBoostValue(), aMaxBoostValue, iLimit);
   5.131 +      printf("%10s: Boost: %4d, Max: %4d, Limit: %4d\n", getName(), getBoostValue(), aMaxBoostValue, iLimit);
   5.132  #endif // DEBUG_SHOPPING
   5.133  
   5.134        if ( ((dMood <= 1.25) && (iLimit > 0)) || ((dMood <= 2.0) && (iLimit > getBoostValue())) )
   5.135 @@ -1192,25 +1195,26 @@
   5.136      {
   5.137        // Due to this first check, a preselected item is inserted first
   5.138        while (currItem == oldItem)
   5.139 -        currItem = abs (selectRandomItem());
   5.140 +        currItem = selectRandomItem();
   5.141        oldItem = currItem;
   5.142 -      if (currItem >= THINGS)
   5.143 -        currItem  %=  THINGS;  // Put in range
   5.144 -      // now currItem is 0<= currItem < THINGS
   5.145 -      if ( (_env->isItemAvailable (currItem)) && (currItem != 0))
   5.146 -        {
   5.147 -          iDesiredItems[i]  =  currItem;
   5.148 -          i++;
   5.149 -        }
   5.150 +
   5.151 +/// No more need to check, selectRandomItem() ensures safety now!
   5.152 +//      if (currItem >= THINGS)
   5.153 +//        currItem  %=  THINGS;  // Put in range
   5.154 +//      // now currItem is 0<= currItem < THINGS
   5.155 +//      if ( (_env->isItemAvailable (currItem)) && (currItem != 0))
   5.156 +//        {
   5.157 +      iDesiredItems[i]  =  currItem;
   5.158 +      i++;
   5.159 +//        }
   5.160      }
   5.161  
   5.162    // 2.: sort these items by preferences
   5.163    while (!bIsSorted)
   5.164      {
   5.165 +      i = 1;
   5.166        if (bIsPreSelected)
   5.167          i = 2; // The first item shall not be sorted somewhere else!
   5.168 -      else
   5.169 -        i = 1;
   5.170        bIsSorted  =  true;
   5.171        while (i < iTRIES)
   5.172          {
   5.173 @@ -1237,6 +1241,15 @@
   5.174            if (nm[currItem] >= 99)
   5.175              continue;
   5.176  
   5.177 +          // It's not usefull to have too many of the following items:
   5.178 +          if ( ( ( (currItem >= TREMOR)      && (currItem <= TECTONIC) )
   5.179 +               ||( (currItem >= RIOT_BOMB)   && (currItem <= HVY_RIOT_BOMB) )
   5.180 +               ||( (currItem >= RIOT_CHARGE) && (currItem <= RIOT_BLAST) )
   5.181 +               ||( (currItem >= DIRT_BALL)   && (currItem <= SUP_DIRT_BALL) ) )
   5.182 +             &&(nm[currItem] > weapon[currItem].amt) // We have some already!
   5.183 +             &&(rand() % ((int)round(nm[currItem] / weapon[currItem].amt) + 1)) )
   5.184 +            continue;
   5.185 +
   5.186            //purchase the item
   5.187            if (money >= weapon[currItem].cost)
   5.188              {
   5.189 @@ -1253,6 +1266,18 @@
   5.190            //don't buy if already maxed out
   5.191            if (ni[itemNum] >= 999)
   5.192              continue;
   5.193 +
   5.194 +          // It's not usefull to have too many of the following items:
   5.195 +          if ( ( ( (itemNum >= ITEM_TELEPORT)  && (itemNum <= ITEM_MASS_TELEPORT) )
   5.196 +               ||(  itemNum == ITEM_FAN)
   5.197 +               ||( (itemNum >= ITEM_VENGEANCE) && (itemNum <= ITEM_FATAL_FURY) )
   5.198 +               ||( (itemNum >= ITEM_SLICKP)    && (itemNum <= ITEM_DIMPLEP) )
   5.199 +               ||(  itemNum == ITEM_PARACHUTE)
   5.200 +               ||(  itemNum == ITEM_FUEL) )
   5.201 +             &&(ni[itemNum] > item[itemNum].amt) // We have some already!
   5.202 +             &&(rand() % ((int)round(ni[itemNum] / item[itemNum].amt) + 1)) )
   5.203 +            continue;
   5.204 +
   5.205            //purchase the item
   5.206            if (money >= item[itemNum].cost)
   5.207              {
   5.208 @@ -3414,7 +3439,8 @@
   5.209              {
   5.210                sAIData.iCurrWeaponScore  = -1 * _weaponPreference[sAIData.iCurrWeapon] * iCalcType;
   5.211                sAIData.iCurrWeaponScore += RNG.random(sAIData.iCurrWeaponScore * iCalcType);
   5.212 -              sAIData.iCurrWeaponScore -= iCalcType * 100;
   5.213 +              if (sAIData.iAttackMode != ATTACK_MODE_NONDMG)
   5.214 +                sAIData.iCurrWeaponScore -= iCalcType * 100;
   5.215              }
   5.216            else
   5.217              {
   5.218 @@ -4018,19 +4044,19 @@
   5.219      iMoneyToSave = iMaxCost;
   5.220  
   5.221    /* Jedi, Neutral and Sith handle their money differently:
   5.222 -   * If tehy have enough money, everything is okay. If they have less, then iMoneyToSave is manipulated
   5.223 -   * Jedi    : dMoneyToSave is set to 50% of iMaxCost to save for a) bad times and b) helping team mates
   5.224 -   * Neutrals: dMoneyToSave is set to 25% of iMaxCost to save for bad times
   5.225 -   * Sith    : dMoneyToSave is set to 10% of iMaxCost to save for bad times, the rest is squandered!
   5.226 +   * If they have enough money, iMoneyToSave is manipulated
   5.227 +   * Jedi    : iMoneyToSave is set to 50% of iMaxCost to save for a) bad times and b) helping team mates
   5.228 +   * Neutrals: iMoneyToSave is set to 25% of iMaxCost to save for bad times
   5.229 +   * Sith    : iMoneyToSave is set to 10% of iMaxCost to save for bad times, the rest is squandered!
   5.230     * Without this iMoneyToSave will reach exorbitant heights when inventories get fuller and fuller */
   5.231 -  if ((money <= iMoneyToSave) && (iMoneyToSave > iMaxCost))
   5.232 +  if ((money > iMoneyToSave) && (iMoneyToSave > iMaxCost))
   5.233      {
   5.234        if ((int)team == TEAM_JEDI)
   5.235 -        iMoneyToSave = (int)round((double)iMoneyToSave * 0.50);
   5.236 +        iMoneyToSave = (int)round((double)iMaxCost * 0.50);
   5.237        if ((int)team == TEAM_NEUTRAL)
   5.238 -        iMoneyToSave = (int)round((double)iMoneyToSave * 0.25);
   5.239 +        iMoneyToSave = (int)round((double)iMaxCost * 0.25);
   5.240        if ((int)team == TEAM_SITH)
   5.241 -        iMoneyToSave = (int)round((double)iMoneyToSave * 0.10);
   5.242 +        iMoneyToSave = (int)round((double)iMaxCost * 0.10);
   5.243      }
   5.244  
   5.245  #ifdef DEBUG_SHOPPING
   5.246 @@ -4211,12 +4237,7 @@
   5.247                    int iRadius = tank->cw < WEAPONS
   5.248                                  ? weapon[tank->cw].radius + (TANKHEIGHT / 2)
   5.249                                  : _global->screenWidth / 2;
   5.250 -//                  // Multiply with spread count (more intelligent bots are less optimistic, though...)
   5.251 -//                  if ((tank->cw < WEAPONS) && (weapon[tank->cw].spread > 1))
   5.252 -//                    iRadius *= 1 + (weapon[tank->cw].spread / iCalcType);
   5.253 -//                  // ...and submunition if it has some (Cluster/Napalm)
   5.254 -//                  if ((tank->cw < WEAPONS) && (weapon[tank->cw].numSubmunitions > 2))
   5.255 -//                    iRadius *= 1 + (weapon[tank->cw].numSubmunitions / iCalcType);
   5.256 +
   5.257                    // We need a minimum radius to calc with
   5.258                    if (iRadius < 10)
   5.259                      iRadius = 10;
   5.260 @@ -4233,7 +4254,8 @@
   5.261                         ||(abs(sAIData.iBestOvershoot) < iRadius)
   5.262                         ||(tank->cw >= WEAPONS) // Items never fail
   5.263                         ||(!(rand() % (iCalcType + 1))) // The more intelligent, the more they care
   5.264 -                       || sAIData.bIsAlternate) // Already an alternate action...
   5.265 +                       || sAIData.bIsAlternate // Already an alternate action...
   5.266 +                       ||(sAIData.iAttackMode != ATTACK_MODE_BALLIS) ) // NONDMG, DIRECT and SUICID ARE always accepted
   5.267                      {
   5.268                        // Yes!
   5.269                        TANK * pTargetTank = sAIData.iBestTarget >=0 ? sAIData.pTankPool[sAIData.iBestTarget] : NULL;
   5.270 @@ -4252,7 +4274,7 @@
   5.271  
   5.272                        // Every Bot has a chance to have a critical success on a d20 with their skill level:
   5.273                        if (RNG.random(1, 20) > iCalcType)
   5.274 -                        applyErrorMod(); // Nice... a bit lake AD&D, isn't it?
   5.275 +                        applyErrorMod(); // Nice... a bit like AD&D, isn't it?
   5.276  
   5.277                        // Last one: if we are revenging, tell em!
   5.278                        if  ( (tank->cw < WEAPONS)                          // Wepaon selected?
   5.279 @@ -5261,9 +5283,9 @@
   5.280        dAirTime = dxTime + ((dDistY * dSlopeY) * _env->gravity * (100.0 / _global->frames_per_second)) * 2.0;
   5.281  
   5.282        if (_global->bIsBoxed || (_env->current_wallType == WALL_STEEL))
   5.283 -        iPower = (int)(sqrt (dAirTime * _env->gravity * (100.0 / _global->frames_per_second))) * (100 - (iCalcType * 5));
   5.284 -      else
   5.285 -        iPower = (int)(sqrt (dAirTime * _env->gravity * (100.0 / _global->frames_per_second))) * 100;
   5.286 +        iPower = (int)(sqrt (dAirTime * _env->gravity * (100.0 / _global->frames_per_second))) * (50 - (iCalcType * 5));
   5.287 +      else
   5.288 +        iPower = (int)(sqrt (dAirTime * _env->gravity * (100.0 / _global->frames_per_second))) * 50;
   5.289  
   5.290        // Power variation for more flavour:
   5.291        // substituted :   dPowerVariation = (rand() % 51) * sAIData.dFocusRate; // useless: 0-10, deadly: 0-50
   5.292 @@ -5328,7 +5350,7 @@
   5.293    // iCalcType needs to be in range
   5.294    if (iCalcType < (int)USELESS_PLAYER) iCalcType = (int)USELESS_PLAYER;
   5.295    if (iCalcType > (int)DEADLY_PLAYER)  iCalcType = (int)DEADLY_PLAYER;
   5.296 -  iAngleMod += iCalcType;
   5.297 +  iAngleMod += RNG.random(iCalcType * 2, iCalcType * 5);
   5.298  
   5.299    // Calculate Hit Position
   5.300    if (tank->dPosX >= sAIData.dTargetX) // Shooting left
   5.301 @@ -5360,18 +5382,18 @@
   5.302    if (sAIData.iBestAngle <= 180)
   5.303      {
   5.304        // Test for shooting to the right
   5.305 -      if ( (!tank->shootClearance(sAIData.iBestAngle - iAngleMod, weapon[RIOT_CHARGE].radius - iAngleMod))
   5.306 -           &&nm[RIOT_CHARGE])
   5.307 +      if ( (!tank->shootClearance(sAIData.iBestAngle - iAngleMod, weapon[RIOT_BLAST].radius - iAngleMod))
   5.308 +         &&nm[RIOT_BLAST])
   5.309 +        {
   5.310 +          bIsFinished = true;
   5.311 +          sAIData.iBestWeapon = RIOT_BLAST;
   5.312 +        }
   5.313 +      else if ( (!tank->shootClearance(sAIData.iBestAngle - iAngleMod, weapon[RIOT_CHARGE].radius - iAngleMod))
   5.314 +              &&nm[RIOT_CHARGE])
   5.315          {
   5.316            bIsFinished = true;
   5.317            sAIData.iBestWeapon = RIOT_CHARGE;
   5.318          }
   5.319 -      else if ( (!tank->shootClearance(sAIData.iBestAngle - iAngleMod, weapon[RIOT_BLAST].radius - iAngleMod))
   5.320 -                &&nm[RIOT_BLAST])
   5.321 -        {
   5.322 -          bIsFinished = true;
   5.323 -          sAIData.iBestWeapon = RIOT_BLAST;
   5.324 -        }
   5.325        // If we had success with this idea, the angle needs to be modified:
   5.326        if (bIsFinished)
   5.327          {
   5.328 @@ -5383,18 +5405,18 @@
   5.329    else
   5.330      {
   5.331        // Test for shooting to the left
   5.332 -      if ( (!tank->shootClearance(sAIData.iBestAngle + iAngleMod, weapon[RIOT_CHARGE].radius - iAngleMod))
   5.333 -           &&nm[RIOT_CHARGE])
   5.334 +      if ( (!tank->shootClearance(sAIData.iBestAngle + iAngleMod, weapon[RIOT_BLAST].radius - iAngleMod))
   5.335 +         &&nm[RIOT_BLAST])
   5.336 +        {
   5.337 +          bIsFinished = true;
   5.338 +          sAIData.iBestWeapon = RIOT_BLAST;
   5.339 +        }
   5.340 +      else if ( (!tank->shootClearance(sAIData.iBestAngle + iAngleMod, weapon[RIOT_CHARGE].radius - iAngleMod))
   5.341 +              &&nm[RIOT_CHARGE])
   5.342          {
   5.343            bIsFinished = true;
   5.344            sAIData.iBestWeapon = RIOT_CHARGE;
   5.345          }
   5.346 -      else if ( (!tank->shootClearance(sAIData.iBestAngle + iAngleMod, weapon[RIOT_BLAST].radius - iAngleMod))
   5.347 -                &&nm[RIOT_BLAST])
   5.348 -        {
   5.349 -          bIsFinished = true;
   5.350 -          sAIData.iBestWeapon = RIOT_BLAST;
   5.351 -        }
   5.352        // If we had success with this idea, the angle needs to be modified:
   5.353        if (bIsFinished)
   5.354          {
   5.355 @@ -5430,13 +5452,13 @@
   5.356    if (!bIsFinished)
   5.357      {
   5.358        // Nope, we had no success... Look for blasting the way free with riot bombs
   5.359 -      if ( (iHitLevel > (iTgtLevel + weapon[HVY_RIOT_BOMB].radius))
   5.360 +      if ( (iHitLevel > (iTgtLevel + weapon[RIOT_BOMB].radius))
   5.361             &&nm[HVY_RIOT_BOMB])
   5.362          {
   5.363            bIsFinished = true;
   5.364            sAIData.iBestWeapon = HVY_RIOT_BOMB;
   5.365          }
   5.366 -      else if ( (iHitLevel > (iTgtLevel + weapon[RIOT_BOMB].radius))
   5.367 +      else if ( (iHitLevel <= (iTgtLevel + weapon[RIOT_BOMB].radius))
   5.368                  &&nm[RIOT_BOMB])
   5.369          {
   5.370            bIsFinished = true;
   5.371 @@ -5653,7 +5675,8 @@
   5.372    while (!bHasOne && (i < sAIData.iWeapPoolSize))
   5.373      {
   5.374        if (  (sAIData.iWeaponPool[i] == BURROWER)
   5.375 -            ||(sAIData.iWeaponPool[i] == PENETRATOR) )
   5.376 +          ||(sAIData.iWeaponPool[i] == PENETRATOR)
   5.377 +          ||((sAIData.iWeaponPool[i] >= SML_LAZER) && (sAIData.iWeaponPool[i] <= LRG_LAZER)) )
   5.378          bHasOne = true;
   5.379        i++;
   5.380      }
     6.1 --- a/src/player.h	Fri Sep 18 18:36:29 2009 +0200
     6.2 +++ b/src/player.h	Sun Sep 20 08:32:58 2009 +0200
     6.3 @@ -1,290 +1,290 @@
     6.4 -#ifndef PLAYER_HEADER_
     6.5 -#define PLAYER_HEADER_
     6.6 -
     6.7 -/*
     6.8 - * atanks - obliterate each other with oversize weapons
     6.9 - * Copyright (C) 2003  Thomas Hudson
    6.10 - *
    6.11 - * This program is free software; you can redistribute it and/or
    6.12 - * modify it under the terms of the GNU General Public License
    6.13 - * as published by the Free Software Foundation; either version 2
    6.14 - * of the License, or (at your option) any later version.
    6.15 - *
    6.16 - * This program is distributed in the hope that it will be useful,
    6.17 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    6.18 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    6.19 - * GNU General Public License for more details.
    6.20 - *
    6.21 - * You should have received a copy of the GNU General Public License
    6.22 - * along with this program; if not, write to the Free Software
    6.23 - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    6.24 - * */
    6.25 -
    6.26 -
    6.27 -#include "main.h"
    6.28 -#include "menu.h"
    6.29 -
    6.30 -
    6.31 -enum playerType
    6.32 -{
    6.33 -  HUMAN_PLAYER,
    6.34 -  USELESS_PLAYER,
    6.35 -  GUESSER_PLAYER,
    6.36 -  RANGEFINDER_PLAYER,
    6.37 -  TARGETTER_PLAYER,
    6.38 -  DEADLY_PLAYER,
    6.39 -  LAST_PLAYER_TYPE,
    6.40 -  PART_TIME_BOT         // normally a human, but acting as a deadly computer
    6.41 -};
    6.42 -
    6.43 -enum playerPrefType
    6.44 -{
    6.45 -  PERPLAY_PREF, // <- choose weapon preferences once per game
    6.46 -  ALWAYS_PREF   // <- only choose weapon preferences once on player creation
    6.47 -};
    6.48 -
    6.49 -enum turnStages
    6.50 -{
    6.51 -  AISTAGE_INIT,      /* Initialize sAIData and back up old target */
    6.52 -  AISTAGE_SELECT,    /* Dynamically select a weapon and a target */
    6.53 -  AISTAGE_ALTERNATE, /* If either target or weapon fail, select an alternative */
    6.54 -  AISTAGE_PREPARE,   /* Prepare (and ensure) safe attack values */
    6.55 -  AISTAGE_CALCULATE, /* Calculates attack angle and power */
    6.56 -  AISTAGE_AIMWEAPON, /* should be obvious.... */
    6.57 -  AISTAGE_FIREWEAPON /* erm... obvious, too, isn'T it? */
    6.58 -};
    6.59 -
    6.60 -#define  NUM_ROULETTE_SLOTS (THINGS * 100)
    6.61 -#define  MAX_WEAP_PROBABILITY  10000
    6.62 -#define BURIED_LEVEL 135
    6.63 -
    6.64 -#include "tank.h"
    6.65 -
    6.66 -// Helper Values for AI Data
    6.67 -#define ATTACK_MODE_BALLIS 0x01 // Everything shot in a ballistic curve triggers this
    6.68 -#define ATTACK_MODE_DIRECT 0x02 // Direct weapons (laser) and items (teleport/fan) trigger this
    6.69 -#define ATTACK_MODE_NONDMG 0x03 // Triggered when freeing or moving own tank
    6.70 -#define ATTACK_MODE_SUICID 0x04 // Enters kamikaze mode
    6.71 -
    6.72 -/** @brief struct for AI data
    6.73 -  *
    6.74 -  * This struct holds AI-only data together. The regular way would be to write an AI
    6.75 -  * class that inherits from PLAYER and let bots be of that class. But in atanks
    6.76 -  * human players can become "part time bots" (via F10) and thus all players would
    6.77 -  * need to be of that type.
    6.78 -  *
    6.79 -  * However, it is neccessary to keep these data together to have a better overview,
    6.80 -  * and to get a good deal less members within the player class.
    6.81 -  */
    6.82 -struct AIData
    6.83 -  {
    6.84 -    // Aiming and targeting
    6.85 -    TANK     * pTankPool[MAXPLAYERS]; //!< A list of all available opponents
    6.86 -    double     dTargetX;              //!< Shortcut to the targets X-Position
    6.87 -    double     dTargetY;              //!< Shortcut to the targets Y-Position
    6.88 -    int        iAimingRound;          //!< Current aiming round, solely used by calculateAttack()
    6.89 -    int        iAimFailCount;         //!< Helper counter to give up on hopeless targetting tries early
    6.90 -    int        iAttackMode;           //!< Set attack mode according to ATTACK_MODE_* defines
    6.91 -    int        iAttemptNr;            //!< The current attempt number to find an item/target or aiming
    6.92 -    /* iBest* Values are used and set by computerSelectItem() and computerSelectTarget(). These are
    6.93 -       the values that are actually used for shooting something on someone. */
    6.94 -    int        iBestAngle;            //!< The angle to the best overshoot found yet
    6.95 -    int        iBestOvershoot;        //!< The best overshoot found yet
    6.96 -    int        iBestPower;            //!< The power to the best overshoot found yet
    6.97 -    int        iBestTarget;           //!< Index of the best target selected śo far (from pTankPool[])
    6.98 -    int        iBestTargetScore;      //!< The score the best target achieved
    6.99 -    int        iBestWeapon;           //!< Index of the best weapon selected so far (from iWeaponPool[])
   6.100 -    int        iBestWeaponScore;      //!< The Score the best weapon achieved
   6.101 -    /* iCurr* and iPrev* are used by computerSelectItem() and computerSelectTarget() to be able
   6.102 -       to dynamically choose items/targets. */
   6.103 -    int        iCurrAngle;            //!< The angle to the current overshoot
   6.104 -    int        iCurrOvershoot;        //!< The currently calculated overshoot
   6.105 -    int        iCurrPower;            //!< The power to the current overshoot
   6.106 -    int        iCurrTarget;           //!< The index (pTankPool) of the currently selected target
   6.107 -    int        iCurrTargetScore;      //!< The score this very target achieved
   6.108 -    int        iCurrWeapon;           //!< Number of the currently selected weapon (from iWeaponPool[])
   6.109 -    int        iCurrWeaponScore;      //!< The Score the currently selected weapon achieves
   6.110 -    int        iHitDistance;          //!< Records the distance of the hit done with iBest* values
   6.111 -    int        iOldTarget;            //!< The target shot at in the last round.
   6.112 -    int        iOvershootLimit;       //!< The maximum overshoot the bot is happy with
   6.113 -    int        iPrevAngle;            //!< The angle to the last overshoot (to be able to "revert")
   6.114 -    int        iPrevOvershoot;        //!< Previously saved overshoot for dynamic aiming
   6.115 -    int        iPrevPower;            //!< The power to the last overshoot (to be able to "revert")
   6.116 -    int        iPrevTarget;           //!< The index (pTankPool) of the previously selected target
   6.117 -    int        iPrevWeapon;           //!< The index (iWeaponPool) of the previously selected weapon
   6.118 -    int        iTankPoolSize;         //!< Records the number of available opponents when filling pTankPool[]
   6.119 -    int        iWeaponPool[15];       //!< Selected weapons for a targetting attempt
   6.120 -    int        iWeapPoolSize;         //!< How many weapons a bot is allowed to think about
   6.121 -    // Characteristics
   6.122 -    double     dDefensiveness;        //!< -1.0 - 1.0, aggressive - defensive
   6.123 -    double     dErrorMultiplier;      //!< Multiplier to targeting according to bot type
   6.124 -    double     dFocusRate;            //!< Rate aiming is modified according to bot type
   6.125 -    double     dPainSensitivity;      //!< How sensitive to damage
   6.126 -    double     dSelfPreservation;      //!< Lengths gone to to avoid self-harm
   6.127 -    double     dVengThres;            //!< Damage (% of max armour) required to warrant revenge
   6.128 -    int        iAvgPref;              //!< The average preferences of this bot, used for calculating money needs
   6.129 -    int        iPrefValue;            //!< The average price of all items liked more than iAvgPref
   6.130 -    int        iRangeFindAttempts;    //!< Number of attempts the bot has to modify its aiming
   6.131 -    int        iRetargetAttempts;     //!< Number of attempts the bot has to aim at an opponent
   6.132 -    int        iVengeful;              //!< 0-100 chance of retaliation
   6.133 -    // Round specific values
   6.134 -    bool       bIsAlternate;          //!< true if an alternative action is chosen.
   6.135 -    bool       bIsBuried;             //!< obvious, isn't it?
   6.136 -    bool       bIsFinished;           //!< Is set to true if the aiming is over
   6.137 -    bool       bIsSkipped;            //!< Is set to true if this turn ends due to time
   6.138 -    double     dAnnoyanceFactor;      //!< Sums up damage, until a certain amount is achieved, then pRevengeTo is set
   6.139 -    int        iOHWCount;             //!< Counts OneHitWonders (OHW = Killed with one hit)
   6.140 -    turnStages eAIStage;              //!< The current "thinking" stage the AI is in.
   6.141 -    PLAYER   * pDamager;              //!< The one who caused damage, substitites TANK::creditTo
   6.142 -    PLAYER   * pRevengeTo;            //!< The opponent the bot has a grudge against.
   6.143 -  };
   6.144 -
   6.145 -class PLAYER
   6.146 -  {
   6.147 -  private:
   6.148 -    // empty ctor, copy-ctor and assign operator are private, so the compiler won't create implicit ones!
   6.149 -    inline PLAYER () { }
   6.150 -    inline PLAYER (PLAYER &sourcePlayer _PWX_UNUSED) _PWX_UNUSED;
   6.151 -    inline const PLAYER& operator= (const PLAYER &sourcePlayer)
   6.152 -    {
   6.153 -      return(sourcePlayer);
   6.154 -    }
   6.155 -
   6.156 -    /* --- private attributes --- */
   6.157 -    char          _name[NAME_LENGTH];
   6.158 -    GLOBALDATA  * _global;
   6.159 -    ENVIRONMENT * _env;
   6.160 -    int           _weaponPreference[THINGS];
   6.161 -    int           _rouletteWheel[NUM_ROULETTE_SLOTS];
   6.162 -    int           iBoostItemsBought;
   6.163 -    AIData        sAIData;
   6.164 -
   6.165 -    /* --- private methods --- */
   6.166 -    int         adjustOvershoot(int &aOvershoot, double aReachedX, double aReachedY);
   6.167 -    inline void calcBallisAttack() _PWX_FORCEINLINE;
   6.168 -    inline void calcDirectAttack() _PWX_FORCEINLINE;
   6.169 -    inline void calcNonDmgAttack() _PWX_FORCEINLINE;
   6.170 -    inline void calcSuicidAttack() _PWX_FORCEINLINE;
   6.171 -    int         calculateAttack(TANK *aTarget = NULL);
   6.172 -    int         calculateDirectAngle (int dx, int dy);
   6.173 -    int         computerSelectPreBuyItem (int aMaxBoostValue = 0);
   6.174 -    void        computerSelectTarget (int aPreferredWeapon, bool aRotationMode = false);
   6.175 -    int         getBlastValue (TANK * aTarget, int aDamage, int aWeapon, double aDamageMod = 1.0);
   6.176 -    int         rangeFind (bool aIsOneShot = false);
   6.177 -    char      * selectKamikazePhrase();
   6.178 -    char      * selectRetaliationPhrase();
   6.179 -    int         traceShellTrajectory (double aStartX, double aStartY, double aVelocityX, double aVelocityY, double &aReachedX, double &aReachedY);
   6.180 -
   6.181 -    /* --- private methods to access AI Data --- */
   6.182 -    inline bool AIControl (bool bIsAIOnly) _PWX_WARNUNUSED _PWX_FORCEINLINE; //!< Base method for "thinking", controlling workflow
   6.183 -    inline bool AIStageInit() _PWX_WARNUNUSED _PWX_FORCEINLINE;              //!< Initialize sAIData and back up old target
   6.184 -    inline bool AIStageSelect() _PWX_WARNUNUSED _PWX_FORCEINLINE;            //!< Dynamically select a weapon and a target
   6.185 -    inline bool AIStageAlternate() _PWX_WARNUNUSED _PWX_FORCEINLINE;         //!< If Select() fails, select an alternative
   6.186 -    inline bool AIStagePrepare() _PWX_WARNUNUSED _PWX_FORCEINLINE;           //!< Prepare (and ensure) safe attack values
   6.187 -    inline bool AIStageCalculate() _PWX_WARNUNUSED _PWX_FORCEINLINE;         //!< calculates angle and power
   6.188 -    inline bool AIStageAimWeapon(bool bIsAIOnly) _PWX_WARNUNUSED _PWX_FORCEINLINE; //!< should be obvious....
   6.189 -    inline bool AIStageFireWeapon() _PWX_WARNUNUSED _PWX_FORCEINLINE;        //!< erm... obvious, too, isn't it?
   6.190 -    inline void applyErrorMod() _PWX_FORCEINLINE;                            //!< Mod iBestAngle/Power to be somewhat wrong
   6.191 -    inline void calculateAttackValues(bool aAllowFlip) _PWX_FORCEINLINE;     //!< set up basic angle/power values
   6.192 -    inline void checkShootingPath() _PWX_FORCEINLINE;                        //!< reverts to freeing methods if the path is blocked
   6.193 -    inline void checkTankPool() _PWX_FORCEINLINE;                            //!< ensures pTankPool safety
   6.194 -    inline bool hasAntiDirtWeapon() _PWX_WARNUNUSED;                         //!< returns true if the bot has a weapon in the pool that can be fired when buried
   6.195 -    inline void setAdjustedTargetX() _PWX_FORCEINLINE;                       //!< varies sAIData.dTargetX according to tank->cw
   6.196 -    inline bool setKamikazeValues() _PWX_WARNUNUSED;                         //!< Decides whether to blow up and sets appropriate values
   6.197 -    void        setUnburyingTool();
   6.198 -    void        setupAIData();
   6.199 -
   6.200 -  public:
   6.201 -    double      type;
   6.202 -    double      type_saved;
   6.203 -    double      preftype;
   6.204 -    char      * preftypeText[ALWAYS_PREF + 1];
   6.205 -    char      * tank_type[2];
   6.206 -    char      * teamText[3];
   6.207 -    int         score;
   6.208 -    double      played;
   6.209 -    double      won;
   6.210 -    int         kills;
   6.211 -    int         killed;
   6.212 -    double      selected;
   6.213 -    int         money;
   6.214 -    double      damageMultiplier;
   6.215 -    TANK      * tank;
   6.216 -    int         color;
   6.217 -    void      * pColor;
   6.218 -    int         color2;
   6.219 -    int         nm[WEAPONS];
   6.220 -    int         ni[ITEMS];
   6.221 -    MENUENTRY * menuopts;
   6.222 -    MENUDESC  * menudesc;
   6.223 -    char      * typeText[LAST_PLAYER_TYPE];
   6.224 -    bool        changed_weapon;
   6.225 -    bool        gloating;
   6.226 -    bool        threatening;
   6.227 -    double      tank_bitmap;      // which type of tank do we have
   6.228 -    double      team;
   6.229 -    int         time_left_to_fire;
   6.230 -
   6.231 -    /* --- public methods --- */
   6.232 -    PLAYER (GLOBALDATA *global, ENVIRONMENT *env);
   6.233 -    ~PLAYER  ();
   6.234 -    void    setName (char *name);
   6.235 -    char  * getName () { return (_name); }
   6.236 -    void    exitShop ();
   6.237 -    void    newRound ();
   6.238 -    void    initialise ();
   6.239 -    bool    controlTank (bool bIsAIOnly);
   6.240 -    bool    humanControls ();
   6.241 -    double  calcDefenseValue (TANK *ctank, TANK *ltank);
   6.242 -    double  selectTarget (); // select x to aim for
   6.243 -    double  selectTarget (int *targetXCoord, int *targetYCoord); // select x to aim for
   6.244 -    double  Select_Target (int *target_X, int *target_Y); // select x to aim for
   6.245 -    void    computerSelectItem (); // Choose weapon to fire
   6.246 -    int     chooseItemToBuy (int aMaxBoostValue = 0);
   6.247 -    void    generatePreferences ();
   6.248 -    void    setComputerValues (int aOffset = 0);
   6.249 -    int     getMoneyToSave();
   6.250 -    int     selectRandomItem ();
   6.251 -    char  * selectRevengePhrase ();
   6.252 -    char  * selectGloatPhrase ();
   6.253 -    char  * selectSuicidePhrase ();
   6.254 -    int     saveToFile (ofstream &ofsFile);
   6.255 -    int     loadFromFile (ifstream &ifsFile);
   6.256 -    void    initMenuDesc ();
   6.257 -    char  * Get_Team_Name();
   6.258 -    int     Select_Random_Weapon();
   6.259 -    int     Select_Random_Item();
   6.260 -    int     Reduce_Time_Clock();
   6.261 -
   6.262 -    /* --- AI Access Methods --- */
   6.263 -    inline void     addOHW()                       { sAIData.iOHWCount++; }
   6.264 -    inline int      getBoostValue()                { return ( (ni[ITEM_ARMOUR]        *  3)
   6.265 -                                                             +(ni[ITEM_PLASTEEL]      * 21)
   6.266 -                                                             +(ni[ITEM_INTENSITY_AMP] *  7)
   6.267 -                                                             +(ni[ITEM_VIOLENT_FORCE] * 21)); }
   6.268 -    inline PLAYER * getDamager()                   { return(sAIData.pDamager);  }
   6.269 -    inline int      getDefensiveness()             { return(sAIData.dDefensiveness); }
   6.270 -    inline PLAYER * getRevengeTo()                 { return(sAIData.pRevengeTo); }
   6.271 -    inline bool     isAnnoyed()                    { if (  (sAIData.dAnnoyanceFactor > (sAIData.dVengThres * tank->maxLife))
   6.272 -                                                        &&((rand() % 100) <= sAIData.iVengeful))
   6.273 -                                                       return(true); else return(false); }
   6.274 -    inline bool     isRevengeOnDamager()           { return(sAIData.pDamager == sAIData.pRevengeTo ? true : false); }
   6.275 -    inline bool     isSelfDamage()                 { return(sAIData.pDamager == this ? true : false); }
   6.276 -    inline bool     isSkipped()                    { return(sAIData.bIsSkipped); }
   6.277 -    inline void     setActive()                    { setFinished();  sAIData.bIsFinished= false; }
   6.278 -    inline void     setAnnoyance(double aDamage)   { isRevengeOnDamager()
   6.279 -	                                                   ? sAIData.dAnnoyanceFactor += aDamage
   6.280 -                                                     : sAIData.dAnnoyanceFactor  = aDamage; }
   6.281 -    inline void     setDamager(PLAYER * aPlayer)   { sAIData.pDamager = aPlayer; }
   6.282 -    inline void     setFinished()                  { sAIData.bIsSkipped = false;
   6.283 -                                                     sAIData.bIsFinished= true;
   6.284 -                                                     sAIData.eAIStage   = AISTAGE_INIT;
   6.285 -                                                     time_left_to_fire  = _global->max_fire_time; }
   6.286 -    inline void     setRevengeOnDamager()          { sAIData.pRevengeTo = sAIData.pDamager; }
   6.287 -    inline void     setRevengeTo(PLAYER * aPlayer) { sAIData.pRevengeTo = aPlayer; }
   6.288 -    inline void     setSkip()                      { sAIData.bIsSkipped = true; sAIData.bIsFinished= false; }
   6.289 -    inline void     setTargetToSelf();
   6.290 -  };
   6.291 -
   6.292 -#endif // PLAYER_HEADER_
   6.293 -
   6.294 +#ifndef PLAYER_HEADER_
   6.295 +#define PLAYER_HEADER_
   6.296 +
   6.297 +/*
   6.298 + * atanks - obliterate each other with oversize weapons
   6.299 + * Copyright (C) 2003  Thomas Hudson
   6.300 + *
   6.301 + * This program is free software; you can redistribute it and/or
   6.302 + * modify it under the terms of the GNU General Public License
   6.303 + * as published by the Free Software Foundation; either version 2
   6.304 + * of the License, or (at your option) any later version.
   6.305 + *
   6.306 + * This program is distributed in the hope that it will be useful,
   6.307 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   6.308 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   6.309 + * GNU General Public License for more details.
   6.310 + *
   6.311 + * You should have received a copy of the GNU General Public License
   6.312 + * along with this program; if not, write to the Free Software
   6.313 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   6.314 + * */
   6.315 +
   6.316 +
   6.317 +#include "main.h"
   6.318 +#include "menu.h"
   6.319 +
   6.320 +
   6.321 +enum playerType
   6.322 +{
   6.323 +  HUMAN_PLAYER,
   6.324 +  USELESS_PLAYER,
   6.325 +  GUESSER_PLAYER,
   6.326 +  RANGEFINDER_PLAYER,
   6.327 +  TARGETTER_PLAYER,
   6.328 +  DEADLY_PLAYER,
   6.329 +  LAST_PLAYER_TYPE,
   6.330 +  PART_TIME_BOT         // normally a human, but acting as a deadly computer
   6.331 +};
   6.332 +
   6.333 +enum playerPrefType
   6.334 +{
   6.335 +  PERPLAY_PREF, // <- choose weapon preferences once per game
   6.336 +  ALWAYS_PREF   // <- only choose weapon preferences once on player creation
   6.337 +};
   6.338 +
   6.339 +enum turnStages
   6.340 +{
   6.341 +  AISTAGE_INIT,      /* Initialize sAIData and back up old target */
   6.342 +  AISTAGE_SELECT,    /* Dynamically select a weapon and a target */
   6.343 +  AISTAGE_ALTERNATE, /* If either target or weapon fail, select an alternative */
   6.344 +  AISTAGE_PREPARE,   /* Prepare (and ensure) safe attack values */
   6.345 +  AISTAGE_CALCULATE, /* Calculates attack angle and power */
   6.346 +  AISTAGE_AIMWEAPON, /* should be obvious.... */
   6.347 +  AISTAGE_FIREWEAPON /* erm... obvious, too, isn'T it? */
   6.348 +};
   6.349 +
   6.350 +/// no longer needed #define  NUM_ROULETTE_SLOTS (THINGS * 100)
   6.351 +#define MAX_WEAP_PROBABILITY  10000
   6.352 +#define BURIED_LEVEL 135
   6.353 +
   6.354 +#include "tank.h"
   6.355 +
   6.356 +// Helper Values for AI Data
   6.357 +#define ATTACK_MODE_BALLIS 0x01 // Everything shot in a ballistic curve triggers this
   6.358 +#define ATTACK_MODE_DIRECT 0x02 // Direct weapons (laser) and items (teleport/fan) trigger this
   6.359 +#define ATTACK_MODE_NONDMG 0x03 // Triggered when freeing or moving own tank
   6.360 +#define ATTACK_MODE_SUICID 0x04 // Enters kamikaze mode
   6.361 +
   6.362 +/** @brief struct for AI data
   6.363 +  *
   6.364 +  * This struct holds AI-only data together. The regular way would be to write an AI
   6.365 +  * class that inherits from PLAYER and let bots be of that class. But in atanks
   6.366 +  * human players can become "part time bots" (via F10) and thus all players would
   6.367 +  * need to be of that type.
   6.368 +  *
   6.369 +  * However, it is neccessary to keep these data together to have a better overview,
   6.370 +  * and to get a good deal less members within the player class.
   6.371 +  */
   6.372 +struct AIData
   6.373 +  {
   6.374 +    // Aiming and targeting
   6.375 +    TANK     * pTankPool[MAXPLAYERS]; //!< A list of all available opponents
   6.376 +    double     dTargetX;              //!< Shortcut to the targets X-Position
   6.377 +    double     dTargetY;              //!< Shortcut to the targets Y-Position
   6.378 +    int        iAimingRound;          //!< Current aiming round, solely used by calculateAttack()
   6.379 +    int        iAimFailCount;         //!< Helper counter to give up on hopeless targetting tries early
   6.380 +    int        iAttackMode;           //!< Set attack mode according to ATTACK_MODE_* defines
   6.381 +    int        iAttemptNr;            //!< The current attempt number to find an item/target or aiming
   6.382 +    /* iBest* Values are used and set by computerSelectItem() and computerSelectTarget(). These are
   6.383 +       the values that are actually used for shooting something on someone. */
   6.384 +    int        iBestAngle;            //!< The angle to the best overshoot found yet
   6.385 +    int        iBestOvershoot;        //!< The best overshoot found yet
   6.386 +    int        iBestPower;            //!< The power to the best overshoot found yet
   6.387 +    int        iBestTarget;           //!< Index of the best target selected śo far (from pTankPool[])
   6.388 +    int        iBestTargetScore;      //!< The score the best target achieved
   6.389 +    int        iBestWeapon;           //!< Index of the best weapon selected so far (from iWeaponPool[])
   6.390 +    int        iBestWeaponScore;      //!< The Score the best weapon achieved
   6.391 +    /* iCurr* and iPrev* are used by computerSelectItem() and computerSelectTarget() to be able
   6.392 +       to dynamically choose items/targets. */
   6.393 +    int        iCurrAngle;            //!< The angle to the current overshoot
   6.394 +    int        iCurrOvershoot;        //!< The currently calculated overshoot
   6.395 +    int        iCurrPower;            //!< The power to the current overshoot
   6.396 +    int        iCurrTarget;           //!< The index (pTankPool) of the currently selected target
   6.397 +    int        iCurrTargetScore;      //!< The score this very target achieved
   6.398 +    int        iCurrWeapon;           //!< Number of the currently selected weapon (from iWeaponPool[])
   6.399 +    int        iCurrWeaponScore;      //!< The Score the currently selected weapon achieves
   6.400 +    int        iHitDistance;          //!< Records the distance of the hit done with iBest* values
   6.401 +    int        iOldTarget;            //!< The target shot at in the last round.
   6.402 +    int        iOvershootLimit;       //!< The maximum overshoot the bot is happy with
   6.403 +    int        iPrevAngle;            //!< The angle to the last overshoot (to be able to "revert")
   6.404 +    int        iPrevOvershoot;        //!< Previously saved overshoot for dynamic aiming
   6.405 +    int        iPrevPower;            //!< The power to the last overshoot (to be able to "revert")
   6.406 +    int        iPrevTarget;           //!< The index (pTankPool) of the previously selected target
   6.407 +    int        iPrevWeapon;           //!< The index (iWeaponPool) of the previously selected weapon
   6.408 +    int        iTankPoolSize;         //!< Records the number of available opponents when filling pTankPool[]
   6.409 +    int        iWeaponPool[15];       //!< Selected weapons for a targetting attempt
   6.410 +    int        iWeapPoolSize;         //!< How many weapons a bot is allowed to think about
   6.411 +    // Characteristics
   6.412 +    double     dDefensiveness;        //!< -1.0 - 1.0, aggressive - defensive
   6.413 +    double     dErrorMultiplier;      //!< Multiplier to targeting according to bot type
   6.414 +    double     dFocusRate;            //!< Rate aiming is modified according to bot type
   6.415 +    double     dPainSensitivity;      //!< How sensitive to damage
   6.416 +    double     dSelfPreservation;      //!< Lengths gone to to avoid self-harm
   6.417 +    double     dVengThres;            //!< Damage (% of max armour) required to warrant revenge
   6.418 +    int        iAvgPref;              //!< The average preferences of this bot, used for calculating money needs
   6.419 +    int        iPrefValue;            //!< The average price of all items liked more than iAvgPref
   6.420 +    int        iRangeFindAttempts;    //!< Number of attempts the bot has to modify its aiming
   6.421 +    int        iRetargetAttempts;     //!< Number of attempts the bot has to aim at an opponent
   6.422 +    int        iVengeful;              //!< 0-100 chance of retaliation
   6.423 +    // Round specific values
   6.424 +    bool       bIsAlternate;          //!< true if an alternative action is chosen.
   6.425 +    bool       bIsBuried;             //!< obvious, isn't it?
   6.426 +    bool       bIsFinished;           //!< Is set to true if the aiming is over
   6.427 +    bool       bIsSkipped;            //!< Is set to true if this turn ends due to time
   6.428 +    double     dAnnoyanceFactor;      //!< Sums up damage, until a certain amount is achieved, then pRevengeTo is set
   6.429 +    int        iOHWCount;             //!< Counts OneHitWonders (OHW = Killed with one hit)
   6.430 +    turnStages eAIStage;              //!< The current "thinking" stage the AI is in.
   6.431 +    PLAYER   * pDamager;              //!< The one who caused damage, substitites TANK::creditTo
   6.432 +    PLAYER   * pRevengeTo;            //!< The opponent the bot has a grudge against.
   6.433 +  };
   6.434 +
   6.435 +class PLAYER
   6.436 +  {
   6.437 +  private:
   6.438 +    // empty ctor, copy-ctor and assign operator are private, so the compiler won't create implicit ones!
   6.439 +    inline PLAYER () { }
   6.440 +    inline PLAYER (PLAYER &sourcePlayer _PWX_UNUSED) _PWX_UNUSED;
   6.441 +    inline const PLAYER& operator= (const PLAYER &sourcePlayer)
   6.442 +    {
   6.443 +      return(sourcePlayer);
   6.444 +    }
   6.445 +
   6.446 +    /* --- private attributes --- */
   6.447 +    char          _name[NAME_LENGTH];
   6.448 +    GLOBALDATA  * _global;
   6.449 +    ENVIRONMENT * _env;
   6.450 +    int           _weaponPreference[THINGS];
   6.451 +/// no longer needed    int           _rouletteWheel[NUM_ROULETTE_SLOTS];
   6.452 +    int           iBoostItemsBought;
   6.453 +    AIData        sAIData;
   6.454 +
   6.455 +    /* --- private methods --- */
   6.456 +    int         adjustOvershoot(int &aOvershoot, double aReachedX, double aReachedY);
   6.457 +    inline void calcBallisAttack() _PWX_FORCEINLINE;
   6.458 +    inline void calcDirectAttack() _PWX_FORCEINLINE;
   6.459 +    inline void calcNonDmgAttack() _PWX_FORCEINLINE;
   6.460 +    inline void calcSuicidAttack() _PWX_FORCEINLINE;
   6.461 +    int         calculateAttack(TANK *aTarget = NULL);
   6.462 +    int         calculateDirectAngle (int dx, int dy);
   6.463 +    int         computerSelectPreBuyItem (int aMaxBoostValue = 0);
   6.464 +    void        computerSelectTarget (int aPreferredWeapon, bool aRotationMode = false);
   6.465 +    int         getBlastValue (TANK * aTarget, int aDamage, int aWeapon, double aDamageMod = 1.0);
   6.466 +    int         rangeFind (bool aIsOneShot = false);
   6.467 +    char      * selectKamikazePhrase();
   6.468 +    char      * selectRetaliationPhrase();
   6.469 +    int         traceShellTrajectory (double aStartX, double aStartY, double aVelocityX, double aVelocityY, double &aReachedX, double &aReachedY);
   6.470 +
   6.471 +    /* --- private methods to access AI Data --- */
   6.472 +    inline bool AIControl (bool bIsAIOnly) _PWX_WARNUNUSED _PWX_FORCEINLINE; //!< Base method for "thinking", controlling workflow
   6.473 +    inline bool AIStageInit() _PWX_WARNUNUSED _PWX_FORCEINLINE;              //!< Initialize sAIData and back up old target
   6.474 +    inline bool AIStageSelect() _PWX_WARNUNUSED _PWX_FORCEINLINE;            //!< Dynamically select a weapon and a target
   6.475 +    inline bool AIStageAlternate() _PWX_WARNUNUSED _PWX_FORCEINLINE;         //!< If Select() fails, select an alternative
   6.476 +    inline bool AIStagePrepare() _PWX_WARNUNUSED _PWX_FORCEINLINE;           //!< Prepare (and ensure) safe attack values
   6.477 +    inline bool AIStageCalculate() _PWX_WARNUNUSED _PWX_FORCEINLINE;         //!< calculates angle and power
   6.478 +    inline bool AIStageAimWeapon(bool bIsAIOnly) _PWX_WARNUNUSED _PWX_FORCEINLINE; //!< should be obvious....
   6.479 +    inline bool AIStageFireWeapon() _PWX_WARNUNUSED _PWX_FORCEINLINE;        //!< erm... obvious, too, isn't it?
   6.480 +    inline void applyErrorMod() _PWX_FORCEINLINE;                            //!< Mod iBestAngle/Power to be somewhat wrong
   6.481 +    inline void calculateAttackValues(bool aAllowFlip) _PWX_FORCEINLINE;     //!< set up basic angle/power values
   6.482 +    inline void checkShootingPath() _PWX_FORCEINLINE;                        //!< reverts to freeing methods if the path is blocked
   6.483 +    inline void checkTankPool() _PWX_FORCEINLINE;                            //!< ensures pTankPool safety
   6.484 +    inline bool hasAntiDirtWeapon() _PWX_WARNUNUSED;                         //!< returns true if the bot has a weapon in the pool that can be fired when buried
   6.485 +    inline void setAdjustedTargetX() _PWX_FORCEINLINE;                       //!< varies sAIData.dTargetX according to tank->cw
   6.486 +    inline bool setKamikazeValues() _PWX_WARNUNUSED;                         //!< Decides whether to blow up and sets appropriate values
   6.487 +    void        setUnburyingTool();
   6.488 +    void        setupAIData();
   6.489 +
   6.490 +  public:
   6.491 +    double      type;
   6.492 +    double      type_saved;
   6.493 +    double      preftype;
   6.494 +    char      * preftypeText[ALWAYS_PREF + 1];
   6.495 +    char      * tank_type[2];
   6.496 +    char      * teamText[3];
   6.497 +    int         score;
   6.498 +    double      played;
   6.499 +    double      won;
   6.500 +    int         kills;
   6.501 +    int         killed;
   6.502 +    double      selected;
   6.503 +    int         money;
   6.504 +    double      damageMultiplier;
   6.505 +    TANK      * tank;
   6.506 +    int         color;
   6.507 +    void      * pColor;
   6.508 +    int         color2;
   6.509 +    int         nm[WEAPONS];
   6.510 +    int         ni[ITEMS];
   6.511 +    MENUENTRY * menuopts;
   6.512 +    MENUDESC  * menudesc;
   6.513 +    char      * typeText[LAST_PLAYER_TYPE];
   6.514 +    bool        changed_weapon;
   6.515 +    bool        gloating;
   6.516 +    bool        threatening;
   6.517 +    double      tank_bitmap;      // which type of tank do we have
   6.518 +    double      team;
   6.519 +    int         time_left_to_fire;
   6.520 +
   6.521 +    /* --- public methods --- */
   6.522 +    PLAYER (GLOBALDATA *global, ENVIRONMENT *env);
   6.523 +    ~PLAYER  ();
   6.524 +    void    setName (char *name);
   6.525 +    char  * getName () { return (_name); }
   6.526 +    void    exitShop ();
   6.527 +    void    newRound ();
   6.528 +    void    initialise ();
   6.529 +    bool    controlTank (bool bIsAIOnly);
   6.530 +    bool    humanControls ();
   6.531 +    double  calcDefenseValue (TANK *ctank, TANK *ltank);
   6.532 +    double  selectTarget (); // select x to aim for
   6.533 +    double  selectTarget (int *targetXCoord, int *targetYCoord); // select x to aim for
   6.534 +    double  Select_Target (int *target_X, int *target_Y); // select x to aim for
   6.535 +    void    computerSelectItem (); // Choose weapon to fire
   6.536 +    int     chooseItemToBuy (int aMaxBoostValue = 0);
   6.537 +    void    generatePreferences ();
   6.538 +    void    setComputerValues (int aOffset = 0);
   6.539 +    int     getMoneyToSave();
   6.540 +    int     selectRandomItem ();
   6.541 +    char  * selectRevengePhrase ();
   6.542 +    char  * selectGloatPhrase ();
   6.543 +    char  * selectSuicidePhrase ();
   6.544 +    int     saveToFile (ofstream &ofsFile);
   6.545 +    int     loadFromFile (ifstream &ifsFile);
   6.546 +    void    initMenuDesc ();
   6.547 +    char  * Get_Team_Name();
   6.548 +    int     Select_Random_Weapon();
   6.549 +    int     Select_Random_Item();
   6.550 +    int     Reduce_Time_Clock();
   6.551 +
   6.552 +    /* --- AI Access Methods --- */
   6.553 +    inline void     addOHW()                       { sAIData.iOHWCount++; }
   6.554 +    inline int      getBoostValue()                { return ( (ni[ITEM_ARMOUR]        *  3)
   6.555 +                                                             +(ni[ITEM_PLASTEEL]      * 21)
   6.556 +                                                             +(ni[ITEM_INTENSITY_AMP] *  7)
   6.557 +                                                             +(ni[ITEM_VIOLENT_FORCE] * 21)); }
   6.558 +    inline PLAYER * getDamager()                   { return(sAIData.pDamager);  }
   6.559 +    inline int      getDefensiveness()             { return(sAIData.dDefensiveness); }
   6.560 +    inline PLAYER * getRevengeTo()                 { return(sAIData.pRevengeTo); }
   6.561 +    inline bool     isAnnoyed()                    { if (  (sAIData.dAnnoyanceFactor > (sAIData.dVengThres * tank->maxLife))
   6.562 +                                                        &&((rand() % 100) <= sAIData.iVengeful))
   6.563 +                                                       return(true); else return(false); }
   6.564 +    inline bool     isRevengeOnDamager()           { return(sAIData.pDamager == sAIData.pRevengeTo ? true : false); }
   6.565 +    inline bool     isSelfDamage()                 { return(sAIData.pDamager == this ? true : false); }
   6.566 +    inline bool     isSkipped()                    { return(sAIData.bIsSkipped); }
   6.567 +    inline void     setActive()                    { setFinished();  sAIData.bIsFinished= false; }
   6.568 +    inline void     setAnnoyance(double aDamage)   { isRevengeOnDamager()
   6.569 +	                                                   ? sAIData.dAnnoyanceFactor += aDamage
   6.570 +                                                     : sAIData.dAnnoyanceFactor  = aDamage; }
   6.571 +    inline void     setDamager(PLAYER * aPlayer)   { sAIData.pDamager = aPlayer; }
   6.572 +    inline void     setFinished()                  { sAIData.bIsSkipped = false;
   6.573 +                                                     sAIData.bIsFinished= true;
   6.574 +                                                     sAIData.eAIStage   = AISTAGE_INIT;
   6.575 +                                                     time_left_to_fire  = _global->max_fire_time; }
   6.576 +    inline void     setRevengeOnDamager()          { sAIData.pRevengeTo = sAIData.pDamager; }
   6.577 +    inline void     setRevengeTo(PLAYER * aPlayer) { sAIData.pRevengeTo = aPlayer; }
   6.578 +    inline void     setSkip()                      { sAIData.bIsSkipped = true; sAIData.bIsFinished= false; }
   6.579 +    inline void     setTargetToSelf();
   6.580 +  };
   6.581 +
   6.582 +#endif // PLAYER_HEADER_
   6.583 +