home *** CD-ROM | disk | FTP | other *** search
/ Chip 2011 November / CHIP_2011_11.iso / Programy / Inne / Gry / Atomic_Tanks / Atomic-Tanks-5.1.exe / src / player.cpp < prev    next >
C/C++ Source or Header  |  2011-03-06  |  175KB  |  4,825 lines

  1. /*
  2.  * atanks - obliterate each other with oversize weapons
  3.  * Copyright (C) 2003  Thomas Hudson
  4.  *
  5.  * This program is free software; you can redistribute it and/or
  6.  * modify it under the terms of the GNU General Public License
  7.  * as published by the Free Software Foundation; either version 2
  8.  * of the License, or (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18.  * */
  19.  
  20.  
  21. #include "environment.h"
  22. #include "globaldata.h"
  23. #include "player.h"
  24. #include "tank.h"
  25. #include "menu.h"
  26. #include "files.h"
  27. #include "floattext.h"
  28. #include "network.h"
  29.  
  30. // When defined draws AI 'planning'
  31. //#define AI_PLANNING_DEBUG    1
  32.  
  33. PLAYER::PLAYER (GLOBALDATA *global, ENVIRONMENT *env):_global(global),_env(env),_turnStage(0),_target(NULL),
  34.     _oldTarget(NULL),iTargettingRound(0),revenge(NULL),tank(NULL),pColor(NULL),menuopts(NULL),menudesc(NULL)
  35. {
  36.   int my_colour;
  37.  
  38.   money = 15000;
  39.   score = 0;
  40.   played = 0;
  41.   won = 0;
  42.   tank = NULL;
  43.   team = TEAM_NEUTRAL;
  44.  
  45.   iBoostItemsBought = -1;
  46.  
  47.   selected = FALSE;
  48.   changed_weapon = false;
  49.  
  50.  
  51.   tank_bitmap = 0.0;
  52.  
  53.   nm[0] = 99;
  54.   for (int count = 1; count < WEAPONS; count++)
  55.     nm[count] = 0;
  56.  
  57.   for (int count = 0; count < ITEMS; count++)
  58.     ni[count] = 0;
  59.  
  60.   strncpy (_name, "New", NAME_LENGTH);
  61.   type = HUMAN_PLAYER;
  62.  
  63.   // 25% of time set to perplay weapon preferences
  64.   preftype = (rand () % 4)?ALWAYS_PREF:PERPLAY_PREF;
  65. //  generatePreferences (); <-- not here!
  66.  
  67.   defensive = (double)((rand () % 10000) - 5000) / 5000.0;
  68.   vengeful = rand () % 100;
  69.   vengeanceThreshold = (double)(rand () % 1000) / 1000.0;
  70.   revenge = NULL;
  71.   /** @TODO: These two values are nowhere used.
  72.     * We should think about a usage, because bot characteristics could become
  73.     * alot more wide spread with their help. **/
  74.   selfPreservation = (double)(rand () % 3000) / 1000;
  75.   painSensitivity = (double)(rand () % 3000) / 1000;
  76.  
  77.   // color = rand () % WHITE;
  78.   my_colour = rand() % 4;
  79.   switch (my_colour)
  80.     {
  81.     case 0:
  82.       color = makecol(200 + (rand() % 56), rand() % 25, rand() % 25);
  83.       break;
  84.     case 1:
  85.       color = makecol( rand() % 25, 200 + (rand() % 56), rand() % 25);
  86.       break;
  87.     case 2:
  88.       color = makecol( rand() % 25, rand() % 25, 200 + (rand() % 56) );
  89.       break;
  90.     case 3:
  91.       color = makecol( 200 + (rand() % 56), rand() % 25, 200 + (rand() % 56));
  92.       break;
  93.     }
  94.   typeText[0] = global->ingame->complete_text[54];
  95.   typeText[1] = global->ingame->complete_text[55];
  96.   typeText[2] = global->ingame->complete_text[56];
  97.   typeText[3] = global->ingame->complete_text[57];
  98.   typeText[4] = global->ingame->complete_text[58];
  99.   typeText[5] = global->ingame->complete_text[59];
  100.   preftypeText[0] = global->ingame->complete_text[60];
  101.   preftypeText[1] = global->ingame->complete_text[61];
  102.   tank_type[0] = global->ingame->complete_text[62];
  103.   tank_type[1] = global->ingame->complete_text[63];
  104.   tank_type[2] = global->ingame->complete_text[64];
  105.   tank_type[3] = global->ingame->complete_text[65];
  106.   tank_type[4] = global->ingame->complete_text[73];
  107.   tank_type[5] = global->ingame->complete_text[74];
  108.   tank_type[6] = global->ingame->complete_text[75];
  109.   tank_type[7] = global->ingame->complete_text[76];
  110.   teamText[0] = global->ingame->complete_text[66];
  111.   teamText[1] = global->ingame->complete_text[67];
  112.   teamText[2] = global->ingame->complete_text[68];
  113.  
  114.   // Weapon Preferences need to be initalized!
  115.   for (int weapCount = 0; weapCount < THINGS; weapCount++)
  116.     _weaponPreference[weapCount] = 0;
  117.  
  118.   menudesc = NULL;
  119.   menuopts = NULL;
  120.   initMenuDesc ();
  121.   skip_me = false;
  122.   #ifdef NETWORK
  123.   server_socket = 0;
  124.   #endif
  125. }
  126.  
  127. /*
  128. PLAYER::PLAYER (GLOBALDATA *global, ENVIRONMENT *env, ifstream &ifsFile, bool file):_global(global),_env(env),
  129.     _target(NULL),revenge(NULL),tank(NULL),pColor(NULL),menuopts(NULL),menudesc(NULL)
  130. {
  131.   money = 15000;
  132.   score = 0;
  133.   _turnStage = 0;
  134.   selected = FALSE;
  135.  
  136.   tank_bitmap = 0.0;
  137.   team = TEAM_NEUTRAL;
  138.  
  139.   iBoostItemsBought = -1;
  140.  
  141.   nm[0] = 99;
  142.   for (int count = 1; count < WEAPONS; count++)
  143.     nm[count] = 0;
  144.  
  145.   for (int count = 0; count < ITEMS; count++)
  146.     ni[count] = 0;
  147.  
  148.   if (file)
  149.     loadFromFile(ifsFile);
  150.  
  151.   type = (int)type;
  152.  
  153.   typeText[0] = "Human";
  154.   typeText[1] = "Useless";
  155.   typeText[2] = "Guesser";
  156.   typeText[3] = "Ranger";
  157.   typeText[4] = "Targetter";
  158.   typeText[5] = "Deadly";
  159.   preftypeText[0] = "Per Game";
  160.   preftypeText[1] = "Only Once";
  161.   tank_type[0] = "Normal";
  162.   tank_type[1] = "Classic";
  163.   tank_type[2] = "Big Grey";
  164.   tank_type[3] = "T34";
  165.   teamText[0] = "Sith";
  166.   teamText[1] = "Neutral";
  167.   teamText[2] = "Jedi";
  168.   initMenuDesc ();
  169.   skip_me = false;
  170.   #ifdef NETWORK
  171.   net_command[0] = '\0';
  172.   #endif
  173. }
  174. */
  175.  
  176.  
  177. int PLAYER::getBoostValue()
  178. {
  179.       return ((int)(ni[ITEM_ARMOUR] * ((double) item[ITEM_ARMOUR].vals[0] / (double) item[ITEM_PLASTEEL].vals[0])) + ni[ITEM_PLASTEEL] +
  180.               (int)(ni[ITEM_INTENSITY_AMP] * ((double) item[ITEM_INTENSITY_AMP].vals[0] / (double) item[ITEM_VIOLENT_FORCE].vals[0])) +
  181.               ni[ITEM_VIOLENT_FORCE]);
  182. }
  183.  
  184.  
  185.  
  186. void PLAYER::setComputerValues (int aOffset)
  187. {
  188.   int iType = (int) type + aOffset;
  189.   if (iType > (int) DEADLY_PLAYER)
  190.     iType    =    (int) DEADLY_PLAYER;
  191.   rangeFindAttempts    =    (int)(pow(iType + 1, 2) + 1);                // Useless: 5  , Deadly: 37
  192.   retargetAttempts  = (int)(pow(iType, 2) + 1);                        // Useless: 2  , Deadly: 26
  193.   focusRate         = ( (double) iType * 2.0) / 10.0;     // Useless: 0.2, Deadly:  1.0
  194.   errorMultiplier    =    (double)(DEADLY_PLAYER + 1 - iType) / (double)rangeFindAttempts;
  195.   if (errorMultiplier > 2.0) errorMultiplier    = 2.0;
  196. }
  197.  
  198. int displayPlayerName (GLOBALDATA *global, ENVIRONMENT *env, int x, int y, void *data);
  199. void PLAYER::initMenuDesc ()
  200. {
  201.  GLOBALDATA *global = _global;
  202.   int destroyPlayer (GLOBALDATA *global, ENVIRONMENT *env, void *data);
  203.   int i = 0;
  204.   int height = -68;
  205.  
  206.   // before we get started, eraw any old version of the menu
  207.   if (menuopts)
  208.      free(menuopts);
  209.   if (menudesc)
  210.      free(menudesc);
  211.  
  212.   // menudesc = new MENUDESC;
  213.   menudesc = (MENUDESC *) calloc(1, sizeof(MENUDESC));
  214.   if (!menudesc)
  215.     {
  216.       perror ( "player.cc: Failed allocating memory for menudesc in PLAYER::initMenuDesc");
  217.       return;
  218.       // exit (1);
  219.     }
  220.   menudesc->title = _name;
  221.  
  222.   // Name,Color
  223.   menudesc->numEntries = 10;
  224.   menudesc->okayButton = TRUE;
  225.   menudesc->quitButton = FALSE;
  226.  
  227.   // menuopts = new MENUENTRY[menudesc->numEntries];
  228.   menuopts = (MENUENTRY *) calloc(menudesc->numEntries, sizeof(MENUENTRY));
  229.   if (!menuopts)
  230.     {
  231.       perror ( "player.cc: Failed allocating memory for menuopts in PLAYER::initMenuDesc");
  232.       return;
  233.       // exit (1);
  234.     }
  235.  
  236.   //init memory
  237.   // memset(menuopts, 0, menudesc->numEntries * sizeof(MENUENTRY));
  238.  
  239.   // Player name
  240.   menuopts[i].name = global->ingame->complete_text[29];
  241.   menuopts[i].displayFunc = NULL;
  242.   menuopts[i].color = color;
  243.   menuopts[i].value = (double*)_name;
  244.   menuopts[i].specialOpts = NULL;
  245.   menuopts[i].type = OPTION_TEXTTYPE;
  246.   menuopts[i].viewonly = FALSE;
  247.   menuopts[i].x = _global->halfWidth - 3;
  248.   menuopts[i].y = _global->halfHeight + height;
  249.   i++;
  250.   height += 20;
  251.  
  252.   // Player colour
  253.   menuopts[i].name = global->ingame->complete_text[30];
  254.   menuopts[i].displayFunc = NULL;
  255.   menuopts[i].color = WHITE;
  256.   pColor = &color;
  257.   menuopts[i].value = (double*) pColor;
  258.   menuopts[i].specialOpts = NULL;
  259.   menuopts[i].type = OPTION_COLORTYPE;
  260.   menuopts[i].viewonly = FALSE;
  261.   menuopts[i].x = _global->halfWidth - 3;
  262.   menuopts[i].y = _global->halfHeight + height;
  263.   i++;
  264.   height += 20;
  265.  
  266.   // Player type (human, computer)
  267.   menuopts[i].name = global->ingame->complete_text[31];
  268.   menuopts[i].displayFunc = NULL;
  269.   menuopts[i].color = WHITE;
  270.   menuopts[i].value = (double*)&type;
  271.   menuopts[i].min = 0;
  272.   menuopts[i].max = LAST_PLAYER_TYPE - 1;
  273.   menuopts[i].increment = 1;
  274.   menuopts[i].defaultv = 0;
  275.   menuopts[i].format = "%s";
  276.   menuopts[i].specialOpts = typeText;
  277.   menuopts[i].type = OPTION_SPECIALTYPE;
  278.   menuopts[i].viewonly = FALSE;
  279.   menuopts[i].x = _global->halfWidth - 3;
  280.   menuopts[i].y = _global->halfHeight + height;
  281.   i++;
  282.   height += 20;
  283.  
  284.   menuopts[i].name = global->ingame->complete_text[20];
  285.   menuopts[i].displayFunc = NULL;
  286.   menuopts[i].color = WHITE;
  287.   menuopts[i].value = (double *)&team;
  288.   menuopts[i].min = 0;
  289.   menuopts[i].max = TEAM_JEDI;
  290.   menuopts[i].increment = 1;
  291.   menuopts[i].defaultv = TEAM_NEUTRAL;
  292.   menuopts[i].format = "%s";
  293.   menuopts[i].specialOpts = teamText;
  294.   menuopts[i].type = OPTION_SPECIALTYPE;
  295.   menuopts[i].viewonly = FALSE;
  296.   menuopts[i].x = _global->halfWidth - 3;
  297.   menuopts[i].y = _global->halfHeight + height;
  298.   i++;
  299.   height += 20;
  300.  
  301.   // Player preftype (human, computer)
  302.   menuopts[i].name = global->ingame->complete_text[32];
  303.   menuopts[i].displayFunc = NULL;
  304.   menuopts[i].color = WHITE;
  305.   menuopts[i].value = (double*)&preftype;
  306.   menuopts[i].min = 0;
  307.   menuopts[i].max = ALWAYS_PREF;
  308.   menuopts[i].increment = 1;
  309.   menuopts[i].defaultv = 0;
  310.   menuopts[i].format = "%s";
  311.   menuopts[i].specialOpts = preftypeText;
  312.   menuopts[i].type = OPTION_SPECIALTYPE;
  313.   menuopts[i].viewonly = FALSE;
  314.   menuopts[i].x = _global->halfWidth - 3;
  315.   menuopts[i].y = _global->halfHeight + height;
  316.   i++;
  317.   height += 20;
  318.  
  319.   menuopts[i].name = global->ingame->complete_text[33];
  320.   menuopts[i].displayFunc = NULL;
  321.   menuopts[i].color = WHITE;
  322.   menuopts[i].value = (double*)&played;
  323.   menuopts[i].format = "%.0f";
  324.   menuopts[i].specialOpts = NULL;
  325.   menuopts[i].type = OPTION_DOUBLETYPE;
  326.   menuopts[i].viewonly = TRUE;
  327.   menuopts[i].x = _global->halfWidth - 3;
  328.   menuopts[i].y = _global->halfHeight + height;
  329.   i++;
  330.   height += 20;
  331.  
  332.   menuopts[i].name = global->ingame->complete_text[34];
  333.   menuopts[i].displayFunc = NULL;
  334.   menuopts[i].color = WHITE;
  335.   menuopts[i].value = (double*)&won;
  336.   menuopts[i].format = "%.0f";
  337.   menuopts[i].specialOpts = NULL;
  338.   menuopts[i].type = OPTION_DOUBLETYPE;
  339.   menuopts[i].viewonly = TRUE;
  340.   menuopts[i].x = _global->halfWidth - 3;
  341.   menuopts[i].y = _global->halfHeight + height;
  342.   i++;
  343.   height += 20;
  344.  
  345.   menuopts[i].name = global->ingame->complete_text[35];
  346.   menuopts[i].displayFunc = Display_Tank_Bitmap;
  347.   menuopts[i].color = WHITE;
  348.   menuopts[i].value = &tank_bitmap;
  349.   menuopts[i].min = 0;
  350.   menuopts[i].max = TANK_TYPES;
  351.   menuopts[i].increment = 1;
  352.   menuopts[i].defaultv = 0;
  353.   menuopts[i].format = "%1.0f";
  354.   menuopts[i].specialOpts = tank_type;
  355.   menuopts[i].type = OPTION_SPECIALTYPE;
  356.   menuopts[i].viewonly = FALSE;
  357.   menuopts[i].x = _global->halfWidth - 3;
  358.   menuopts[i].y = _global->halfHeight + height;
  359.   i++;
  360.   height += 20;
  361.  
  362.   menuopts[i].name = global->ingame->complete_text[36];
  363.   menuopts[i].displayFunc = NULL;
  364.   menuopts[i].color = WHITE;
  365.   menuopts[i].value = (double*)destroyPlayer;
  366.   menuopts[i].data = (void*)this;
  367.   menuopts[i].type = OPTION_ACTIONTYPE;
  368.   menuopts[i].viewonly = FALSE;
  369.   menuopts[i].x = _global->halfWidth - 3;
  370.   menuopts[i].y = _global->halfHeight + height;
  371.   i++;
  372.   height += 20;
  373.  
  374.   menudesc->entries = menuopts;
  375. }
  376.  
  377. PLAYER::~PLAYER ()
  378. {
  379.   if (tank)
  380.     delete(tank);
  381.  
  382.   if (menuopts)
  383.      free(menuopts);
  384.     // delete(menuopts);
  385.   if (menudesc)
  386.     free(menudesc);
  387.     // delete(menudesc);
  388.  
  389.   _global = NULL;
  390.   _env    = NULL;
  391.   _target = NULL;
  392.   revenge = NULL;
  393.  
  394.   pColor = NULL;
  395. }
  396.  
  397.  
  398.  
  399.  
  400. /*
  401. Save the player settings in text form. Fields are
  402. formateed as
  403. [name]=[value]\n
  404.  
  405. Function returns true on success and false on failure.
  406. */
  407. int PLAYER::saveToFile_Text (FILE *file)
  408. {
  409.   int count;
  410.  
  411.   if (! file) return FALSE;
  412.   // start section with (char *)"*PLAYER*"
  413.   fprintf (file, "*PLAYER*\n");
  414.   fprintf (file, "NAME=%s\n", _name);
  415.   fprintf (file, "VENGEFUL=%d\n", vengeful);
  416.   fprintf (file, "VENGEANCETHRESHOLD=%lf\n", vengeanceThreshold);
  417.   fprintf (file, "TYPE=%lf\n", type);
  418.   fprintf (file, "TYPESAVED=%lf\n", type_saved);
  419.   fprintf (file, "COLOR=%d\n", color);
  420.   fprintf (file, "COLOR2=%d\n", color2);
  421.   for (count = 0; count < THINGS; count++)
  422.     fprintf (file, "WEAPONPREFERENCES=%d %d\n", count, _weaponPreference[count]);
  423.  
  424.   fprintf (file, "PLAYED=%lf\n", played);
  425.   fprintf (file, "WON=%lf\n", won);
  426.   fprintf (file, "PREFTYPE=%lf\n", preftype);
  427.   fprintf (file, "SELFPRESERVATION=%lf\n", selfPreservation);
  428.   fprintf (file, "PAINSENSITIVITY=%lf\n", painSensitivity);
  429.   fprintf (file, "DEFENSIVE=%lf\n", defensive);
  430.   fprintf (file, "TANK_BITMAP=%lf\n", tank_bitmap);
  431.   fprintf (file, "TEAM=%lf\n", team);
  432.   fprintf (file, "***\n");
  433.   return TRUE;
  434. }
  435.  
  436.  
  437. /*
  438. This function tries to load player data from a text file.
  439. Each line is parsed for (char *)"field=value", except WEAPONPREFERENCES
  440. which is parsed (char *)"field=index value".
  441. If all goes well TRUE is returned, on error the function returns FALSE.
  442. -- Jesse
  443. */
  444.  
  445. int PLAYER::loadFromFile_Text (FILE *file)
  446. {
  447.   char line[MAX_CONFIG_LINE];
  448.   int equal_position, line_length;
  449.   int index, wp_value;
  450.   char field[MAX_CONFIG_LINE], value[MAX_CONFIG_LINE];
  451.   char *result = NULL;
  452.   bool done = false;
  453.  
  454.   if (! file) return FALSE;
  455.  
  456.   // read until we hit line (char *)"*PLAYER*" or "***" or EOF
  457.   do
  458.     {
  459.       result = fgets(line, MAX_CONFIG_LINE, file);
  460.       if (! result)     // eof
  461.         return FALSE;
  462.        if (! strncmp(line, "***", 3) )     // end of record
  463.          return FALSE;
  464.     }
  465.   while ( strncmp(line, "*PLAYER*", 8) );     // read until we hit new record
  466.  
  467.   while ( (result) && (!done) )
  468.     {
  469.       // read a line
  470.       memset(line, '\0', MAX_CONFIG_LINE);
  471.       result = fgets(line, MAX_CONFIG_LINE, file);
  472.       if (result)
  473.         {
  474.           // if we hit end of the record, stop
  475.           if (! strncmp(line, "***", 3) ) return TRUE;
  476.           // find equal sign
  477.          line_length = strlen(line);
  478.           // strip newline character
  479.           if ( line[line_length - 1] == '\n')
  480.             {
  481.               line[line_length - 1] = '\0';
  482.               line_length--;
  483.             }
  484.           equal_position = 1;
  485.           while ( ( equal_position < line_length) && (line[equal_position] != '=') )
  486.            equal_position++;
  487.           // make sure we have valid equal sign
  488.           if (equal_position <= line_length)
  489.             {
  490.               // seperate field from value
  491.               memset(field, '\0', MAX_CONFIG_LINE);
  492.               memset(value, '\0', MAX_CONFIG_LINE);
  493.               strncpy(field, line, equal_position);
  494.               strcpy(value, & (line[equal_position + 1]));
  495.               // check which field we have and process value
  496.               if (! strcasecmp(field, "name") )
  497.                 strncpy(_name, value, NAME_LENGTH);
  498.               else if (! strcasecmp(field, "vengeful") )
  499.                 sscanf(value, "%d", &vengeful);
  500.               else if (! strcasecmp(field, "vengeancethreshold") )
  501.                 sscanf(value, "%lf", &vengeanceThreshold);
  502.               else if (! strcasecmp(field, "type") )
  503.                 sscanf(value, "%lf", &type);
  504.               else if (! strcasecmp(field, "typesaved") )
  505.                 {
  506.                   sscanf(value, "%lf", &type_saved);
  507.                   if (type_saved > HUMAN_PLAYER)
  508.                     type = type_saved;
  509.                 }
  510.               else if (! strcasecmp(field, "color") )
  511.                 sscanf(value, "%d", &color);
  512.               else if (! strcasecmp(field, "color2") )
  513.                 sscanf(value, "%d", &color2);
  514.               else if (! strcasecmp(field, "played") )
  515.                 sscanf(value, "%lf", &played);
  516.               else if (! strcasecmp(field, "won") )
  517.                 sscanf(value, "%lf", &won);
  518.               else if (! strcasecmp(field, "preftype") )
  519.                 sscanf(value, "%lf", &preftype);
  520.               else if (! strcasecmp(field, "selfpreservation") )
  521.                 sscanf(value, "%lf", &selfPreservation);
  522.               else if (! strcasecmp(field, "painsensititveity") )
  523.                 sscanf(value, "%lf", &painSensitivity);
  524.               else if (! strcasecmp(field, "defensive") )
  525.                 sscanf(value, "%lf", &defensive);
  526.               else if (! strcasecmp(field, "tank_bitmap") )
  527.                 sscanf(value, "%lf", &tank_bitmap);
  528.               else if (! strcasecmp(field, "team") )
  529.                 sscanf(value, "%lf", &team);
  530.               else if (! strcasecmp(field, "weaponpreferences") )
  531.                 {
  532.                   sscanf(value, "%d %d", &index, &wp_value);
  533.                   if ( (index < THINGS) && (index >= 0) )
  534.                     _weaponPreference[index] = wp_value;
  535.                 }
  536.  
  537.             }    // end of valid data line
  538.         }      // end of if we read a line properly
  539.     }   // end of while not done
  540.  
  541.   // make sure previous human players are restored as humans
  542.   if (type == PART_TIME_BOT)
  543.     type = HUMAN_PLAYER;
  544.  
  545.   return TRUE;
  546. }
  547.  
  548.  
  549.  
  550.  
  551. void PLAYER::exitShop ()
  552. {
  553.   double tmpDM = 0;
  554.  
  555.   damageMultiplier = 1.0;
  556.   tmpDM += ni[ITEM_INTENSITY_AMP] * item[ITEM_INTENSITY_AMP].vals[0];
  557.   tmpDM += ni[ITEM_VIOLENT_FORCE] * item[ITEM_VIOLENT_FORCE].vals[0];
  558.   if (tmpDM > 0)
  559.     damageMultiplier += pow (tmpDM, 0.6);
  560. }
  561.  
  562.  
  563. // run this at the begining of each turn
  564. void PLAYER::newRound ()
  565. {
  566.   // if the player is under computer control, give it back to the player
  567.   if ( type == PART_TIME_BOT )
  568.     type = HUMAN_PLAYER;
  569.  
  570.   setComputerValues ();
  571.  
  572.   if (!tank)
  573.     {
  574.       tank = new TANK(_global, _env);
  575.       if (tank)
  576.       {
  577.         tank->player = this;
  578.         tank->initialise();
  579.       }
  580.       else
  581.           perror ( "player.cc: Failed allocating memory for tank in PLAYER::newRound");
  582.     }
  583.   // newRound() doesn't need to be called, because ENVIRONMENT::newRound() has already done that!
  584.  
  585.   changed_weapon = false;
  586.   // if we are playing in a campaign, raise the AI level
  587.   if (_global->campaign_mode)
  588.     {
  589.       if ( (type > HUMAN_PLAYER) && (type < DEADLY_PLAYER) )
  590.         type += 1.0;
  591.     }
  592.  
  593.   // forget revenge under certain circumstances
  594.   if (revenge)
  595.     {
  596.       if ((team != TEAM_NEUTRAL) && (team == revenge->team))
  597.         revenge = NULL; // No more round breaking revenge on team mates!
  598.       else if (  (team == TEAM_NEUTRAL)
  599.                  ||((team != TEAM_NEUTRAL) && (revenge->team == TEAM_NEUTRAL)))
  600.         {
  601.           // neutral to !neutral and vice versa might forget...
  602.           if (  (!(rand() % (int)labs((type + 3) / 2)))
  603.                 ||((rand() % 100) > vengeful) )
  604.             revenge = NULL;
  605.           /* This gives:
  606.            * USELESS: (1 + 3) / 2) = 2 => 50%
  607.            * GUESSER: (2 + 3) / 2) = 2 => 50%
  608.            * RANGEFI: (3 + 3) / 2) = 3 => 34%
  609.            * TARGETT: (4 + 3) / 2) = 3 => 34%
  610.            * DEADLY : (5 + 3) / 2) = 4 => 25%
  611.            * chance to "forgive". Should be okay...
  612.            * The check against "vengeful" makes "peacefull" bots to forget more easily
  613.            */
  614.         }
  615.     }
  616.  
  617.   if (!revenge && (rand() % ((int)type + 1)))
  618.     {
  619.       // If there is no revengee there is a small chance the player will seek the leader!
  620.       int iMaxScore = score;
  621.       int iCurScore = 0;
  622.       for (int i = 0; i < _global->numPlayers; i++)
  623.         {
  624.           if (_global->players[i])
  625.             {
  626.               iCurScore = _global->players[i]->score;
  627.               if (  (iCurScore > iMaxScore)
  628.                     &&( (team == TEAM_NEUTRAL)
  629.                         ||(team != _global->players[i]->team)) )
  630.                 {
  631.                   // Higher score found, record as possible revengee
  632.                   iMaxScore = iCurScore;
  633.                   if (abs(iMaxScore - score) > (int)type)
  634.                     revenge = _global->players[i];
  635.                 }
  636.             }
  637.         }
  638.     }
  639.  
  640.   time_left_to_fire = (int) _global->max_fire_time;
  641.   skip_me = false;
  642.   iTargettingRound = 0;
  643.   _target = NULL;
  644.   _oldTarget = NULL;
  645.   last_shield_used = 0;
  646. }
  647.  
  648.  
  649. void PLAYER::initialise ()
  650. {
  651.   long int totalPrefs;
  652.   int rouletteCount;
  653.  
  654.   nm[0] = 99;
  655.   for (int count = 1; count < WEAPONS; count++)
  656.     nm[count] = 0;
  657.  
  658.   for (int count = 0; count < ITEMS; count++)
  659.     ni[count] = 0;
  660.  
  661.   totalPrefs = 0;
  662.   for (int weapCount = 0; weapCount < THINGS; weapCount++)
  663.     totalPrefs += _weaponPreference[weapCount];
  664.  
  665.   rouletteCount = 0;
  666.   for (int weapCount = 0; weapCount < THINGS; weapCount++)
  667.     {
  668.       int weapRSpace = (int)((double)_weaponPreference[weapCount] / totalPrefs * NUM_ROULETTE_SLOTS);
  669.       int weapRCount = 0;
  670.  
  671.       if (weapRSpace < 1)
  672.         weapRSpace = 1;
  673.       while (weapRCount < weapRSpace && rouletteCount + weapRCount < NUM_ROULETTE_SLOTS)
  674.         {
  675.           _rouletteWheel[rouletteCount + weapRCount] = weapCount;
  676.           weapRCount++;
  677.         }
  678.       rouletteCount += weapRSpace;
  679.     }
  680.   while (rouletteCount < NUM_ROULETTE_SLOTS)
  681.     _rouletteWheel[rouletteCount++] = rand () % THINGS;
  682.  
  683.   kills       = 0;
  684.   killed       = 0;
  685.   tank        = NULL;
  686.   _target     = NULL;
  687.   _oldTarget  = NULL;
  688. }
  689.  
  690. void PLAYER::generatePreferences ()
  691. {
  692.   double dBaseProb    =    (double) MAX_WEAP_PROBABILITY / 2.0;
  693.   int currItem    =    0;
  694.   double dWorth;
  695.   int iValue;
  696.   bool    bIsWarhead    = false;
  697.   int iMaxWeapPref = 0;
  698.   int iMaxItemPref = 0;
  699.  
  700.   defensive    =    (2.0 * ( (double) rand () / (double) RAND_MAX)) - 1.0;
  701.   double dDefenseMod = (defensive * -1.0) + 2.0;
  702.   // DefenseMod will be between 1.0 (defensive) and 3.0 (offensive)
  703.   // and is used to modifiy vengeful, vengeanceThreshold, selfPreservation and painSensitivity
  704.   vengeful *= 1.0 + (dDefenseMod / 5.0); // +0% - +60% (defensive - offensive)
  705.   if (vengeful > 100) vengeful = 100;
  706.   vengeanceThreshold *= 1.0 - (dDefenseMod / 5.0); // -0% - -60%
  707.   selfPreservation /= dDefenseMod;
  708.   painSensitivity  /= dDefenseMod;
  709.  
  710.   // Now defensive can be modified by team:
  711.   if (team == TEAM_JEDI)
  712.     {
  713.       defensive += (double)((double)(rand() % 1000)) / 1000.0;
  714.       if (defensive > 1.25)
  715.         defensive = 1.25; // + 1.25 is SuperDefensive
  716.     }
  717.   if (team == TEAM_SITH)
  718.     {
  719.       defensive -= (double)((double)(rand() % 1000)) / 1000.0;
  720.       if (defensive < -1.25)
  721.         defensive = -1.25; // - 1.25 is SuperAggressive
  722.     }
  723. #ifdef DEBUG
  724.   cout << "Generating Preferences for \"" << getName() << "\" (" << defensive << ")" << endl;
  725.   cout << "----------------------------------------------------" << endl;
  726. #endif // DEBUG
  727.   _weaponPreference[0] = 0; // small missiles are always zero!
  728.   for (int weapCount = 1; weapCount < THINGS; weapCount++)
  729.     {
  730.       dWorth            =    -1.0 * ( (double) MAX_WEAP_PROBABILITY / 4.0);
  731.       bIsWarhead    =    false;
  732.       if (weapCount < WEAPONS)
  733.         {
  734.           // Talking about weapons
  735.           currItem    =    weapCount;
  736.           if (weapon[currItem].warhead || ( (currItem >= SML_METEOR) && (currItem <= LRG_LIGHTNING)))
  737.             bIsWarhead    =    true; // Bots don't think about warheads or environment!
  738.           else
  739.             {
  740.               // 1. Damage:
  741.               int iWarheads = weapon[currItem].spread; // For non-spread this is always 1
  742.               if (weapon[currItem].numSubmunitions > 0)
  743.                 {
  744.                   iWarheads    =    weapon[currItem].numSubmunitions;    // It's a cluster
  745.  
  746.                   dWorth        = weapon[weapon[currItem].submunition].damage * iWarheads; // total damage for clusters
  747.  
  748.                   if    ( ( (currItem >= SML_NAPALM) && (currItem <= LRG_NAPALM))
  749.                        || ( (currItem >= FUNKY_BOMB) && (currItem <= FUNKY_DEATH)))
  750.                     dWorth /= defensive + 1.0 + ( (double) type / 2.0);
  751.                   // These weapons are too unpredictable to be counted full
  752.                   // But a true offensive useless bot devides only by 1.0 (not all all, doesn't mind)
  753.                   // And a true defensive deadly bot devides by 5.0
  754.                   if (dWorth > dBaseProb) dWorth = dBaseProb; // Or Large Napalm will always be everybodys favorite
  755.                 }
  756.               else
  757.                 dWorth            =    weapon[currItem].damage * (iWarheads * 2.0);    // total damage for (non-)spreads
  758.               // Note: iWarheads is counted twice, because otherwise spread weapons get far too low score!
  759.               dWorth = dWorth * (dBaseProb * 0.0005); // 1 Damage is worth 0.5%o of dBaseProb
  760.  
  761.               // 2. Defensiveness multiplier
  762.               // As said above, defensive players avoid spread/cluster weapons. Thus they rate non-spreads higher:
  763.               if (iWarheads == 1)
  764.                 dWorth *= (defensive + 1.5) * ( (double) type / 2.0);
  765.               else
  766.                 dWorth -= (defensive * ( (double) type / 2.0)) * dWorth;
  767.  
  768.               // 3. Dirtballs do no damage and have to be rated by defensiveness
  769.               if ( (currItem >= DIRT_BALL) && (currItem <= SUP_DIRT_BALL))
  770.                 dWorth = (double) (currItem - (double) DIRT_BALL + 1.0) * 150.0 * ( (double) type / 2.0) * (defensive + 2.0);
  771.  
  772.               // 4.: Shaped charges, wide boys and cutters are deadly but limited:
  773.               if ( (currItem    >= SHAPED_CHARGE) && (currItem <= CUTTER))
  774.                 dWorth *= 1.0 - ( ( (double) type + (defensive * 5.0)) / 20.0);
  775.               // useless, full offensive: * 1.20
  776.               // deadly, full defensive : * 0.50
  777.  
  778.               // 5.: rollers and penetrators are modified by type, as they *are* usefull
  779.               if    (    ((currItem >= SML_ROLLER) && (currItem <= DTH_ROLLER))
  780.                    ||((currItem >= BURROWER) && (currItem <= PENETRATOR)))
  781.                 dWorth *= 1.0 + ((double)type / 10.0) + (defensive  / 2);
  782.  
  783.               // 6.: Tectonis need to be raised!
  784.               if ((currItem >= TREMOR) && (currItem <= TECTONIC))
  785.                 dWorth *= 2.0 + ((double)type / 10.0) + (defensive  / 2);
  786.  
  787.               // finally dWorth must not be greater than the 3/4 of MAX_WEAPON_PROBABILITY
  788.               if (dWorth > (MAX_WEAP_PROBABILITY * 0.75))
  789.                 dWorth    =    MAX_WEAP_PROBABILITY * 0.75;
  790.             }
  791.         }
  792.       else
  793.         {
  794.           // Talking about items
  795.           currItem    =    weapCount - WEAPONS;
  796.           // unfortunately we can only switch here...
  797.           /* Theory:
  798.                   As for armour/amps/shields, offensive bots go for amps and reflector
  799.                   shields, defensive bots go for armour and hard shields.    */
  800.           switch (currItem)
  801.             {
  802.             case ITEM_TELEPORT:
  803.               dWorth = -1.0 * ((defensive - 1.5) * ((double)MAX_WEAP_PROBABILITY / 10.0));
  804.               break;
  805.             case ITEM_SWAPPER:
  806.               dWorth = -1.0 * ((defensive - 1.5) * ((double)MAX_WEAP_PROBABILITY / 7.5));
  807.               break;
  808.             case ITEM_FAN:
  809.               dWorth = 0.0; // useless things!
  810.               break;
  811.             case ITEM_VENGEANCE:
  812.             case ITEM_DYING_WRATH:
  813.             case ITEM_FATAL_FURY:
  814.               dWorth = (defensive + 1.5) * ((double)weapon[(int)item[currItem].vals[0]].damage * (double)item[currItem].vals[1]);
  815.               break;
  816.             case ITEM_ARMOUR:
  817.             case ITEM_PLASTEEL:
  818.               dWorth    =    dBaseProb * ( (double) item[currItem].vals[0] / (double) item[ITEM_PLASTEEL].vals[0]);
  819.               dWorth    *=    defensive;
  820.               break;
  821.             case ITEM_LGT_SHIELD:
  822.             case ITEM_MED_SHIELD:
  823.             case ITEM_HVY_SHIELD:
  824.               dWorth    =    dBaseProb * ( (double) item[currItem].vals[0] / (double) item[ITEM_HVY_SHIELD].vals[0]);
  825.               dWorth    *=    defensive;
  826.               break;
  827.             case ITEM_INTENSITY_AMP:
  828.             case ITEM_VIOLENT_FORCE:
  829.               dWorth    =    dBaseProb * ( (double) item[currItem].vals[0] / (double) item[ITEM_VIOLENT_FORCE].vals[0]);
  830.               dWorth    *=    (-1.0 * defensive);
  831.               break;
  832.             case ITEM_LGT_REPULSOR_SHIELD:
  833.             case ITEM_MED_REPULSOR_SHIELD:
  834.             case ITEM_HVY_REPULSOR_SHIELD:
  835.               dWorth    =    dBaseProb * ( (double) item[currItem].vals[0] / (double) item[ITEM_HVY_REPULSOR_SHIELD].vals[0]);
  836.               dWorth    *=    (-1.0 * defensive);
  837.               break;
  838.             case ITEM_REPAIRKIT:
  839.               dWorth    =    dBaseProb * ( (defensive + 1.0) / 2.0);
  840.               break;
  841.             case ITEM_PARACHUTE:
  842.               dWorth    =    dBaseProb * ( (defensive + 1.0) / 1.5);    // Parachutes *are* popular! :)
  843.               break;
  844.             case ITEM_SLICKP:
  845.               dWorth    =    (int) type * 250;
  846.               break;
  847.             case ITEM_DIMPLEP:
  848.               dWorth    =    (int) type * 500;
  849.               break;
  850.             case ITEM_FUEL:
  851.               dWorth    =    -5000;    // Bots don't need  fuel
  852.               bIsWarhead    =    true;    // Yes, it's a lie. ;-)
  853.             }
  854.           // dWorth must not be greater than the half of MAX_WEAPON_PROBABILITY
  855.           if (dWorth > (MAX_WEAP_PROBABILITY / 2))
  856.             dWorth    =    MAX_WEAP_PROBABILITY / 2;
  857.         }
  858.       iValue    =    fabs (dWorth);
  859.       if (iValue < (MAX_WEAP_PROBABILITY / 10)) iValue = MAX_WEAP_PROBABILITY / 10;
  860.       dWorth += (double) (rand() % iValue); // allow to double (more or less)
  861.  
  862.       if (dWorth    >    MAX_WEAP_PROBABILITY)
  863.         dWorth    =    MAX_WEAP_PROBABILITY;
  864.       if (dWorth    <    (MAX_WEAP_PROBABILITY / 100.0))
  865.         dWorth    =    MAX_WEAP_PROBABILITY / 100.0; // Which is very very little...
  866.  
  867.       if (bIsWarhead)
  868.         _weaponPreference[weapCount] = 0;    // It will not get any slot!
  869.       else
  870.         _weaponPreference[weapCount] = (int) dWorth;
  871.  
  872.       if ((weapCount  < WEAPONS) && (_weaponPreference[weapCount] > iMaxWeapPref))
  873.         iMaxWeapPref = _weaponPreference[weapCount];
  874.       if ((weapCount >= WEAPONS) && (_weaponPreference[weapCount] > iMaxItemPref))
  875.         iMaxItemPref = _weaponPreference[weapCount];
  876.  
  877. #ifdef DEBUG
  878.       if (weapCount < WEAPONS)
  879.         printf( "%23s (weapon): % 5d", weapon[weapCount].name, _weaponPreference[weapCount]);
  880.       else
  881.         printf( "%23s ( item ): % 5d", item[weapCount-WEAPONS].name, _weaponPreference[weapCount]);
  882.       cout << endl;
  883. #endif // DEBUG
  884.     }
  885.  
  886.   // Before we are finished, we need to amplify the preferences (well, maybe...)
  887.   if (iMaxWeapPref < MAX_WEAP_PROBABILITY)
  888.     {
  889.       // Yes, amplification for the weapons needed!
  890.       dWorth = (double)MAX_WEAP_PROBABILITY / (double)iMaxWeapPref;
  891.       for (int weapCount = 1; weapCount < WEAPONS; weapCount++)
  892.         {
  893.           if (_weaponPreference[weapCount] > (MAX_WEAP_PROBABILITY / 100.0))
  894.             {
  895.               _weaponPreference[weapCount] = (int)((double)_weaponPreference[weapCount] * dWorth);
  896. #ifdef DEBUG
  897.               printf( "%23s (weapon) amplified to: % 5d", weapon[weapCount].name, _weaponPreference[weapCount]);
  898.               cout << endl;
  899. #endif // DEBUG
  900.             }
  901.         }
  902.     }
  903.   if (iMaxItemPref < MAX_WEAP_PROBABILITY)
  904.     {
  905.       // Yes, amplification for the items needed!
  906.       dWorth = (double)MAX_WEAP_PROBABILITY / (double)iMaxItemPref;
  907.       for (int weapCount = WEAPONS; weapCount < THINGS; weapCount++)
  908.         {
  909.           if (_weaponPreference[weapCount] > (MAX_WEAP_PROBABILITY / 100.0))
  910.             {
  911.               _weaponPreference[weapCount] = (int)((double)_weaponPreference[weapCount] * dWorth);
  912. #ifdef DEBUG
  913.               printf( "%23s ( item ) amplified to: % 5d", item[weapCount-WEAPONS].name, _weaponPreference[weapCount]);
  914.               cout << endl;
  915. #endif // DEBUG
  916.             }
  917.         }
  918.     }
  919.  
  920. #ifdef DEBUG
  921.   cout << "===================================================" << endl << endl;
  922. #endif // DEBUG
  923. }
  924.  
  925. int PLAYER::selectRandomItem ()
  926. {
  927.   // return (_rouletteWheel[rand () % NUM_ROULETTE_SLOTS]);
  928.   return rand() % THINGS;
  929. }
  930.  
  931. void PLAYER::setName (char *name)
  932. {
  933.   // initalize name
  934.   memset(_name, '\0', NAME_LENGTH);
  935.   strncpy (_name, name, NAME_LENGTH - 1);
  936. }
  937.  
  938. int PLAYER::controlTank ()
  939. {
  940.   if (key[KEY_F1])
  941.     save_bmp ( "scrnshot.bmp", _env->db, NULL);
  942.  
  943.   if (key_shifts & KB_CTRL_FLAG && ctrlUsedUp)
  944.     {
  945.       if (key[KEY_LEFT] || key[KEY_RIGHT] ||
  946.           key[KEY_UP] || key[KEY_DOWN] ||
  947.           key[KEY_PGUP] || key[KEY_PGDN] ||
  948.           key[KEY_A] || key[KEY_D] ||         //additional control
  949.           key[KEY_W] || key[KEY_S] ||
  950.           key[KEY_R] || key[KEY_F])
  951.         ctrlUsedUp = TRUE;
  952.       else
  953.         ctrlUsedUp = FALSE;
  954.     }
  955.   else
  956.     {
  957.       ctrlUsedUp = FALSE;
  958.     }
  959.  
  960.   if (_global->computerPlayersOnly &&
  961.       ((int)_global->skipComputerPlay >= SKIP_HUMANS_DEAD))
  962.     {
  963.       if (_env->stage == STAGE_ENDGAME)
  964.         return (-1);
  965.     }
  966.  
  967.   k = 0;
  968.   #ifdef NEW_GAMELOOP
  969.   if ( keypressed() )
  970.   #else
  971.   if (keypressed () && !fi)
  972.   #endif
  973.     {
  974.       k = readkey ();
  975.  
  976.       if ((_env->stage == STAGE_ENDGAME) &&
  977.           (k >> 8 == KEY_ENTER ||
  978.            k >> 8 == KEY_ESC ||
  979.            k >> 8 == KEY_SPACE))
  980.         return (-1);
  981.       if ( (k >> 8 == KEY_ESC) || (k >> 8 == KEY_P) )
  982.         {
  983.           void clockadd ();
  984.           install_int_ex (clockadd, SECS_TO_TIMER(6000));
  985.           int mm = _env->ingamemenu ();
  986.           install_int_ex(clockadd, BPS_TO_TIMER(_global->frames_per_second));
  987.           _env->make_update (0, 0, _global->screenWidth, _global->screenHeight);
  988.           _env->make_bgupdate (0, 0, _global->screenWidth, _global->screenHeight);
  989.  
  990.           //Main Menu
  991.           if (mm == 1)
  992.           {
  993.               _global->command = GLOBAL_COMMAND_MENU;
  994.               return (-1);
  995.           }
  996.           else if (mm == 2)  //Quit game
  997.           {
  998.               _global->command = GLOBAL_COMMAND_QUIT;
  999.               return (-1);
  1000.           }
  1001.           else if (mm == 3)   // skip AI
  1002.           {
  1003.               return (-2);
  1004.           }
  1005.         }
  1006.       // check for number key being pressed
  1007.       if ( (k >> 8 >= KEY_0) && (k >> 8 <= KEY_9) )
  1008.         {
  1009.           int value = (k >> 8) - KEY_0;
  1010.  
  1011.           // make sure the value is within range
  1012.           if (value < _global->numPlayers)
  1013.             {
  1014.               if ( _global->players[value] )
  1015.                 {
  1016.                   TANK *my_tank = _global->players[value]->tank;
  1017.                   if (my_tank)
  1018.                     {
  1019.                       sprintf(_global->tank_status, "%s: %d + %d -- Team: %s", _global->players[value]->_name, my_tank->l, my_tank->sh, _global->players[value]->Get_Team_Name() );
  1020.                       /* We do this in atanks.cpp, to kill this wretched "No Format Error"
  1021.                       strcat(_global->tank_status, _global->players[value]->Get_Team_Name()); */
  1022.                       _global->tank_status_colour = _global->players[value]->color;
  1023.                       _global->updateMenu = 1;
  1024.                     }
  1025.                   else
  1026.                     _global->tank_status[0] = 0;
  1027.                 }
  1028.             }
  1029.  
  1030.         }    // end of check status keys
  1031.     }
  1032.  
  1033.   if ((int)type == HUMAN_PLAYER || !tank)
  1034.   {
  1035.       return (humanControls ());
  1036.   }
  1037.   #ifdef NETWORK
  1038.   else if ((int) type == NETWORK_CLIENT)
  1039.       return (Execute_Network_Command(TRUE));
  1040.   #endif
  1041.   else if (_env->stage == STAGE_AIM)
  1042.   {
  1043.       return (computerControls ());
  1044.   }
  1045.   return (0);
  1046. }
  1047.  
  1048. int PLAYER::computerSelectPreBuyItem (int aMaxBoostValue)
  1049. {
  1050.   double dMood = 1.0 + defensive + (double) ( (double) (rand() / ( (double) RAND_MAX / 2.0)));
  1051.   // dMood is 0.0 <= x <= 4.0
  1052.   int currItem = 0;
  1053.   /*    Prior buying anything else, a 5 step system takes place:
  1054.           1.: Parachutes (if gravity is on)
  1055.           2.: Minimum weapon probability (aka 5 medium and 3 large missiles
  1056.           3.: Armor/Amps
  1057.           4.: "Tools" to free themselves like Riot Blasts
  1058.           5.: Shields, if enough money is there
  1059.           6.: if all is set, look for dimpled/slick projectiles! */
  1060.  
  1061.   // Step 1:
  1062.  
  1063.   if    ( (type >= RANGEFINDER_PLAYER)
  1064.        && (_env->landSlideType > LANDSLIDE_NONE)
  1065.        && (ni[ITEM_PARACHUTE] < 10))
  1066.     currItem = WEAPONS + ITEM_PARACHUTE;
  1067.  
  1068.   // Step 3:
  1069.   // Even here bots might forget
  1070.   if    (!currItem && (rand() % ( (int) type + 1)))
  1071.     {
  1072.       int iLimit = aMaxBoostValue - getBoostValue(); // > 0 means: Someone has more than we have!
  1073. #ifdef DEBUG
  1074.       printf( "%10s: Boost: %4d, Max: %4d, Limit: %4d\n", getName(), getBoostValue(), aMaxBoostValue, iLimit);
  1075. #endif // DEBUG
  1076.       if ( ((dMood >= 2.75) && (iLimit > 0)) || ((dMood >= 2.0) && (iLimit > getBoostValue())) )
  1077.         {
  1078.           // The player is in a defensive mood
  1079.           // If we have 25% more money than the plasteel cost, buy it, else the armour will do
  1080.           if (money >= (item[ITEM_PLASTEEL].cost * 1.25))
  1081.             currItem = WEAPONS + ITEM_PLASTEEL;
  1082.           else
  1083.             if    ( (money >= (item[ITEM_ARMOUR].cost * 2.0))
  1084.                  && ( (ni[ITEM_ARMOUR] < ni[ITEM_PLASTEEL])
  1085.                       || (dMood >= 3.5)))
  1086.               currItem = WEAPONS + ITEM_ARMOUR;
  1087.         }
  1088.  
  1089.       // Now iBoostItemsBought must be checked:
  1090.       if (currItem && (iBoostItemsBought >= (int)type))
  1091.         currItem = 0; // Sorry, bought enough this round!
  1092.       if (currItem && (iBoostItemsBought < (int)type))
  1093.         iBoostItemsBought++; // Okay, take it!
  1094.     }
  1095.  
  1096.   // 5.: Shields
  1097.   if (!currItem && (rand() % ( (int) type + 1)) && ( (int) type >= RANGEFINDER_PLAYER))
  1098.     {
  1099.       if (dMood <= 1.5)
  1100.         {
  1101.           // offensive type, go through reflectors
  1102.           if ( (ni[ITEM_LGT_REPULSOR_SHIELD] <= (item[ITEM_LGT_REPULSOR_SHIELD].amt * (int) type))
  1103.                && (money >= (item[ITEM_LGT_REPULSOR_SHIELD].cost * 2.0)))
  1104.             currItem    =    WEAPONS + ITEM_LGT_REPULSOR_SHIELD;
  1105.  
  1106.           if ( (ni[ITEM_MED_REPULSOR_SHIELD] <= (item[ITEM_MED_REPULSOR_SHIELD].amt * (int) type))
  1107.                && (money >= (item[ITEM_MED_REPULSOR_SHIELD].cost * 1.75)))
  1108.             currItem    =    WEAPONS + ITEM_MED_REPULSOR_SHIELD;
  1109.  
  1110.           if ( (ni[ITEM_HVY_REPULSOR_SHIELD] <= (item[ITEM_HVY_REPULSOR_SHIELD].amt * (int) type))
  1111.                && (money >= (item[ITEM_HVY_REPULSOR_SHIELD].cost * 1.5)))
  1112.             currItem    =    WEAPONS + ITEM_HVY_REPULSOR_SHIELD;
  1113.         }
  1114.  
  1115.       if (dMood >= 2.5)
  1116.         {
  1117.           // defensive type, go through hard shields
  1118.           if ( (ni[ITEM_LGT_SHIELD] <= (item[ITEM_LGT_SHIELD].amt * (int) type))
  1119.                && (money >= (item[ITEM_LGT_SHIELD].cost * 2.0)))
  1120.             currItem    =    WEAPONS + ITEM_LGT_SHIELD;
  1121.  
  1122.           if ( (ni[ITEM_MED_SHIELD] <= (item[ITEM_MED_SHIELD].amt * (int) type))
  1123.                && (money >= (item[ITEM_MED_SHIELD].cost * 1.75)))
  1124.             currItem    =    WEAPONS + ITEM_MED_SHIELD;
  1125.  
  1126.           if ( (ni[ITEM_HVY_SHIELD] <= (item[ITEM_HVY_SHIELD].amt * (int) type))
  1127.                && (money >= (item[ITEM_HVY_SHIELD].cost * 1.5)))
  1128.             currItem    =    WEAPONS + ITEM_HVY_SHIELD;
  1129.         }
  1130.     }
  1131.  
  1132.   return (currItem);
  1133. }
  1134.  
  1135. int PLAYER::chooseItemToBuy (int aMaxBoostValue)
  1136. {
  1137.   int currItem = computerSelectPreBuyItem (aMaxBoostValue);
  1138.   int oldItem = 0;
  1139.   int itemNum = 0;
  1140.   int cumulative;
  1141.   int nextMod, curramt, newamt;
  1142.   int iTRIES = THINGS; // pow((int)type + 1, 2);
  1143.   int iDesiredItems[iTRIES]; // Deadly bots have large shopping carts. ;)
  1144.   bool bIsSorted = false; // Whether the cart is sorted or not
  1145.   bool bIsPreSelected = currItem?true:false;    // Whether or not the PreBuy steps found something
  1146.   int i = 0;
  1147.  
  1148.   if (currItem)
  1149.   {
  1150.        if ( Buy_Something(currItem) )
  1151.          return currItem;
  1152.   }
  1153.  
  1154.   // init desired items
  1155.   for (i = 0; i < iTRIES; i++)
  1156.      iDesiredItems[i] = 0;
  1157.  
  1158.   // 1.: Fill cart
  1159.   i = 0;
  1160.   while (i < iTRIES)
  1161.     {
  1162.       // while (currItem == oldItem)
  1163.       currItem = (int) fabs (selectRandomItem());
  1164.       oldItem = currItem;
  1165.       if (currItem >= THINGS)
  1166.         currItem    %=    THINGS;    // Put in range
  1167.       // now currItem is 0<= currItem < THINGS
  1168.       if ( (_env->isItemAvailable (currItem)) && (currItem != 0))
  1169.         {
  1170.           iDesiredItems[i]    =    currItem;
  1171.         }
  1172.         i++;
  1173.     }
  1174.  
  1175.   // 2.: sort these items by preferences
  1176.   while (!bIsSorted)
  1177.     {
  1178.       if (bIsPreSelected)
  1179.         i = 2; // The first item shall not be sorted somewhere else!
  1180.       else
  1181.         i = 1;
  1182.       bIsSorted    =    true;
  1183.       while (i < iTRIES)
  1184.         {
  1185.           if (_weaponPreference[iDesiredItems[i-1]] < _weaponPreference[iDesiredItems[i]])
  1186.             {
  1187.               bIsSorted    =    false;
  1188.               currItem    =    iDesiredItems[i];
  1189.               iDesiredItems[i]    =    iDesiredItems[i-1];
  1190.               iDesiredItems[i-1] =    currItem;
  1191.             }
  1192.           i++;
  1193.         }
  1194.     }
  1195.  
  1196.   // 3.: loop through all weapon preferences
  1197.   for (int i = 0; i < iTRIES; i++)
  1198.     {
  1199.       currItem    =    iDesiredItems[i];
  1200.       itemNum = currItem - WEAPONS;
  1201.       //determine the likelyhood of purchasing this selection
  1202.       //less likely the more of the item is owned
  1203.       //if have zero of item, it is a fifty/fifty chance of purchase
  1204.       if (currItem < WEAPONS)
  1205.         {
  1206.           curramt = nm[currItem];
  1207.           newamt = weapon[currItem].amt;
  1208.           cumulative = FALSE;
  1209.         }
  1210.       else
  1211.         {
  1212.           curramt = ni[itemNum];
  1213.           newamt = item[itemNum].amt;
  1214.           if ( (itemNum >= ITEM_INTENSITY_AMP &&
  1215.                 itemNum <= ITEM_VIOLENT_FORCE) ||
  1216.                (itemNum == ITEM_REPAIRKIT) ||
  1217.                (itemNum >= ITEM_ARMOUR &&
  1218.                 itemNum <= ITEM_PLASTEEL))
  1219.             cumulative = TRUE;
  1220.           else
  1221.             cumulative = FALSE;
  1222.         }
  1223.       nextMod = 1;
  1224.       if (!cumulative)
  1225.         nextMod = curramt / newamt;
  1226.       if (nextMod <= 0)
  1227.         nextMod = 1;
  1228.       if (rand () % nextMod)
  1229.         continue;
  1230.  
  1231.       //weapon
  1232.       if (currItem < WEAPONS)
  1233.         {
  1234.           //don't buy if already maxed out
  1235.           if (nm[currItem] >= 99)
  1236.             continue;
  1237.  
  1238.           //purchase the item
  1239.           if (money >= weapon[currItem].cost)
  1240.             {
  1241.               money -= weapon[currItem].cost;
  1242.               nm[currItem] += weapon[currItem].amt;
  1243.               //don't allow more than 99
  1244.               if (nm[currItem] > 99)
  1245.                 nm[currItem] = 99;
  1246.               return currItem;
  1247.             }
  1248.         }
  1249.       else   //item
  1250.         {
  1251.           //don't buy if already maxed out
  1252.           if (ni[itemNum] >= 99)
  1253.             continue;
  1254.           //purchase the item
  1255.           if (money >= item[itemNum].cost)
  1256.             {
  1257.               // Check against iBoostItemsBought
  1258.               if ( (itemNum >= ITEM_INTENSITY_AMP &&
  1259.                     itemNum <= ITEM_VIOLENT_FORCE) ||
  1260.                    (itemNum >= ITEM_ARMOUR &&
  1261.                     itemNum <= ITEM_PLASTEEL))
  1262.                 {
  1263.                   if ((iBoostItemsBought >= (int)type)
  1264.                       ||(getBoostValue() > aMaxBoostValue))
  1265.                     continue; // no chance pal!
  1266.                   else
  1267.                     iBoostItemsBought++; // Okay, take it!
  1268.                 }
  1269.               money -= item[itemNum].cost;
  1270.               ni[itemNum] += item[itemNum].amt;
  1271.               //don't allow more than 999
  1272.               if (ni[itemNum] > 99)
  1273.                 ni[itemNum] = 99;
  1274.               return (currItem);
  1275.             }
  1276.         }
  1277.     }
  1278.   return (-1);
  1279. }
  1280.  
  1281.  
  1282.  
  1283. // An item has been selected, this function merely buys it. It
  1284. // first does checks to make sure the item can be bought.
  1285. // The function returns TRUE if we successfuly bought the item or
  1286. // FALSE if we could not get it for some reason.
  1287. int PLAYER::Buy_Something(int currItem)
  1288. {
  1289.    int itemNum = currItem - WEAPONS;
  1290.    int bought = FALSE;
  1291.  
  1292.     if (currItem < WEAPONS)
  1293.     {
  1294.         if ( (money >= weapon[currItem].cost) && (nm[currItem] < 99) )
  1295.         {
  1296.              money -= weapon[currItem].cost;
  1297.              nm[currItem] += weapon[currItem].amt;
  1298.              //don't allow more than 99
  1299.              if (nm[currItem] > 99)
  1300.                 nm[currItem] = 99;
  1301.              bought = TRUE;
  1302.         }
  1303.         // check tech level
  1304.         if (weapon[currItem].techLevel <= _env->weapontechLevel)
  1305.            bought = TRUE;
  1306.         else
  1307.            bought = FALSE;
  1308.        
  1309.     }    // end of weapons
  1310.  
  1311.     else   // item
  1312.     { 
  1313.       if ( (money > item[itemNum].cost) && (ni[itemNum] < 99) )
  1314.       {
  1315.          money -= item[itemNum].cost;
  1316.          ni[itemNum] += item[itemNum].amt;
  1317.          // don't allow more than 99
  1318.          if (ni[itemNum] > 99)
  1319.             ni[itemNum] = 99;
  1320.          bought = TRUE;
  1321.       }
  1322.       // check technology level
  1323.       if (item[itemNum].techLevel <= _env->itemtechLevel)
  1324.           bought = TRUE;
  1325.       else
  1326.           bought = FALSE;
  1327.     }    
  1328.    return bought;
  1329. }
  1330.  
  1331.  
  1332.  
  1333. char *PLAYER::selectRevengePhrase ()
  1334. {
  1335.   char *line;
  1336.  
  1337.   line = _global->revenge->Get_Random_Line();
  1338.   return line;
  1339. }
  1340.  
  1341. char *PLAYER::selectGloatPhrase ()
  1342. {
  1343.   char *line;
  1344.   line = _global->gloat->Get_Random_Line();
  1345.   return line;
  1346. }
  1347.  
  1348. char *PLAYER::selectSuicidePhrase ()
  1349. {
  1350.   char *line;
  1351.   line = _global->suicide->Get_Random_Line();
  1352.   return line;
  1353. }
  1354.  
  1355.  
  1356. char *PLAYER::selectKamikazePhrase ()
  1357. {
  1358.   char *line;
  1359.   line = _global->kamikaze->Get_Random_Line();
  1360.   return line;
  1361. }
  1362.  
  1363. char *PLAYER::selectRetaliationPhrase ()
  1364. {
  1365.   char *line;
  1366.   char *pText;
  1367.  
  1368.   if (! revenge)
  1369.      return NULL;
  1370.  
  1371.   line = _global->retaliation->Get_Random_Line();
  1372.   pText = (char *) calloc (strlen (revenge->getName()) + 32 + strlen(line), sizeof (char));
  1373.   if (! pText)
  1374.     return NULL;
  1375.  
  1376.   strcpy(pText, line);
  1377.   strcat(pText, revenge->getName());
  1378.   strcat(pText, " !!!");
  1379.  
  1380.   return(pText);
  1381. }
  1382.  
  1383. int PLAYER::traceShellTrajectory (double aStartX, double aStartY, double aVelocityX, double aVelocityY, double &aReachedX, double &aReachedY)
  1384. {
  1385.   TANK *tankPool[10];                        // For repulsion
  1386.   bool bHasHit                = false;    // will be set to true if something is hit
  1387.   bool bIsWallHit            = false;    // For steel walls and wrap floor/ceiling
  1388.   bool bIsWrapped            =    false;    // For special handling in distance calcualtion when a shot goes through the wall
  1389.   int iWallBounce         = 0;             // For wrap, rubber and spring walls
  1390.   int iMaxBounce            =    pow((int)type, 2); // Useless can calculate 1, deadly bots 25 bounces
  1391.   int iTargetDistance    =    ABSDISTANCE(_targetX, _targetY, aStartX, aStartY);
  1392.   int iDistance                =    MAX_OVERSHOOT;
  1393.   int iDirection            =    0;    // records the current direction of the shot
  1394.   int iPriStageTicks    =    0;    // There is no unlimited tracking!
  1395.   int iMaxPriStTicks    =    MAX_POWER * focusRate * 2.5;
  1396.   int iSecStageTicks    =    0;    // rollers and burrowers can't be followed forever!
  1397.   int iMaxSecStTicks    =    MAX_POWER * focusRate * 1.5;
  1398.   double dDrag                =    weapon[tank->cw].drag;
  1399.   double dMass                =    weapon[tank->cw].mass;
  1400.   double dPosX                =    aStartX;
  1401.   double dPosY                =    aStartY;
  1402.   double dVelX                =    aVelocityX;
  1403.   double dVelY                =    aVelocityY;
  1404.   double dMaxVelocity = 0;
  1405.   double dPrevX, dPrevY;    // to check pixels between two points
  1406.   bool bIsSecondStage    =    false;    // Applies to burrowers and rollers
  1407.  
  1408.   dMaxVelocity = _global->dMaxVelocity * (1.20 + (dMass / ((double)MAX_POWER / 10.0)));
  1409.  
  1410.   // Set drag do the correct value:
  1411.   if             (ni[ITEM_DIMPLEP])
  1412.     {
  1413.       dDrag *= item[ITEM_DIMPLEP].vals[0];
  1414.     }
  1415.   else if (ni[ITEM_SLICKP])
  1416.     {
  1417.       dDrag *= item[ITEM_SLICKP].vals[0];
  1418.     }
  1419.  
  1420.   // Fill tankPool
  1421.   for (int i = 0; i < _global->numPlayers; i++)
  1422.     {
  1423.       if ( (_global->players[i]) && (_global->players[i]->tank) )
  1424.         tankPool[i] = _global->players[i]->tank;
  1425.       else
  1426.         tankPool[i] = NULL;
  1427.     }
  1428.  
  1429.   // Initial direction:
  1430.   if (dVelX > 0.0) iDirection = 1;
  1431.   if (dVelX < 0.0) iDirection = -1;
  1432.  
  1433.   // On y va!
  1434.   while (!bHasHit && !bIsWallHit && (iWallBounce < iMaxBounce)
  1435.          && (iPriStageTicks < iMaxPriStTicks) && (iSecStageTicks < iMaxSecStTicks))
  1436.     {
  1437.       /*    ---    First Stage - Applies to all weapons    ---    */
  1438.       if (!bIsSecondStage)
  1439.         {
  1440.           // Apply Repulsor effects
  1441.           for (int i = 0; i < _global->numPlayers; i++)
  1442.             {
  1443.               if (tankPool[i] && (tankPool[i] != tank))
  1444.                 {
  1445.                   double xAccel = 0.0, yAccel = 0.0;
  1446.                   tankPool[i]->repulse (dPosX + dVelX, dPosY + dVelY, &xAccel, &yAccel, tank->cw);
  1447.                   if (tankPool[i] == _target)
  1448.                     {
  1449.                       // Without this, the shield would be nearly useless!
  1450.                       xAccel *= focusRate;
  1451.                       yAccel *= focusRate;
  1452.                       // But the lesser bots wouldn't hit anything anymore if more than _target would be handled like that!
  1453.                     }
  1454.                   dVelX += xAccel;
  1455.                   dVelY += yAccel;
  1456.                 }
  1457.             }
  1458.  
  1459.           dPrevX = dPosX;
  1460.           dPrevY = dPosY;
  1461.           //motion - wind affected
  1462.           if (((dPosX + dVelX) < 1) || ((dPosX + dVelX) > (_global->screenWidth - 2)))
  1463.             {
  1464.               if    (    (((dPosX + dVelX) < 1) && checkPixelsBetweenTwoPoints (_global, _env, &dPrevX, &dPrevY, 1, dPosY))
  1465.                    ||(    ((dPosX + dVelX) > (_global->screenWidth - 2))
  1466.                        && checkPixelsBetweenTwoPoints (_global, _env, &dPrevX, &dPrevY, (_global->screenWidth - 2), dPosY))    )
  1467.                 {
  1468.                   dPosX = dPrevX;
  1469.                   dPosY = dPrevY;
  1470.                   bHasHit = true;
  1471.                 }
  1472.               else
  1473.                 {
  1474.                   switch (_env->current_wallType)
  1475.                     {
  1476.                     case WALL_RUBBER:
  1477.                       dVelX = -dVelX;    //bounce on the border
  1478.                       iWallBounce++;
  1479.                       break;
  1480.                     case WALL_SPRING:
  1481.                       dVelX = -dVelX * SPRING_CHANGE;
  1482.                       iWallBounce++;
  1483.                       break;
  1484.                     case WALL_WRAP:
  1485.                       if (dVelX < 0)
  1486.                         dPosX = _global->screenWidth - 2; // -1 is the wall itself
  1487.                       else
  1488.                         dPosX = 1;
  1489.                       iWallBounce++;
  1490.                       bIsWrapped = true;
  1491.                       break;
  1492.                     default:
  1493.                       dPosX += dVelX;
  1494.                       if (dPosX < 1)
  1495.                         dPosX = 1;
  1496.                       if (dPosX > (_global->screenWidth - 2))
  1497.                         dPosX= _global->screenWidth - 2;
  1498.                       dVelX = 0; // already applied!
  1499.                       bIsWallHit = true;
  1500.                     }
  1501.                 }
  1502.             }
  1503.  
  1504.           // hit floor or boxed top
  1505.           if (    (     (dPosY + dVelY) >= (_global->screenHeight - 1))
  1506.                ||(((dPosY + dVelY) <= MENUHEIGHT) && _global->bIsBoxed))
  1507.             {
  1508.               if    (    (    _global->bIsBoxed && ((dPosY + dVelY) <= MENUHEIGHT)
  1509.                      && checkPixelsBetweenTwoPoints (_global, _env, &dPrevX, &dPrevY, dPosX, MENUHEIGHT + 1))
  1510.                    ||(    ((dPosY + dVelY) > (_global->screenHeight - 2))
  1511.                        && checkPixelsBetweenTwoPoints (_global, _env, &dPrevX, &dPrevY, dPosX, (_global->screenHeight - 2)))    )
  1512.                 {
  1513.                   dPosX = dPrevX;
  1514.                   dPosY = dPrevY;
  1515.                   bHasHit = true;
  1516.                 }
  1517.               else
  1518.                 {
  1519.                   switch (_env->current_wallType)
  1520.                     {
  1521.                     case WALL_RUBBER:
  1522.                       dVelY = -dVelY * 0.5;
  1523.                       dVelX *= 0.95;
  1524.                       iWallBounce++;
  1525.                       break;
  1526.                     case WALL_SPRING:
  1527.                       dVelY = -dVelY * SPRING_CHANGE;
  1528.                       dVelX *= 1.05;
  1529.                       iWallBounce++;
  1530.                       break;
  1531.                     default:
  1532.                       // steel or wrap floor (ceiling)
  1533.                       dPosY += dVelY;
  1534.                       if (dPosY >= (_global->screenHeight - 1))
  1535.                         dPosY = _global->screenHeight - 2; // -1 would be the floor itself!
  1536.                       else
  1537.                         dPosY= MENUHEIGHT + 1; // +1 or it would be the wall itself
  1538.                       dVelY = 0; // already applied!
  1539.                       bIsWallHit = true;
  1540.                     }
  1541.                   if (!bIsWallHit &&((fabs(dVelX) + fabs(dVelY)) < 0.8))
  1542.                     bHasHit = true;
  1543.                 }
  1544.             }
  1545.  
  1546.           // velocity check:
  1547.           double dActVelocity = ABSDISTANCE(dVelX, dVelY, 0, 0); // a┬▓+b┬▓=c┬▓ ... says Pythagoras :)
  1548.           if ((dActVelocity > dMaxVelocity) && !bHasHit && !bIsWallHit)
  1549.             {
  1550.               // Velocity is applied first and modified by errorMultiplier
  1551.               dPosY += dVelY * errorMultiplier;
  1552.               dPosX += dVelX * errorMultiplier;
  1553.               dVelX = 0.0;
  1554.               dVelY = 0.0;
  1555.               if ((dPosY <= MENUHEIGHT) && _global->bIsBoxed)
  1556.                 dPosY = MENUHEIGHT + 1;
  1557.               if (dPosY > (_global->screenHeight - 2))
  1558.                 dPosY = _global->screenHeight - 2;
  1559.               if (dPosX < 1)
  1560.                 {
  1561.                   if (_env->current_wallType == WALL_WRAP)
  1562.                     dPosX += _global->screenWidth - 2;
  1563.                   else
  1564.                     dPosX = 1;
  1565.                 }
  1566.               if (dPosX > (_global->screenWidth - 2))
  1567.                 {
  1568.                   if (_env->current_wallType == WALL_WRAP)
  1569.                     dPosX -= _global->screenWidth - 2;
  1570.                   else
  1571.                     dPosX = _global->screenWidth - 2;
  1572.                 }
  1573.               bHasHit = true;
  1574.             }
  1575.  
  1576.           // Now check for hits
  1577.           if (!bHasHit && !bIsWallHit)
  1578.             {
  1579.               // Save Positions:
  1580.               dPrevX = dPosX;
  1581.               dPrevY = dPosY;
  1582.               // Apply movement:
  1583.               dPosX += dVelX;
  1584.               dPosY += dVelY;
  1585.               dVelX += (double)(_env->wind - dVelX) / dMass * dDrag * _env->viscosity;
  1586.               dVelY += _env->gravity * (100.0 / _global->frames_per_second);
  1587.               // Barrier test:
  1588.               if ( (dVelY <= -1.0) && (dPosY <= (_global->screenHeight * -25.0)))
  1589.                 dVelY *= -1.0;
  1590.  
  1591.               // See, if we have hit something
  1592.               //    ---    Environment-Test -- flight?
  1593.               if (checkPixelsBetweenTwoPoints (_global, _env, &dPrevX, &dPrevY, dPosX, dPosY))
  1594.                 {
  1595.                   dPosX = dPrevX;
  1596.                   dPosY = dPrevY;
  1597.                   bHasHit = true;
  1598.                 }
  1599.             }
  1600.           // Now that all modifications are applied, record direction:
  1601.           if (dVelX > 0.0) iDirection = 1;
  1602.           if (dVelX < 0.0) iDirection = -1;
  1603.         }
  1604.  
  1605.       /*    ---    Second Stage - Applies to burrowers and rollers    ---    */
  1606.       if (bIsSecondStage)
  1607.         {
  1608.           iSecStageTicks++;
  1609.           // The weapon is on the ground and rolling or penetrating the ground:
  1610.           if ((tank->cw >= SML_ROLLER) && (tank->cw <= DTH_ROLLER))
  1611.             {
  1612.               // check whether we have hit anything
  1613.               if (    (dPosX < 1)
  1614.                    ||(dPosX > (_global->screenWidth - 2))
  1615.                    ||(dPosY > (_global->screenHeight - 2))
  1616.                    ||(getpixel(_env->terrain, (int)dPosX, (int)dPosY) != PINK))
  1617.                 bHasHit = true;
  1618.               else
  1619.                 {
  1620.                   // roll roller
  1621.                   float fSurfY = (float)_env->surface[(int)dPosX] - 1;
  1622.                   if ((fSurfY > dPosY) && (dPosY < (_global->screenHeight - 5)))
  1623.                     {
  1624.                       if (fSurfY < (dPosY + 5))
  1625.                         dPosY = fSurfY;
  1626.                       else
  1627.                         dPosY+=5;
  1628.                     }
  1629.                   else
  1630.                     {
  1631.                       if (dVelX > 0.0)
  1632.                         dPosX++;
  1633.                       else
  1634.                         dPosX--;
  1635.                       if    (dPosY >= MENUHEIGHT)
  1636.                         {
  1637.                           if (fSurfY > dPosY)
  1638.                             dPosY++;
  1639.                           else if (fSurfY >= (dPosY - 2))
  1640.                             dPosY = fSurfY - 1;
  1641.                         }
  1642.                     }
  1643.                   if (dPosY > (_global->screenHeight - 5) && !bHasHit)
  1644.                     dPosY = (_global->screenHeight - 5);
  1645.                 }
  1646.             }
  1647.  
  1648.           if ((tank->cw >= BURROWER) && (tank->cw <= PENETRATOR))
  1649.             {
  1650.               // Apply Repulsor effects, but not fully, as it is a burrower!
  1651.               for (int i = 0; i < _global->numPlayers; i++)
  1652.                 {
  1653.                   if (tankPool[i] && (tankPool[i] != tank))
  1654.                     {
  1655.                       double xAccel = 0.0, yAccel = 0.0;
  1656.                       tankPool[i]->repulse (dPosX + dVelX, dPosY + dVelY, &xAccel, &yAccel, tank->cw);
  1657.                       if (tankPool[i] == _target)
  1658.                         {
  1659.                           // Without this, the shield would be nearly useless!
  1660.                           xAccel *= focusRate;
  1661.                           yAccel *= focusRate;
  1662.                           // But the lesser bots wouldn't hit anything anymore if more than _target would be handled like that!
  1663.                         }
  1664.                       dVelX += xAccel * 0.1;
  1665.                       dVelY += yAccel * 0.1;
  1666.                     }
  1667.                 }
  1668.               if (((dPosX + dVelX) < 1) || ((dPosX + dVelX) > (_global->screenWidth-1)))
  1669.                 {
  1670.                   // if the wall is rubber, then bouce
  1671.                   if ( _env->current_wallType == WALL_RUBBER )
  1672.                     dVelX = -dVelX;    //bounce on the border
  1673.                   // bounce with more force
  1674.                   else if ( _env->current_wallType == WALL_SPRING )
  1675.                     dVelX = -dVelX * SPRING_CHANGE;
  1676.                   // wall is steel, detonate
  1677.                   else if ( _env->current_wallType == WALL_STEEL )
  1678.                     bHasHit = true;
  1679.                   // wrap around to other side of the screen
  1680.                   else if ( _env->current_wallType == WALL_WRAP )
  1681.                     {
  1682.                       if (dVelX < 1)
  1683.                         dPosX = _global->screenWidth - 2;
  1684.                       else
  1685.                         dPosX = 1;
  1686.                     }
  1687.                 }
  1688.               if ((dPosY + dVelY) >= (_global->screenHeight - 1))
  1689.                 {
  1690.                   dVelY = -dVelY * 0.5;
  1691.                   dVelX *= 0.95;
  1692.                 }
  1693.               else if ((dPosY + dVelY) < MENUHEIGHT)   //hit screen top
  1694.                 {
  1695.                   dVelY = -dVelY *0.5;
  1696.                   dVelX *= 0.95;
  1697.                 }
  1698.               dPosY += dVelY;
  1699.               dPosX += dVelX;
  1700.               dVelY -= _env->gravity * 0.05 * (100.0 / _global->frames_per_second);
  1701.               if (getpixel (_env->terrain, (int)dPosX, (int)dPosY) == PINK)
  1702.                 bHasHit = true;
  1703.             }
  1704.         }
  1705.       else
  1706.         iPriStageTicks++;
  1707.  
  1708. #ifdef DEBUG_AIM_SHOW
  1709.       if (_global->bASD)
  1710.         {
  1711.           // Plot trajectories for debugging purposes
  1712.           circlefill (screen, (int)dPosX, (int)dPosY, 2, color);
  1713.         }
  1714. #endif
  1715.  
  1716.       /*    ---    Third Stage - Applies to burrowers and rollers    ---    */
  1717.       if    (    (!bIsSecondStage && (bHasHit || bIsWallHit))
  1718.            &&(    ((tank->cw >= SML_ROLLER) && (tank->cw <= DTH_ROLLER))
  1719.                ||((tank->cw >= BURROWER) && (tank->cw <= PENETRATOR))))
  1720.         {
  1721.           // Only Rollers and Penetrators can enter the second stage!
  1722.           bIsSecondStage = true;
  1723.           bHasHit = false;
  1724.           bIsWallHit = false;
  1725.           if ((tank->cw >= SML_ROLLER) && (tank->cw <= DTH_ROLLER))
  1726.             {
  1727.               dPosY -= 5;
  1728.               if (dPosX < 1)
  1729.                 dPosX = 1;
  1730.               if (dPosX > (_global->screenWidth - 2))
  1731.                 dPosX = (_global->screenWidth - 2);
  1732.  
  1733.               if    (    (dPosY >= _env->surface[(int)dPosX])                // y is surface or below
  1734.                    &&(dPosY <= _env->surface[(int)dPosX] + 2) )  // but not burried more than 2 px
  1735.                 dPosY = _env->surface[(int)dPosX] - 1;
  1736.  
  1737.  
  1738.               dVelX = 0.0;
  1739.               if (getpixel (_env->terrain, (int)dPosX + 1, (int)dPosY + 1) == PINK)
  1740.                 dVelX = 1.0;
  1741.               if (getpixel (_env->terrain, (int)dPosX - 1, (int)dPosY + 1) == PINK)
  1742.                 dVelX = -1.0;
  1743.               if (dVelX == 0.0)
  1744.                 // if both sides are free (should be impossible, but might be a 1-pixel-peak) the shooting direction decides
  1745.                 dVelX = (double)iDirection;
  1746.               dVelY = 0.0;
  1747.             }
  1748.           if ((tank->cw == BURROWER) || (tank->cw == PENETRATOR))
  1749.             {
  1750.               dVelX *= 0.1;
  1751.               dVelY *= 0.1;
  1752.             }
  1753.           if ((dPosY <= MENUHEIGHT) && !_global->bIsBoxed)
  1754.             dPosY = MENUHEIGHT + 1;
  1755.           if ((dPosY <= MENUHEIGHT) && _global->bIsBoxed)
  1756.             bIsWallHit = true; // Sorry, no more ceiling-drop!
  1757.         }
  1758.  
  1759.       /*    ---    Fourth Stage - Tank hit test    ---    */
  1760.       if (!bIsWallHit)
  1761.         {
  1762.           for (int i = 0; i < _global->numPlayers; i++)
  1763.             {
  1764.               if (tankPool[i])
  1765.                 {
  1766.                   if    (     (dPosX > (tankPool[i]->x - TANKWIDTH))
  1767.                         &&(dPosX < (tankPool[i]->x + TANKWIDTH))
  1768.                         &&(dPosY > (tankPool[i]->y))
  1769.                         &&(dPosY < (tankPool[i]->y + TANKHEIGHT))
  1770.                         &&(tankPool[i]->l > 0))
  1771.                     bHasHit = true;
  1772.                 }
  1773.             }
  1774.         }
  1775.  
  1776.       // if something is hit, be sure the values are in range and movement stopped!
  1777.       if (bHasHit || bIsWallHit)
  1778.         {
  1779.           dVelX = 0.0;
  1780.           dVelY = 0.0;
  1781.           if (dPosY <= MENUHEIGHT)
  1782.             dPosY = MENUHEIGHT + 1;
  1783.           if (dPosY > (_global->screenHeight - 2))
  1784.             dPosY = _global->screenHeight - 2;
  1785.           if (dPosX < 1)
  1786.             dPosX = 1;
  1787.           if (dPosX > (_global->screenWidth - 2))
  1788.             dPosX = _global->screenWidth - 2;
  1789.         }
  1790.     }
  1791.  
  1792. #ifdef DEBUG_AIM_SHOW
  1793.   if (_global->bASD)
  1794.     {
  1795.       // Targetting circle for debugging purposes
  1796.       circlefill (screen, (int)dPosX, (int)dPosY, 10, BLACK);
  1797.       circlefill (screen, (int)_targetX, (int)_targetY, 20, color);
  1798.       LINUX_REST;
  1799.     }
  1800. #endif
  1801. #ifdef DEBUG_AIM
  1802.   if (bIsWallHit)
  1803.     cout << "WALL ";
  1804.   if (bHasHit)
  1805.     cout << "HIT ";
  1806.   if (iWallBounce >= iMaxBounce)
  1807.     cout << "BOUNCE (" << iMaxBounce << ") ";
  1808.   if (iPriStageTicks >= iMaxPriStTicks)
  1809.     cout << "TICKS1 (" << iPriStageTicks << ") ";
  1810.   if (iSecStageTicks >= iMaxSecStTicks)
  1811.     cout << "TICKS2 (" << iSecStageTicks << ") ";
  1812. #endif // DEBUG_AIM
  1813.  
  1814.   // Now see where we are and calculate the distance difference between (char *)"hit" and "has to be hit"
  1815.   if (!bIsWallHit && (iWallBounce < iMaxBounce))
  1816.     {
  1817.       iDistance = ABSDISTANCE(_targetX, _targetY, dPosX, dPosY);
  1818.  
  1819. #ifdef DEBUG_AIM
  1820.       cout << "(" << iDistance << " <- " << (int)dPosX << " x " << (int)dPosY << ") ";
  1821. #endif // DEBUG_AIM
  1822.  
  1823.       // Special handling for wrapped shots:
  1824.       if (bIsWrapped)
  1825.         {
  1826.           int iHalfX = _global->halfWidth;
  1827.  
  1828.           // Only shots where dPosX and _targetX are on opposite sides are relevant
  1829.           if    (    (((int)dPosX < iHalfX) && (_targetX >=iHalfX))
  1830.                ||(((int)dPosX >=iHalfX) && (_targetX < iHalfX))    )
  1831.             {
  1832.               if (((int)dPosX < iHalfX) && (_targetX >=iHalfX))
  1833.                 iDistance = ABSDISTANCE(dPosX + iHalfX, dPosY, _targetX, _targetY);
  1834.               else
  1835.                 iDistance = ABSDISTANCE(_targetX + iHalfX, _targetY, dPosX, dPosY);
  1836.             }
  1837.           else
  1838.             bIsWrapped = false; // not relevant
  1839.         }
  1840.  
  1841.       bool bIsWrongDir = false;
  1842.       if    (!bIsWrapped
  1843.           &&(    ((dPosX < aStartX) && (_targetX > aStartX))
  1844.               ||((dPosX > aStartX) && (_targetX < aStartX))    )    )
  1845.         bIsWrongDir = true; // wrong side!
  1846.  
  1847.       if (!iDirection)
  1848.         {
  1849.           // If we have no direction (very unlikely!) we can only guess by comparing x-coordinates:
  1850.           if    (    (iTargetDistance > ABSDISTANCE(aStartX, aStartY, dPosX, dPosY))
  1851.                ||(bIsWrongDir && (_env->current_wallType != WALL_STEEL))    )
  1852.             // Too short or wrong direction, negate iDistance!
  1853.             iDistance *= -1;
  1854.         }
  1855.       else
  1856.         {
  1857.           // with the help of the direction, we can exactly see, whether the shot is too short or too far
  1858.           if    (!bIsWrapped    // This doesn't apply for wrapped shots!
  1859.               &&(    (    (iDirection < 0.0)         // shoot to the left...
  1860.                     &&(dPosX > _targetX))        // ...and the shot hits right of target
  1861.                   ||(    (iDirection > 0.0)         // shoot to the right...
  1862.                       &&(dPosX < _targetX))    )// ...and the shot hits left of target
  1863.              )    iDistance *= -1; // too short!
  1864.         }
  1865.  
  1866.       if (bIsWrongDir && (_env->current_wallType == WALL_STEEL))
  1867.         iDistance = MAX_OVERSHOOT; // wrong side and target unreachable!
  1868.     }
  1869.   else
  1870.     iDistance = MAX_OVERSHOOT;
  1871.  
  1872.   // Give the X- and Y-position back:
  1873.   aReachedX = dPosX;
  1874.   aReachedY    =    dPosY;
  1875.  
  1876.   return(iDistance);
  1877. }
  1878.  
  1879. int PLAYER::rangeFind (int &aAngle, int &aPower)
  1880. {
  1881. #ifdef DEBUG_AIM
  1882. printf("This is range find function\n");
  1883. #endif
  1884.   int iOvershoot            =    MAX_OVERSHOOT;
  1885.   int iActOvershoot        = MAX_OVERSHOOT;
  1886.   int iSpreadOvershoot= MAX_OVERSHOOT;
  1887.   int iBestOvershoot    =    MAX_OVERSHOOT + 1; // So there will be at least one recorded!
  1888.   int iLastOvershoot    =    MAX_OVERSHOOT;
  1889.   int iAngle                    =    aAngle;
  1890.   int iAngleMod                =    0;
  1891.   double dAngleMul        =    0.0;
  1892.   double dPowerMul        =    0.0;
  1893.   int iCalcAngle            = aAngle;
  1894.   int iLastAngle             = aAngle;
  1895.   int iBestAngle            = aAngle;
  1896.   int iAngleBarrier        =    aAngle;    // This will be the flattest angle possible, thus normalized aAngle + savety
  1897.   int iIdealAngle            =    135;    // 135 translates to 45┬░, but will be raised if the barrier is higher
  1898.   bool bIsRaisingMode = false;
  1899.   int iPower                    = aPower;
  1900.   int iPowerMod                =    0;
  1901.   int iPowerModFixed    =    0;
  1902.   int iLastPower            = aPower;
  1903.   int iBestPower            =    aPower;
  1904.   int iAttempts                =    0;
  1905.   int iSelfHitMult        =    0;
  1906.   int iSpread                    =    weapon[tank->cw].spread;
  1907.   int iSpreadCount        =    0;
  1908.   int iSpreadOdd[]        = {0,-1 * SPREAD, SPREAD, -2 * SPREAD, 2 * SPREAD};
  1909.   int iSpreadEven[]        =    {-0.5 * SPREAD, 0.5 * SPREAD, -1.5 * SPREAD, 1,5 * SPREAD};
  1910.   int iMaxSpread            =    (int)type; // more intelligent bots can calculate more shots bearing spreads!
  1911.   double dStartX, dStartY, dVelocityX, dVelocityY;    // No initialization needed, they are calculated anyway.
  1912.   double dHitX, dHitY;
  1913.  
  1914. #ifdef DEBUG_AIM
  1915. printf("Starting RangeFind\n");
  1916. #endif
  1917.  
  1918.   // We need to be sure that iSpread is at least 1:
  1919.   if (iSpread < 1)    iSpread    =    1;
  1920.  
  1921.   // iMaxSpread needs to be adapted:
  1922.   switch ((int)type)
  1923.     {
  1924.     case USELESS_PLAYER:
  1925.     case GUESSER_PLAYER:
  1926.       if (iSpread % 2)    iMaxSpread    =    1;
  1927.       else                            iMaxSpread    =    2;
  1928.       break;
  1929.     case RANGEFINDER_PLAYER:
  1930.       if (iSpread % 2)    iMaxSpread    =    3;
  1931.       else                            iMaxSpread    =    4;
  1932.       break;
  1933.     case TARGETTER_PLAYER:
  1934.       if (iSpread % 2)    iMaxSpread    =    5;
  1935.       else                            iMaxSpread    =    4;
  1936.       break;
  1937.     default:
  1938.       if (iSpread % 2)    iMaxSpread    =    9; // I know there is nothing like that...
  1939.       else                            iMaxSpread    =    8; // ... but maybe in the future?
  1940.       break;
  1941.     }
  1942.   if (iSpread > iMaxSpread)    iSpread = iMaxSpread;    // Now iSpread is limited to iMaxSpread...
  1943.   // ...adapted to a test like (iSpreadCount < iSpread) but not larger then weapon[x].spread
  1944.  
  1945.   /* Outline:
  1946.    * RangeFind tries to adapt the given angle and power to minimize overshoot.
  1947.    * to do so we have some facts to keep in mind:
  1948.    * - ideal angle is 45┬░ in both directions, giving most distance.
  1949.    * - This angle translates into 135┬░ for a shoot to the left, and 225┬░ to the right
  1950.    * - aAngle is considered to be the flattest shot possible, as it has been manipulated
  1951.    *   by calculateAttackValues(), meaning that lowering it will lead to an obstacle
  1952.    *   hit.
  1953.    * - as a safety measure, when lowering an angle, a savety range of (int)type above
  1954.    *   aAngle is applied
  1955.    *
  1956.    * The calculation is done in four steps:
  1957.    * 1.: Calculate the real angle, as it is, at least for spreads, different with every shot shell
  1958.    * 2.: Calculate the overshoot for each shot (or the one if no spread is given)
  1959.    * 3.: Record angle, power and overshoot if the overshoot is smaller than the best recorderd so far
  1960.    * 4.: Alter angle, or power if the angle can't be manipulated anymore, and start over, if the best
  1961.    *     overshoot is != 0;
  1962.    *
  1963.    * This fourth step is divided into the following parts:
  1964.    * a) short shots (overshoot < 0) - raise power
  1965.    *    i.: angle lowering mode     - adjust the angle towards 45┬░ until it (or aAngle) is reached
  1966.    *    ii.:power raising mode      - after the first positive overshoot is reached, the angle won't be changed any more
  1967.    * b) long shots (overshoot > 0)  - lower power
  1968.    *    i.: angle raising mode      - adjust the angle towards 89┬░ until it is reached
  1969.    *    ii.:power lowering mode     - once the angle can't be changed any more, only power is changed
  1970.    * angle raising/lowering modes are entered, and not left until iLastAngle == iAngle
  1971.    *
  1972.    * Note: Before you think all those step 4 calculations are wrong, they aren't, I am using a math trick to reduce
  1973.    *       the amount of calculations:
  1974.    *       180 + (180 - Angle) flips an angle between left and right. So the angle will be normalized to the right,
  1975.    *       (aka 90┬░ - 180┬░) and all calculations done there. If it was flipped, it will be flipped back for
  1976.    *       traceShellTrajectory. This saves alot of (char *)"if(angle > 180)" lines!
  1977.    */
  1978.  
  1979.   // Preperations:
  1980.   if (aAngle > 180)    iAngleBarrier = 180 + (180 - iAngleBarrier); // flip
  1981.  
  1982.   // if we have some (char *)"space", add a savety distance:
  1983.   if (iAngleBarrier < 170)    iAngleBarrier += (int)type - 1;
  1984.  
  1985.   // We need some savety distance for spreads:
  1986.   if (weapon[tank->cw].spread > 1)
  1987.     {
  1988.       // normal spread size:
  1989.       iAngleBarrier += SPREAD * ((int)weapon[tank->cw].spread / 2);
  1990.       if (!(weapon[tank->cw].spread % 2))
  1991.         // Even spreads have half of SPREAD more than neccessary, so adapt:
  1992.         iAngleBarrier -= SPREAD / 2;
  1993.       // but be sure the barrier isn't too large:
  1994.       if (iAngleBarrier > 179) iAngleBarrier = 179;
  1995.     }
  1996.  
  1997.   // Adapt the ideal angle if we are facing an obstacel:
  1998.   if (iAngleBarrier > 135)    iIdealAngle = iAngleBarrier;
  1999.  
  2000. #ifdef DEBUG_AIM
  2001.   printf("New Target Try using....\n");
  2002.   if (tank->cw < WEAPONS)
  2003.     printf("%s\n", weapon[tank->cw].name);
  2004.   else
  2005.     printf("%s\n", item[tank->cw - WEAPONS].name);
  2006. #endif // DEBUG_AIM
  2007.  
  2008.   // Okay, here we go:
  2009.   #ifdef DEBUG_AIM
  2010.   printf("Best: %d .. Attempts %d  ..  RFattempts %d\n", iBestOvershoot, iAttempts, rangeFindAttempts);
  2011.   #endif
  2012.   while (iBestOvershoot && (iAttempts < rangeFindAttempts))
  2013.     {
  2014.       iAttempts++;
  2015.  
  2016. #ifdef DEBUG_AIM
  2017.       printf("--> %d. rangeFind:\n", iAttempts);
  2018.       printf( (char *)" Angle: %3d Power: %4d\n", (iAngle - 90), iPower);
  2019. #endif // DEBUG_AIM
  2020.  
  2021.       /* --- Step 1: Calculate angle for spreads: --- */
  2022.       iSpreadCount    = 0;
  2023.       iOvershoot        =    MAX_OVERSHOOT;
  2024.       iSpreadOvershoot    =    0;
  2025.       iSelfHitMult    =    0;
  2026.       iLastOvershoot= iOvershoot;
  2027.  
  2028.       while (iSpreadCount < iSpread)
  2029.         {
  2030.           #ifdef DEBUG_AIM
  2031.           printf("Inside the wee while loop.\n");
  2032.           #endif
  2033.           iCalcAngle        = iAngle;
  2034.  
  2035.           // Two cases: Even and odd spreads.
  2036.           if (iSpread > 1)
  2037.             {
  2038.               // odd spreads:
  2039.               if (weapon[tank->cw].spread % 2)
  2040.                 iCalcAngle += iSpreadOdd[iSpreadCount];
  2041.               // even spreads:
  2042.               else
  2043.                 iCalcAngle += iSpreadEven[iSpreadCount];
  2044.             }
  2045.           dVelocityX = _global->slope[iCalcAngle][0] * (double)iPower * (100.0 / _global->frames_per_second) / 100.0;
  2046.           dVelocityY = _global->slope[iCalcAngle][1] * (double)iPower * (100.0 / _global->frames_per_second) / 100.0;
  2047.  
  2048.           dStartX = tank->x + (_global->slope[iCalcAngle][0] * GUNLENGTH);
  2049.           dStartY    = tank->y + (_global->slope[iCalcAngle][1] * GUNLENGTH);
  2050.  
  2051.           /* --- Step 2: Calculate overshoots: --- */
  2052.           #ifdef DEBUG_AIM
  2053.           printf("Trace the shell\n");
  2054.           #endif
  2055.           iActOvershoot = traceShellTrajectory(dStartX, dStartY, dVelocityX, dVelocityY, dHitX, dHitY);
  2056.           #ifdef DEBUG_AIM
  2057.           printf("Back from shell tracing.\n");
  2058.           #endif
  2059.           if (abs(iActOvershoot) < _global->screenWidth)
  2060.             iSelfHitMult += adjustOvershoot(iActOvershoot, dHitX, dHitY);
  2061.           // Otherwise it's a wall hit and not relevant!
  2062.  
  2063.           #ifdef DEBUG_AIM
  2064.           printf("Passed hit multi\n");
  2065.           #endif
  2066.           // With this method only the best hit of spreads is counted.
  2067.           if (abs(iActOvershoot) < abs(iOvershoot))
  2068.             iOvershoot = iActOvershoot;
  2069.           
  2070.           #ifdef DEBUG_AIM
  2071.           printf("Passed overshoot. Spread: %d\n", iSpread);
  2072.           #endif
  2073.           // iSpreadOvershoot calculates the average absolute Overshoot
  2074.           if (iActOvershoot)
  2075.             iSpreadOvershoot += (int)(fabs((double)iActOvershoot) / (double)iSpread);
  2076.  
  2077.           iSpreadCount++;
  2078.  
  2079. #ifdef DEBUG_AIM
  2080.           if (iSpreadCount > 1)
  2081.             {
  2082.               if (iSpreadCount == 2)
  2083.                 printf( (char *)" (%3d ", iCalcAngle - 90);
  2084.               else
  2085.                 printf( (char *)",%3d ", iCalcAngle - 90);
  2086.               if (iSpread == iSpreadCount)
  2087.                 printf( (char *)"\b)");
  2088.             }
  2089. #endif // DEBUG_AIM
  2090.         }
  2091.  
  2092.      #ifdef DEBUG_AIM
  2093.      printf("Moving right along\n");
  2094.      #endif
  2095.  
  2096.       if (iOvershoot < 0)
  2097.         iSpreadOvershoot *= -1;
  2098.  
  2099.       if (iSelfHitMult > 0)
  2100.         {
  2101.           // We hit ourselves, so the larger Overshoot will be multiplied and taken!
  2102.           if (abs(iSpreadOvershoot) > abs(iOvershoot))
  2103.             iOvershoot = iSpreadOvershoot;
  2104.           if (abs(iOvershoot) < _global->screenWidth)
  2105.             iOvershoot = _global->screenWidth;
  2106.           if (abs(iOvershoot) < MAX_OVERSHOOT)
  2107.             iOvershoot*= iSelfHitMult;
  2108.         }
  2109.       else
  2110.         {
  2111.           // everything okay, just take the smaller one!
  2112.           if (abs(iSpreadOvershoot) < abs(iOvershoot))
  2113.             iOvershoot = (int)(((double)iSpreadOvershoot + ((double)iOvershoot * focusRate)) / 2);
  2114.           else
  2115.             iOvershoot = (int)(((double)iOvershoot + ((double)iSpreadOvershoot * focusRate)) / 2);
  2116.         }
  2117.  
  2118.       /* --- Step 3.: Record angle, power and overshoot if the overshoot is smaller than the best recorderd so far: --- */
  2119.       if (abs(iOvershoot) < abs(iBestOvershoot))
  2120.         {
  2121.           iBestOvershoot    = iOvershoot;
  2122.           iBestAngle            =    iAngle;
  2123.           iBestPower            =    iPower;
  2124.         }
  2125.  
  2126. #ifdef DEBUG_AIM
  2127.       printf( (char *)" Overshoot: %6d (%4d x %4d) SH: %1d\n", iOvershoot, (int)dHitX, (int)dHitY, iSelfHitMult);
  2128. #endif // DEBUG_AIM
  2129.  
  2130.       /* --- Step 4.: Alter angle, or power if the angle can't be manipulated anymore, and start over: --- */
  2131.       if (iOvershoot)
  2132.         {
  2133.           // Preperation: flip iAngle if neccessary
  2134.           if (iAngle > 180) iAngle = 180 + (180 - iAngle); // flip
  2135.  
  2136.           // Preperation: Decide over angle and power modification depending on overshoot
  2137.           if (abs(iOvershoot) < _global->screenWidth)
  2138.             {
  2139.               dAngleMul = 1.0 + fabs((double)iOvershoot / (double)_global->screenWidth); // between 1.0 and 2.0
  2140.               iAngleMod = (int)(fabs((double)iOvershoot) / 10.0);
  2141.               if (iAngleMod > 15)
  2142.                 iAngleMod = 15; // need a barrier here, too
  2143.  
  2144.               // Power modification is calculated depending on overshoot
  2145.               // To raise or lower by 100 pixels, we need aproximately 100 power (at 45┬░)
  2146.               dPowerMul                =    pow(1.0 + (fabs((double)iAngle - 135.0) / 50.0), 2.0);         // between 1.0 and 3,61
  2147.               iPowerModFixed    =    5 + (int)(                (double)type    *    (fabs(iOvershoot) / 10.0)); // useless 10%, deadly 50% fix
  2148.               iPowerMod                =    5 + (int)((10.0    -    (double)type)    *    (fabs(iOvershoot) / 10.0)); // useless 90%, deadly 50% variable
  2149.               iPowerMod = (rand() % iPowerMod) + iPowerModFixed;
  2150.             }
  2151.           else
  2152.             {
  2153.               // As the overshoot is too high, probably a wall hit, Modification is done in a more limited way:
  2154.               dAngleMul = 1.0 + ((double)type / 10.0);
  2155.               dPowerMul = 1.0 + ((double)type / 10.0);
  2156.               iAngleMod = (rand() % 11) + 5;
  2157.               iPowerMod    =    (MAX_POWER / 8) + (rand() % (MAX_POWER / 8));
  2158.               if ((iOvershoot < 0) || (iAngle >= 170))
  2159.                 // we need to raise distance urgently, so cancel bIsRaisingMod
  2160.                 bIsRaisingMode = false;
  2161.               else
  2162.                 // we need to lower distance urgently, so enter bIsRaisingMod
  2163.                 bIsRaisingMode = true;
  2164.             }
  2165.  
  2166.           // before entering the step 4 modification parts, we could try a trick:
  2167.           if    (    (abs(iLastOvershoot) < abs(iOvershoot))
  2168.                &&(    ((iLastOvershoot < 0) && (iOvershoot > 0))        // be sure that the overshoots switches
  2169.                    ||((iLastOvershoot > 0) && (iOvershoot < 0))    ) // between signed and unsigned
  2170.                &&(    (abs(iLastAngle - iAngle) >= 2)                                // There has to be something to do or
  2171.                    ||(abs(iLastPower - iPower) >=10)    )                        // we might waste all remaining attempts
  2172.                &&(abs(iLastOvershoot) < _global->screenWidth)            // don't try it on wallhits, selfhits
  2173.                &&(abs(iOvershoot) < _global->screenWidth)    )
  2174.             {
  2175.               // the current modification made the shot worse,
  2176.               // but switched between too short and too long,
  2177.               // so revert to half the modification:
  2178.               iAngleMod        = (iLastAngle + iAngle) / 2; // We use the mod to save declaring
  2179.               iPowerMod        = (iLastPower + iPower) / 2; // two new vars!
  2180.               iLastAngle    = iAngle;
  2181.               iLastPower    = iPower;
  2182.               iAngle            = iAngleMod;
  2183.               iPower            = iPowerMod;
  2184.               // Note: This trick won't work when both are too short or too long,
  2185.               // because then bots would never get over too high obstacles!
  2186.             }
  2187.           else
  2188.             {
  2189.               // No trick needed, we are getting nearer!
  2190.               iLastAngle    =    iAngle;
  2191.               iLastPower    =    iPower;
  2192.               iAngleMod        =    (int)((double)iAngleMod * focusRate * dAngleMul);
  2193.               iPowerMod        =    (int)((double)iPowerMod * focusRate * dPowerMul);
  2194.               //     * a) short shots (overshoot < 0) - raise power
  2195.               if (iOvershoot < 0)
  2196.                 {
  2197.                   // If we are too short and have (char *)"overbend" in raising mode, it has to be cancelled!
  2198.                   if (bIsRaisingMode && (iAngle > 180))
  2199.                     bIsRaisingMode = false;
  2200.  
  2201.                   if (!bIsRaisingMode)
  2202.                     {
  2203.                       //     *    i.: angle lowering mode     - adjust the angle towards 45┬░ until it (or aAngle) is reached
  2204.                       if (iAngle > iIdealAngle)
  2205.                         {
  2206.                           iAngle -= iAngleMod;
  2207.                           if (iAngle < iIdealAngle)    iAngle = iIdealAngle;
  2208.                         }
  2209.                       if (iAngle < iIdealAngle)
  2210.                         {
  2211.                           iAngle += iAngleMod;
  2212.                           if (iAngle > iIdealAngle) iAngle = iIdealAngle;
  2213.                         }
  2214.  
  2215.                       // Apply as much power as is neccessary:
  2216.                       iPower += iPowerMod - (abs(iLastAngle - iAngle) * 10);
  2217.  
  2218.                       // check to see whether Raising mode should be entered:
  2219.                       if ((iAngle == iIdealAngle) && (iAngle == iLastAngle))
  2220.                         bIsRaisingMode = true;
  2221.                     }
  2222.                   else
  2223.                     //     *    ii.:power raising mode      - after the first positive overshoot is reached, the angle won't be changed any more
  2224.                     iPower += (int)((double)iPowerMod * dAngleMul);
  2225.                 }
  2226.               //     * b) long shots (overshoot > 0)  - lower power
  2227.               if (iOvershoot > 0)
  2228.                 {
  2229.                   if (bIsRaisingMode)
  2230.                     {
  2231.                       //   *    i.: angle raising mode      - adjust the angle towards 89┬░ until it is reached
  2232.                       if (iAngle > 178)
  2233.                         iAngleMod = 1;  // for small (char *)"overbends"
  2234.                       iAngle += iAngleMod;
  2235.  
  2236.                       // Apply as much power as is neccessary:
  2237.                       iPower -= iPowerMod - (abs(iLastAngle - iAngle) * 10);
  2238.                     }
  2239.                   else
  2240.                     //   *    ii.:power lowering mode     - once the angle can't be changed any more, only power is changed
  2241.                     iPower -= (int)((double)iPowerMod * dAngleMul);
  2242.                 }
  2243.             }
  2244.  
  2245.           // last step: check iPower and iAngle:
  2246.           while    (    (iPower >= MAX_POWER)
  2247.                   ||(iPower <= (MAX_POWER / 20)))
  2248.             iPower = ((MAX_POWER / 2) + iPower) / 2;
  2249.           iPower -= iPower % 5;
  2250.  
  2251.           // check the angle, it must not be flatter than iAngleBarrirer!
  2252.           if (iAngle < iAngleBarrier)
  2253.             iAngle = iAngleBarrier;
  2254.  
  2255.           // Now flip the angle back if neccessary:
  2256.           if (aAngle > 180)
  2257.             iAngle = 180 + (180 - iAngle); // flip back!
  2258.  
  2259. #ifdef DEBUG_AIM
  2260.           printf( (char *)" --> AngleMod: %3d PowerMod: %4d\n", (iAngle - iLastAngle), (iPower - iLastPower));
  2261. #endif // DEBUG_AIM
  2262.         } // end of if(iOvershoot)
  2263.     }
  2264.  
  2265. #ifdef DEBUG_AIM
  2266.   printf("looks like the end of the while loop in aiming\n");
  2267. #endif
  2268.   // Record the best found values in tank if possible
  2269.   if (abs(iBestOvershoot) < tank->smallestOvershoot)
  2270.     {
  2271. #ifdef DEBUG_AIM
  2272.       printf( (char *)" New best Overshoot: %5d\n", iBestOvershoot);
  2273. #endif // DEBUG_AIM
  2274.       tank->smallestOvershoot = abs(iBestOvershoot);
  2275.       tank->bestAngle                    =    iBestAngle;
  2276.       tank->bestPower                    =    iBestPower;
  2277.       // Give the best ones back if possible
  2278.       if (iBestAngle != aAngle)    aAngle = iBestAngle;
  2279.       if (iBestPower != aPower)    aPower = iBestPower;
  2280.     }
  2281. #ifdef DEBUG_AIM
  2282.   else
  2283.     cout << " No new best Overshoot..." << endl;
  2284. #endif // DEBUG_AIM
  2285.   return(iBestOvershoot);
  2286. }
  2287.  
  2288. int PLAYER::adjustOvershoot(int &aOvershoot, double aReachedX, double aReachedY)
  2289. {
  2290.   TANK *pTankHit            =    NULL;                //    For hitting quality analysis
  2291.   long int iOvershoot    =    aOvershoot;    // To calculate with locally
  2292.   bool bIsDirectHit        =    true;                //    false for shaped charges and napalm is chosen
  2293.   bool bIsShaped            =    false;            //    true for shaped charges (special radius calculation needed!)
  2294.   int iDamage                    =    weapon[tank->cw].damage;
  2295.   int iRadius                    =    weapon[tank->cw].radius;
  2296.   int iSelfHits                =    0;                    // Will be raised for every self- and team-hit and then returned
  2297.  
  2298.   if (iRadius < 10)    iRadius = 10;    // several things wouldn't function otherwise
  2299.   if (iDamage < 10)    iDamage = 10; // if we didn't set minimum values
  2300.  
  2301.   if ((tank->cw >= SHAPED_CHARGE) && (tank->cw <= CUTTER))
  2302.     bIsShaped = true;
  2303.   if (bIsShaped || ((tank->cw >= SML_NAPALM) && (tank->cw <= LRG_NAPALM)))
  2304.     bIsDirectHit = false;
  2305.  
  2306.   /* --- Step 1: See whether a tank is hit: */
  2307.   for (int i = 0; i < 10; i++)
  2308.     {
  2309.       if (_global->players[i] && _global->players[i]->tank)
  2310.         {
  2311.           if    (    (aReachedX > (_global->players[i]->tank->x - TANKWIDTH))
  2312.                &&(aReachedX < (_global->players[i]->tank->x + TANKWIDTH))
  2313.                &&(aReachedY > (_global->players[i]->tank->y))
  2314.                &&(aReachedY < (_global->players[i]->tank->y + TANKHEIGHT))
  2315.                &&(_global->players[i]->tank->l > 0))
  2316.             pTankHit = _global->players[i]->tank;
  2317.         }
  2318.     }
  2319.  
  2320.   /* --- Step 2: See whether the target tank is hit or in weapon radius: --- */
  2321.   // check these values in case of segfault
  2322.   if ( (_target) && (pTankHit) &&
  2323.        (pTankHit == _target) && bIsDirectHit && (pTankHit->player != this))
  2324.     // A Direct hit with a direct weapon is what we want, so give 0 back
  2325.     {
  2326.       iOvershoot = 0;
  2327. #ifdef DEBUG_AIM
  2328.       cout << "ON TARGET! ";
  2329. #endif // DEBUG_AIM
  2330.     }
  2331.   else
  2332.     {
  2333.       if (!iOvershoot)
  2334.         {
  2335.           if    (    (    ((tank->x < _targetX) && (_targetX > aReachedX))
  2336.                  ||((tank->x > _targetX) && (_targetX < aReachedX)) )
  2337.                &&(iOvershoot > 0)
  2338.                &&(iOvershoot < MAX_OVERSHOOT)    )
  2339.             iOvershoot *= -1; // the shot is too short
  2340.           else
  2341.             iOvershoot = 1;
  2342.         }
  2343.       if (pTankHit)
  2344.         {
  2345.           // We *have* hit a tank, let's see to that it isn't us or a friend:
  2346.           if ((pTankHit == tank) || (pTankHit->player == this))
  2347.             {
  2348.               // Ourselves, not good!
  2349.               iOvershoot *= iRadius * iDamage;
  2350.               iSelfHits++;
  2351.             }
  2352.           else if (pTankHit->player->team == team)
  2353.             {
  2354.               // We hit someone of the same team, but only Jedi and SitH care, of course:
  2355.               if (team == TEAM_JEDI)
  2356.                 iOvershoot *= iRadius * (defensive + 2) * focusRate;
  2357.               if (team == TEAM_SITH)
  2358.                 iOvershoot *= iRadius * (-1 * (defensive - 2)) * focusRate;
  2359.               if (team != TEAM_NEUTRAL)
  2360.                 iSelfHits++;
  2361.             }
  2362.         }
  2363.  
  2364.       /* --- Step 3: See, whether we, or a team member, is in blast radius and manipulate Overshoot accordingly --- */
  2365.       for (int i = 0; i < 10; i++)
  2366.         {
  2367.           if (_global->players[i] && _global->players[i]->tank && (_global->players[i]->tank != _target))
  2368.             {
  2369.               // _target is skipped, so we don't get wrong values when revenging on a team mate!
  2370.               int iX                    = _global->players[i]->tank->x;
  2371.               int iY                     = _global->players[i]->tank->y;
  2372.               int iRadiusDist;
  2373.               int iBlastDist    = ABSDISTANCE(aReachedX, aReachedY, iX, iY);
  2374.  
  2375.               iRadiusDist = iRadius - iBlastDist;
  2376.               if (!iRadiusDist)    iRadiusDist = 1;
  2377.  
  2378.               if (iBlastDist < iRadius)
  2379.                 {
  2380.                   // Is in Blast range. (maybe)
  2381.                   if (    !bIsShaped
  2382.                        ||(bIsShaped && (abs(iY - (int)aReachedY) <= (iRadius / 20))))
  2383.                     {
  2384.                       // Either no shaped charge or in radius
  2385.                       if (_global->players[i]->tank == tank)
  2386.                         {
  2387.                           // Ourselves, not good!
  2388.                           iOvershoot *= iRadiusDist * iDamage;
  2389.                           iSelfHits++;
  2390.                         }
  2391.                       else if (_global->players[i]->team == team)
  2392.                         {
  2393.                           // We hit someone of the same team, but only Jedi and SitH care, of course:
  2394.                           if (team == TEAM_JEDI)
  2395.                             iOvershoot *= iRadiusDist * (defensive + 2) * focusRate;
  2396.                           if (team == TEAM_SITH)
  2397.                             iOvershoot *= iRadiusDist * (-1 * (defensive - 2)) * focusRate;
  2398.                           if (team != TEAM_NEUTRAL)
  2399.                             iSelfHits++;
  2400.                         }
  2401.                     }
  2402.                 }
  2403.             }
  2404.         }
  2405.  
  2406.       // Be sure to not give a ridiculously high overshoot back:
  2407.       while (abs(iOvershoot) >= MAX_OVERSHOOT)
  2408.         iOvershoot /= 2;
  2409.     }
  2410.   aOvershoot = (int)iOvershoot;
  2411.  
  2412.   return(iSelfHits);
  2413. }
  2414.  
  2415. // If Napalm or a shaped weapon is chosen, the target has to be modified!
  2416. int PLAYER::getAdjustedTargetX(TANK * aTarget)
  2417. {
  2418.   int iTargetX, iTargetY;
  2419.   int iMinOffset    =    0;
  2420.   int iMaxOffset    = 1;
  2421.  
  2422.   if (aTarget)
  2423.     {
  2424.       iTargetX  = aTarget->x;
  2425.       iTargetY  = aTarget->y;
  2426.     }
  2427.   else if (_target)
  2428.     {
  2429.       iTargetX  = _target->x;
  2430.       iTargetY  = _target->y;
  2431.     }
  2432.   else   // avoid segfault
  2433.     return ( rand() % _global->screenWidth ) ;
  2434.  
  2435.  
  2436.   // tank is dead, bail out to avoid segfault
  2437.   if (! tank)
  2438.       return iTargetX;
  2439.  
  2440.   if ( (tank->cw >= SHAPED_CHARGE) && (tank->cw <= CUTTER))
  2441.     {
  2442.       int iBestLeftOffset    =    0;
  2443.       int iBestRightOffset    =    0;
  2444.       int iLeftY = 0;
  2445.       int iBestLeftY = 0;
  2446.       int iRightY = 0;
  2447.       int iBestRightY = 0;
  2448.       iMinOffset    =    (int)((TANKWIDTH / 2) + ((double)TANKWIDTH * focusRate));
  2449.       iMaxOffset    = iMinOffset + (TANKWIDTH * (((int)type + 1) / 2));
  2450.  
  2451.       for (int i = iMinOffset; i < iMaxOffset; i++)
  2452.         {
  2453.           // Get Y-Data:
  2454.           if ((iTargetX - i) > 1)
  2455.             iLeftY = _env->surface[iTargetX - i];
  2456.           if ((iTargetX + i) < (_global->screenWidth - 1))
  2457.             iRightY= _env->surface[iTargetX + i];
  2458.           // Check whether new Y-Data is better than what we have:
  2459.           if (abs(iLeftY - iTargetY) < abs(iBestLeftY - iTargetY))
  2460.             {
  2461.               iBestLeftOffset = i;
  2462.               iBestLeftY             = iLeftY;
  2463.             }
  2464.           if (abs(iRightY - iTargetY) < abs(iBestRightY - iTargetY))
  2465.             {
  2466.               iBestRightOffset    = i;
  2467.               iBestRightY                =    iRightY;
  2468.             }
  2469.         }
  2470.       // Now see whether we go left or right:
  2471.       if (abs(iBestLeftY - iTargetY) < abs(iBestRightY - iTargetY))
  2472.         // use left:
  2473.         iTargetX -= iBestLeftOffset;
  2474.       else
  2475.         // use right:
  2476.         iTargetX += iBestRightOffset;
  2477.     }
  2478.  
  2479.   if ( (tank->cw >= SML_NAPALM) && (tank->cw <= LRG_NAPALM))
  2480.     {
  2481.       // here we only check one side, the one to the wind:
  2482.       iMaxOffset            = abs((int)((double)iMaxOffset * _env->wind * focusRate));
  2483.       iMinOffset            =    abs((int)(_env->wind * (double)TANKWIDTH * focusRate));
  2484.       int iSurfY            =    0;
  2485.       int iBestY            =    0;
  2486.       int iOffset            =    0;
  2487.       int iBestOffset    =    0;
  2488.       int iDirection    =    0;
  2489.  
  2490.       if (_env->wind < 0)    iDirection = 1;        // for some reason I do not know, wind is
  2491.       if (_env->wind > 0)    iDirection = -1;    // used (char *)"the wrong way". (???)
  2492.  
  2493.       // Don't stretch the offset onto ourselves:
  2494.       if    (    ((tank->x < iTargetX) && ((iTargetX - tank->x - (iDirection * iMaxOffset)) < (TANKWIDTH * 2)))
  2495.            ||((tank->x > iTargetX) && ((tank->x - iTargetX - (iDirection * iMaxOffset)) < (TANKWIDTH * 2)))    )
  2496.         iMaxOffset = abs(iTargetX - (int)tank->x) - (TANKWIDTH * 2);
  2497.  
  2498.       // And don't allow a negative offset either (would be useless due to wind!)
  2499.       if (iMaxOffset < TANKWIDTH)
  2500.         iMaxOffset = TANKWIDTH;
  2501.  
  2502.       // But be sure iMinOffset is smaller than iMaxOffset:
  2503.       if (iMinOffset >= iMaxOffset)
  2504.         iMinOffset = iMaxOffset - (int)type;
  2505.  
  2506.       if (iDirection)
  2507.         {
  2508.           // Without wind there is nothing to do!
  2509.           for (int i = iMinOffset; i < iMaxOffset; i++)
  2510.             {
  2511.               iOffset = i * iDirection;
  2512.               // Get Y-Data:
  2513.               if (((iTargetX + iOffset) > 1) && (((iTargetX + iOffset) < (_global->screenWidth - 1))))
  2514.                 iSurfY = _env->surface[iTargetX + iOffset];
  2515.  
  2516.               // Check whether new Y-Data is better than what we have:
  2517.               if    (    ((iTargetY - iSurfY) < (iTargetY - iBestY))
  2518.                    ||(!iBestY)    )
  2519.                 {
  2520.                   iBestOffset = iOffset;
  2521.                   iBestY             = iSurfY;
  2522.                 }
  2523.             }
  2524.           iTargetX += iBestOffset;
  2525.         }
  2526.     }
  2527.  
  2528.   return (iTargetX);
  2529. }
  2530.  
  2531. int PLAYER::calculateAttack(TANK *aTarget)
  2532. {
  2533.   /* There are two general possibilities:
  2534.    * - aTarget provided:
  2535.    *   This function was called from computerSelectTarget() and will only check if it is possible
  2536.    *   to reach a target, aka try only once and give the overshoot back.
  2537.    * - default (aTarget is automatically NULL)
  2538.    *   This function was called from computerControl() and will go forth and try to hit _target
  2539.    *
  2540.    * Outline:
  2541.    * --------
  2542.    * There are the following possibilities:
  2543.    * a) An Item or a laser is chosen
  2544.    *    -> a direct angle will do!
  2545.    * b) We are burried
  2546.    *    -> fire unburying tool at +/-60┬░ to the middle of the screen
  2547.    * c) Kamikaze
  2548.    *    -> indicated by targetX/Y being tank.x/y
  2549.    *    -> if shaped weapon is chosen, fire 45┬░ and power 100 to the side where y is nearest to tank.y
  2550.    *    -> if napalm is chosen, fire 90┬░ and power 0
  2551.    *    -> otherwise fire 90┬░ and power 250
  2552.    * d) Fire in non-boxed mode
  2553.    *    -> normal calculation
  2554.    * e) Fire in non-boxed mode
  2555.    *    -> extended power-control after normal calculation
  2556.    *    -> if the target can't be reached while staying below the ceiling,
  2557.    *       check for an obstacle than can be removed and do so if found.
  2558.    * --> boxed mode is included in non-boxed mode now. I hope it works as I intent!
  2559.    *
  2560.    * Update for dynamization: The previous target is recorded, aiming starts at the old values,
  2561.    *                          if the target didn't change. */
  2562.  
  2563.   int iXdistance, iYdistance;
  2564.   if (aTarget)
  2565.     {
  2566.       iXdistance = aTarget->x - tank->x;
  2567.       iYdistance = aTarget->y - tank->y;
  2568.     }
  2569.   else
  2570.     {
  2571.       iXdistance = _targetX - tank->x;
  2572.       iYdistance = _targetY - tank->y;
  2573.     }
  2574.  
  2575. #ifdef DEBUG_AIM
  2576.   if (!aTarget && !iTargettingRound)
  2577.     {
  2578.       printf("\n-----------------------------------------------\n");
  2579.       if (_target)
  2580.          printf("%s is starting to aim at %s\n", getName(), _target->player->getName() );
  2581.       else
  2582.          printf("%s is aiming without a target!\n", getName());
  2583.     }
  2584. #endif // DEBUG_AIM
  2585.  
  2586.   /* --- case a) An Item is chosen --- */
  2587.   if    ( (!aTarget) && ( (tank->cw >= WEAPONS) ||
  2588.           ((tank->cw >= SML_LAZER) && (tank->cw <= LRG_LAZER))))
  2589.     {
  2590. #ifdef DEBUG_AIM
  2591.       printf("About to calc direct angle.\n");
  2592. #endif
  2593.       _targetAngle = calculateDirectAngle (iXdistance, iYdistance);
  2594.       _targetPower = MAX_POWER / 2;
  2595.  
  2596. #ifdef DEBUG_AIM
  2597.       cout << "Direct " << _targetAngle - 90 << " for ";
  2598.       printf("Direct %d for %s\n", _targetAngle -90, (tank->cw < WEAPONS) ? weapon[tank->cw].name : item[tank->cw - WEAPONS].name);
  2599. #endif // DEBUG_AIM
  2600.  
  2601.       iTargettingRound = retargetAttempts; // So it is done!
  2602.  
  2603.       return(0);
  2604.     }
  2605.  
  2606.  
  2607.   /* --- case b) We are burried --- */
  2608.   if (!aTarget &&(tank->howBuried () > BURIED_LEVEL))
  2609.     {
  2610.       // Angle is 60┬░ to the middle of the screen:
  2611.       int iAngleVariation = rand() % (20 - ((int)type * 3));
  2612.       iAngleVariation -= (int)((double)type / 1.5);
  2613.       if (tank->x <= _global->halfWidth)
  2614.         _targetAngle = 150 + iAngleVariation;
  2615.       else
  2616.         _targetAngle = 210 - iAngleVariation;
  2617. #ifdef DEBUG_AIM
  2618.       printf("Freeing self with Angle %d and Power %d\n", _targetAngle, _targetPower);
  2619. #endif // DEBUG_AIM
  2620.  
  2621.       iTargettingRound = retargetAttempts;
  2622.  
  2623.       return(0);
  2624.     }
  2625.  
  2626.   /* --- case c) Kamikaze --- */
  2627.   if (!aTarget && (_targetX == tank->x) && (_targetY == tank->y))
  2628.     {
  2629. #ifdef DEBUG_AIM
  2630.       cout << "Going bye bye with ";
  2631.       if (tank->cw < WEAPONS)
  2632.         cout << weapon[tank->cw].name;
  2633.       else
  2634.         cout << item[tank->cw - WEAPONS].name;
  2635.       cout << " !!!" << endl;
  2636.       cout << "GERONNNNNIIIIIMOOOOOOOOOO !!!" << endl;
  2637. #endif // DEBUG_AIM
  2638.  
  2639.       iTargettingRound = retargetAttempts;
  2640.  
  2641.       // For a nice bye bye we set angle/power directly
  2642.       if ((tank->cw >= SHAPED_CHARGE) && (tank->cw <= CUTTER))
  2643.         {
  2644.           // shaped weapons are a bit special:
  2645.           int iHLeft, iHRight;
  2646.           int iCount = 0;
  2647.           for (int i = TANKWIDTH; i <= (TANKWIDTH * 2); i++)
  2648.             {
  2649.               iCount++;
  2650.               iHLeft = _env->surface[(int)(tank->x - i)];
  2651.               iHRight= _env->surface[(int)(tank->x + i)];
  2652.             }
  2653.           iHRight /= iCount;
  2654.           iHLeft  /= iCount;
  2655.           if (fabs(iHRight - tank->y) < fabs(iHLeft - tank->y))
  2656.             _targetAngle = 135;
  2657.           else
  2658.             _targetAngle = 225;
  2659.           _targetPower = MAX_POWER / 20;
  2660.           return(0);
  2661.         }
  2662.       // The other possibilities are easier:
  2663.       if ((tank->cw >= SML_NAPALM) && (tank->cw <= LRG_NAPALM))
  2664.         _targetPower = 0;
  2665.       else
  2666.         _targetPower = MAX_POWER / 8;
  2667.       _targetAngle = 180;
  2668.  
  2669.       return(0);
  2670.     }
  2671.  
  2672.   /* --- case d) Fire in non-boxed mode --- */
  2673.   int iRawAngle, iAdjAngle, iPower, iLastPower, iSavetyPower;
  2674.   int iOvershoot = MAX_OVERSHOOT;
  2675.   int iLastOvershoot = MAX_OVERSHOOT;
  2676.   int iBestOvershoot = MAX_OVERSHOOT;
  2677.  
  2678.   _targetX = getAdjustedTargetX(aTarget);
  2679.   calculateAttackValues(iXdistance, iYdistance, iRawAngle, iAdjAngle, iPower, false);
  2680.  
  2681.   if (!iTargettingRound)
  2682.     {
  2683.       // Initializiation, if this is the first try:
  2684.  
  2685.       if (!aTarget && (_oldTarget == _target) )
  2686.         {
  2687.           // Target didn't change, use last rounds values:
  2688.           tank->bestAngle = tank->a;
  2689.           tank->bestPower = tank->p;
  2690.           iRawAngle = tank->a;
  2691.           iAdjAngle = tank->a;
  2692.           iPower    = tank->p;
  2693.         }
  2694.       else
  2695.         {
  2696.           // new target, new values:
  2697.           tank->bestAngle = 180;
  2698.           tank->bestPower = MAX_POWER;
  2699.           if (!aTarget)
  2700.             _oldTarget = _target;
  2701.         }
  2702.  
  2703.       tank->smallestOvershoot = MAX_OVERSHOOT + 1; // So there will be at least one recorded
  2704.  
  2705.     }
  2706.   else
  2707.     {
  2708.       // This is a follow-up round, get the last values back:
  2709.       iAdjAngle = _targetAngle;
  2710.       iPower    = _targetPower;
  2711.       iBestOvershoot = tank->smallestOvershoot;
  2712.     }
  2713.  
  2714.   iLastPower=    iPower;
  2715.  
  2716.   if (aTarget)
  2717.   {
  2718.     #ifdef DEBUG_AIM
  2719.     printf("Returning a range find.\n");
  2720.     #endif
  2721.     return(rangeFind(iAdjAngle, iPower));
  2722.   }
  2723.  
  2724.   // Now that we are here, normal handling is needed:
  2725. #ifdef DEBUG_AIM
  2726.   if (!iTargettingRound)
  2727.     {
  2728.       printf("Intitial try:\n");
  2729.       printf("About to range find.\n");
  2730.     }
  2731. #endif // DEBUG_AIM
  2732.  
  2733.   iOvershoot            = rangeFind(iAdjAngle, iPower);
  2734.   iLastOvershoot    =    iBestOvershoot;
  2735.   if (abs(iOvershoot) < abs(iBestOvershoot))
  2736.     iBestOvershoot = iOvershoot;
  2737.  
  2738. #ifdef DEBUG_AIM
  2739.   cout << "Overshoot: " << iOvershoot << " (best so far: " << iBestOvershoot << ")" << endl << endl;
  2740.   cout << iTargettingRound + 1 << ". re-try:";
  2741. #endif // DEBUG_AIM
  2742.  
  2743.   // There is still an Overshoot, so see what we can do:
  2744.   if (abs(iLastOvershoot - iOvershoot) <= 1)
  2745.     {
  2746.       // rangeFind can't get a better version, so start completely different!
  2747.       int iAngleRange = 180;
  2748.       if (_env->current_wallType == WALL_STEEL)
  2749.         iAngleRange /= 2;
  2750.       if (_targetX > tank->x)
  2751.         // shoot basically to the right:
  2752.         iRawAngle = 90 + (rand() % iAngleRange);
  2753.       else
  2754.         // shoot basically to the left:
  2755.         iRawAngle = 270 - (rand() % iAngleRange);
  2756.       iAdjAngle = (iAdjAngle + iRawAngle) / 2;
  2757.       iPower        =    (iPower + (rand() % (MAX_POWER / 2))) / 2;
  2758. #ifdef DEBUG_AIM
  2759.       cout << " Starting over! " << endl;
  2760. #endif
  2761.     }
  2762.   else
  2763.     {
  2764.       // Just calculate anew:
  2765.       calculateAttackValues(iXdistance, iYdistance, iRawAngle, iAdjAngle, iPower, true);
  2766. #ifdef DEBUG_AIM
  2767.       cout << " recalculating! " << endl;
  2768. #endif
  2769.     }
  2770.  
  2771.   /* SavetyPower is as follows:
  2772.    * abs(iAdjAngle -180) changes the value to be between:
  2773.    * ->  0 : Straight upwards       (originally 180)
  2774.    * -> 90 : Straight Left or right (originally 270/90)
  2775.    * 91 - abs(...) gives a value between 1 and 91 which is multiplied with the radius */
  2776.   iSavetyPower = ((91 - abs(iAdjAngle - 180)) * (weapon[tank->cw].radius * focusRate)) / focusRate;
  2777.   if (iSavetyPower > (MAX_POWER / 4))    iSavetyPower    = MAX_POWER / 4;
  2778.   if (iPower < iSavetyPower)                    iPower                 = iSavetyPower;
  2779.  
  2780.   iLastOvershoot = iOvershoot;
  2781.   iOvershoot = rangeFind(iAdjAngle, iPower);
  2782.  
  2783.   if (abs(iOvershoot) < abs(iBestOvershoot))
  2784.     iBestOvershoot = iOvershoot;
  2785.  
  2786.   iTargettingRound++;
  2787.  
  2788.   // Do not count this attempt if the currently best overshoot is too high:
  2789.   if ((iBestOvershoot > _global->screenWidth) && (rand() % ((int)type * 2)))
  2790.     iTargettingRound--; // if ibestOvershoot is negative it will alwys be counted!
  2791.  
  2792.   if (tank->smallestOvershoot < (weapon[tank->cw].radius / (int)type))
  2793.     iTargettingRound = retargetAttempts;
  2794.  
  2795.   // Now the best Version has to be tested:
  2796.   iOvershoot    = tank->smallestOvershoot; // Always a positive number!
  2797.   _targetAngle = tank->bestAngle; // Record for foolow-up rounds
  2798.   _targetPower = tank->bestPower; // Record for follow-up rounds
  2799.  
  2800.   if (iTargettingRound == retargetAttempts)
  2801.     {
  2802. #ifdef DEBUG_AIM
  2803.       cout << "  ---  ---" << endl;
  2804.       cout << "Final Decision:" << endl;
  2805.       cout << "Angle    : " << _targetAngle - 90 << endl;
  2806.       cout << "Power    : " << _targetPower << endl;
  2807.       cout << "Overshoot: " << iBestOvershoot << endl;
  2808. #endif // DEBUG_AIM
  2809.  
  2810.       if (iOvershoot > weapon[tank->cw].radius)
  2811.         {
  2812.           if (_targetAngle > 180)
  2813.             iRawAngle = _targetAngle + 10 + (rand() % ((int)type * 4));
  2814.           else
  2815.             iRawAngle = _targetAngle - (10 + (rand() % ((int)type * 4)));
  2816.  
  2817.           // There are three possibilities:
  2818.           if    (    (iBestOvershoot < 0)
  2819.                &&(abs(_targetAngle - 180) <= (10 + ((iXdistance / _global->screenWidth) * 10)))
  2820.                &&(!tank->shootClearance(iRawAngle, weapon[tank->cw].radius * 1.5)))
  2821.             {
  2822.               // a) there is an obstacle in our way!
  2823.               tank->cw = getUnburyingTool();
  2824.               if (tank->cw < WEAPONS)
  2825.                 {
  2826.                   _targetAngle = iRawAngle;
  2827. #ifdef DEBUG_AIM
  2828.                   cout << "Revised Angle for unburying: " << _targetAngle - 90 << endl;
  2829. #endif // DEBUG_AIM
  2830.                 }
  2831.             }
  2832.           else
  2833.             {
  2834.               // b) the way is clear, so either try to switch weapon or fire away!
  2835.               if (iBestOvershoot < 0)
  2836.                 {
  2837.                   // As we are too short, see whether another weapon might be good
  2838.                   if (nm[TREMOR])    tank->cw = TREMOR;
  2839.                   if (nm[SHOCKWAVE])    tank->cw = SHOCKWAVE;
  2840.                   if ((_targetY < (_global->screenHeight - iOvershoot)) && nm[BURROWER])
  2841.                     tank->cw = BURROWER;
  2842.                   if (nm[TECTONIC])    tank->cw = TECTONIC;
  2843.                   if ((_targetY < (_global->screenHeight - iOvershoot)) && nm[PENETRATOR])
  2844.                     tank->cw = PENETRATOR;
  2845.  
  2846.                   // if it is boxed, it is better to teleport out!
  2847.                   if ((_global->bIsBoxed) && (iOvershoot > (weapon[tank->cw].radius * 2)))
  2848.                     {
  2849.                       // no way, teleport out!
  2850.                       if (ni[ITEM_TELEPORT])    tank->cw = ITEM_TELEPORT + WEAPONS;
  2851.                       if (ni[ITEM_SWAPPER])        tank->cw = ITEM_SWAPPER + WEAPONS;
  2852.                     }
  2853.                 }
  2854.               else
  2855.                 {
  2856.                   // look for tectonis and their range
  2857.                   if (nm[TREMOR]        && (weapon[TREMOR].radius >= iOvershoot))            tank->cw = TREMOR;
  2858.                   if (nm[SHOCKWAVE] && (weapon[SHOCKWAVE].radius >= iOvershoot))    tank->cw = SHOCKWAVE;
  2859.                   if (nm[TECTONIC]    && (weapon[TECTONIC].radius >= iOvershoot))        tank->cw = TECTONIC;
  2860.                 }
  2861.  
  2862.               // c) we have no chance to reach anything!
  2863.               if (iOvershoot > _global->screenWidth)
  2864.                 {
  2865.                   // no way, teleport out!
  2866.                   if (ni[ITEM_TELEPORT])    tank->cw = ITEM_TELEPORT + WEAPONS;
  2867.                   if (ni[ITEM_SWAPPER])        tank->cw = ITEM_SWAPPER + WEAPONS;
  2868.                 }
  2869.  
  2870.             }
  2871.         }
  2872.       else
  2873.         {
  2874.           // If we *are* hitting ok, Angle and Power need to be manipulated by errorMultiplier
  2875.           int iAngleMod, iPowerMod;
  2876.           int iAngleModLimit = (42 - (10 * ((int)type - 1)));     // =  42,  32,  22, 12,  2 for useless -> deadly
  2877.           int iPowerModLimit = (210 - (50 * ((int)type - 1)));    // = 210, 160, 110, 60, 10 for useless -> deadly
  2878.  
  2879.           iAngleMod = (rand() % 51) * errorMultiplier;    //    useless: 0 -  100, deadly: 0 - 2 (with 2 being very unlikely)
  2880.           if (iAngleMod > iAngleModLimit)
  2881.             iAngleMod = iAngleModLimit;
  2882.  
  2883.           iPowerMod    =    (rand() % 251) * errorMultiplier;    //    useless: 0 - 500, deadly: 0 - 10 (with 10 being *very* unlikely)
  2884.           if (iPowerMod > iPowerModLimit)
  2885.             iPowerMod = iPowerModLimit;
  2886.  
  2887.           // In boxed mode, errors have quite more impact and are therefore cut down to be only two thirds
  2888.           if (_global->bIsBoxed)
  2889.             {
  2890.               iAngleMod = (int)((double)iAngleMod / 3.0 * 2.0);
  2891.               iPowerMod = (int)((double)iPowerMod / 3.0 * 2.0);
  2892.             }
  2893.  
  2894.           // 25 % to get a flattening anglemod (aka nearing to the ground)
  2895.           if ((!(rand() % 4)) && (_targetAngle < 180))
  2896.             iAngleMod *= -1; // right side angle
  2897.           if ((rand() % 4) && (_targetAngle > 180))
  2898.             iAngleMod *= -1; // right side angle (other way round, because here a positive mod is flattening!
  2899.  
  2900.           // 25 % to get a lessening powermod. (it is more likely to overshoot than be too short!)
  2901.           if (!(rand() % 4))         // (and it leads to too many stupid self
  2902.             iPowerMod    *=    -1;    // hits to allow negative PoerMod too often
  2903.  
  2904.           // The modification must not lead to a too stupid angle
  2905.           while (    iAngleMod
  2906.                   &&(    (    (_targetAngle < 180)
  2907.                         &&(    ((iAngleMod + _targetAngle) < 95)
  2908.                             ||((iAngleMod    +    _targetAngle) > 175) ) )
  2909.                       ||(    (_targetAngle >=180)
  2910.                           &&(    ((iAngleMod + _targetAngle) > 265)
  2911.                               ||((iAngleMod    +    _targetAngle) < 185) ) )
  2912.                     ) ) iAngleMod /= 2;
  2913.           _targetAngle += iAngleMod;
  2914.  
  2915.           // Same applies for Power:
  2916.           while    (    iPowerMod
  2917.                   &&(    ((iPowerMod + _targetPower) > MAX_POWER)
  2918.                       ||((iPowerMod + _targetPower) < (MAX_POWER * 0.1)) ) )
  2919.             iPowerMod /= 2;
  2920.  
  2921.           // iPowerMod needs to be a multiplier of 5:
  2922.           iPowerMod -= iPowerMod % 5;
  2923.  
  2924.           _targetPower += iPowerMod;
  2925.  
  2926. #ifdef DEBUG_AIM
  2927.           printf( "Error-Adjusting: Angle %3d Power %4d\n", iAngleMod, iPowerMod);
  2928. #endif // DEBUG_AIM
  2929.         }
  2930.  
  2931. #ifdef DEBUG_AIM
  2932.       cout << "HITTING " << _target->player->getName() << " ???" << endl << endl;
  2933. #endif // DEBUG_AIM
  2934.  
  2935.       // Last one: if we are revenging, tell em!
  2936.       // Added _target check to avoid segfault
  2937.       if  ( (tank->cw < WEAPONS) && (_target)                      // Wepaon selected?
  2938.             && (weapon[tank->cw].damage > 1)                       // One that delivers damage?
  2939.             && (revenge == _target->player)                        // And the target is our revengee?
  2940.             && ((rand() % ((int)DEADLY_PLAYER + 2 - (int)type)))    ) // And we do really want to taunt?
  2941.         {
  2942.           FLOATTEXT *DIEText;
  2943.           char *my_text;
  2944.           my_text = selectRetaliationPhrase();
  2945.           DIEText = new FLOATTEXT (_global, _env, my_text, (int) tank->x, (int) tank->y - 30, color, CENTRE);
  2946.           if (my_text) free(my_text);
  2947.           if ( DIEText)
  2948.           {
  2949.               //DIEText->xv = 0;
  2950.               //DIEText->yv = -0.4;
  2951.               DIEText->set_speed(0.0, -0.4);
  2952.               DIEText->maxAge = 150;
  2953.           }
  2954.           else
  2955.               perror ( "player.cc: Failed allocating memory for DieText in calculateAttack().");
  2956.         }
  2957.       // As we might finish here because of a good overshoot, iTargettingRounds need to be maxed!
  2958.       iTargettingRound = retargetAttempts;
  2959.     }
  2960.   return(0);
  2961. }
  2962.  
  2963. void PLAYER::calculateAttackValues(int &aDistanceX, int aDistanceY, int &aRawAngle, int &aAdjAngle, int &aPower, bool aAllowFlip)
  2964. {
  2965.   double dAirTime, dxTime;
  2966.   double dSlopeX, dSlopeY;
  2967.   double dAngleVariation, dPowerVariation;
  2968.   bool bIsWrapped = false; // Special handling for wrapped walls
  2969.  
  2970.   aDistanceX = _targetX - tank->x;
  2971.  
  2972.   /* --- Step 1: find the raw angle, aka the obstacle-free optimal angle --- */
  2973.  
  2974.   aRawAngle = (int)(atan2((double)aDistanceX, (double)(aDistanceY - abs (aDistanceX))) / PI * 180.0);
  2975.  
  2976.   // Bring in Range:
  2977.   if (aRawAngle < 0)        aRawAngle = aRawAngle + 360;
  2978.   if (aRawAngle < 90)        aRawAngle = 135;
  2979.   if (aRawAngle > 270)    aRawAngle = 225;
  2980.  
  2981.   if ((_env->current_wallType == WALL_WRAP) && ((rand() % ((int)type + 1)) || !aAllowFlip))
  2982.     {
  2983.       // Note: We always wrap when possible and flipping not allowed, to have the shorter distance for sure!
  2984.       int iWrapDistance = 0;
  2985.  
  2986.       // if the distance through the wall is shorter, take it!
  2987.       if (tank->x < _targetX)
  2988.         iWrapDistance = -1 * (tank->x -1 + _global->screenWidth - 2 - _targetX);
  2989.       else
  2990.         iWrapDistance = tank->x -1 + _global->screenWidth - 2 - _targetX;
  2991.  
  2992.       if (abs(iWrapDistance) < abs(aDistanceX))
  2993.         {
  2994.           bIsWrapped = true;
  2995.           aDistanceX = iWrapDistance;
  2996.           aRawAngle = 180 + (180 - aRawAngle); // flip!
  2997.         }
  2998.     }
  2999.  
  3000.   // Angle variation for more flavour:
  3001.   dAngleVariation = (rand() % 21) * focusRate; // useless: 0-4, deadly: 0-20
  3002.   if (rand() % 2) dAngleVariation *= -1.0;
  3003.   while    (    (dAngleVariation > 0.0)
  3004.           &&(    ((aRawAngle > 180) && (    ((aRawAngle + dAngleVariation) < 190)
  3005.                                       ||((aRawAngle + dAngleVariation) > 260)))
  3006.               ||((aRawAngle < 180) && (    ((aRawAngle + dAngleVariation) > 170)
  3007.                                         ||((aRawAngle + dAngleVariation) < 100))) )    )
  3008.     dAngleVariation /= 2.0;
  3009.   aRawAngle += (int)dAngleVariation;
  3010.  
  3011.   // Maybe we could switch sides?
  3012.   if ((_env->current_wallType != WALL_STEEL) && (rand() % 2) && (rand() % (int)type) && aAllowFlip)
  3013.     {
  3014.       // Yes, we can! ( (char *)"Change is coming!" ;-) (b.h.o) )
  3015.       aRawAngle = 180 + (180 - aRawAngle); // This switches sides, yes. :-D
  3016.  
  3017.       if (tank->x < _targetX)
  3018.         {
  3019.           // We switch angle from right to left: (original distance is positive)
  3020.           switch (_env->current_wallType)
  3021.             {
  3022.             case WALL_RUBBER:
  3023.               // Add Distances to the wall to bounce from:
  3024.               aDistanceX += 2 * (tank->x - 1);
  3025.               break;
  3026.             case WALL_SPRING:
  3027.               // Spring walls add velocity, so adapt a bit
  3028.               aDistanceX += tank->x - 1 + ((tank->x - 1) * 0.75);
  3029.               break;
  3030.             case WALL_WRAP:
  3031.               // Distance is new: only distances from the wall:
  3032.               if (bIsWrapped)
  3033.                 aDistanceX = tank->x -1 + _global->screenWidth - 2 - _targetX;
  3034.               else
  3035.                 aDistanceX = -1 * (tank->x -1 + _global->screenWidth - 2 - _targetX);
  3036.               break;
  3037.             }
  3038.         }
  3039.       else
  3040.         {
  3041.           // We switch angle from left to right: (original distance is negative)
  3042.           switch (_env->current_wallType)
  3043.             {
  3044.             case WALL_RUBBER:
  3045.               // Add Distances to the wall to bounce from:
  3046.               aDistanceX += 2 * (_global->screenWidth - tank->x - 2);
  3047.               break;
  3048.             case WALL_SPRING:
  3049.               // Spring walls add velocity, so adapt a bit
  3050.               aDistanceX += _global->screenWidth - tank->x - 2 + ((_global->screenWidth - tank->x - 2) * 0.75);
  3051.               break;
  3052.             case WALL_WRAP:
  3053.               // Distance is new: only distances from the wall:
  3054.               if (bIsWrapped)
  3055.                 aDistanceX = -1 * (tank->x -1 + _global->screenWidth - 2 - _targetX);
  3056.               else
  3057.                 aDistanceX = tank->x -1 + _global->screenWidth - 2 - _targetX;
  3058.               break;
  3059.             }
  3060.         }
  3061.     }
  3062.  
  3063.   /* --- Step 2: Adjust the Angle given clearance --- */
  3064.  
  3065.   aAdjAngle = aRawAngle;
  3066.   while ((aAdjAngle < 180) && !(tank->shootClearance(aAdjAngle)))
  3067.     aAdjAngle++;
  3068.   while ((aAdjAngle > 180) && !(tank->shootClearance(aAdjAngle)))
  3069.     aAdjAngle--;
  3070.  
  3071.   /* --- Step 3: Find neccessary Power --- */
  3072.   dSlopeX = _global->slope[aAdjAngle][0];
  3073.   dSlopeY = _global->slope[aAdjAngle][1];
  3074.  
  3075.   if (dSlopeX != 0.0)
  3076.     dxTime = ((double)aDistanceX / fabs(dSlopeX));
  3077.   else
  3078.     // entirely down to the elements now
  3079.     dxTime = ((double)aDistanceX / 0.000001);
  3080.  
  3081.   // Low target, less power
  3082.   // xdistance proportional to sqrt(dy)
  3083.   dAirTime = fabs(dxTime) + (((double)aDistanceY * dSlopeY) * _env->gravity * (100.0 / _global->frames_per_second)) * 2.0;
  3084.  
  3085.   // Less airTime doesn't necessarily mean less power
  3086.   // Horizontal firing means more power needed even though
  3087.   //   airTime is minimised
  3088.  
  3089.   aPower = (int)(sqrt (dAirTime * _env->gravity * (100.0 / _global->frames_per_second))) * 100;
  3090.  
  3091.   // Power variation for more flavour:
  3092.   dPowerVariation = (rand() % 51) * focusRate; // useless: 0-10, deadly: 0-50
  3093.   dPowerVariation -= (int)dPowerVariation % 5;
  3094.   if (rand() % 2) dPowerVariation *= -1.0;
  3095.   aPower += dPowerVariation;
  3096.  
  3097.   if (aPower > MAX_POWER)                    aPower = MAX_POWER;
  3098.   if (aPower < (MAX_POWER / 20))    aPower = MAX_POWER / 20;
  3099.  
  3100. }
  3101.  
  3102. int PLAYER::calculateDirectAngle (int dx, int dy)
  3103. {
  3104.   double angle;
  3105.  
  3106.   angle = atan2 ((double)dx, (double)dy) / PI * 180;
  3107.   angle += (rand () % 40 - 20) * errorMultiplier;
  3108.  
  3109.   if (angle < 0)
  3110.     angle = angle + 360;
  3111.   if (angle < 90)
  3112.     angle = 90;
  3113.   else if (angle > 270)
  3114.     angle = 270;
  3115.  
  3116.   return ((int)angle);
  3117. }
  3118.  
  3119. TANK * PLAYER::computerSelectTarget (int aPreferredWeapon, bool aRotationMode)
  3120. {
  3121.   int random_target;
  3122.   int attempts = 0;
  3123.   int max_attempts = (int)type * 3;
  3124.   TANK *best_target = NULL;
  3125.   int current_score = 0;
  3126.   int best_score = -1 * MAX_OVERSHOOT; // start with a loooooow score, so that even score<0 tanks can become best target
  3127.   TANK *current_tank = NULL;
  3128.   TANK *tankPool[10];
  3129.   int iMoneyNeed = getMoneyToSave() - money;    // Are we in need for money?
  3130.   int target_count = 0;
  3131.  
  3132. #ifdef DEBUG
  3133.   cout << " -> I need " << iMoneyNeed << " Credits *urgently*!" << endl;
  3134.   if (aPreferredWeapon < WEAPONS)
  3135.     cout << " -  Searching target for " << weapon[aPreferredWeapon].name << endl;
  3136.   else
  3137.     cout << " -  Searching target for " << item[aPreferredWeapon - WEAPONS].name << endl;
  3138. #endif // DEBUG
  3139.   // find out how many tries we have to find a good target
  3140.  
  3141.   // Fill tankPool
  3142.   for (int i = 0; i < _global->numPlayers; i++)
  3143.     {
  3144.       if ( (_global->players[i]) && (_global->players[i]->tank) )
  3145.       {
  3146.         tankPool[i] = _global->players[i]->tank;
  3147.         target_count++;
  3148.       }
  3149.       else
  3150.         tankPool[i] = NULL;
  3151.     }
  3152.  
  3153.   if (target_count < 2)     // just us left or nobody
  3154.      return NULL;
  3155.  
  3156.   // who do we want to shoot at?
  3157.   while (attempts < max_attempts)
  3158.     {
  3159.       // select random tank for target
  3160.       if (_global->numPlayers > 0)
  3161.           random_target = rand() % _global->numPlayers;
  3162.       else
  3163.           random_target = 0;
  3164.       current_tank    =    tankPool[random_target];
  3165.  
  3166.       if (current_tank)
  3167.         {
  3168.           current_score    = 0;
  3169.           // only consider living tanks that are not us
  3170.  
  3171.           if ( (current_tank->l > 0) && (current_tank->player != this))
  3172.             {
  3173.               int iDamage        = 0;
  3174.  
  3175.               if (weapon[aPreferredWeapon].numSubmunitions > 1)
  3176.                 iDamage    =    (damageMultiplier
  3177.                            * weapon[weapon[aPreferredWeapon].submunition].damage
  3178.                            * (weapon[aPreferredWeapon].numSubmunitions / 3.0));
  3179.               else
  3180.                 iDamage    =    (damageMultiplier
  3181.                            * weapon[aPreferredWeapon].damage
  3182.                            * weapon[aPreferredWeapon].spread);
  3183.  
  3184.               // compare the targets strength to ours
  3185.               int iDiffStrength =    ( (tank->l + tank->sh) - (current_tank->l + current_tank->sh));
  3186.  
  3187.               current_score = iDiffStrength;
  3188.  
  3189.               if (iDiffStrength < 0)
  3190.                 {
  3191.                   // The target is stronger. Are we impressed?
  3192.                   if ( (defensive < 0.0) && ( (rand() % ( (int) type + 1))))
  3193.                     // No we aren't, add defensive-modified Strength
  3194.                     // (The more offensive, the less impressed we are)
  3195.                     current_score += (int) ( ( (defensive - 3.0) / 2.0) * (double) iDiffStrength);
  3196.                 }
  3197.               else
  3198.                 // the target is weaker, add points modified by how defensive we are
  3199.                 current_score    +=    (int) ( (double) iDiffStrength * ( (defensive + 3.0) / 2.0));
  3200. #ifdef DEBUG
  3201.               cout << " (str)" << current_score;
  3202. #endif
  3203.               // check to see if we are on the same team
  3204.               switch ( (int) team)
  3205.                 {
  3206.                 case TEAM_JEDI:
  3207.                   if ((current_tank->player->team == TEAM_JEDI) && !aRotationMode)
  3208.                     current_score -= 500 * (int) type;
  3209.                   if ((current_tank->player->team == TEAM_JEDI) && aRotationMode)
  3210.                     current_score -= MAX_OVERSHOOT; // no team consideration in rotation mode!
  3211.  
  3212.                   if (current_tank->player->team == TEAM_SITH)
  3213.                     current_score    += -200.0 * (defensive - 2.0) * ( (double) type / 2.0);
  3214.                   break;
  3215.  
  3216.                 case TEAM_SITH:
  3217.                   if ((current_tank->player->team == TEAM_SITH) && !aRotationMode)
  3218.                     current_score -= 500 * (int) type;
  3219.                   if ((current_tank->player->team == TEAM_SITH) && aRotationMode)
  3220.                     current_score -= MAX_OVERSHOOT; // no team consideration in rotation mode!
  3221.  
  3222.                   if (current_tank->player->team == TEAM_JEDI)
  3223.                     current_score    += -200.0 * (defensive - 2.0) * ( (double) type / 2.0);
  3224.                   break;
  3225. /*
  3226.                 default:
  3227.                   // Neutrals go rather for sith than jedi. (but not much)
  3228.                   if (current_tank->player->team == TEAM_JEDI)
  3229.                     current_score += -25.0 * (defensive - 2.0) * ( (double) type / 2.0);
  3230.  
  3231.                   if (current_tank->player->team == TEAM_SITH)
  3232.                     current_score    += -50.0 * (defensive - 2.0) * ( (double) type / 2.0);
  3233. */
  3234.                 }
  3235. #ifdef DEBUG
  3236.               cout << " (team)" << current_score;
  3237. #endif // DEBUG
  3238.               // do we have a grudge against the target
  3239.               if (current_tank->player == revenge)
  3240.                 {
  3241.                   /*
  3242.                   switch ( (int) team)
  3243.                     {
  3244.                     case TEAM_JEDI:
  3245.                       // Revenge is a dark force!
  3246.                       current_score += (50 * ( (defensive - 1.5) * -1.0));
  3247.                       break;
  3248.                     case TEAM_SITH:
  3249.                       // Revenge means power!
  3250.                       current_score += (200 * ( (defensive - 1.5) * -1.0));
  3251.                       break;
  3252.                     default:
  3253.                    */
  3254.                       current_score += (100 * ( (defensive - 1.5) * -1.0));
  3255.                  //   }
  3256.                 }
  3257. #ifdef DEBUG
  3258.               cout << " (rev)" << current_score;
  3259. #endif // DEBUG
  3260.               // prefer targets further away when violent death is on
  3261.               if (_global->violent_death)
  3262.                 {
  3263.                   int distance;
  3264.                   distance = (int) fabs (tank->x - current_tank->x);
  3265.  
  3266.                   if (distance > _global->halfWidth)
  3267.                     current_score += 100.0 * ( (defensive + 3.0) / 2.0);
  3268.                 }
  3269. #ifdef DEBUG
  3270.               cout << " (dis)" << current_score;
  3271. #endif // DEBUG
  3272.               // Add some points if the target is more intelligent than we are (get'em DOWN!)
  3273.               // or substract if we are the better one. (Deal with the nutter later...)
  3274.               if ( (current_tank->player->type != HUMAN_PLAYER)
  3275.                    && (current_tank->player->type    <    DEADLY_PLAYER))
  3276.                 current_score +=    50 *    ( (int) current_tank->player->type - (int) type);
  3277.               else
  3278.                 current_score    +=    50 *    ( (int) DEADLY_PLAYER - (int) type);
  3279.               // Players, last player type and part time bots are counted as deadly bots.
  3280. #ifdef DEBUG
  3281.               cout << " (typ)" << current_score;
  3282. #endif // DEBUG
  3283.               // Add points for score difference if they have more than us
  3284.               // useless bot: 1 * diff * 60 --> 4 points difference would mean +240 score
  3285.               // deadly bot : 3 * diff * 60 --> 4 points difference would mean +720 score
  3286.               if (current_tank->player->score > score)
  3287.                 current_score    +=    ( (int) type + 1) / 2 * (current_tank->player->score - score) * 60;
  3288.  
  3289. #ifdef DEBUG
  3290.               cout << " (scr)" << current_score;
  3291. #endif // DEBUG
  3292.               if (aPreferredWeapon < WEAPONS)
  3293.                 {
  3294.                   // As we are wanting to fire a weapon, add points, if the damage is greater than the targets health
  3295.                   // (if we are in need for money, but not on the same team)
  3296.                   int iDamageDiff = iDamage - (current_tank->l + current_tank->sh);
  3297.  
  3298.                   if ( (iDamageDiff > 0) && (iMoneyNeed > 0)
  3299.                        &&( ( team == TEAM_NEUTRAL )
  3300.                            ||((team != TEAM_NEUTRAL) && (current_tank->player->team != team))  ) )
  3301.                     current_score += (double) iDamageDiff * (1.0 + ( (defensive + (double) type) / 10.0));
  3302. #ifdef DEBUG
  3303.                   cout << " (dmg)" << current_score;
  3304. #endif // DEBUG
  3305.                   // Check whether the target is buried, and substract points for non-burrower/-penetrator
  3306.                   int iBurylevel = current_tank->howBuried();
  3307.  
  3308.                   if (iBurylevel > BURIED_LEVEL)
  3309.                     {
  3310.                       // The target is burried!
  3311.                       if    ( ( (aPreferredWeapon < BURROWER) || (aPreferredWeapon > PENETRATOR))
  3312.                            && ( (aPreferredWeapon < TREMOR) || (aPreferredWeapon    > TECTONIC)))
  3313.                         {
  3314.                           // Napalm and shaped charges are absolutely useless
  3315.                           if    ( ( (aPreferredWeapon >= SML_NAPALM) && (aPreferredWeapon <= LRG_NAPALM))
  3316.                                || ( (aPreferredWeapon >= SHAPED_CHARGE) && (aPreferredWeapon <= CUTTER)))
  3317.                             current_score    -=    (int) type * 500; // Even the useless bot isn't *that* stupid
  3318.                           else
  3319.                             {
  3320.                               // For all other weapons we go for the radius of the blast
  3321.                               if (iBurylevel < weapon[aPreferredWeapon].radius)
  3322.                                 current_score *= 1.0 - ( ( (double) iBurylevel / (double) weapon[aPreferredWeapon].radius) / 2.0);
  3323.                               else
  3324.                                 current_score    -=    (double) iDamage * (double) type * ( (double) defensive + 3.0);
  3325.                             }
  3326.                         }
  3327.                       else
  3328.                         // As we *want* to fire an appropriate weapon, the target looks rather nice to us!
  3329.                         current_score += ( (double) (iBurylevel - BURIED_LEVEL) / ( ( (double) type + 1.0) / 2.0)) * (double) iDamage;
  3330. #ifdef DEBUG
  3331.                       cout << " (bur)" << current_score;
  3332. #endif // DEBUG
  3333.                     }
  3334.  
  3335.                   // Finally, for weapons, see, if we can do good blast damage!
  3336.                   if (type >= RANGEFINDER_PLAYER)
  3337.                     {
  3338.                       int iBlastBonus = 0;
  3339.                       iBlastBonus = (int) ( (1.0 - ( (double) type / 10.0))
  3340.                                             * getBlastValue (current_tank, weapon[aPreferredWeapon].damage, aPreferredWeapon)
  3341.                                             * ( (defensive - 3.0) / -2.0));
  3342.  
  3343.                       if (iBlastBonus > 0)
  3344.                         {
  3345.                           current_score    +=    iBlastBonus;
  3346.                           // if we need money, blast bonus is valued higher:
  3347.  
  3348.                           if (iMoneyNeed > 0)
  3349.                             current_score    +=    iBlastBonus * ( (double) type / 2.0);
  3350.                         }
  3351.                     }
  3352.                 }
  3353.  
  3354.               if (aRotationMode && ((team == TEAM_NEUTRAL) || (team != current_tank->player->team)))
  3355.                 {
  3356.                   // In rotationmode we try to actually reach the target with the preferred weapon
  3357.                   tank->cw    = aPreferredWeapon;
  3358.                   _target     = current_tank;
  3359.                   _targetX    =    current_tank->x;
  3360.                   _targetY    =    current_tank->y;
  3361.                   iTargettingRound = 0;
  3362.                   int iOvershoot = abs(calculateAttack(current_tank));
  3363.                   if (iOvershoot > _global->screenWidth)
  3364.                     current_score = -1 * MAX_OVERSHOOT; // Wall-Hit! Target is unreachable!
  3365.                   else
  3366.                     current_score -=  iOvershoot;    // substract overshoot!
  3367. #ifdef DEBUG
  3368.                   cout << " (rot)" << current_score;
  3369. #endif // DEBUG
  3370.                 }
  3371.  
  3372. #ifdef DEBUG
  3373.               cout << " => " << current_score<< " : " << current_tank->player->getName() << endl;
  3374. #endif // DEBUG
  3375.  
  3376.               // decide if this target is better than others
  3377.               if ((current_score > best_score) || (!aRotationMode && !best_target))
  3378.                 {
  3379.                   best_score = current_score;
  3380.                   best_target = current_tank;
  3381.                 }
  3382.               attempts++;
  3383.             }
  3384.         }     // end of if we have a valid tank
  3385.     }
  3386.  
  3387.   if (best_target)
  3388.     {
  3389.       _target = best_target;
  3390.       if (_target)
  3391.       {
  3392.         _targetX= _target->x;
  3393.         _targetY= _target->y;
  3394. #ifdef DEBUG
  3395.         cout << " -> " << best_target->player->getName() << " wins! (";
  3396.         cout << best_target->l << " life, " << best_target->sh << " shield)" << endl;
  3397. #endif // DEBUG
  3398.       }
  3399.     }
  3400. #ifdef DEBUG
  3401.   else
  3402.     cout << " -> Unable to find target!!!" << endl;
  3403. #endif // DEBUG
  3404.  
  3405.   return (best_target);
  3406. }
  3407.  
  3408. int PLAYER::getUnburyingTool()
  3409. {
  3410.   int iTool = 0; // small missile, if nothing else fits
  3411.   if (nm[LRG_LAZER])            iTool = LRG_LAZER;
  3412.   if (nm[MED_LAZER])            iTool = MED_LAZER;
  3413.   if (nm[SML_LAZER])            iTool = SML_LAZER;
  3414.   if (ni[ITEM_TELEPORT])    iTool = WEAPONS + ITEM_TELEPORT;
  3415.   if (ni[ITEM_SWAPPER])        iTool = WEAPONS + ITEM_SWAPPER;
  3416.   if (nm[HVY_RIOT_BOMB])    iTool = HVY_RIOT_BOMB;
  3417.   if (nm[RIOT_BOMB])            iTool = RIOT_BOMB;
  3418.   if (nm[RIOT_CHARGE])        iTool = RIOT_CHARGE;
  3419.   if (nm[RIOT_BLAST])            iTool = RIOT_BLAST;
  3420.   return(iTool);
  3421. }
  3422.  
  3423. int PLAYER::computerSelectItem ()
  3424. {
  3425.   int current_weapon = 0; // Current Weapon (defaults to small missile)
  3426.   int iWeaponPool[15];
  3427.   int iPoolSize        =    (int)type * 3;
  3428.   int count             = 0;
  3429.   // Initialize targetting:
  3430.   _target = NULL;
  3431.   _targetX= 0;
  3432.   _targetY= 0;
  3433.  
  3434. #ifdef DEBUG
  3435.   cout << getName() << " : Starting target and weapon evaluation..." << endl;
  3436.   if (defensive < -0.9) cout << "(True Offensive)" << endl;
  3437.   if ((defensive >=-0.9) && (defensive < -0.75)) cout << "(Very Offensive)" << endl;
  3438.   if ((defensive >=-0.75) && (defensive < -0.25)) cout << "(Offensive)" << endl;
  3439.   if ((defensive >=-0.25) && (defensive < 0.00)) cout << "(Slightly Offensive)" << endl;
  3440.   if (defensive == 0.0)    cout << "(Neutral)" << endl;
  3441.   if ((defensive >0.0) && (defensive <= 0.25)) cout << "(Slightly Defensive)" << endl;
  3442.   if ((defensive >0.25) && (defensive <= 0.75)) cout << "(Defensive)" << endl;
  3443.   if ((defensive >0.75) && (defensive <= 0.9)) cout << "(Very Defensive)" << endl;
  3444.   if (defensive > 0.9)    cout << "(True Defensive)" << endl;
  3445.   cout << "----------------------------------" << endl;
  3446. #endif // DEBUG
  3447.   // 1.: Preselection if buried
  3448.  
  3449.   if (tank->howBuried () > BURIED_LEVEL)
  3450.     {
  3451.       current_weapon = getUnburyingTool();
  3452. #ifdef DEBUGctank
  3453.       if (current_weapon < WEAPONS)
  3454.         cout << "I have chosen a \"" << weapon[current_weapon].name << "\" to free myself first!" << endl;
  3455.       else
  3456.         cout << "I have chosen a \"" << item[current_weapon - WEAPONS].name << "\" to free myself first!" << endl;
  3457. #endif // DEBUG
  3458.     }
  3459.   else
  3460.     {
  3461.       // 2.: Determine iPoolSize
  3462.       if (iPoolSize > 15)
  3463.         iPoolSize = 15; // Or part-time-bots would bust array size!
  3464.  
  3465.       // 3.: Fill iWeaponPool
  3466.       iWeaponPool[0] = 0;    // The Small missile is always there!
  3467.       count                    =    1;    // ...so start from second slot!
  3468.  
  3469.       while (count < iPoolSize)
  3470.         {
  3471.           int i    =    0;
  3472.           // bots get a number of tries depending on their intelligence
  3473.           current_weapon    =    0;
  3474.           while (!current_weapon && (i < ( (int) type * 2)))
  3475.             {
  3476.               current_weapon = Select_Random_Weapon();
  3477.               if (! current_weapon)
  3478.                 current_weapon = Select_Random_Item();
  3479.               if    ( (current_weapon    >=    THINGS)    //should never occur, but make it sure!
  3480.                    || ( (current_weapon < WEAPONS) && (!nm[current_weapon]))
  3481.                    || ( (current_weapon >= WEAPONS) && (!ni[current_weapon - WEAPONS])))
  3482.                 current_weapon = 0;
  3483.               i++;
  3484.             }
  3485.  
  3486.           if (!current_weapon && (count > 1))
  3487.             {
  3488.               // Slot 0 is allways the small missile, slot 1 is always the first thing the bot "things" of
  3489.               // "Last Resort" switching takes place from slot 2 on
  3490.               if (nm[MED_MIS])    current_weapon    =    MED_MIS;
  3491.  
  3492.               // Only bots with 4+ slots (rangefinder and up) revert to the large missile
  3493.               if ( (count > 2) &&    nm[LRG_MIS])    current_weapon    = LRG_MIS;
  3494.             }
  3495.           iWeaponPool[count]    =    current_weapon;
  3496.           count++;
  3497.         }
  3498.  
  3499.       // 4a.: check if a dirtball is chosen
  3500.       if ( (iWeaponPool[1] >= DIRT_BALL) && (iWeaponPool[1] <= SUP_DIRT_BALL))
  3501.         current_weapon    =    iWeaponPool[1];
  3502.       else
  3503.         {
  3504.           // 4b.: Sort iWeaponPool, so that the most liked weapon is first
  3505.           //  (...if the bot doesn't "forget" to sort ...)
  3506.           if (rand() % ( (int) type + 1))
  3507.             {
  3508.               bool bIsSorted = false;
  3509.               while (!bIsSorted)
  3510.                 {
  3511.                   bIsSorted    =    true;
  3512.                   // The bot does only sort the first few weapons (type+1)
  3513.                   // Stupid: first two, deadly: first 6
  3514.                   for (int i = 1; i < ( (int) type + 1); i++)
  3515.                     {
  3516.                       if (_weaponPreference[iWeaponPool[i-1]] < _weaponPreference[iWeaponPool[i]])
  3517.                         {
  3518.                           bIsSorted    =    false;
  3519.                           current_weapon    =    iWeaponPool[i-1];
  3520.                           iWeaponPool[i-1] = iWeaponPool[i];
  3521.                           iWeaponPool[i]    =    current_weapon;
  3522.                         }
  3523.                     }
  3524.                 }
  3525.             }
  3526.           current_weapon    =    iWeaponPool[0]; // Most liked weapon, or, if not sorted, the small missile
  3527.           // Having the small missile here means, that the bot is selecting the most easy target.
  3528.           // Obviously that means, that the more stupid a bot is, the more often it will go for the easy strike.
  3529.         }
  3530. #ifdef DEBUG
  3531.       for (int i = 0; i < iPoolSize; i++)
  3532.         {
  3533.           cout << i << ".: ";
  3534.           printf( "% 5d Pref - ", _weaponPreference[iWeaponPool[i]]);
  3535.           if (iWeaponPool[i] < WEAPONS)
  3536.             cout << "\"" << weapon[iWeaponPool[i]].name << "\"" << endl;
  3537.           else
  3538.             cout << "\"" << item[iWeaponPool[i] - WEAPONS].name << "\"" << endl;
  3539.         }
  3540. #endif // DEBUG
  3541.       // if boxed mode is on, we have to try to find a target in rotation mode first!
  3542.       if (    _global->bIsBoxed    && (current_weapon < WEAPONS)
  3543.            && ( (current_weapon <    DIRT_BALL)
  3544.                 ||(current_weapon >    SUP_DIRT_BALL)))
  3545.         {
  3546.           int i        =    0;
  3547.           while ((i < iPoolSize) && !_target)
  3548.             {
  3549.               _target = computerSelectTarget(iWeaponPool[i], true);
  3550.               i++;
  3551.             }
  3552.           if (_target)
  3553.             current_weapon = iWeaponPool[i - 1];
  3554.         }
  3555.       if (!_target)               _target =    computerSelectTarget(current_weapon);
  3556.       #ifdef OLD_GAMELOOP
  3557.       if (!_target && _oldTarget) _target = _oldTarget;
  3558.       #endif
  3559.       if (!_target) return (0); // If there is no target available, we have nothing more to do.
  3560.       _targetX                = _target->x;
  3561.       _targetY                = _target->y;
  3562.  
  3563.       // 5.: if a weapon is choosen, cycle through the pool
  3564.       // to find the best fitting one
  3565.       if ( (current_weapon < WEAPONS)
  3566.            && ( (current_weapon <    DIRT_BALL)
  3567.                 || (current_weapon >    SUP_DIRT_BALL)))
  3568.         {
  3569.           int iBestWeapon        = current_weapon;
  3570.           int iWeaponScore    = 0;         // Score of the currently used weapon
  3571.           int iBestWeapScr    =    -5000;// Best score calculated so far
  3572.           double dDamageMod    =    0.0;    // modifier for how close to targets health
  3573.           int iWeaponDamage    =    0;        // Calculate the (real) weapon damage
  3574.           int iTargetLife   = _target->l + _target->sh;
  3575.           int iBurylevel    = _target->howBuried();
  3576.           for (int i = 0; i < iPoolSize; i++)
  3577.             {
  3578.               current_weapon    =    iWeaponPool[i];
  3579.  
  3580.               // 1.: avoid trying to shoot below the tank's level with lasers
  3581.               if ( (_targetY >= tank->y) &&
  3582.                    ( (current_weapon >= SML_LAZER) && (current_weapon <= LRG_LAZER)))
  3583.                 iWeaponPool[i] = current_weapon = 0; // revert to small missile
  3584. #ifdef DEBUG
  3585.               if (current_weapon < WEAPONS)
  3586.                 cout << " -> \"" << weapon[current_weapon].name << "\" : ";
  3587.               else
  3588.                 cout << " -> (ERROR!) \"" << item[current_weapon - WEAPONS].name << "\" : ";
  3589. #endif // DEBUG
  3590.               // 2.: The closer the weapon damage is to the target health, the:
  3591.               //     - more points added if it kills
  3592.               //     - less points substracted if it doesn't kill
  3593.               
  3594.               // avoid trying to use weapons we do not have
  3595.               if ( ( current_weapon < 0) || (current_weapon >= WEAPONS) )
  3596.                  current_weapon = 0;
  3597.  
  3598.               if (weapon[current_weapon].numSubmunitions > 1)
  3599.                 iWeaponDamage    =    damageMultiplier
  3600.                                 * weapon[weapon[current_weapon].submunition].damage
  3601.                                 * (weapon[current_weapon].numSubmunitions / (defensive + 3.0)); // Clusters don't hit well (napalm?)
  3602.               else
  3603.                 {
  3604.                   if (weapon[current_weapon].spread > 1)
  3605.                     iWeaponDamage    =    damageMultiplier
  3606.                                     * weapon[current_weapon].damage
  3607.                                     * (weapon[current_weapon].spread / (defensive + 2.0)); // Spreads *might* hit well, but do seldom
  3608.                   else
  3609.                     iWeaponDamage    =    damageMultiplier
  3610.                                     * weapon[current_weapon].damage;
  3611.                 }
  3612.  
  3613.               // The more intelligent and defensive a bot is, the more (char *)"savety bonus" is granted:
  3614.               iWeaponDamage = (int) ( (double) iWeaponDamage / (1.0 + ( ( (double) type * (defensive + 2.0)) / 50.0)));
  3615.  
  3616.               // Examples:
  3617.               // Full offensive, useless: 1.0 + ((1.0 * 1.0) / 50.0) = 1.02 <== The damage is like 102% of the real damage
  3618.               // Full defensive, deadly : 1.0 + ((5.0 * 3.0) / 50.0) = 1.30 <== The damage is like 130% of the real damage
  3619. #ifdef DEBUG
  3620.               cout << iWeaponDamage << " damage, ";
  3621. #endif // DEBUG
  3622.               if ( iTargetLife > iWeaponDamage)
  3623.                 {
  3624.                   // The weapon is too weak, substract points. (Less if we are offensive)
  3625.                   dDamageMod    =    (double) iWeaponDamage / (double) iTargetLife;
  3626.                   if (defensive < 0)
  3627.                     iWeaponScore = (10 - (int) type) * (int) ( (-10.0 * (1.1 + defensive)) / dDamageMod);
  3628.                   else
  3629.                     iWeaponScore = (10 - (int) type) * (int) (-10.0 / dDamageMod);
  3630. #ifdef DEBUG
  3631.                   cout << "weak: ";
  3632. #endif // DEBUG
  3633.                   /*    Example calculations:
  3634.                           Bot is deadly: (int)type = 5
  3635.                           Weapon does 25% damage of the targets health: dDamegeMod = 0.25
  3636.                           iWeaponScore = (10 - 5) * (-10 / 0.25) = 5 * -40   = -200
  3637.                           Weapon does 50% damage of the targets health: dDamegeMod = 0.5
  3638.                           iWeaponScore = (10 - 5) * (-10 / 0.5)  = 5 * -20   = -100
  3639.                           Weapon does 75% damage of the targets health: dDamegeMod = 0.75
  3640.                           iWeaponScore = (10 - 5) * (-10 / 0.75) = 5 * -13,3 = -66,5
  3641.                           Weapon does 95% damage of the targets health: dDamegeMod = 0.95
  3642.                           iWeaponScore = (10 - 5) * (-10 / 0.95) = 5 * -10,5 = -52,5 */
  3643.                 }
  3644.               else
  3645.                 {
  3646.                   // The weapon is strong enough, add points (More, if we are defensive)
  3647.                   dDamageMod    =    (double) iTargetLife / (double) iWeaponDamage;
  3648.                   if (defensive > 0)
  3649.                     iWeaponScore    =    (int) type * (int) ( (100.0 * (1.0 + defensive)) * dDamageMod);
  3650.                   else
  3651.                     iWeaponScore    =    (int) type * (int) (100.0 * dDamageMod);
  3652. #ifdef DEBUG
  3653.                   cout << "strong: ";
  3654. #endif // DEBUG
  3655.                   /*    Example calculations:
  3656.                           Bot is deadly: (int)type = 5
  3657.                           Weapon does 105% damage of the targets health: dDamegeMod = 0.95
  3658.                           iWeaponScore = 5 * (100 * 0.95) = 5 * 95   = 475
  3659.                           Weapon does 125% damage of the targets health: dDamegeMod = 0.8
  3660.                           iWeaponScore = 5 * (100 * 0.8)  = 5 * 80   = 400
  3661.                           Weapon does 150% damage of the targets health: dDamegeMod = 0.67
  3662.                           iWeaponScore = 5 * (100 * 0.67) = 5 * 67   = 335
  3663.                           Weapon does 200% damage of the targets health: dDamegeMod = 0.5
  3664.                           iWeaponScore = 5 * (100 * 0.5)  = 5 * 50   = 250 */
  3665.                 }
  3666. #ifdef DEBUG
  3667.               cout << dDamageMod << " dMod -> ";
  3668. #endif // DEBUG
  3669.               // 3.: Check if the way for a laser is clear if choosen
  3670.               if ( (current_weapon >= SML_LAZER) && (current_weapon <= LRG_LAZER))
  3671.                 {
  3672.                   int iXlow, iXhigh, iX, iY;    // temp vars to calculate with
  3673.                   int iRockAmount    =    0;    // How much mountain there is in between
  3674.  
  3675.                   if (tank->x < _targetX)
  3676.                     {
  3677.                       iXlow    =    tank->x;
  3678.                       iXhigh =    _targetX;
  3679.                     }
  3680.                   else
  3681.                     {
  3682.                       iXlow    =    _targetX;
  3683.                       iXhigh =    tank->x;
  3684.                     }
  3685.  
  3686.                   for (iX = iXlow; iX < iXhigh; iX++)
  3687.                     {
  3688.                       iY = tank->y - ( (tank->y - _targetY) / (iXhigh - iX)); // y the laser will be on it's way
  3689.                       if (_env->surface[iX] < iY)
  3690.                         iRockAmount++;    // Rock in the way!
  3691.                     }
  3692.                   iWeaponScore    -=    (int) type * iRockAmount * 10.0;
  3693.                 }
  3694.  
  3695.               // 4.: If the target is burried, add points if we are using an adequate weapon
  3696.               if (iBurylevel > BURIED_LEVEL)
  3697.                 {
  3698.                   // The target is burried!
  3699.                   if    ( ( (current_weapon < BURROWER) || (current_weapon > PENETRATOR))
  3700.                        && ( (current_weapon < TREMOR) || (current_weapon    > TECTONIC)))
  3701.                     {
  3702.                       // Napalm and shaped charges are absolutely useless
  3703.                       if    ( ( (current_weapon >= SML_NAPALM) && (current_weapon <= LRG_NAPALM))
  3704.                            || ( (current_weapon >= SHAPED_CHARGE) && (current_weapon <= CUTTER)))
  3705.                         iWeaponScore    -=    (int) type * 500; // Even the useless bot isn't *that* stupid
  3706.                       else
  3707.                         {
  3708.                           // For all other weapons we go for the radius of the blast
  3709.                           if (iBurylevel < weapon[current_weapon].radius)
  3710.                             iWeaponScore *= 1.0 - ( ( (double) iBurylevel / (double) weapon[current_weapon].radius) / 2.0);
  3711.                           else
  3712.                             iWeaponScore    -=    (double) iWeaponDamage * (double) type * ( (double) defensive + 3.0);
  3713.                         }
  3714.                     }
  3715.                   else
  3716.                     // As we *want* to fire an appropriate weapon, the target looks rather nice to us!
  3717.                     iWeaponScore += ( (double) (iBurylevel - BURIED_LEVEL) / ( ( (double) type + 1.0) / 2.0)) * (double) iWeaponDamage;
  3718.                 }
  3719.  
  3720.               // 5.: Substract points, if we are within the blast radius
  3721.               // 6.: Check, whether other tanks are in the blast radius, and add points according
  3722.               //     to the additional damage delivered
  3723.               // Note: It seems to be paradox, that defensive bots add points for spread/cluster
  3724.               //       weapons, but they a) buy onyl few of them and b) add only noticably points
  3725.               //       if the collateral damage is enough to kill.
  3726.               if (type >= RANGEFINDER_PLAYER)
  3727.                 iWeaponScore    +=    getBlastValue (_target, iWeaponDamage, current_weapon, dDamageMod);
  3728.  
  3729.               // 7.: Try to hit this target with the weapon, and substract the overshoot
  3730.               if ((type >= RANGEFINDER_PLAYER) && (current_weapon < WEAPONS))
  3731.                 {
  3732.                   tank->cw = current_weapon;
  3733.                   iTargettingRound = 0;
  3734.                   iWeaponScore -= abs(calculateAttack(_target));
  3735.                 }
  3736.  
  3737.               // 8.: Modify the Score by weapon preferences
  3738.               if (iWeaponScore > 0)
  3739.                 iWeaponScore    =    (int) ( (double) iWeaponScore
  3740.                                        * (1.0
  3741.                                           + ( (double) _weaponPreference[current_weapon]
  3742.                                               / (double) MAX_WEAP_PROBABILITY)));
  3743.               // (it will only have a slight effect unless the prefs are really wide apart and the
  3744.               //  original points very close to each other)
  3745. #ifdef DEBUG
  3746.               cout << iWeaponScore << " points!" << endl;
  3747. #endif // DEBUG
  3748.               // See if the choice fits better
  3749.               if (iWeaponScore > iBestWeapScr)
  3750.                 {
  3751.                   iBestWeapScr    =    iWeaponScore;
  3752.                   iBestWeapon        =    current_weapon;
  3753.                 }
  3754.             }
  3755.           current_weapon    =    iBestWeapon;
  3756.         }
  3757.     }
  3758.  
  3759.   // 6.: Kamikaze probability:
  3760.   // The more stupid and offensive a bot is, the more chance he has
  3761.   // to blow himself up when life is falling below a certain limit.
  3762.   if    ( ( (tank->l + tank->sh) < 30)
  3763.        || ( ( (tank->l + tank->sh) < 50) && (tank->howBuried () > BURIED_LEVEL)))
  3764.     {
  3765.       // A buried bot needs to free himself first, leaving all others a free shot!
  3766.       double dBlowMod = defensive + 2.0;    // gives 1.0 to 3.0
  3767.       dBlowMod    *=    (double) type / 2.0;        // gives 1.0 to 9.0
  3768.       dBlowMod    +=    1.0;                                    // gives 2.0 to 10.0
  3769.       // dBlowMod is now between  2.0 <== useless full offensive bot (50% chance)
  3770.       //                     and 10.0 <== deadly full defensive bots (10% chance)
  3771.  
  3772.       if (! (rand() % (int) dBlowMod))
  3773.         {
  3774.           // okay, I'm a goner, so BANZAAAAIIII! (maybe, check for a way first)
  3775.           int iWeaponDamage = 0;
  3776.           current_weapon    =    0;
  3777.           if (nm[NUKE])                            current_weapon    =    NUKE;
  3778.           if (nm[DTH_SPREAD])                current_weapon    =    DTH_SPREAD;
  3779.           if (nm[DTH_HEAD])                    current_weapon    =    DTH_HEAD;
  3780.           if (nm[ARMAGEDDON])                current_weapon    =    ARMAGEDDON;
  3781.           if (nm[CUTTER])                        current_weapon    =    CUTTER;
  3782.           if (ni[ITEM_VENGEANCE])        current_weapon    =    ITEM_VENGEANCE        +    WEAPONS;
  3783.           if (ni[ITEM_DYING_WRATH])    current_weapon    =    ITEM_DYING_WRATH    +    WEAPONS;
  3784.           if (ni[ITEM_FATAL_FURY])    current_weapon    =    ITEM_FATAL_FURY        +    WEAPONS;
  3785.           if (current_weapon < WEAPONS)
  3786.             iWeaponDamage = weapon[current_weapon].damage * damageMultiplier;
  3787.           else
  3788.             iWeaponDamage    =        weapon[ (int) item[current_weapon - WEAPONS].vals[0]].damage
  3789.                              *    item[current_weapon - WEAPONS].vals[1]
  3790.                              * damageMultiplier;
  3791.  
  3792.           // Is something available, and do we take others with us?
  3793.           if ( (current_weapon > 0) && (getBlastValue (tank, iWeaponDamage, current_weapon) > 0.0))
  3794.             {
  3795.               // Yieha! Here we go!
  3796. #ifdef DEBUG
  3797.               printf("I have chosen to go bye bye with a...\n");
  3798.               if (current_weapon < WEAPONS)
  3799.                 printf("%s\n", weapon[current_weapon].name);
  3800.               else
  3801.                 printf("%s\n", item[current_weapon - WEAPONS].name);
  3802. #endif // DEBUG
  3803.               _targetX = tank->x;
  3804.               _targetY = tank->y;
  3805.               FLOATTEXT *kamikazeText;
  3806.               kamikazeText = new FLOATTEXT (_global, _env, selectKamikazePhrase(),
  3807.                                             (int) tank->x, (int) tank->y - 30, color, CENTRE);
  3808.               if ( kamikazeText)
  3809.               {
  3810.                 // kamikazeText->xv = 0;
  3811.                 // kamikazeText->yv = -0.4;
  3812.                 kamikazeText->set_speed(0.0, -0.4);
  3813.                 kamikazeText->maxAge = 300;
  3814.               }
  3815.               else
  3816.                   perror ( "player.cc: Failed allocating memory for kamikazeText in computerSelectItem.");
  3817.             }
  3818.         }
  3819.     }
  3820.  
  3821. #ifdef DEBUG
  3822.   if (_target)
  3823.     printf("Finished!\nShooting at %s\n", _target->player->getName() );
  3824.   else
  3825.     printf("Finished!\nI'll free myself.\n");
  3826.   if (current_weapon < WEAPONS)
  3827.     printf("Using weapon %s\n", weapon[current_weapon].name);
  3828.   else
  3829.     printf("Using item %s\n", item[current_weapon - WEAPONS].name);
  3830.   printf("=============================================\n");
  3831. #endif // DEBUG
  3832.   tank->cw = current_weapon;
  3833.   _global->updateMenu = 1;
  3834.   return (current_weapon);
  3835. }
  3836.  
  3837. int PLAYER::computerControls ()
  3838. {
  3839.   int status = 0;
  3840.   // At the most basic: select target, select weapon, aim and fire
  3841.   tank->requireUpdate ();
  3842.   if (_turnStage == SELECT_WEAPON)
  3843.     {
  3844.       computerSelectItem();
  3845.       iTargettingRound = 0;
  3846.       _turnStage = CALCULATE_ATTACK;
  3847.       _global->updateMenu = 1;
  3848.     }
  3849.   else if (_turnStage == SELECT_TARGET)
  3850.     {
  3851.       cout << "ERROR: _turnstage became SELECT_TARGET!" << endl;
  3852.       // Target is already chosen by computerSelectItem()
  3853.       _turnStage = SELECT_WEAPON;
  3854.     }
  3855.   else if (_turnStage == CALCULATE_ATTACK)
  3856.     {
  3857.  
  3858. #ifdef DEBUG_AIM_SHOW
  3859.       _global->bASD = true; // Now it is allowed to be true
  3860. #endif
  3861.  
  3862.       calculateAttack(NULL);
  3863.       _turnStage = AIM_WEAPON; // If the targetting wasn't finished, it will be done later!
  3864.  
  3865. #ifdef DEBUG_AIM_SHOW
  3866.       _global->bASD = false; // And now it isn't!
  3867. #endif
  3868.     }
  3869.   else if (_turnStage == AIM_WEAPON)
  3870.     {
  3871.       // First: Determine whether there are any updates to be done yet:
  3872.       bool bDoAngleUpdate = false;
  3873.       bool bDoPowerUpdate = false;
  3874.       if (iTargettingRound == retargetAttempts)
  3875.         {
  3876.           // Do both when finished aiming
  3877.           bDoAngleUpdate = true;
  3878.           bDoPowerUpdate = true;
  3879.         }
  3880.       else if (iTargettingRound && (abs(_targetAngle - tank->a) <= 90))
  3881.         // Only do Angle update if still aiming and difference isn't too high
  3882.         bDoAngleUpdate = true;
  3883.  
  3884.       if (bDoAngleUpdate && (_targetAngle > tank->a && tank->a < 270) )
  3885.         {
  3886.           // Left (If already aimed)
  3887.           tank->a++;
  3888.           _global->updateMenu = 1;
  3889.         }
  3890.       else if (bDoAngleUpdate && (_targetAngle < tank->a && tank->a > 90) )
  3891.         {
  3892.           // Right (if already aimed)
  3893.           tank->a--;
  3894.           _global->updateMenu = 1;
  3895.         }
  3896.       else if (bDoPowerUpdate && (_targetPower < (tank->p - 3) && tank->p > 0))
  3897.         {
  3898.           // Reduce power (if targetting is finished)
  3899.           tank->p -= 5;
  3900.           _global->updateMenu = 1;
  3901.         }
  3902.       else if (bDoPowerUpdate && (_targetPower > (tank->p + 3) && tank->p < MAX_POWER) )
  3903.         {
  3904.           // Increase Power (if targetting is finished)
  3905.           tank->p += 5;
  3906.           _global->updateMenu = 1;
  3907.         }
  3908.       else
  3909.         {
  3910.           // Targetting finished?
  3911.           if (iTargettingRound == retargetAttempts)
  3912.             _turnStage = FIRE_WEAPON; // Finished aiming, go ahead!
  3913.           else
  3914.             _turnStage = CALCULATE_ATTACK; // Not finished, do some more aiming
  3915.         }
  3916.     }
  3917.   #ifdef OLD_GAMELOOP
  3918.   else if (fi)
  3919.     {
  3920.       // if (fi) don't do any of the following
  3921.     }
  3922.   #endif
  3923.   else if (_turnStage == FIRE_WEAPON)
  3924.     {
  3925.       // tank->activateCurrentSelection ();
  3926.       _global->updateMenu = 1;
  3927.       #ifdef DEBUG
  3928.       printf("About to activate weapon.\n");
  3929.       #endif
  3930.       tank->simActivateCurrentSelection();
  3931.       if (type == VERY_PART_TIME_BOT)
  3932.           type = NETWORK_CLIENT;
  3933.       #ifdef DEBUG
  3934.       printf("Weapon was activated.\n");
  3935.       #endif
  3936.       gloating = false;
  3937.       _turnStage = 0;
  3938.       status = CONTROL_FIRE;
  3939.     }
  3940.   return (status);
  3941. }
  3942.  
  3943. int PLAYER::humanControls ()
  3944. {
  3945.   int moved = 0;
  3946.   int status = 0;
  3947.  
  3948.   if (tank)
  3949.     {
  3950.       if (!_env->mouseclock && mouse_b & 1 && mouse_x >= 250
  3951.           && mouse_x < 378 && mouse_y >= 11 && mouse_y < 19)
  3952.         {
  3953.           _global->updateMenu = 1;
  3954.           if (tank->fs)
  3955.             {
  3956.               tank->sht++;
  3957.             }
  3958.           tank->fs = 1;
  3959.           if (tank->sht > SHIELDS - 1)
  3960.             {
  3961.               tank->sht = 0;
  3962.             }
  3963.         }
  3964.       if (!_env->mouseclock && mouse_b & 1 && mouse_x >= 250
  3965.           && mouse_x < 378 && mouse_y >= 21
  3966.           && mouse_y < 29 && tank->player->ni[tank->sht] > 0 && (tank->fs || tank->sh > 0))
  3967.         {
  3968.           _global->updateMenu = 1;
  3969.           tank->ds = tank->sht;
  3970.           tank->player->ni[tank->sht]--;
  3971.           tank->sh = (int)item[tank->sht].vals[SHIELD_ENERGY];
  3972.         }
  3973.  
  3974.       tank->requireUpdate ();
  3975.     }
  3976.  
  3977.   //Keyboard control
  3978.   if ( _env->stage == STAGE_AIM)
  3979.     {
  3980.       if (tank)
  3981.         {
  3982.           if ( (key[KEY_LEFT] || key[KEY_A]) && !ctrlUsedUp && tank->a < 270)
  3983.             {
  3984.               tank->a++;
  3985.               _global->updateMenu = 1;
  3986.               if (key_shifts & KB_CTRL_FLAG)
  3987.                 ctrlUsedUp = TRUE;
  3988.             }
  3989.           if ( (key[KEY_RIGHT] || key[KEY_D]) && !ctrlUsedUp && tank->a > 90)
  3990.             {
  3991.               tank->a--;
  3992.               _global->updateMenu = 1;
  3993.               if (key_shifts & KB_CTRL_FLAG)
  3994.                 ctrlUsedUp = TRUE;
  3995.             }
  3996.           if ( (key[KEY_DOWN] || key[KEY_S]) && !ctrlUsedUp && tank->p > 0)
  3997.             {
  3998.               tank->p -= 5;
  3999.               _global->updateMenu = 1;
  4000.               if (key_shifts & KB_CTRL_FLAG)
  4001.                 ctrlUsedUp = TRUE;
  4002.             }
  4003.           if ( (key[KEY_UP] || key[KEY_W]) && !ctrlUsedUp && tank->p < MAX_POWER)
  4004.             {
  4005.               tank->p += 5;
  4006.               _global->updateMenu = 1;
  4007.               if (key_shifts & KB_CTRL_FLAG)
  4008.                 ctrlUsedUp = TRUE;
  4009.             }
  4010.           if ( (key[KEY_PGUP] || key[KEY_R]) && !ctrlUsedUp && tank->p < MAX_POWER )
  4011.             {
  4012.               tank->p += 100;
  4013.               if (tank->p > MAX_POWER)
  4014.                 tank->p = MAX_POWER;
  4015.               _global->updateMenu = 1;
  4016.               if (key_shifts & KB_CTRL_FLAG)
  4017.                 ctrlUsedUp = TRUE;
  4018.             }
  4019.           if ( (key[KEY_PGDN] || key[KEY_F]) && !ctrlUsedUp && tank->p > 0)
  4020.             {
  4021.               tank->p -= 100;
  4022.               if (tank->p < 0)
  4023.                 tank->p = 0;
  4024.               _global->updateMenu = 1;
  4025.               if (key_shifts & KB_CTRL_FLAG)
  4026.                 ctrlUsedUp = TRUE;
  4027.             }
  4028.         }
  4029.     }
  4030.  
  4031.     #ifdef OLD_GAMELOOP
  4032.     if (k && !fi)
  4033.     #endif
  4034.     #ifdef NEW_GAMELOOP
  4035.     // if ( keypressed() )
  4036.     //    k = readkey();
  4037.     // else
  4038.     //   k = 0;
  4039.     if (! k)
  4040.     {
  4041.        if ( keypressed() )
  4042.          k = readkey();
  4043.     }
  4044.     if (k)
  4045.     #endif
  4046.     {
  4047.       status = CONTROL_PRESSED;
  4048.       if ( _env->stage == STAGE_AIM)
  4049.         {
  4050.           if (tank)
  4051.             {
  4052.               if (k >> 8 == KEY_N)
  4053.                 {
  4054.                   tank->a = 180;
  4055.                   _global->updateMenu = 1;
  4056.                 }
  4057.               if ( (k >> 8 == KEY_TAB) || (k >> 8 == KEY_C) )
  4058.                 {
  4059.                   _global->updateMenu = 1;
  4060.                   while (1)
  4061.                     {
  4062.                       tank->cw++;
  4063.                       if (tank->cw >= THINGS)
  4064.                         tank->cw = 0;
  4065.                       if (tank->cw < WEAPONS)
  4066.                         {
  4067.                           if (tank->player->nm[tank->cw])
  4068.                             break;
  4069.                         }
  4070.                       else
  4071.                         {
  4072.                           if (item[tank->cw - WEAPONS].selectable && tank->player->ni[tank->cw - WEAPONS])
  4073.                             break;
  4074.  
  4075.                         }
  4076.                     }
  4077.                   //calcDamageMatrix (tank, tank->cw);
  4078.                   //selectTarget ();
  4079.                   changed_weapon = false;
  4080.                 }
  4081.               if ( ( k >> 8 == KEY_BACKSPACE) || ( k >> 8 == KEY_Z) )
  4082.                 {
  4083.                   _global->updateMenu = 1;
  4084.                   while (1)
  4085.                     {
  4086.                       tank->cw--;
  4087.                       if (tank->cw < 0)
  4088.                         tank->cw = THINGS - 1;
  4089.  
  4090.                       if (tank->cw < WEAPONS)
  4091.                         {
  4092.                           if (tank->player->nm[tank->cw])
  4093.                             break;
  4094.                         }
  4095.                       else
  4096.                         {
  4097.                           if (item[tank->cw - WEAPONS].selectable && tank->player->ni[tank->cw - WEAPONS])
  4098.                             break;
  4099.  
  4100.                         }
  4101.                     }
  4102.                   changed_weapon = false;
  4103.                 }
  4104.  
  4105.               // put the tank under computer control
  4106.               if (k >> 8 == KEY_F10) 
  4107.                 {
  4108.                   type = PART_TIME_BOT;
  4109.                   setComputerValues();
  4110.                   return (computerControls());
  4111.                 }
  4112.  
  4113.               // move the tank
  4114.               if (k >> 8 == KEY_COMMA) // || (key >> 8 == KEY_H) )
  4115.                 moved = tank->Move_Tank(DIR_LEFT);
  4116.               else if (k >> 8 == KEY_H)
  4117.                 moved = tank->Move_Tank(DIR_LEFT);
  4118.  
  4119.               if (k >> 8 == KEY_STOP) // || (key >> 8 == KEY_J) )
  4120.                 moved = tank->Move_Tank(DIR_RIGHT);
  4121.               else if (k >> 8 == KEY_J)
  4122.                 moved = tank->Move_Tank(DIR_RIGHT);
  4123.               if (moved)
  4124.                 _global->updateMenu = 1;
  4125.  
  4126.               if ((k >> 8 == KEY_SPACE) &&
  4127.                   (((tank->cw < WEAPONS) && (tank->player->nm[tank->cw] > 0)) ||
  4128.                    ((tank->cw < THINGS) && (tank->player->ni[tank->cw - WEAPONS] > 0))))
  4129.                 {
  4130.                   //    tank->activateCurrentSelection ();
  4131.                   tank->simActivateCurrentSelection();
  4132.                   gloating = false;
  4133.                   status = CONTROL_FIRE;
  4134.                 }
  4135.             }
  4136.         }
  4137.       if ((_env->stage == STAGE_ENDGAME) && (k >> 8 == KEY_ENTER || k >> 8 == KEY_ESC || k >> 8 == KEY_SPACE))
  4138.         return (-1);
  4139.     }
  4140.   return (status);
  4141. }
  4142.  
  4143.  
  4144.  
  4145. // returns a static string to the player's team name
  4146. char *PLAYER::Get_Team_Name()
  4147. {
  4148.   char *team_name = "";
  4149.  
  4150.   switch ( (int) team)
  4151.     {
  4152.     case TEAM_JEDI:
  4153.       team_name = "Jedi";
  4154.       break;
  4155.     case TEAM_NEUTRAL:
  4156.       team_name = "Neutral";
  4157.       break;
  4158.     case TEAM_SITH:
  4159.       team_name = "Sith";
  4160.       break;
  4161.     }
  4162.  
  4163.   return team_name;
  4164. }
  4165.  
  4166.  
  4167.  
  4168. // Pick a weapon to fire at random.
  4169. // The weapon number is returned. If no other
  4170. // weapon is in inventory, then 0 - small missile is
  4171. // used.
  4172. int PLAYER::Select_Random_Weapon()
  4173. {
  4174.   int index;
  4175.   int num_weapons = 0;
  4176.   int random_weapon;
  4177.  
  4178.   // count number of different weapons we have
  4179.   for (index = 1; index < WEAPONS; index++)
  4180.     {
  4181.       if ( nm[index] )
  4182.         num_weapons++;
  4183.     }
  4184.  
  4185.   // we have no weapons, use default
  4186.   if ( num_weapons == 0 )
  4187.     return 0;
  4188.  
  4189.   // pick a random offset from the bottom of the list
  4190.   random_weapon = (rand() % num_weapons) + 1;
  4191.  
  4192.   index = WEAPONS - 1;
  4193.   while ( (index) && (random_weapon) )
  4194.     {
  4195.       if ( nm[index] )
  4196.         random_weapon--;
  4197.       if (random_weapon)
  4198.         index--;
  4199.     }
  4200.  
  4201.   // If the (char *)"weapon" is a dirtball, skip it 75% of the time:
  4202.   if    ( ( (index    ==    DIRT_BALL) || (index    ==    LRG_DIRT_BALL)) && (rand() % 4))
  4203.     return 0;
  4204.  
  4205.   // Skip if it is an unburying tool
  4206.   if    ( (index    ==    RIOT_CHARGE)
  4207.        || (index    ==    RIOT_BOMB)
  4208.        || (index    ==    RIOT_BLAST)
  4209.        || (index    ==    HVY_RIOT_BOMB))
  4210.     return 0;
  4211.   return index;
  4212. }
  4213.  
  4214.  
  4215.  
  4216. // This function selects a random item for
  4217. // the AI to use.
  4218. // This item to use is returned. If no
  4219. // item can be found, then the function
  4220. // returns 0
  4221. int PLAYER::Select_Random_Item()
  4222. {
  4223.   int index, item_num;
  4224.   int num_items = 0;
  4225.   int random_item;
  4226.  
  4227.   // count the items we have
  4228.  
  4229.   for (index = WEAPONS; index < THINGS; index++)
  4230.     {
  4231.       item_num = index - WEAPONS;
  4232.  
  4233.       if ( (ni[item_num]) && (item[item_num].selectable))
  4234.         num_items++;
  4235.     }    // end of counting items
  4236.  
  4237.   if (! num_items)
  4238.     return 0;
  4239.  
  4240.   // if we have an item, there is still a 75% chance
  4241.   // that we may not use it
  4242.   if (rand() % 4)
  4243.     return 0;
  4244.  
  4245.   // pick a random offset from the bottom of the list
  4246.   random_item = (rand() % num_items) + 1;
  4247.  
  4248.   index        = THINGS - 1;
  4249.  
  4250.   item_num =    index - WEAPONS;
  4251.  
  4252.   while (item_num && random_item)
  4253.     {
  4254.       if ( (ni[item_num]) && (item[item_num].selectable))
  4255.         random_item--;
  4256.  
  4257.       if (random_item)
  4258.         {
  4259.           index--;
  4260.           item_num    =    index - WEAPONS;
  4261.         }
  4262.     }
  4263.  
  4264.   if ((item_num    ==    ITEM_TELEPORT) || (item_num == ITEM_SWAPPER))
  4265.     index = 0;
  4266.  
  4267. //  // do not use teleport without a parachute
  4268. //  if ( (item_num == ITEM_TELEPORT) && (! ni[ITEM_PARACHUTE]))
  4269. //    index = 0;
  4270.  
  4271.   // do not self destruct
  4272.   if ( (item_num    ==    ITEM_VENGEANCE)
  4273.        || (item_num    ==    ITEM_DYING_WRATH)
  4274.        || (item_num    ==    ITEM_FATAL_FURY))
  4275.     index = 0;
  4276.  
  4277.   if (index > 0)
  4278.     index = item_num + WEAPONS;
  4279.   return index;
  4280. }
  4281.  
  4282. // This function takes one off the player's time to fire.
  4283. // If the player runs out of time, the function returns TRUE.
  4284. // If the player has time left, or no time clock is being used,
  4285. // then the function returns FALSE.
  4286. int PLAYER::Reduce_Time_Clock()
  4287. {
  4288.   if (! time_left_to_fire)     // not using clock
  4289.     return FALSE;
  4290.  
  4291.   time_left_to_fire--;
  4292.   if (! time_left_to_fire)    // ran out of time
  4293.     {
  4294.       tank->shots_fired++;      // pretend we fired
  4295.       time_left_to_fire = _global->max_fire_time;
  4296.       return TRUE;
  4297.     }
  4298.   return FALSE;
  4299. }
  4300.  
  4301. // The damage provided is not the weapon damage, but what the bot calculated!
  4302. int PLAYER::getBlastValue (TANK * aTarget, int aDamage, int aWeapon, double aDamageMod)
  4303. {
  4304.   int iHealth                = 0;    //    Health of the evaluated tank
  4305.   int iTeam;                            //    Team of the evaluated tank
  4306.   double dDistance;                //    Distance of the target to the evaluated tank
  4307.   double dXdist, dYdist;    //    needed to calculate dDistance
  4308.   int iDmgValue            =    0;    //    The value of the blast
  4309.   int iBlastDamage    = 0;    //    The damage the weapon is doing right there
  4310.   double dTeamMod     = 1.0;//    Teams react differently on TAs
  4311.   int iWeapon                =    0;    //    Items (self destruc) count as CUTTERS for their blast radius!
  4312.   TANK * cOppTank;
  4313.  
  4314.   if (aWeapon < WEAPONS)
  4315.     iWeapon = aWeapon;
  4316.   else
  4317.     iWeapon    =    CUTTER;
  4318.  
  4319.   for (int i = 0; i < _global->numPlayers; i++)
  4320.     {
  4321.       cOppTank    =    _global->players[i]->tank;
  4322.       if (cOppTank && aTarget)
  4323.         {
  4324.           if (this == _global->players[i])
  4325.             // If we'd hit ourself, life is valued twice.
  4326.             iHealth = (cOppTank->l / 2) + cOppTank->sh;
  4327.           else
  4328.             iHealth = cOppTank->l + cOppTank->sh;
  4329.           iTeam        =    _global->players[i]->team;
  4330.           // Only count living targets that are not the official target
  4331.           if ( (iHealth > 0) && (cOppTank != aTarget))
  4332.             {
  4333.               dXdist    =    fabs (aTarget->x - cOppTank->x) - (TANKWIDTH / 2);
  4334.               dYdist    =    fabs (aTarget->y - cOppTank->y) - (TANKHEIGHT / 2);
  4335.               if (dXdist < 0) dXdist = 1.0;
  4336.               if (dYdist < 0) dYdist = 0.0;
  4337.               if    ( (aWeapon >= SHAPED_CHARGE)
  4338.                    && (aWeapon <= CUTTER)
  4339.                    && ( (dYdist        >=    (weapon[iWeapon].radius / 20))
  4340.                         || (dXdist        <=    (TANKWIDTH / 2))))
  4341.                 dDistance = 2.0 * (double) weapon[iWeapon].radius; // out of the way!
  4342.               else
  4343.                 dDistance = ABSDISTANCE(dXdist, dYdist, 0, 0) - ( (double) type * (defensive + 2.0));
  4344.  
  4345.               // Now see whether the tank is in weapon blast range:
  4346.               if (dDistance <= weapon[iWeapon].radius)
  4347.                 {
  4348.                   // Yep, it is!
  4349.                   iBlastDamage = (int) ( (double) aDamage * (1.0 - ( (dDistance / (double) weapon[iWeapon].radius) / 2.0)));
  4350.                   if    ( ( (team != TEAM_NEUTRAL) && (team == iTeam))
  4351.                        || (this == _global->players[i]))
  4352.                     {
  4353.                       // it is a fellow team mate, or ourself
  4354.                       // teams act differently
  4355.                       switch ( (int) team)
  4356.                         {
  4357.                         case TEAM_JEDI:
  4358.                           dTeamMod    = ( (double) type    / 2.0) + 1.5;        // 2.0 up to 4.0
  4359.                           break;
  4360.                         case TEAM_SITH:
  4361.                           dTeamMod    = ( (double) type / 4.0) + 0.75;    // 1.0 up to 2.0
  4362.                           break;
  4363.                         default:
  4364.                           dTeamMod    =    (double) type;    // neutrals won't like to hit themselves at all!
  4365.                         }
  4366.  
  4367.                       if (iBlastDamage > iHealth)
  4368.                         // We would kill our mate, or cost us too much health!
  4369.                         iDmgValue -= (int) (dTeamMod    * ( (double) aDamage             / aDamageMod)    * ( (defensive + 3.0) / 2.0));
  4370.                       else
  4371.                         // We don't kill, but count the blast at least
  4372.                         iDmgValue -= (int) (dTeamMod    * ( (double) iBlastDamage / aDamageMod)    * ( (defensive + 3.0) / 2.0));
  4373.  
  4374.                       // Note: The /=aDamageMod results in blast damage counted worse, if the aDamageMod is low
  4375.                     }
  4376.                   else
  4377.                     {
  4378.                       // Just Someone... see if we kill them
  4379.                       if (iBlastDamage > iHealth)
  4380.                         // Woohoo!
  4381.                         iDmgValue    +=    (int) ( ( (1.0 + (double) type / 10))
  4382.                                              * (double) (iBlastDamage + iHealth)
  4383.                                              * ( (defensive + 3.0) / 2.0));
  4384.                       else
  4385.                         // Well, at least...
  4386.                         iDmgValue    +=    (int) ( ( (1.0 + (double) type / 10))
  4387.                                              * aDamageMod
  4388.                                              * (double) (iBlastDamage)
  4389.                                              * ( (defensive - 3.0) / -2.0));
  4390.                     }
  4391.                 }
  4392.             }
  4393.         }
  4394.     }
  4395. #ifdef DEBUG
  4396.   if ((iDmgValue != 0) && (aDamageMod != 1.0))
  4397.     cout << "(blast: " << iDmgValue << ") ";
  4398. #endif
  4399.   return (iDmgValue);
  4400. }
  4401.  
  4402. int PLAYER::getMoneyToSave()
  4403. {
  4404.   double dMoneyToSave    = 0.0;
  4405.   int iAvgPref            =    0;
  4406.   int iPrefLimit    = 0;
  4407.   int iPrefCount    = 0;
  4408.   int iInvCount     = 0;
  4409.   int iInvMoney     = 0;
  4410.   int iMaxCost            =    0;
  4411.  
  4412.   // if the preferences are exceptionally low, a div by 0 might occur, so it has to be dynamized:
  4413.   for (int i = 0; i < WEAPONS; i++)
  4414.     {
  4415.       if (_weaponPreference[i] > iPrefLimit)
  4416.         iPrefLimit = (iPrefLimit + _weaponPreference[i]) / 2;
  4417.     }
  4418.   // Now it is guaranteed, that iPrefCount and iAvgPref will result in values > 0!
  4419.   for (int i = 0; i < WEAPONS; i++)
  4420.     {
  4421.       if (_weaponPreference[i] > iPrefLimit)
  4422.         {
  4423.           iPrefCount++;
  4424.           iAvgPref += _weaponPreference[i];
  4425.         }
  4426.     }
  4427.   // we are running into divide by zero here.
  4428.   if (iPrefCount < 1) 
  4429.       iPrefCount = 1;
  4430.   iAvgPref /= iPrefCount; // Now the average of all relevant weapon preferences
  4431.  
  4432.   // Now we search for everything over this average and count costs and inventory value:
  4433.   for (int i = 0; i < WEAPONS; i++)
  4434.     {
  4435.       if (_weaponPreference[i] > iAvgPref)
  4436.         {
  4437.           // This weapon is (char *)"wanted"
  4438.           dMoneyToSave += weapon[i].cost;
  4439.           if (weapon[i].cost > iMaxCost)
  4440.             iMaxCost    =    weapon[i].cost;
  4441.           if (nm[i])
  4442.             {
  4443.               // We have (some) if this already!
  4444.               iInvCount++;
  4445.               iInvMoney += (int) ( ( (double) nm[i] / (double) weapon[i].amt) * (double) weapon[i].cost);
  4446.             }
  4447.         }
  4448.     }
  4449.  
  4450.   // The Maximum amount is (type * 2) times the most expensive weapon we like:
  4451.   iMaxCost *= (int) type * 2;
  4452.   // As of writing this, this means a maximum of:
  4453.   // Armageddon: 100,000 credits
  4454.   // Deadly Bot: (int)type == 5
  4455.   // iMaxCost = 100,000 * 5 * 2 = 1,000,000 credits maximum!
  4456.  
  4457.      dMoneyToSave = (double)iMaxCost * 0.25;
  4458. #ifdef DEBUG
  4459.   printf( (char *)"% 4d Prefs, worth % 6d\n", iPrefCount, iAvgPref);
  4460.   printf( (char *)"% 4d Items, worth % 6d\n", iInvCount, iInvMoney);
  4461.   printf( (char *)"MoneyToSave:      % 6d\n", (int)dMoneyToSave);
  4462.   printf( (char *)"Money: % 6d -> MaxCost: %6d\n", (int)money, iMaxCost);
  4463. #endif // DEBUG
  4464.   // Whenever dMoneyToSave is less than the money owned, iBoostItemsBought is resetted
  4465.   if (money > (int)dMoneyToSave)
  4466.     iBoostItemsBought = 0; // Let's go!
  4467.   return ( (int) dMoneyToSave);
  4468. }
  4469.  
  4470.  
  4471.  
  4472. // if we have some shield strength at the end of the round, then
  4473. // reclaim this shield back into our inventory
  4474.  
  4475. int PLAYER::Reclaim_Shield()
  4476. {
  4477.     if (! tank)
  4478.        return FALSE; 
  4479.     if (! last_shield_used)
  4480.        return FALSE;
  4481.  
  4482.     if (tank->sh > 0)
  4483.        ni[last_shield_used] += 1;
  4484.    
  4485.     last_shield_used = 0;
  4486.     return TRUE;
  4487. }
  4488.  
  4489.  
  4490.  
  4491. #ifdef NETWORK
  4492.  
  4493.  
  4494. // Removes the newline character and anything after it
  4495. // from a string.
  4496. void PLAYER::Trim_Newline(char *line)
  4497. {
  4498.     int index = 0;
  4499.  
  4500.     while ( line[index] )
  4501.     {
  4502.        if ( (line[index] == '\n') || (line[index] == '\r') )
  4503.           line[index] = '\0';
  4504.        else
  4505.           index++;
  4506.     }
  4507. }
  4508.  
  4509.  
  4510. // This function checks for incoming data from a client.
  4511. // If data is coming in, we put the incoming data in the net_command
  4512. // variable. If the socket connection is broken, then we will
  4513. // close the socket and hand control over to the AI.
  4514. int PLAYER::Get_Network_Command()
  4515. {
  4516.    int status;
  4517.  
  4518.    status = Check_For_Incoming_Data(server_socket);
  4519.    if (status)
  4520.    {
  4521.       // we have something coming down the pipe
  4522.       memset(net_command, '\0', NET_COMMAND_SIZE);    // clear buffer
  4523.       status = read(server_socket, net_command, NET_COMMAND_SIZE);
  4524.       if (! status)   // connection is broken
  4525.       {
  4526.          close(server_socket);
  4527.          type = DEADLY_PLAYER;
  4528.          printf("%s lost network connection. Returning control to AI.\n", _name);
  4529.          return FALSE;
  4530.       }
  4531.       else   // we got data
  4532.       {
  4533.          net_command[NET_COMMAND_SIZE - 1] = '\0';
  4534.          Trim_Newline(net_command);
  4535.       }
  4536.    }
  4537.    return TRUE;
  4538. }
  4539.  
  4540.  
  4541. // This function gets called during a round when a networked
  4542. // player gets to act. The function checks to see if anything
  4543. // is in the net_command variable. If there is, it handles
  4544. // the request. If not, the function returns.
  4545. //
  4546. // We should have some time keeping in here before this goes live
  4547. // to avoid hanging the game.
  4548.  
  4549. int PLAYER::Execute_Network_Command(int my_turn)
  4550. {
  4551.    char buffer[NET_COMMAND_SIZE];
  4552.    static int player_index = -1;
  4553.    static int fire_delay = 0, net_delay = 0;
  4554.  
  4555.    if (my_turn)
  4556.    {
  4557.        fire_delay++;
  4558.        // if enough time has passed, we give up and turn control over to the AI
  4559.        if (fire_delay >= NET_DELAY)
  4560.        {
  4561.           setComputerValues();
  4562.           type = VERY_PART_TIME_BOT;
  4563.           fire_delay = 0;
  4564.           return ( computerControls() );
  4565.        }
  4566.    }
  4567.  
  4568.    if (! net_command[0])
  4569.    {
  4570.            net_delay++;
  4571.            /*
  4572.            if (my_delay >= NET_DELAY)
  4573.            {
  4574.               my_delay = 0;
  4575.               setComputerValues();
  4576.               type = VERY_PART_TIME_BOT;
  4577.               strcpy(buffer, "PING");
  4578.               write(server_socket, buffer, strlen(buffer));
  4579.               return (computerControls());
  4580.            }
  4581.            else*/ 
  4582.            if (net_delay >= NET_DELAY_SHORT)
  4583.            {
  4584.                // prompt the client to respond
  4585.                strcpy(buffer, "PING");
  4586.                write(server_socket, buffer, strlen(buffer));
  4587.                return 0;
  4588.            }
  4589.            return 0;
  4590.    }     // we did not get a command to process
  4591.  
  4592.    else
  4593.      net_delay = 0;       // we got something, so reset timer
  4594.  
  4595.    if (! strncmp(net_command, "VERSION", 7) )
  4596.    {
  4597.        sprintf(buffer, "SERVERVERSION %s", VERSION);
  4598.        write(server_socket, buffer, strlen(buffer) );
  4599.    }
  4600.    else if (! strncmp(net_command, "CLOSE", 5) )
  4601.    {
  4602.        close(server_socket);
  4603.        type = DEADLY_PLAYER;
  4604.    }
  4605.    else if (! strncmp(net_command, "BOXED", 5) )
  4606.    {
  4607.       char buffer[32];
  4608.       if (_global->bIsBoxed)
  4609.          strcpy(buffer, "BOXED 1");
  4610.       else
  4611.          strcpy(buffer, "BOXED 0");
  4612.       write(server_socket, buffer, strlen(buffer));
  4613.    }
  4614.    else if (! strncmp(net_command, "GOSSIP", 6) )
  4615.    {
  4616.       snprintf(_global->tank_status, 128, "%s", & (net_command[7]) );
  4617.       _global->updateMenu = TRUE;
  4618.    }
  4619.    else if (! strncmp(net_command, "HEALTH", 6) )
  4620.    {
  4621.        int tank_index;
  4622.        char buffer[64];
  4623.  
  4624.        sscanf( &(net_command[7]), "%d", &tank_index );
  4625.        if ( (tank_index >= 0) && (tank_index < _global->numPlayers) )
  4626.        {
  4627.            if (_global->players[tank_index]->tank)
  4628.            {
  4629.                snprintf(buffer, 64, "HEALTH %d %d %d %d", tank_index,
  4630.                         _global->players[tank_index]->tank->l,
  4631.                         _global->players[tank_index]->tank->sh,
  4632.                         _global->players[tank_index]->tank->sht);
  4633.                write(server_socket, buffer, strlen(buffer));
  4634.            }
  4635.        }
  4636.  
  4637.    }
  4638.    else if (! strncmp(net_command, "ITEM", 4) )
  4639.    {
  4640.        char buffer[32];
  4641.        int item_index;
  4642.        sscanf( &(net_command[5]), "%d", &item_index);
  4643.        if ( (item_index >= 0) && (item_index < ITEMS) )
  4644.        {
  4645.           sprintf(buffer, "ITEM %d %d", item_index, ni[item_index]);
  4646.           write(server_socket, buffer, strlen(buffer));
  4647.        }
  4648.    }
  4649.    else if (! strncmp(net_command, "MOVE", 4) )
  4650.    {
  4651.        if (! my_turn) return 0;
  4652.        if (tank)
  4653.        {
  4654.            if (strstr(net_command, "LEFT") )
  4655.               tank->Move_Tank(DIR_LEFT);
  4656.            else
  4657.               tank->Move_Tank(DIR_RIGHT);
  4658.            _global->updateMenu = 1;
  4659.        }
  4660.    }
  4661.    else if (! strncmp(net_command, "FIRE", 4) ) 
  4662.    {
  4663.        int angle = 180, power = 1000, item = 0;
  4664.        if (! my_turn) return 0;
  4665.        sscanf( & (net_command[5]), "%d %d %d", &item, &angle, &power);
  4666.        fire_delay = 0;
  4667.        if (tank)
  4668.        {
  4669.            if (item >= THINGS) item = 0;
  4670.            tank->cw = item;
  4671.            if (item < WEAPONS)
  4672.            {
  4673.               if (nm[tank->cw] < 1)
  4674.                  tank->cw = 0;
  4675.            }
  4676.            else   // item
  4677.            {
  4678.               if ( ni[tank->cw - WEAPONS] < 1)
  4679.                   tank->cw = 0;
  4680.            }
  4681.            tank->a = angle;
  4682.            if (tank->a > 270) tank->a = 270;
  4683.            else if (tank->a < 90) tank->a = 90;
  4684.            tank->p = power;
  4685.            if (tank->p > 2000) tank->p = 2000;
  4686.            else if (tank->p < 0) tank->p = 0;
  4687.            tank->simActivateCurrentSelection();
  4688.            gloating = false;
  4689.            net_command[0] = '\0';
  4690.            return CONTROL_FIRE;
  4691.        }
  4692.    }
  4693.  
  4694.    // find out which player this is
  4695.    else if (! strncmp(net_command, "WHOAMI", 6) )
  4696.    {
  4697.        bool found = false;
  4698.        char buffer[128];
  4699.  
  4700.        while ( (player_index < _global->numPlayers) && (! found) )
  4701.        {
  4702.            if ( _global->players[player_index] == this )
  4703.            {
  4704.                found = true;
  4705.                sprintf(buffer, "YOUARE %d", player_index);
  4706.            }
  4707.            else
  4708.               player_index++;
  4709.        }
  4710.        // check to see if something went very wrong
  4711.        if (! found)
  4712.            strcpy(buffer, "YOUARE -1");
  4713.        write(server_socket, buffer, strlen(buffer));
  4714.    }
  4715.    // return wind speed
  4716.    else if (! strncmp(net_command, "WIND", 4) )
  4717.    {
  4718.       char buffer[64];
  4719.       sprintf(buffer, "WIND %f", _env->wind);
  4720.       write(server_socket, buffer, strlen(buffer));
  4721.    }
  4722.  
  4723.    // find out how many players we have
  4724.    else if (! strncmp(net_command, "NUMPLAYERS", 10) )
  4725.    {
  4726.       char buffer[32];
  4727.       sprintf(buffer, "NUMPLAYERS %d", _global->numPlayers);
  4728.       write(server_socket, buffer, strlen(buffer));
  4729.    }
  4730.    else if (! strncmp(net_command, "PLAYERNAME", 10) )
  4731.    {
  4732.       int my_number;
  4733.       char buffer[128];
  4734.       sscanf( &(net_command[11]), "%d", &my_number);
  4735.       if ( (my_number >= 0) && (my_number < _global->numPlayers) )
  4736.       {
  4737.            sprintf(buffer, "PLAYERNAME %d %s", my_number, _global->players[my_number]->getName() );
  4738.            write(server_socket, buffer, strlen(buffer));
  4739.       }
  4740.    }
  4741.  
  4742.    // how many rounds are we playing
  4743.    else if (! strncmp(net_command, "ROUNDS", 6) )
  4744.    {
  4745.        char buffer[64];
  4746.        sprintf(buffer, "ROUNDS %d %d", (int) _global->rounds, _global->currentround);
  4747.        write(server_socket, buffer, strlen(buffer));
  4748.    }
  4749.    // send back the position of each tank
  4750.    else if (! strncmp(net_command, "TANKPOSITION", 12) )
  4751.    {
  4752.       char buffer[64];
  4753.       int count;
  4754.  
  4755.       sscanf( &(net_command[13]), "%d", &count);
  4756.       if ( (count >= 0) && (count < _global->numPlayers) )
  4757.       { 
  4758.           if (_global->players[count]->tank)
  4759.           {
  4760.              sprintf(buffer, "TANKPOSITION %d %d %d", count, (int) _global->players[count]->tank->x,
  4761.                  (int) _global->players[count]->tank->y);
  4762.              write(server_socket, buffer, strlen(buffer));
  4763.           }
  4764.       }
  4765.    }
  4766.   
  4767.    // send back the surface height of the dirt
  4768.    else if (! strncmp(net_command, "SURFACE", 7) )
  4769.    {
  4770.        char buffer[32];
  4771.        int x;
  4772.  
  4773.        sscanf( &(net_command[8]), "%d", &x);
  4774.        if ( (x >= 0) && (x < _global->screenWidth) )
  4775.        {
  4776.            sprintf(buffer, "SURFACE %d %d", x, _env->surface[x]);
  4777.            write(server_socket, buffer, strlen(buffer));
  4778.        }
  4779.    }
  4780.    else if (! strncmp(net_command, "SCREEN", 6) )
  4781.    {
  4782.       char buffer[64];
  4783.       sprintf(buffer, "SCREEN %d %d", _global->screenWidth, _global->screenHeight);
  4784.       write(server_socket, buffer, strlen(buffer));
  4785.    }
  4786.    else if (! strncmp(net_command, "TEAMS", 5) )
  4787.    {
  4788.       int count;
  4789.       char buffer[32];
  4790.  
  4791.       sscanf( &(net_command[6]), "%d", &count);
  4792.       if ( (count < _global->numPlayers) && (count >= 0) )
  4793.       {
  4794.           sprintf(buffer, "TEAM %d %d", count, (int) _global->players[count]->team);
  4795.           write(server_socket, buffer, strlen(buffer));
  4796.       }
  4797.    }
  4798.    else if (! strncmp(net_command, "WALLTYPE", 8) )
  4799.    {
  4800.       char buffer[32];
  4801.       sprintf(buffer, "WALLTYPE %d", _env->current_wallType);
  4802.       write(server_socket, buffer, strlen(buffer));
  4803.    }
  4804.    else if (! strncmp(net_command, "WEAPON", 6) )
  4805.    {
  4806.       char buffer[32];
  4807.       int weapon_number;
  4808.       sscanf( &(net_command[7]), "%d", &weapon_number);
  4809.       if ( (weapon_number >= 0) && (weapon_number < WEAPONS) )
  4810.       {
  4811.          sprintf(buffer, "WEAPON %d %d", weapon_number, nm[weapon_number]);
  4812.          write(server_socket, buffer, strlen(buffer));
  4813.       }
  4814.    }
  4815.  
  4816.    net_command[0] = '\0';
  4817.    return 0;
  4818. }
  4819.  
  4820. #endif
  4821.  
  4822.  
  4823.  
  4824.  
  4825.