- 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.
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 +