home *** CD-ROM | disk | FTP | other *** search
- /*
- * atanks - obliterate each other with oversize weapons
- * Copyright (C) 2003 Thomas Hudson
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- * */
-
-
- #include "environment.h"
- #include "globaldata.h"
- #include "player.h"
- #include "tank.h"
- #include "menu.h"
- #include "files.h"
- #include "floattext.h"
- #include "network.h"
-
- // When defined draws AI 'planning'
- //#define AI_PLANNING_DEBUG 1
-
- PLAYER::PLAYER (GLOBALDATA *global, ENVIRONMENT *env):_global(global),_env(env),_turnStage(0),_target(NULL),
- _oldTarget(NULL),iTargettingRound(0),revenge(NULL),tank(NULL),pColor(NULL),menuopts(NULL),menudesc(NULL)
- {
- int my_colour;
-
- money = 15000;
- score = 0;
- played = 0;
- won = 0;
- tank = NULL;
- team = TEAM_NEUTRAL;
-
- iBoostItemsBought = -1;
-
- selected = FALSE;
- changed_weapon = false;
-
-
- tank_bitmap = 0.0;
-
- nm[0] = 99;
- for (int count = 1; count < WEAPONS; count++)
- nm[count] = 0;
-
- for (int count = 0; count < ITEMS; count++)
- ni[count] = 0;
-
- strncpy (_name, "New", NAME_LENGTH);
- type = HUMAN_PLAYER;
-
- // 25% of time set to perplay weapon preferences
- preftype = (rand () % 4)?ALWAYS_PREF:PERPLAY_PREF;
- // generatePreferences (); <-- not here!
-
- defensive = (double)((rand () % 10000) - 5000) / 5000.0;
- vengeful = rand () % 100;
- vengeanceThreshold = (double)(rand () % 1000) / 1000.0;
- revenge = NULL;
- /** @TODO: These two values are nowhere used.
- * We should think about a usage, because bot characteristics could become
- * alot more wide spread with their help. **/
- selfPreservation = (double)(rand () % 3000) / 1000;
- painSensitivity = (double)(rand () % 3000) / 1000;
-
- // color = rand () % WHITE;
- my_colour = rand() % 4;
- switch (my_colour)
- {
- case 0:
- color = makecol(200 + (rand() % 56), rand() % 25, rand() % 25);
- break;
- case 1:
- color = makecol( rand() % 25, 200 + (rand() % 56), rand() % 25);
- break;
- case 2:
- color = makecol( rand() % 25, rand() % 25, 200 + (rand() % 56) );
- break;
- case 3:
- color = makecol( 200 + (rand() % 56), rand() % 25, 200 + (rand() % 56));
- break;
- }
- typeText[0] = global->ingame->complete_text[54];
- typeText[1] = global->ingame->complete_text[55];
- typeText[2] = global->ingame->complete_text[56];
- typeText[3] = global->ingame->complete_text[57];
- typeText[4] = global->ingame->complete_text[58];
- typeText[5] = global->ingame->complete_text[59];
- preftypeText[0] = global->ingame->complete_text[60];
- preftypeText[1] = global->ingame->complete_text[61];
- tank_type[0] = global->ingame->complete_text[62];
- tank_type[1] = global->ingame->complete_text[63];
- tank_type[2] = global->ingame->complete_text[64];
- tank_type[3] = global->ingame->complete_text[65];
- tank_type[4] = global->ingame->complete_text[73];
- tank_type[5] = global->ingame->complete_text[74];
- tank_type[6] = global->ingame->complete_text[75];
- tank_type[7] = global->ingame->complete_text[76];
- teamText[0] = global->ingame->complete_text[66];
- teamText[1] = global->ingame->complete_text[67];
- teamText[2] = global->ingame->complete_text[68];
-
- // Weapon Preferences need to be initalized!
- for (int weapCount = 0; weapCount < THINGS; weapCount++)
- _weaponPreference[weapCount] = 0;
-
- menudesc = NULL;
- menuopts = NULL;
- initMenuDesc ();
- skip_me = false;
- #ifdef NETWORK
- server_socket = 0;
- #endif
- }
-
- /*
- PLAYER::PLAYER (GLOBALDATA *global, ENVIRONMENT *env, ifstream &ifsFile, bool file):_global(global),_env(env),
- _target(NULL),revenge(NULL),tank(NULL),pColor(NULL),menuopts(NULL),menudesc(NULL)
- {
- money = 15000;
- score = 0;
- _turnStage = 0;
- selected = FALSE;
-
- tank_bitmap = 0.0;
- team = TEAM_NEUTRAL;
-
- iBoostItemsBought = -1;
-
- nm[0] = 99;
- for (int count = 1; count < WEAPONS; count++)
- nm[count] = 0;
-
- for (int count = 0; count < ITEMS; count++)
- ni[count] = 0;
-
- if (file)
- loadFromFile(ifsFile);
-
- type = (int)type;
-
- typeText[0] = "Human";
- typeText[1] = "Useless";
- typeText[2] = "Guesser";
- typeText[3] = "Ranger";
- typeText[4] = "Targetter";
- typeText[5] = "Deadly";
- preftypeText[0] = "Per Game";
- preftypeText[1] = "Only Once";
- tank_type[0] = "Normal";
- tank_type[1] = "Classic";
- tank_type[2] = "Big Grey";
- tank_type[3] = "T34";
- teamText[0] = "Sith";
- teamText[1] = "Neutral";
- teamText[2] = "Jedi";
- initMenuDesc ();
- skip_me = false;
- #ifdef NETWORK
- net_command[0] = '\0';
- #endif
- }
- */
-
-
- int PLAYER::getBoostValue()
- {
- return ((int)(ni[ITEM_ARMOUR] * ((double) item[ITEM_ARMOUR].vals[0] / (double) item[ITEM_PLASTEEL].vals[0])) + ni[ITEM_PLASTEEL] +
- (int)(ni[ITEM_INTENSITY_AMP] * ((double) item[ITEM_INTENSITY_AMP].vals[0] / (double) item[ITEM_VIOLENT_FORCE].vals[0])) +
- ni[ITEM_VIOLENT_FORCE]);
- }
-
-
-
- void PLAYER::setComputerValues (int aOffset)
- {
- int iType = (int) type + aOffset;
- if (iType > (int) DEADLY_PLAYER)
- iType = (int) DEADLY_PLAYER;
- rangeFindAttempts = (int)(pow(iType + 1, 2) + 1); // Useless: 5 , Deadly: 37
- retargetAttempts = (int)(pow(iType, 2) + 1); // Useless: 2 , Deadly: 26
- focusRate = ( (double) iType * 2.0) / 10.0; // Useless: 0.2, Deadly: 1.0
- errorMultiplier = (double)(DEADLY_PLAYER + 1 - iType) / (double)rangeFindAttempts;
- if (errorMultiplier > 2.0) errorMultiplier = 2.0;
- }
-
- int displayPlayerName (GLOBALDATA *global, ENVIRONMENT *env, int x, int y, void *data);
- void PLAYER::initMenuDesc ()
- {
- GLOBALDATA *global = _global;
- int destroyPlayer (GLOBALDATA *global, ENVIRONMENT *env, void *data);
- int i = 0;
- int height = -68;
-
- // before we get started, eraw any old version of the menu
- if (menuopts)
- free(menuopts);
- if (menudesc)
- free(menudesc);
-
- // menudesc = new MENUDESC;
- menudesc = (MENUDESC *) calloc(1, sizeof(MENUDESC));
- if (!menudesc)
- {
- perror ( "player.cc: Failed allocating memory for menudesc in PLAYER::initMenuDesc");
- return;
- // exit (1);
- }
- menudesc->title = _name;
-
- // Name,Color
- menudesc->numEntries = 10;
- menudesc->okayButton = TRUE;
- menudesc->quitButton = FALSE;
-
- // menuopts = new MENUENTRY[menudesc->numEntries];
- menuopts = (MENUENTRY *) calloc(menudesc->numEntries, sizeof(MENUENTRY));
- if (!menuopts)
- {
- perror ( "player.cc: Failed allocating memory for menuopts in PLAYER::initMenuDesc");
- return;
- // exit (1);
- }
-
- //init memory
- // memset(menuopts, 0, menudesc->numEntries * sizeof(MENUENTRY));
-
- // Player name
- menuopts[i].name = global->ingame->complete_text[29];
- menuopts[i].displayFunc = NULL;
- menuopts[i].color = color;
- menuopts[i].value = (double*)_name;
- menuopts[i].specialOpts = NULL;
- menuopts[i].type = OPTION_TEXTTYPE;
- menuopts[i].viewonly = FALSE;
- menuopts[i].x = _global->halfWidth - 3;
- menuopts[i].y = _global->halfHeight + height;
- i++;
- height += 20;
-
- // Player colour
- menuopts[i].name = global->ingame->complete_text[30];
- menuopts[i].displayFunc = NULL;
- menuopts[i].color = WHITE;
- pColor = &color;
- menuopts[i].value = (double*) pColor;
- menuopts[i].specialOpts = NULL;
- menuopts[i].type = OPTION_COLORTYPE;
- menuopts[i].viewonly = FALSE;
- menuopts[i].x = _global->halfWidth - 3;
- menuopts[i].y = _global->halfHeight + height;
- i++;
- height += 20;
-
- // Player type (human, computer)
- menuopts[i].name = global->ingame->complete_text[31];
- menuopts[i].displayFunc = NULL;
- menuopts[i].color = WHITE;
- menuopts[i].value = (double*)&type;
- menuopts[i].min = 0;
- menuopts[i].max = LAST_PLAYER_TYPE - 1;
- menuopts[i].increment = 1;
- menuopts[i].defaultv = 0;
- menuopts[i].format = "%s";
- menuopts[i].specialOpts = typeText;
- menuopts[i].type = OPTION_SPECIALTYPE;
- menuopts[i].viewonly = FALSE;
- menuopts[i].x = _global->halfWidth - 3;
- menuopts[i].y = _global->halfHeight + height;
- i++;
- height += 20;
-
- menuopts[i].name = global->ingame->complete_text[20];
- menuopts[i].displayFunc = NULL;
- menuopts[i].color = WHITE;
- menuopts[i].value = (double *)&team;
- menuopts[i].min = 0;
- menuopts[i].max = TEAM_JEDI;
- menuopts[i].increment = 1;
- menuopts[i].defaultv = TEAM_NEUTRAL;
- menuopts[i].format = "%s";
- menuopts[i].specialOpts = teamText;
- menuopts[i].type = OPTION_SPECIALTYPE;
- menuopts[i].viewonly = FALSE;
- menuopts[i].x = _global->halfWidth - 3;
- menuopts[i].y = _global->halfHeight + height;
- i++;
- height += 20;
-
- // Player preftype (human, computer)
- menuopts[i].name = global->ingame->complete_text[32];
- menuopts[i].displayFunc = NULL;
- menuopts[i].color = WHITE;
- menuopts[i].value = (double*)&preftype;
- menuopts[i].min = 0;
- menuopts[i].max = ALWAYS_PREF;
- menuopts[i].increment = 1;
- menuopts[i].defaultv = 0;
- menuopts[i].format = "%s";
- menuopts[i].specialOpts = preftypeText;
- menuopts[i].type = OPTION_SPECIALTYPE;
- menuopts[i].viewonly = FALSE;
- menuopts[i].x = _global->halfWidth - 3;
- menuopts[i].y = _global->halfHeight + height;
- i++;
- height += 20;
-
- menuopts[i].name = global->ingame->complete_text[33];
- menuopts[i].displayFunc = NULL;
- menuopts[i].color = WHITE;
- menuopts[i].value = (double*)&played;
- menuopts[i].format = "%.0f";
- menuopts[i].specialOpts = NULL;
- menuopts[i].type = OPTION_DOUBLETYPE;
- menuopts[i].viewonly = TRUE;
- menuopts[i].x = _global->halfWidth - 3;
- menuopts[i].y = _global->halfHeight + height;
- i++;
- height += 20;
-
- menuopts[i].name = global->ingame->complete_text[34];
- menuopts[i].displayFunc = NULL;
- menuopts[i].color = WHITE;
- menuopts[i].value = (double*)&won;
- menuopts[i].format = "%.0f";
- menuopts[i].specialOpts = NULL;
- menuopts[i].type = OPTION_DOUBLETYPE;
- menuopts[i].viewonly = TRUE;
- menuopts[i].x = _global->halfWidth - 3;
- menuopts[i].y = _global->halfHeight + height;
- i++;
- height += 20;
-
- menuopts[i].name = global->ingame->complete_text[35];
- menuopts[i].displayFunc = Display_Tank_Bitmap;
- menuopts[i].color = WHITE;
- menuopts[i].value = &tank_bitmap;
- menuopts[i].min = 0;
- menuopts[i].max = TANK_TYPES;
- menuopts[i].increment = 1;
- menuopts[i].defaultv = 0;
- menuopts[i].format = "%1.0f";
- menuopts[i].specialOpts = tank_type;
- menuopts[i].type = OPTION_SPECIALTYPE;
- menuopts[i].viewonly = FALSE;
- menuopts[i].x = _global->halfWidth - 3;
- menuopts[i].y = _global->halfHeight + height;
- i++;
- height += 20;
-
- menuopts[i].name = global->ingame->complete_text[36];
- menuopts[i].displayFunc = NULL;
- menuopts[i].color = WHITE;
- menuopts[i].value = (double*)destroyPlayer;
- menuopts[i].data = (void*)this;
- menuopts[i].type = OPTION_ACTIONTYPE;
- menuopts[i].viewonly = FALSE;
- menuopts[i].x = _global->halfWidth - 3;
- menuopts[i].y = _global->halfHeight + height;
- i++;
- height += 20;
-
- menudesc->entries = menuopts;
- }
-
- PLAYER::~PLAYER ()
- {
- if (tank)
- delete(tank);
-
- if (menuopts)
- free(menuopts);
- // delete(menuopts);
- if (menudesc)
- free(menudesc);
- // delete(menudesc);
-
- _global = NULL;
- _env = NULL;
- _target = NULL;
- revenge = NULL;
-
- pColor = NULL;
- }
-
-
-
-
- /*
- Save the player settings in text form. Fields are
- formateed as
- [name]=[value]\n
-
- Function returns true on success and false on failure.
- */
- int PLAYER::saveToFile_Text (FILE *file)
- {
- int count;
-
- if (! file) return FALSE;
- // start section with (char *)"*PLAYER*"
- fprintf (file, "*PLAYER*\n");
- fprintf (file, "NAME=%s\n", _name);
- fprintf (file, "VENGEFUL=%d\n", vengeful);
- fprintf (file, "VENGEANCETHRESHOLD=%lf\n", vengeanceThreshold);
- fprintf (file, "TYPE=%lf\n", type);
- fprintf (file, "TYPESAVED=%lf\n", type_saved);
- fprintf (file, "COLOR=%d\n", color);
- fprintf (file, "COLOR2=%d\n", color2);
- for (count = 0; count < THINGS; count++)
- fprintf (file, "WEAPONPREFERENCES=%d %d\n", count, _weaponPreference[count]);
-
- fprintf (file, "PLAYED=%lf\n", played);
- fprintf (file, "WON=%lf\n", won);
- fprintf (file, "PREFTYPE=%lf\n", preftype);
- fprintf (file, "SELFPRESERVATION=%lf\n", selfPreservation);
- fprintf (file, "PAINSENSITIVITY=%lf\n", painSensitivity);
- fprintf (file, "DEFENSIVE=%lf\n", defensive);
- fprintf (file, "TANK_BITMAP=%lf\n", tank_bitmap);
- fprintf (file, "TEAM=%lf\n", team);
- fprintf (file, "***\n");
- return TRUE;
- }
-
-
- /*
- This function tries to load player data from a text file.
- Each line is parsed for (char *)"field=value", except WEAPONPREFERENCES
- which is parsed (char *)"field=index value".
- If all goes well TRUE is returned, on error the function returns FALSE.
- -- Jesse
- */
-
- int PLAYER::loadFromFile_Text (FILE *file)
- {
- char line[MAX_CONFIG_LINE];
- int equal_position, line_length;
- int index, wp_value;
- char field[MAX_CONFIG_LINE], value[MAX_CONFIG_LINE];
- char *result = NULL;
- bool done = false;
-
- if (! file) return FALSE;
-
- // read until we hit line (char *)"*PLAYER*" or "***" or EOF
- do
- {
- result = fgets(line, MAX_CONFIG_LINE, file);
- if (! result) // eof
- return FALSE;
- if (! strncmp(line, "***", 3) ) // end of record
- return FALSE;
- }
- while ( strncmp(line, "*PLAYER*", 8) ); // read until we hit new record
-
- while ( (result) && (!done) )
- {
- // read a line
- memset(line, '\0', MAX_CONFIG_LINE);
- result = fgets(line, MAX_CONFIG_LINE, file);
- if (result)
- {
- // if we hit end of the record, stop
- if (! strncmp(line, "***", 3) ) return TRUE;
- // find equal sign
- line_length = strlen(line);
- // strip newline character
- if ( line[line_length - 1] == '\n')
- {
- line[line_length - 1] = '\0';
- line_length--;
- }
- equal_position = 1;
- while ( ( equal_position < line_length) && (line[equal_position] != '=') )
- equal_position++;
- // make sure we have valid equal sign
- if (equal_position <= line_length)
- {
- // seperate field from value
- memset(field, '\0', MAX_CONFIG_LINE);
- memset(value, '\0', MAX_CONFIG_LINE);
- strncpy(field, line, equal_position);
- strcpy(value, & (line[equal_position + 1]));
- // check which field we have and process value
- if (! strcasecmp(field, "name") )
- strncpy(_name, value, NAME_LENGTH);
- else if (! strcasecmp(field, "vengeful") )
- sscanf(value, "%d", &vengeful);
- else if (! strcasecmp(field, "vengeancethreshold") )
- sscanf(value, "%lf", &vengeanceThreshold);
- else if (! strcasecmp(field, "type") )
- sscanf(value, "%lf", &type);
- else if (! strcasecmp(field, "typesaved") )
- {
- sscanf(value, "%lf", &type_saved);
- if (type_saved > HUMAN_PLAYER)
- type = type_saved;
- }
- else if (! strcasecmp(field, "color") )
- sscanf(value, "%d", &color);
- else if (! strcasecmp(field, "color2") )
- sscanf(value, "%d", &color2);
- else if (! strcasecmp(field, "played") )
- sscanf(value, "%lf", &played);
- else if (! strcasecmp(field, "won") )
- sscanf(value, "%lf", &won);
- else if (! strcasecmp(field, "preftype") )
- sscanf(value, "%lf", &preftype);
- else if (! strcasecmp(field, "selfpreservation") )
- sscanf(value, "%lf", &selfPreservation);
- else if (! strcasecmp(field, "painsensititveity") )
- sscanf(value, "%lf", &painSensitivity);
- else if (! strcasecmp(field, "defensive") )
- sscanf(value, "%lf", &defensive);
- else if (! strcasecmp(field, "tank_bitmap") )
- sscanf(value, "%lf", &tank_bitmap);
- else if (! strcasecmp(field, "team") )
- sscanf(value, "%lf", &team);
- else if (! strcasecmp(field, "weaponpreferences") )
- {
- sscanf(value, "%d %d", &index, &wp_value);
- if ( (index < THINGS) && (index >= 0) )
- _weaponPreference[index] = wp_value;
- }
-
- } // end of valid data line
- } // end of if we read a line properly
- } // end of while not done
-
- // make sure previous human players are restored as humans
- if (type == PART_TIME_BOT)
- type = HUMAN_PLAYER;
-
- return TRUE;
- }
-
-
-
-
- void PLAYER::exitShop ()
- {
- double tmpDM = 0;
-
- damageMultiplier = 1.0;
- tmpDM += ni[ITEM_INTENSITY_AMP] * item[ITEM_INTENSITY_AMP].vals[0];
- tmpDM += ni[ITEM_VIOLENT_FORCE] * item[ITEM_VIOLENT_FORCE].vals[0];
- if (tmpDM > 0)
- damageMultiplier += pow (tmpDM, 0.6);
- }
-
-
- // run this at the begining of each turn
- void PLAYER::newRound ()
- {
- // if the player is under computer control, give it back to the player
- if ( type == PART_TIME_BOT )
- type = HUMAN_PLAYER;
-
- setComputerValues ();
-
- if (!tank)
- {
- tank = new TANK(_global, _env);
- if (tank)
- {
- tank->player = this;
- tank->initialise();
- }
- else
- perror ( "player.cc: Failed allocating memory for tank in PLAYER::newRound");
- }
- // newRound() doesn't need to be called, because ENVIRONMENT::newRound() has already done that!
-
- changed_weapon = false;
- // if we are playing in a campaign, raise the AI level
- if (_global->campaign_mode)
- {
- if ( (type > HUMAN_PLAYER) && (type < DEADLY_PLAYER) )
- type += 1.0;
- }
-
- // forget revenge under certain circumstances
- if (revenge)
- {
- if ((team != TEAM_NEUTRAL) && (team == revenge->team))
- revenge = NULL; // No more round breaking revenge on team mates!
- else if ( (team == TEAM_NEUTRAL)
- ||((team != TEAM_NEUTRAL) && (revenge->team == TEAM_NEUTRAL)))
- {
- // neutral to !neutral and vice versa might forget...
- if ( (!(rand() % (int)labs((type + 3) / 2)))
- ||((rand() % 100) > vengeful) )
- revenge = NULL;
- /* This gives:
- * USELESS: (1 + 3) / 2) = 2 => 50%
- * GUESSER: (2 + 3) / 2) = 2 => 50%
- * RANGEFI: (3 + 3) / 2) = 3 => 34%
- * TARGETT: (4 + 3) / 2) = 3 => 34%
- * DEADLY : (5 + 3) / 2) = 4 => 25%
- * chance to "forgive". Should be okay...
- * The check against "vengeful" makes "peacefull" bots to forget more easily
- */
- }
- }
-
- if (!revenge && (rand() % ((int)type + 1)))
- {
- // If there is no revengee there is a small chance the player will seek the leader!
- int iMaxScore = score;
- int iCurScore = 0;
- for (int i = 0; i < _global->numPlayers; i++)
- {
- if (_global->players[i])
- {
- iCurScore = _global->players[i]->score;
- if ( (iCurScore > iMaxScore)
- &&( (team == TEAM_NEUTRAL)
- ||(team != _global->players[i]->team)) )
- {
- // Higher score found, record as possible revengee
- iMaxScore = iCurScore;
- if (abs(iMaxScore - score) > (int)type)
- revenge = _global->players[i];
- }
- }
- }
- }
-
- time_left_to_fire = (int) _global->max_fire_time;
- skip_me = false;
- iTargettingRound = 0;
- _target = NULL;
- _oldTarget = NULL;
- last_shield_used = 0;
- }
-
-
- void PLAYER::initialise ()
- {
- long int totalPrefs;
- int rouletteCount;
-
- nm[0] = 99;
- for (int count = 1; count < WEAPONS; count++)
- nm[count] = 0;
-
- for (int count = 0; count < ITEMS; count++)
- ni[count] = 0;
-
- totalPrefs = 0;
- for (int weapCount = 0; weapCount < THINGS; weapCount++)
- totalPrefs += _weaponPreference[weapCount];
-
- rouletteCount = 0;
- for (int weapCount = 0; weapCount < THINGS; weapCount++)
- {
- int weapRSpace = (int)((double)_weaponPreference[weapCount] / totalPrefs * NUM_ROULETTE_SLOTS);
- int weapRCount = 0;
-
- if (weapRSpace < 1)
- weapRSpace = 1;
- while (weapRCount < weapRSpace && rouletteCount + weapRCount < NUM_ROULETTE_SLOTS)
- {
- _rouletteWheel[rouletteCount + weapRCount] = weapCount;
- weapRCount++;
- }
- rouletteCount += weapRSpace;
- }
- while (rouletteCount < NUM_ROULETTE_SLOTS)
- _rouletteWheel[rouletteCount++] = rand () % THINGS;
-
- kills = 0;
- killed = 0;
- tank = NULL;
- _target = NULL;
- _oldTarget = NULL;
- }
-
- void PLAYER::generatePreferences ()
- {
- double dBaseProb = (double) MAX_WEAP_PROBABILITY / 2.0;
- int currItem = 0;
- double dWorth;
- int iValue;
- bool bIsWarhead = false;
- int iMaxWeapPref = 0;
- int iMaxItemPref = 0;
-
- defensive = (2.0 * ( (double) rand () / (double) RAND_MAX)) - 1.0;
- double dDefenseMod = (defensive * -1.0) + 2.0;
- // DefenseMod will be between 1.0 (defensive) and 3.0 (offensive)
- // and is used to modifiy vengeful, vengeanceThreshold, selfPreservation and painSensitivity
- vengeful *= 1.0 + (dDefenseMod / 5.0); // +0% - +60% (defensive - offensive)
- if (vengeful > 100) vengeful = 100;
- vengeanceThreshold *= 1.0 - (dDefenseMod / 5.0); // -0% - -60%
- selfPreservation /= dDefenseMod;
- painSensitivity /= dDefenseMod;
-
- // Now defensive can be modified by team:
- if (team == TEAM_JEDI)
- {
- defensive += (double)((double)(rand() % 1000)) / 1000.0;
- if (defensive > 1.25)
- defensive = 1.25; // + 1.25 is SuperDefensive
- }
- if (team == TEAM_SITH)
- {
- defensive -= (double)((double)(rand() % 1000)) / 1000.0;
- if (defensive < -1.25)
- defensive = -1.25; // - 1.25 is SuperAggressive
- }
- #ifdef DEBUG
- cout << "Generating Preferences for \"" << getName() << "\" (" << defensive << ")" << endl;
- cout << "----------------------------------------------------" << endl;
- #endif // DEBUG
- _weaponPreference[0] = 0; // small missiles are always zero!
- for (int weapCount = 1; weapCount < THINGS; weapCount++)
- {
- dWorth = -1.0 * ( (double) MAX_WEAP_PROBABILITY / 4.0);
- bIsWarhead = false;
- if (weapCount < WEAPONS)
- {
- // Talking about weapons
- currItem = weapCount;
- if (weapon[currItem].warhead || ( (currItem >= SML_METEOR) && (currItem <= LRG_LIGHTNING)))
- bIsWarhead = true; // Bots don't think about warheads or environment!
- else
- {
- // 1. Damage:
- int iWarheads = weapon[currItem].spread; // For non-spread this is always 1
- if (weapon[currItem].numSubmunitions > 0)
- {
- iWarheads = weapon[currItem].numSubmunitions; // It's a cluster
-
- dWorth = weapon[weapon[currItem].submunition].damage * iWarheads; // total damage for clusters
-
- if ( ( (currItem >= SML_NAPALM) && (currItem <= LRG_NAPALM))
- || ( (currItem >= FUNKY_BOMB) && (currItem <= FUNKY_DEATH)))
- dWorth /= defensive + 1.0 + ( (double) type / 2.0);
- // These weapons are too unpredictable to be counted full
- // But a true offensive useless bot devides only by 1.0 (not all all, doesn't mind)
- // And a true defensive deadly bot devides by 5.0
- if (dWorth > dBaseProb) dWorth = dBaseProb; // Or Large Napalm will always be everybodys favorite
- }
- else
- dWorth = weapon[currItem].damage * (iWarheads * 2.0); // total damage for (non-)spreads
- // Note: iWarheads is counted twice, because otherwise spread weapons get far too low score!
- dWorth = dWorth * (dBaseProb * 0.0005); // 1 Damage is worth 0.5%o of dBaseProb
-
- // 2. Defensiveness multiplier
- // As said above, defensive players avoid spread/cluster weapons. Thus they rate non-spreads higher:
- if (iWarheads == 1)
- dWorth *= (defensive + 1.5) * ( (double) type / 2.0);
- else
- dWorth -= (defensive * ( (double) type / 2.0)) * dWorth;
-
- // 3. Dirtballs do no damage and have to be rated by defensiveness
- if ( (currItem >= DIRT_BALL) && (currItem <= SUP_DIRT_BALL))
- dWorth = (double) (currItem - (double) DIRT_BALL + 1.0) * 150.0 * ( (double) type / 2.0) * (defensive + 2.0);
-
- // 4.: Shaped charges, wide boys and cutters are deadly but limited:
- if ( (currItem >= SHAPED_CHARGE) && (currItem <= CUTTER))
- dWorth *= 1.0 - ( ( (double) type + (defensive * 5.0)) / 20.0);
- // useless, full offensive: * 1.20
- // deadly, full defensive : * 0.50
-
- // 5.: rollers and penetrators are modified by type, as they *are* usefull
- if ( ((currItem >= SML_ROLLER) && (currItem <= DTH_ROLLER))
- ||((currItem >= BURROWER) && (currItem <= PENETRATOR)))
- dWorth *= 1.0 + ((double)type / 10.0) + (defensive / 2);
-
- // 6.: Tectonis need to be raised!
- if ((currItem >= TREMOR) && (currItem <= TECTONIC))
- dWorth *= 2.0 + ((double)type / 10.0) + (defensive / 2);
-
- // finally dWorth must not be greater than the 3/4 of MAX_WEAPON_PROBABILITY
- if (dWorth > (MAX_WEAP_PROBABILITY * 0.75))
- dWorth = MAX_WEAP_PROBABILITY * 0.75;
- }
- }
- else
- {
- // Talking about items
- currItem = weapCount - WEAPONS;
- // unfortunately we can only switch here...
- /* Theory:
- As for armour/amps/shields, offensive bots go for amps and reflector
- shields, defensive bots go for armour and hard shields. */
- switch (currItem)
- {
- case ITEM_TELEPORT:
- dWorth = -1.0 * ((defensive - 1.5) * ((double)MAX_WEAP_PROBABILITY / 10.0));
- break;
- case ITEM_SWAPPER:
- dWorth = -1.0 * ((defensive - 1.5) * ((double)MAX_WEAP_PROBABILITY / 7.5));
- break;
- case ITEM_FAN:
- dWorth = 0.0; // useless things!
- break;
- case ITEM_VENGEANCE:
- case ITEM_DYING_WRATH:
- case ITEM_FATAL_FURY:
- dWorth = (defensive + 1.5) * ((double)weapon[(int)item[currItem].vals[0]].damage * (double)item[currItem].vals[1]);
- break;
- case ITEM_ARMOUR:
- case ITEM_PLASTEEL:
- dWorth = dBaseProb * ( (double) item[currItem].vals[0] / (double) item[ITEM_PLASTEEL].vals[0]);
- dWorth *= defensive;
- break;
- case ITEM_LGT_SHIELD:
- case ITEM_MED_SHIELD:
- case ITEM_HVY_SHIELD:
- dWorth = dBaseProb * ( (double) item[currItem].vals[0] / (double) item[ITEM_HVY_SHIELD].vals[0]);
- dWorth *= defensive;
- break;
- case ITEM_INTENSITY_AMP:
- case ITEM_VIOLENT_FORCE:
- dWorth = dBaseProb * ( (double) item[currItem].vals[0] / (double) item[ITEM_VIOLENT_FORCE].vals[0]);
- dWorth *= (-1.0 * defensive);
- break;
- case ITEM_LGT_REPULSOR_SHIELD:
- case ITEM_MED_REPULSOR_SHIELD:
- case ITEM_HVY_REPULSOR_SHIELD:
- dWorth = dBaseProb * ( (double) item[currItem].vals[0] / (double) item[ITEM_HVY_REPULSOR_SHIELD].vals[0]);
- dWorth *= (-1.0 * defensive);
- break;
- case ITEM_REPAIRKIT:
- dWorth = dBaseProb * ( (defensive + 1.0) / 2.0);
- break;
- case ITEM_PARACHUTE:
- dWorth = dBaseProb * ( (defensive + 1.0) / 1.5); // Parachutes *are* popular! :)
- break;
- case ITEM_SLICKP:
- dWorth = (int) type * 250;
- break;
- case ITEM_DIMPLEP:
- dWorth = (int) type * 500;
- break;
- case ITEM_FUEL:
- dWorth = -5000; // Bots don't need fuel
- bIsWarhead = true; // Yes, it's a lie. ;-)
- }
- // dWorth must not be greater than the half of MAX_WEAPON_PROBABILITY
- if (dWorth > (MAX_WEAP_PROBABILITY / 2))
- dWorth = MAX_WEAP_PROBABILITY / 2;
- }
- iValue = fabs (dWorth);
- if (iValue < (MAX_WEAP_PROBABILITY / 10)) iValue = MAX_WEAP_PROBABILITY / 10;
- dWorth += (double) (rand() % iValue); // allow to double (more or less)
-
- if (dWorth > MAX_WEAP_PROBABILITY)
- dWorth = MAX_WEAP_PROBABILITY;
- if (dWorth < (MAX_WEAP_PROBABILITY / 100.0))
- dWorth = MAX_WEAP_PROBABILITY / 100.0; // Which is very very little...
-
- if (bIsWarhead)
- _weaponPreference[weapCount] = 0; // It will not get any slot!
- else
- _weaponPreference[weapCount] = (int) dWorth;
-
- if ((weapCount < WEAPONS) && (_weaponPreference[weapCount] > iMaxWeapPref))
- iMaxWeapPref = _weaponPreference[weapCount];
- if ((weapCount >= WEAPONS) && (_weaponPreference[weapCount] > iMaxItemPref))
- iMaxItemPref = _weaponPreference[weapCount];
-
- #ifdef DEBUG
- if (weapCount < WEAPONS)
- printf( "%23s (weapon): % 5d", weapon[weapCount].name, _weaponPreference[weapCount]);
- else
- printf( "%23s ( item ): % 5d", item[weapCount-WEAPONS].name, _weaponPreference[weapCount]);
- cout << endl;
- #endif // DEBUG
- }
-
- // Before we are finished, we need to amplify the preferences (well, maybe...)
- if (iMaxWeapPref < MAX_WEAP_PROBABILITY)
- {
- // Yes, amplification for the weapons needed!
- dWorth = (double)MAX_WEAP_PROBABILITY / (double)iMaxWeapPref;
- for (int weapCount = 1; weapCount < WEAPONS; weapCount++)
- {
- if (_weaponPreference[weapCount] > (MAX_WEAP_PROBABILITY / 100.0))
- {
- _weaponPreference[weapCount] = (int)((double)_weaponPreference[weapCount] * dWorth);
- #ifdef DEBUG
- printf( "%23s (weapon) amplified to: % 5d", weapon[weapCount].name, _weaponPreference[weapCount]);
- cout << endl;
- #endif // DEBUG
- }
- }
- }
- if (iMaxItemPref < MAX_WEAP_PROBABILITY)
- {
- // Yes, amplification for the items needed!
- dWorth = (double)MAX_WEAP_PROBABILITY / (double)iMaxItemPref;
- for (int weapCount = WEAPONS; weapCount < THINGS; weapCount++)
- {
- if (_weaponPreference[weapCount] > (MAX_WEAP_PROBABILITY / 100.0))
- {
- _weaponPreference[weapCount] = (int)((double)_weaponPreference[weapCount] * dWorth);
- #ifdef DEBUG
- printf( "%23s ( item ) amplified to: % 5d", item[weapCount-WEAPONS].name, _weaponPreference[weapCount]);
- cout << endl;
- #endif // DEBUG
- }
- }
- }
-
- #ifdef DEBUG
- cout << "===================================================" << endl << endl;
- #endif // DEBUG
- }
-
- int PLAYER::selectRandomItem ()
- {
- // return (_rouletteWheel[rand () % NUM_ROULETTE_SLOTS]);
- return rand() % THINGS;
- }
-
- void PLAYER::setName (char *name)
- {
- // initalize name
- memset(_name, '\0', NAME_LENGTH);
- strncpy (_name, name, NAME_LENGTH - 1);
- }
-
- int PLAYER::controlTank ()
- {
- if (key[KEY_F1])
- save_bmp ( "scrnshot.bmp", _env->db, NULL);
-
- if (key_shifts & KB_CTRL_FLAG && ctrlUsedUp)
- {
- if (key[KEY_LEFT] || key[KEY_RIGHT] ||
- key[KEY_UP] || key[KEY_DOWN] ||
- key[KEY_PGUP] || key[KEY_PGDN] ||
- key[KEY_A] || key[KEY_D] || //additional control
- key[KEY_W] || key[KEY_S] ||
- key[KEY_R] || key[KEY_F])
- ctrlUsedUp = TRUE;
- else
- ctrlUsedUp = FALSE;
- }
- else
- {
- ctrlUsedUp = FALSE;
- }
-
- if (_global->computerPlayersOnly &&
- ((int)_global->skipComputerPlay >= SKIP_HUMANS_DEAD))
- {
- if (_env->stage == STAGE_ENDGAME)
- return (-1);
- }
-
- k = 0;
- #ifdef NEW_GAMELOOP
- if ( keypressed() )
- #else
- if (keypressed () && !fi)
- #endif
- {
- k = readkey ();
-
- if ((_env->stage == STAGE_ENDGAME) &&
- (k >> 8 == KEY_ENTER ||
- k >> 8 == KEY_ESC ||
- k >> 8 == KEY_SPACE))
- return (-1);
- if ( (k >> 8 == KEY_ESC) || (k >> 8 == KEY_P) )
- {
- void clockadd ();
- install_int_ex (clockadd, SECS_TO_TIMER(6000));
- int mm = _env->ingamemenu ();
- install_int_ex(clockadd, BPS_TO_TIMER(_global->frames_per_second));
- _env->make_update (0, 0, _global->screenWidth, _global->screenHeight);
- _env->make_bgupdate (0, 0, _global->screenWidth, _global->screenHeight);
-
- //Main Menu
- if (mm == 1)
- {
- _global->command = GLOBAL_COMMAND_MENU;
- return (-1);
- }
- else if (mm == 2) //Quit game
- {
- _global->command = GLOBAL_COMMAND_QUIT;
- return (-1);
- }
- else if (mm == 3) // skip AI
- {
- return (-2);
- }
- }
- // check for number key being pressed
- if ( (k >> 8 >= KEY_0) && (k >> 8 <= KEY_9) )
- {
- int value = (k >> 8) - KEY_0;
-
- // make sure the value is within range
- if (value < _global->numPlayers)
- {
- if ( _global->players[value] )
- {
- TANK *my_tank = _global->players[value]->tank;
- if (my_tank)
- {
- 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() );
- /* We do this in atanks.cpp, to kill this wretched "No Format Error"
- strcat(_global->tank_status, _global->players[value]->Get_Team_Name()); */
- _global->tank_status_colour = _global->players[value]->color;
- _global->updateMenu = 1;
- }
- else
- _global->tank_status[0] = 0;
- }
- }
-
- } // end of check status keys
- }
-
- if ((int)type == HUMAN_PLAYER || !tank)
- {
- return (humanControls ());
- }
- #ifdef NETWORK
- else if ((int) type == NETWORK_CLIENT)
- return (Execute_Network_Command(TRUE));
- #endif
- else if (_env->stage == STAGE_AIM)
- {
- return (computerControls ());
- }
- return (0);
- }
-
- int PLAYER::computerSelectPreBuyItem (int aMaxBoostValue)
- {
- double dMood = 1.0 + defensive + (double) ( (double) (rand() / ( (double) RAND_MAX / 2.0)));
- // dMood is 0.0 <= x <= 4.0
- int currItem = 0;
- /* Prior buying anything else, a 5 step system takes place:
- 1.: Parachutes (if gravity is on)
- 2.: Minimum weapon probability (aka 5 medium and 3 large missiles
- 3.: Armor/Amps
- 4.: "Tools" to free themselves like Riot Blasts
- 5.: Shields, if enough money is there
- 6.: if all is set, look for dimpled/slick projectiles! */
-
- // Step 1:
-
- if ( (type >= RANGEFINDER_PLAYER)
- && (_env->landSlideType > LANDSLIDE_NONE)
- && (ni[ITEM_PARACHUTE] < 10))
- currItem = WEAPONS + ITEM_PARACHUTE;
-
- // Step 3:
- // Even here bots might forget
- if (!currItem && (rand() % ( (int) type + 1)))
- {
- int iLimit = aMaxBoostValue - getBoostValue(); // > 0 means: Someone has more than we have!
- #ifdef DEBUG
- printf( "%10s: Boost: %4d, Max: %4d, Limit: %4d\n", getName(), getBoostValue(), aMaxBoostValue, iLimit);
- #endif // DEBUG
- if ( ((dMood >= 2.75) && (iLimit > 0)) || ((dMood >= 2.0) && (iLimit > getBoostValue())) )
- {
- // The player is in a defensive mood
- // If we have 25% more money than the plasteel cost, buy it, else the armour will do
- if (money >= (item[ITEM_PLASTEEL].cost * 1.25))
- currItem = WEAPONS + ITEM_PLASTEEL;
- else
- if ( (money >= (item[ITEM_ARMOUR].cost * 2.0))
- && ( (ni[ITEM_ARMOUR] < ni[ITEM_PLASTEEL])
- || (dMood >= 3.5)))
- currItem = WEAPONS + ITEM_ARMOUR;
- }
-
- // Now iBoostItemsBought must be checked:
- if (currItem && (iBoostItemsBought >= (int)type))
- currItem = 0; // Sorry, bought enough this round!
- if (currItem && (iBoostItemsBought < (int)type))
- iBoostItemsBought++; // Okay, take it!
- }
-
- // 5.: Shields
- if (!currItem && (rand() % ( (int) type + 1)) && ( (int) type >= RANGEFINDER_PLAYER))
- {
- if (dMood <= 1.5)
- {
- // offensive type, go through reflectors
- if ( (ni[ITEM_LGT_REPULSOR_SHIELD] <= (item[ITEM_LGT_REPULSOR_SHIELD].amt * (int) type))
- && (money >= (item[ITEM_LGT_REPULSOR_SHIELD].cost * 2.0)))
- currItem = WEAPONS + ITEM_LGT_REPULSOR_SHIELD;
-
- if ( (ni[ITEM_MED_REPULSOR_SHIELD] <= (item[ITEM_MED_REPULSOR_SHIELD].amt * (int) type))
- && (money >= (item[ITEM_MED_REPULSOR_SHIELD].cost * 1.75)))
- currItem = WEAPONS + ITEM_MED_REPULSOR_SHIELD;
-
- if ( (ni[ITEM_HVY_REPULSOR_SHIELD] <= (item[ITEM_HVY_REPULSOR_SHIELD].amt * (int) type))
- && (money >= (item[ITEM_HVY_REPULSOR_SHIELD].cost * 1.5)))
- currItem = WEAPONS + ITEM_HVY_REPULSOR_SHIELD;
- }
-
- if (dMood >= 2.5)
- {
- // defensive type, go through hard shields
- if ( (ni[ITEM_LGT_SHIELD] <= (item[ITEM_LGT_SHIELD].amt * (int) type))
- && (money >= (item[ITEM_LGT_SHIELD].cost * 2.0)))
- currItem = WEAPONS + ITEM_LGT_SHIELD;
-
- if ( (ni[ITEM_MED_SHIELD] <= (item[ITEM_MED_SHIELD].amt * (int) type))
- && (money >= (item[ITEM_MED_SHIELD].cost * 1.75)))
- currItem = WEAPONS + ITEM_MED_SHIELD;
-
- if ( (ni[ITEM_HVY_SHIELD] <= (item[ITEM_HVY_SHIELD].amt * (int) type))
- && (money >= (item[ITEM_HVY_SHIELD].cost * 1.5)))
- currItem = WEAPONS + ITEM_HVY_SHIELD;
- }
- }
-
- return (currItem);
- }
-
- int PLAYER::chooseItemToBuy (int aMaxBoostValue)
- {
- int currItem = computerSelectPreBuyItem (aMaxBoostValue);
- int oldItem = 0;
- int itemNum = 0;
- int cumulative;
- int nextMod, curramt, newamt;
- int iTRIES = THINGS; // pow((int)type + 1, 2);
- int iDesiredItems[iTRIES]; // Deadly bots have large shopping carts. ;)
- bool bIsSorted = false; // Whether the cart is sorted or not
- bool bIsPreSelected = currItem?true:false; // Whether or not the PreBuy steps found something
- int i = 0;
-
- if (currItem)
- {
- if ( Buy_Something(currItem) )
- return currItem;
- }
-
- // init desired items
- for (i = 0; i < iTRIES; i++)
- iDesiredItems[i] = 0;
-
- // 1.: Fill cart
- i = 0;
- while (i < iTRIES)
- {
- // while (currItem == oldItem)
- currItem = (int) fabs (selectRandomItem());
- oldItem = currItem;
- if (currItem >= THINGS)
- currItem %= THINGS; // Put in range
- // now currItem is 0<= currItem < THINGS
- if ( (_env->isItemAvailable (currItem)) && (currItem != 0))
- {
- iDesiredItems[i] = currItem;
- }
- i++;
- }
-
- // 2.: sort these items by preferences
- while (!bIsSorted)
- {
- if (bIsPreSelected)
- i = 2; // The first item shall not be sorted somewhere else!
- else
- i = 1;
- bIsSorted = true;
- while (i < iTRIES)
- {
- if (_weaponPreference[iDesiredItems[i-1]] < _weaponPreference[iDesiredItems[i]])
- {
- bIsSorted = false;
- currItem = iDesiredItems[i];
- iDesiredItems[i] = iDesiredItems[i-1];
- iDesiredItems[i-1] = currItem;
- }
- i++;
- }
- }
-
- // 3.: loop through all weapon preferences
- for (int i = 0; i < iTRIES; i++)
- {
- currItem = iDesiredItems[i];
- itemNum = currItem - WEAPONS;
- //determine the likelyhood of purchasing this selection
- //less likely the more of the item is owned
- //if have zero of item, it is a fifty/fifty chance of purchase
- if (currItem < WEAPONS)
- {
- curramt = nm[currItem];
- newamt = weapon[currItem].amt;
- cumulative = FALSE;
- }
- else
- {
- curramt = ni[itemNum];
- newamt = item[itemNum].amt;
- if ( (itemNum >= ITEM_INTENSITY_AMP &&
- itemNum <= ITEM_VIOLENT_FORCE) ||
- (itemNum == ITEM_REPAIRKIT) ||
- (itemNum >= ITEM_ARMOUR &&
- itemNum <= ITEM_PLASTEEL))
- cumulative = TRUE;
- else
- cumulative = FALSE;
- }
- nextMod = 1;
- if (!cumulative)
- nextMod = curramt / newamt;
- if (nextMod <= 0)
- nextMod = 1;
- if (rand () % nextMod)
- continue;
-
- //weapon
- if (currItem < WEAPONS)
- {
- //don't buy if already maxed out
- if (nm[currItem] >= 99)
- continue;
-
- //purchase the item
- if (money >= weapon[currItem].cost)
- {
- money -= weapon[currItem].cost;
- nm[currItem] += weapon[currItem].amt;
- //don't allow more than 99
- if (nm[currItem] > 99)
- nm[currItem] = 99;
- return currItem;
- }
- }
- else //item
- {
- //don't buy if already maxed out
- if (ni[itemNum] >= 99)
- continue;
- //purchase the item
- if (money >= item[itemNum].cost)
- {
- // Check against iBoostItemsBought
- if ( (itemNum >= ITEM_INTENSITY_AMP &&
- itemNum <= ITEM_VIOLENT_FORCE) ||
- (itemNum >= ITEM_ARMOUR &&
- itemNum <= ITEM_PLASTEEL))
- {
- if ((iBoostItemsBought >= (int)type)
- ||(getBoostValue() > aMaxBoostValue))
- continue; // no chance pal!
- else
- iBoostItemsBought++; // Okay, take it!
- }
- money -= item[itemNum].cost;
- ni[itemNum] += item[itemNum].amt;
- //don't allow more than 999
- if (ni[itemNum] > 99)
- ni[itemNum] = 99;
- return (currItem);
- }
- }
- }
- return (-1);
- }
-
-
-
- // An item has been selected, this function merely buys it. It
- // first does checks to make sure the item can be bought.
- // The function returns TRUE if we successfuly bought the item or
- // FALSE if we could not get it for some reason.
- int PLAYER::Buy_Something(int currItem)
- {
- int itemNum = currItem - WEAPONS;
- int bought = FALSE;
-
- if (currItem < WEAPONS)
- {
- if ( (money >= weapon[currItem].cost) && (nm[currItem] < 99) )
- {
- money -= weapon[currItem].cost;
- nm[currItem] += weapon[currItem].amt;
- //don't allow more than 99
- if (nm[currItem] > 99)
- nm[currItem] = 99;
- bought = TRUE;
- }
- // check tech level
- if (weapon[currItem].techLevel <= _env->weapontechLevel)
- bought = TRUE;
- else
- bought = FALSE;
-
- } // end of weapons
-
- else // item
- {
- if ( (money > item[itemNum].cost) && (ni[itemNum] < 99) )
- {
- money -= item[itemNum].cost;
- ni[itemNum] += item[itemNum].amt;
- // don't allow more than 99
- if (ni[itemNum] > 99)
- ni[itemNum] = 99;
- bought = TRUE;
- }
- // check technology level
- if (item[itemNum].techLevel <= _env->itemtechLevel)
- bought = TRUE;
- else
- bought = FALSE;
- }
- return bought;
- }
-
-
-
- char *PLAYER::selectRevengePhrase ()
- {
- char *line;
-
- line = _global->revenge->Get_Random_Line();
- return line;
- }
-
- char *PLAYER::selectGloatPhrase ()
- {
- char *line;
- line = _global->gloat->Get_Random_Line();
- return line;
- }
-
- char *PLAYER::selectSuicidePhrase ()
- {
- char *line;
- line = _global->suicide->Get_Random_Line();
- return line;
- }
-
-
- char *PLAYER::selectKamikazePhrase ()
- {
- char *line;
- line = _global->kamikaze->Get_Random_Line();
- return line;
- }
-
- char *PLAYER::selectRetaliationPhrase ()
- {
- char *line;
- char *pText;
-
- if (! revenge)
- return NULL;
-
- line = _global->retaliation->Get_Random_Line();
- pText = (char *) calloc (strlen (revenge->getName()) + 32 + strlen(line), sizeof (char));
- if (! pText)
- return NULL;
-
- strcpy(pText, line);
- strcat(pText, revenge->getName());
- strcat(pText, " !!!");
-
- return(pText);
- }
-
- int PLAYER::traceShellTrajectory (double aStartX, double aStartY, double aVelocityX, double aVelocityY, double &aReachedX, double &aReachedY)
- {
- TANK *tankPool[10]; // For repulsion
- bool bHasHit = false; // will be set to true if something is hit
- bool bIsWallHit = false; // For steel walls and wrap floor/ceiling
- bool bIsWrapped = false; // For special handling in distance calcualtion when a shot goes through the wall
- int iWallBounce = 0; // For wrap, rubber and spring walls
- int iMaxBounce = pow((int)type, 2); // Useless can calculate 1, deadly bots 25 bounces
- int iTargetDistance = ABSDISTANCE(_targetX, _targetY, aStartX, aStartY);
- int iDistance = MAX_OVERSHOOT;
- int iDirection = 0; // records the current direction of the shot
- int iPriStageTicks = 0; // There is no unlimited tracking!
- int iMaxPriStTicks = MAX_POWER * focusRate * 2.5;
- int iSecStageTicks = 0; // rollers and burrowers can't be followed forever!
- int iMaxSecStTicks = MAX_POWER * focusRate * 1.5;
- double dDrag = weapon[tank->cw].drag;
- double dMass = weapon[tank->cw].mass;
- double dPosX = aStartX;
- double dPosY = aStartY;
- double dVelX = aVelocityX;
- double dVelY = aVelocityY;
- double dMaxVelocity = 0;
- double dPrevX, dPrevY; // to check pixels between two points
- bool bIsSecondStage = false; // Applies to burrowers and rollers
-
- dMaxVelocity = _global->dMaxVelocity * (1.20 + (dMass / ((double)MAX_POWER / 10.0)));
-
- // Set drag do the correct value:
- if (ni[ITEM_DIMPLEP])
- {
- dDrag *= item[ITEM_DIMPLEP].vals[0];
- }
- else if (ni[ITEM_SLICKP])
- {
- dDrag *= item[ITEM_SLICKP].vals[0];
- }
-
- // Fill tankPool
- for (int i = 0; i < _global->numPlayers; i++)
- {
- if ( (_global->players[i]) && (_global->players[i]->tank) )
- tankPool[i] = _global->players[i]->tank;
- else
- tankPool[i] = NULL;
- }
-
- // Initial direction:
- if (dVelX > 0.0) iDirection = 1;
- if (dVelX < 0.0) iDirection = -1;
-
- // On y va!
- while (!bHasHit && !bIsWallHit && (iWallBounce < iMaxBounce)
- && (iPriStageTicks < iMaxPriStTicks) && (iSecStageTicks < iMaxSecStTicks))
- {
- /* --- First Stage - Applies to all weapons --- */
- if (!bIsSecondStage)
- {
- // Apply Repulsor effects
- for (int i = 0; i < _global->numPlayers; i++)
- {
- if (tankPool[i] && (tankPool[i] != tank))
- {
- double xAccel = 0.0, yAccel = 0.0;
- tankPool[i]->repulse (dPosX + dVelX, dPosY + dVelY, &xAccel, &yAccel, tank->cw);
- if (tankPool[i] == _target)
- {
- // Without this, the shield would be nearly useless!
- xAccel *= focusRate;
- yAccel *= focusRate;
- // But the lesser bots wouldn't hit anything anymore if more than _target would be handled like that!
- }
- dVelX += xAccel;
- dVelY += yAccel;
- }
- }
-
- dPrevX = dPosX;
- dPrevY = dPosY;
- //motion - wind affected
- if (((dPosX + dVelX) < 1) || ((dPosX + dVelX) > (_global->screenWidth - 2)))
- {
- if ( (((dPosX + dVelX) < 1) && checkPixelsBetweenTwoPoints (_global, _env, &dPrevX, &dPrevY, 1, dPosY))
- ||( ((dPosX + dVelX) > (_global->screenWidth - 2))
- && checkPixelsBetweenTwoPoints (_global, _env, &dPrevX, &dPrevY, (_global->screenWidth - 2), dPosY)) )
- {
- dPosX = dPrevX;
- dPosY = dPrevY;
- bHasHit = true;
- }
- else
- {
- switch (_env->current_wallType)
- {
- case WALL_RUBBER:
- dVelX = -dVelX; //bounce on the border
- iWallBounce++;
- break;
- case WALL_SPRING:
- dVelX = -dVelX * SPRING_CHANGE;
- iWallBounce++;
- break;
- case WALL_WRAP:
- if (dVelX < 0)
- dPosX = _global->screenWidth - 2; // -1 is the wall itself
- else
- dPosX = 1;
- iWallBounce++;
- bIsWrapped = true;
- break;
- default:
- dPosX += dVelX;
- if (dPosX < 1)
- dPosX = 1;
- if (dPosX > (_global->screenWidth - 2))
- dPosX= _global->screenWidth - 2;
- dVelX = 0; // already applied!
- bIsWallHit = true;
- }
- }
- }
-
- // hit floor or boxed top
- if ( ( (dPosY + dVelY) >= (_global->screenHeight - 1))
- ||(((dPosY + dVelY) <= MENUHEIGHT) && _global->bIsBoxed))
- {
- if ( ( _global->bIsBoxed && ((dPosY + dVelY) <= MENUHEIGHT)
- && checkPixelsBetweenTwoPoints (_global, _env, &dPrevX, &dPrevY, dPosX, MENUHEIGHT + 1))
- ||( ((dPosY + dVelY) > (_global->screenHeight - 2))
- && checkPixelsBetweenTwoPoints (_global, _env, &dPrevX, &dPrevY, dPosX, (_global->screenHeight - 2))) )
- {
- dPosX = dPrevX;
- dPosY = dPrevY;
- bHasHit = true;
- }
- else
- {
- switch (_env->current_wallType)
- {
- case WALL_RUBBER:
- dVelY = -dVelY * 0.5;
- dVelX *= 0.95;
- iWallBounce++;
- break;
- case WALL_SPRING:
- dVelY = -dVelY * SPRING_CHANGE;
- dVelX *= 1.05;
- iWallBounce++;
- break;
- default:
- // steel or wrap floor (ceiling)
- dPosY += dVelY;
- if (dPosY >= (_global->screenHeight - 1))
- dPosY = _global->screenHeight - 2; // -1 would be the floor itself!
- else
- dPosY= MENUHEIGHT + 1; // +1 or it would be the wall itself
- dVelY = 0; // already applied!
- bIsWallHit = true;
- }
- if (!bIsWallHit &&((fabs(dVelX) + fabs(dVelY)) < 0.8))
- bHasHit = true;
- }
- }
-
- // velocity check:
- double dActVelocity = ABSDISTANCE(dVelX, dVelY, 0, 0); // a┬▓+b┬▓=c┬▓ ... says Pythagoras :)
- if ((dActVelocity > dMaxVelocity) && !bHasHit && !bIsWallHit)
- {
- // Velocity is applied first and modified by errorMultiplier
- dPosY += dVelY * errorMultiplier;
- dPosX += dVelX * errorMultiplier;
- dVelX = 0.0;
- dVelY = 0.0;
- if ((dPosY <= MENUHEIGHT) && _global->bIsBoxed)
- dPosY = MENUHEIGHT + 1;
- if (dPosY > (_global->screenHeight - 2))
- dPosY = _global->screenHeight - 2;
- if (dPosX < 1)
- {
- if (_env->current_wallType == WALL_WRAP)
- dPosX += _global->screenWidth - 2;
- else
- dPosX = 1;
- }
- if (dPosX > (_global->screenWidth - 2))
- {
- if (_env->current_wallType == WALL_WRAP)
- dPosX -= _global->screenWidth - 2;
- else
- dPosX = _global->screenWidth - 2;
- }
- bHasHit = true;
- }
-
- // Now check for hits
- if (!bHasHit && !bIsWallHit)
- {
- // Save Positions:
- dPrevX = dPosX;
- dPrevY = dPosY;
- // Apply movement:
- dPosX += dVelX;
- dPosY += dVelY;
- dVelX += (double)(_env->wind - dVelX) / dMass * dDrag * _env->viscosity;
- dVelY += _env->gravity * (100.0 / _global->frames_per_second);
- // Barrier test:
- if ( (dVelY <= -1.0) && (dPosY <= (_global->screenHeight * -25.0)))
- dVelY *= -1.0;
-
- // See, if we have hit something
- // --- Environment-Test -- flight?
- if (checkPixelsBetweenTwoPoints (_global, _env, &dPrevX, &dPrevY, dPosX, dPosY))
- {
- dPosX = dPrevX;
- dPosY = dPrevY;
- bHasHit = true;
- }
- }
- // Now that all modifications are applied, record direction:
- if (dVelX > 0.0) iDirection = 1;
- if (dVelX < 0.0) iDirection = -1;
- }
-
- /* --- Second Stage - Applies to burrowers and rollers --- */
- if (bIsSecondStage)
- {
- iSecStageTicks++;
- // The weapon is on the ground and rolling or penetrating the ground:
- if ((tank->cw >= SML_ROLLER) && (tank->cw <= DTH_ROLLER))
- {
- // check whether we have hit anything
- if ( (dPosX < 1)
- ||(dPosX > (_global->screenWidth - 2))
- ||(dPosY > (_global->screenHeight - 2))
- ||(getpixel(_env->terrain, (int)dPosX, (int)dPosY) != PINK))
- bHasHit = true;
- else
- {
- // roll roller
- float fSurfY = (float)_env->surface[(int)dPosX] - 1;
- if ((fSurfY > dPosY) && (dPosY < (_global->screenHeight - 5)))
- {
- if (fSurfY < (dPosY + 5))
- dPosY = fSurfY;
- else
- dPosY+=5;
- }
- else
- {
- if (dVelX > 0.0)
- dPosX++;
- else
- dPosX--;
- if (dPosY >= MENUHEIGHT)
- {
- if (fSurfY > dPosY)
- dPosY++;
- else if (fSurfY >= (dPosY - 2))
- dPosY = fSurfY - 1;
- }
- }
- if (dPosY > (_global->screenHeight - 5) && !bHasHit)
- dPosY = (_global->screenHeight - 5);
- }
- }
-
- if ((tank->cw >= BURROWER) && (tank->cw <= PENETRATOR))
- {
- // Apply Repulsor effects, but not fully, as it is a burrower!
- for (int i = 0; i < _global->numPlayers; i++)
- {
- if (tankPool[i] && (tankPool[i] != tank))
- {
- double xAccel = 0.0, yAccel = 0.0;
- tankPool[i]->repulse (dPosX + dVelX, dPosY + dVelY, &xAccel, &yAccel, tank->cw);
- if (tankPool[i] == _target)
- {
- // Without this, the shield would be nearly useless!
- xAccel *= focusRate;
- yAccel *= focusRate;
- // But the lesser bots wouldn't hit anything anymore if more than _target would be handled like that!
- }
- dVelX += xAccel * 0.1;
- dVelY += yAccel * 0.1;
- }
- }
- if (((dPosX + dVelX) < 1) || ((dPosX + dVelX) > (_global->screenWidth-1)))
- {
- // if the wall is rubber, then bouce
- if ( _env->current_wallType == WALL_RUBBER )
- dVelX = -dVelX; //bounce on the border
- // bounce with more force
- else if ( _env->current_wallType == WALL_SPRING )
- dVelX = -dVelX * SPRING_CHANGE;
- // wall is steel, detonate
- else if ( _env->current_wallType == WALL_STEEL )
- bHasHit = true;
- // wrap around to other side of the screen
- else if ( _env->current_wallType == WALL_WRAP )
- {
- if (dVelX < 1)
- dPosX = _global->screenWidth - 2;
- else
- dPosX = 1;
- }
- }
- if ((dPosY + dVelY) >= (_global->screenHeight - 1))
- {
- dVelY = -dVelY * 0.5;
- dVelX *= 0.95;
- }
- else if ((dPosY + dVelY) < MENUHEIGHT) //hit screen top
- {
- dVelY = -dVelY *0.5;
- dVelX *= 0.95;
- }
- dPosY += dVelY;
- dPosX += dVelX;
- dVelY -= _env->gravity * 0.05 * (100.0 / _global->frames_per_second);
- if (getpixel (_env->terrain, (int)dPosX, (int)dPosY) == PINK)
- bHasHit = true;
- }
- }
- else
- iPriStageTicks++;
-
- #ifdef DEBUG_AIM_SHOW
- if (_global->bASD)
- {
- // Plot trajectories for debugging purposes
- circlefill (screen, (int)dPosX, (int)dPosY, 2, color);
- }
- #endif
-
- /* --- Third Stage - Applies to burrowers and rollers --- */
- if ( (!bIsSecondStage && (bHasHit || bIsWallHit))
- &&( ((tank->cw >= SML_ROLLER) && (tank->cw <= DTH_ROLLER))
- ||((tank->cw >= BURROWER) && (tank->cw <= PENETRATOR))))
- {
- // Only Rollers and Penetrators can enter the second stage!
- bIsSecondStage = true;
- bHasHit = false;
- bIsWallHit = false;
- if ((tank->cw >= SML_ROLLER) && (tank->cw <= DTH_ROLLER))
- {
- dPosY -= 5;
- if (dPosX < 1)
- dPosX = 1;
- if (dPosX > (_global->screenWidth - 2))
- dPosX = (_global->screenWidth - 2);
-
- if ( (dPosY >= _env->surface[(int)dPosX]) // y is surface or below
- &&(dPosY <= _env->surface[(int)dPosX] + 2) ) // but not burried more than 2 px
- dPosY = _env->surface[(int)dPosX] - 1;
-
-
- dVelX = 0.0;
- if (getpixel (_env->terrain, (int)dPosX + 1, (int)dPosY + 1) == PINK)
- dVelX = 1.0;
- if (getpixel (_env->terrain, (int)dPosX - 1, (int)dPosY + 1) == PINK)
- dVelX = -1.0;
- if (dVelX == 0.0)
- // if both sides are free (should be impossible, but might be a 1-pixel-peak) the shooting direction decides
- dVelX = (double)iDirection;
- dVelY = 0.0;
- }
- if ((tank->cw == BURROWER) || (tank->cw == PENETRATOR))
- {
- dVelX *= 0.1;
- dVelY *= 0.1;
- }
- if ((dPosY <= MENUHEIGHT) && !_global->bIsBoxed)
- dPosY = MENUHEIGHT + 1;
- if ((dPosY <= MENUHEIGHT) && _global->bIsBoxed)
- bIsWallHit = true; // Sorry, no more ceiling-drop!
- }
-
- /* --- Fourth Stage - Tank hit test --- */
- if (!bIsWallHit)
- {
- for (int i = 0; i < _global->numPlayers; i++)
- {
- if (tankPool[i])
- {
- if ( (dPosX > (tankPool[i]->x - TANKWIDTH))
- &&(dPosX < (tankPool[i]->x + TANKWIDTH))
- &&(dPosY > (tankPool[i]->y))
- &&(dPosY < (tankPool[i]->y + TANKHEIGHT))
- &&(tankPool[i]->l > 0))
- bHasHit = true;
- }
- }
- }
-
- // if something is hit, be sure the values are in range and movement stopped!
- if (bHasHit || bIsWallHit)
- {
- dVelX = 0.0;
- dVelY = 0.0;
- if (dPosY <= MENUHEIGHT)
- dPosY = MENUHEIGHT + 1;
- if (dPosY > (_global->screenHeight - 2))
- dPosY = _global->screenHeight - 2;
- if (dPosX < 1)
- dPosX = 1;
- if (dPosX > (_global->screenWidth - 2))
- dPosX = _global->screenWidth - 2;
- }
- }
-
- #ifdef DEBUG_AIM_SHOW
- if (_global->bASD)
- {
- // Targetting circle for debugging purposes
- circlefill (screen, (int)dPosX, (int)dPosY, 10, BLACK);
- circlefill (screen, (int)_targetX, (int)_targetY, 20, color);
- LINUX_REST;
- }
- #endif
- #ifdef DEBUG_AIM
- if (bIsWallHit)
- cout << "WALL ";
- if (bHasHit)
- cout << "HIT ";
- if (iWallBounce >= iMaxBounce)
- cout << "BOUNCE (" << iMaxBounce << ") ";
- if (iPriStageTicks >= iMaxPriStTicks)
- cout << "TICKS1 (" << iPriStageTicks << ") ";
- if (iSecStageTicks >= iMaxSecStTicks)
- cout << "TICKS2 (" << iSecStageTicks << ") ";
- #endif // DEBUG_AIM
-
- // Now see where we are and calculate the distance difference between (char *)"hit" and "has to be hit"
- if (!bIsWallHit && (iWallBounce < iMaxBounce))
- {
- iDistance = ABSDISTANCE(_targetX, _targetY, dPosX, dPosY);
-
- #ifdef DEBUG_AIM
- cout << "(" << iDistance << " <- " << (int)dPosX << " x " << (int)dPosY << ") ";
- #endif // DEBUG_AIM
-
- // Special handling for wrapped shots:
- if (bIsWrapped)
- {
- int iHalfX = _global->halfWidth;
-
- // Only shots where dPosX and _targetX are on opposite sides are relevant
- if ( (((int)dPosX < iHalfX) && (_targetX >=iHalfX))
- ||(((int)dPosX >=iHalfX) && (_targetX < iHalfX)) )
- {
- if (((int)dPosX < iHalfX) && (_targetX >=iHalfX))
- iDistance = ABSDISTANCE(dPosX + iHalfX, dPosY, _targetX, _targetY);
- else
- iDistance = ABSDISTANCE(_targetX + iHalfX, _targetY, dPosX, dPosY);
- }
- else
- bIsWrapped = false; // not relevant
- }
-
- bool bIsWrongDir = false;
- if (!bIsWrapped
- &&( ((dPosX < aStartX) && (_targetX > aStartX))
- ||((dPosX > aStartX) && (_targetX < aStartX)) ) )
- bIsWrongDir = true; // wrong side!
-
- if (!iDirection)
- {
- // If we have no direction (very unlikely!) we can only guess by comparing x-coordinates:
- if ( (iTargetDistance > ABSDISTANCE(aStartX, aStartY, dPosX, dPosY))
- ||(bIsWrongDir && (_env->current_wallType != WALL_STEEL)) )
- // Too short or wrong direction, negate iDistance!
- iDistance *= -1;
- }
- else
- {
- // with the help of the direction, we can exactly see, whether the shot is too short or too far
- if (!bIsWrapped // This doesn't apply for wrapped shots!
- &&( ( (iDirection < 0.0) // shoot to the left...
- &&(dPosX > _targetX)) // ...and the shot hits right of target
- ||( (iDirection > 0.0) // shoot to the right...
- &&(dPosX < _targetX)) )// ...and the shot hits left of target
- ) iDistance *= -1; // too short!
- }
-
- if (bIsWrongDir && (_env->current_wallType == WALL_STEEL))
- iDistance = MAX_OVERSHOOT; // wrong side and target unreachable!
- }
- else
- iDistance = MAX_OVERSHOOT;
-
- // Give the X- and Y-position back:
- aReachedX = dPosX;
- aReachedY = dPosY;
-
- return(iDistance);
- }
-
- int PLAYER::rangeFind (int &aAngle, int &aPower)
- {
- #ifdef DEBUG_AIM
- printf("This is range find function\n");
- #endif
- int iOvershoot = MAX_OVERSHOOT;
- int iActOvershoot = MAX_OVERSHOOT;
- int iSpreadOvershoot= MAX_OVERSHOOT;
- int iBestOvershoot = MAX_OVERSHOOT + 1; // So there will be at least one recorded!
- int iLastOvershoot = MAX_OVERSHOOT;
- int iAngle = aAngle;
- int iAngleMod = 0;
- double dAngleMul = 0.0;
- double dPowerMul = 0.0;
- int iCalcAngle = aAngle;
- int iLastAngle = aAngle;
- int iBestAngle = aAngle;
- int iAngleBarrier = aAngle; // This will be the flattest angle possible, thus normalized aAngle + savety
- int iIdealAngle = 135; // 135 translates to 45┬░, but will be raised if the barrier is higher
- bool bIsRaisingMode = false;
- int iPower = aPower;
- int iPowerMod = 0;
- int iPowerModFixed = 0;
- int iLastPower = aPower;
- int iBestPower = aPower;
- int iAttempts = 0;
- int iSelfHitMult = 0;
- int iSpread = weapon[tank->cw].spread;
- int iSpreadCount = 0;
- int iSpreadOdd[] = {0,-1 * SPREAD, SPREAD, -2 * SPREAD, 2 * SPREAD};
- int iSpreadEven[] = {-0.5 * SPREAD, 0.5 * SPREAD, -1.5 * SPREAD, 1,5 * SPREAD};
- int iMaxSpread = (int)type; // more intelligent bots can calculate more shots bearing spreads!
- double dStartX, dStartY, dVelocityX, dVelocityY; // No initialization needed, they are calculated anyway.
- double dHitX, dHitY;
-
- #ifdef DEBUG_AIM
- printf("Starting RangeFind\n");
- #endif
-
- // We need to be sure that iSpread is at least 1:
- if (iSpread < 1) iSpread = 1;
-
- // iMaxSpread needs to be adapted:
- switch ((int)type)
- {
- case USELESS_PLAYER:
- case GUESSER_PLAYER:
- if (iSpread % 2) iMaxSpread = 1;
- else iMaxSpread = 2;
- break;
- case RANGEFINDER_PLAYER:
- if (iSpread % 2) iMaxSpread = 3;
- else iMaxSpread = 4;
- break;
- case TARGETTER_PLAYER:
- if (iSpread % 2) iMaxSpread = 5;
- else iMaxSpread = 4;
- break;
- default:
- if (iSpread % 2) iMaxSpread = 9; // I know there is nothing like that...
- else iMaxSpread = 8; // ... but maybe in the future?
- break;
- }
- if (iSpread > iMaxSpread) iSpread = iMaxSpread; // Now iSpread is limited to iMaxSpread...
- // ...adapted to a test like (iSpreadCount < iSpread) but not larger then weapon[x].spread
-
- /* Outline:
- * RangeFind tries to adapt the given angle and power to minimize overshoot.
- * to do so we have some facts to keep in mind:
- * - ideal angle is 45┬░ in both directions, giving most distance.
- * - This angle translates into 135┬░ for a shoot to the left, and 225┬░ to the right
- * - aAngle is considered to be the flattest shot possible, as it has been manipulated
- * by calculateAttackValues(), meaning that lowering it will lead to an obstacle
- * hit.
- * - as a safety measure, when lowering an angle, a savety range of (int)type above
- * aAngle is applied
- *
- * The calculation is done in four steps:
- * 1.: Calculate the real angle, as it is, at least for spreads, different with every shot shell
- * 2.: Calculate the overshoot for each shot (or the one if no spread is given)
- * 3.: Record angle, power and overshoot if the overshoot is smaller than the best recorderd so far
- * 4.: Alter angle, or power if the angle can't be manipulated anymore, and start over, if the best
- * overshoot is != 0;
- *
- * This fourth step is divided into the following parts:
- * a) short shots (overshoot < 0) - raise power
- * i.: angle lowering mode - adjust the angle towards 45┬░ until it (or aAngle) is reached
- * ii.:power raising mode - after the first positive overshoot is reached, the angle won't be changed any more
- * b) long shots (overshoot > 0) - lower power
- * i.: angle raising mode - adjust the angle towards 89┬░ until it is reached
- * ii.:power lowering mode - once the angle can't be changed any more, only power is changed
- * angle raising/lowering modes are entered, and not left until iLastAngle == iAngle
- *
- * Note: Before you think all those step 4 calculations are wrong, they aren't, I am using a math trick to reduce
- * the amount of calculations:
- * 180 + (180 - Angle) flips an angle between left and right. So the angle will be normalized to the right,
- * (aka 90┬░ - 180┬░) and all calculations done there. If it was flipped, it will be flipped back for
- * traceShellTrajectory. This saves alot of (char *)"if(angle > 180)" lines!
- */
-
- // Preperations:
- if (aAngle > 180) iAngleBarrier = 180 + (180 - iAngleBarrier); // flip
-
- // if we have some (char *)"space", add a savety distance:
- if (iAngleBarrier < 170) iAngleBarrier += (int)type - 1;
-
- // We need some savety distance for spreads:
- if (weapon[tank->cw].spread > 1)
- {
- // normal spread size:
- iAngleBarrier += SPREAD * ((int)weapon[tank->cw].spread / 2);
- if (!(weapon[tank->cw].spread % 2))
- // Even spreads have half of SPREAD more than neccessary, so adapt:
- iAngleBarrier -= SPREAD / 2;
- // but be sure the barrier isn't too large:
- if (iAngleBarrier > 179) iAngleBarrier = 179;
- }
-
- // Adapt the ideal angle if we are facing an obstacel:
- if (iAngleBarrier > 135) iIdealAngle = iAngleBarrier;
-
- #ifdef DEBUG_AIM
- printf("New Target Try using....\n");
- if (tank->cw < WEAPONS)
- printf("%s\n", weapon[tank->cw].name);
- else
- printf("%s\n", item[tank->cw - WEAPONS].name);
- #endif // DEBUG_AIM
-
- // Okay, here we go:
- #ifdef DEBUG_AIM
- printf("Best: %d .. Attempts %d .. RFattempts %d\n", iBestOvershoot, iAttempts, rangeFindAttempts);
- #endif
- while (iBestOvershoot && (iAttempts < rangeFindAttempts))
- {
- iAttempts++;
-
- #ifdef DEBUG_AIM
- printf("--> %d. rangeFind:\n", iAttempts);
- printf( (char *)" Angle: %3d Power: %4d\n", (iAngle - 90), iPower);
- #endif // DEBUG_AIM
-
- /* --- Step 1: Calculate angle for spreads: --- */
- iSpreadCount = 0;
- iOvershoot = MAX_OVERSHOOT;
- iSpreadOvershoot = 0;
- iSelfHitMult = 0;
- iLastOvershoot= iOvershoot;
-
- while (iSpreadCount < iSpread)
- {
- #ifdef DEBUG_AIM
- printf("Inside the wee while loop.\n");
- #endif
- iCalcAngle = iAngle;
-
- // Two cases: Even and odd spreads.
- if (iSpread > 1)
- {
- // odd spreads:
- if (weapon[tank->cw].spread % 2)
- iCalcAngle += iSpreadOdd[iSpreadCount];
- // even spreads:
- else
- iCalcAngle += iSpreadEven[iSpreadCount];
- }
- dVelocityX = _global->slope[iCalcAngle][0] * (double)iPower * (100.0 / _global->frames_per_second) / 100.0;
- dVelocityY = _global->slope[iCalcAngle][1] * (double)iPower * (100.0 / _global->frames_per_second) / 100.0;
-
- dStartX = tank->x + (_global->slope[iCalcAngle][0] * GUNLENGTH);
- dStartY = tank->y + (_global->slope[iCalcAngle][1] * GUNLENGTH);
-
- /* --- Step 2: Calculate overshoots: --- */
- #ifdef DEBUG_AIM
- printf("Trace the shell\n");
- #endif
- iActOvershoot = traceShellTrajectory(dStartX, dStartY, dVelocityX, dVelocityY, dHitX, dHitY);
- #ifdef DEBUG_AIM
- printf("Back from shell tracing.\n");
- #endif
- if (abs(iActOvershoot) < _global->screenWidth)
- iSelfHitMult += adjustOvershoot(iActOvershoot, dHitX, dHitY);
- // Otherwise it's a wall hit and not relevant!
-
- #ifdef DEBUG_AIM
- printf("Passed hit multi\n");
- #endif
- // With this method only the best hit of spreads is counted.
- if (abs(iActOvershoot) < abs(iOvershoot))
- iOvershoot = iActOvershoot;
-
- #ifdef DEBUG_AIM
- printf("Passed overshoot. Spread: %d\n", iSpread);
- #endif
- // iSpreadOvershoot calculates the average absolute Overshoot
- if (iActOvershoot)
- iSpreadOvershoot += (int)(fabs((double)iActOvershoot) / (double)iSpread);
-
- iSpreadCount++;
-
- #ifdef DEBUG_AIM
- if (iSpreadCount > 1)
- {
- if (iSpreadCount == 2)
- printf( (char *)" (%3d ", iCalcAngle - 90);
- else
- printf( (char *)",%3d ", iCalcAngle - 90);
- if (iSpread == iSpreadCount)
- printf( (char *)"\b)");
- }
- #endif // DEBUG_AIM
- }
-
- #ifdef DEBUG_AIM
- printf("Moving right along\n");
- #endif
-
- if (iOvershoot < 0)
- iSpreadOvershoot *= -1;
-
- if (iSelfHitMult > 0)
- {
- // We hit ourselves, so the larger Overshoot will be multiplied and taken!
- if (abs(iSpreadOvershoot) > abs(iOvershoot))
- iOvershoot = iSpreadOvershoot;
- if (abs(iOvershoot) < _global->screenWidth)
- iOvershoot = _global->screenWidth;
- if (abs(iOvershoot) < MAX_OVERSHOOT)
- iOvershoot*= iSelfHitMult;
- }
- else
- {
- // everything okay, just take the smaller one!
- if (abs(iSpreadOvershoot) < abs(iOvershoot))
- iOvershoot = (int)(((double)iSpreadOvershoot + ((double)iOvershoot * focusRate)) / 2);
- else
- iOvershoot = (int)(((double)iOvershoot + ((double)iSpreadOvershoot * focusRate)) / 2);
- }
-
- /* --- Step 3.: Record angle, power and overshoot if the overshoot is smaller than the best recorderd so far: --- */
- if (abs(iOvershoot) < abs(iBestOvershoot))
- {
- iBestOvershoot = iOvershoot;
- iBestAngle = iAngle;
- iBestPower = iPower;
- }
-
- #ifdef DEBUG_AIM
- printf( (char *)" Overshoot: %6d (%4d x %4d) SH: %1d\n", iOvershoot, (int)dHitX, (int)dHitY, iSelfHitMult);
- #endif // DEBUG_AIM
-
- /* --- Step 4.: Alter angle, or power if the angle can't be manipulated anymore, and start over: --- */
- if (iOvershoot)
- {
- // Preperation: flip iAngle if neccessary
- if (iAngle > 180) iAngle = 180 + (180 - iAngle); // flip
-
- // Preperation: Decide over angle and power modification depending on overshoot
- if (abs(iOvershoot) < _global->screenWidth)
- {
- dAngleMul = 1.0 + fabs((double)iOvershoot / (double)_global->screenWidth); // between 1.0 and 2.0
- iAngleMod = (int)(fabs((double)iOvershoot) / 10.0);
- if (iAngleMod > 15)
- iAngleMod = 15; // need a barrier here, too
-
- // Power modification is calculated depending on overshoot
- // To raise or lower by 100 pixels, we need aproximately 100 power (at 45┬░)
- dPowerMul = pow(1.0 + (fabs((double)iAngle - 135.0) / 50.0), 2.0); // between 1.0 and 3,61
- iPowerModFixed = 5 + (int)( (double)type * (fabs(iOvershoot) / 10.0)); // useless 10%, deadly 50% fix
- iPowerMod = 5 + (int)((10.0 - (double)type) * (fabs(iOvershoot) / 10.0)); // useless 90%, deadly 50% variable
- iPowerMod = (rand() % iPowerMod) + iPowerModFixed;
- }
- else
- {
- // As the overshoot is too high, probably a wall hit, Modification is done in a more limited way:
- dAngleMul = 1.0 + ((double)type / 10.0);
- dPowerMul = 1.0 + ((double)type / 10.0);
- iAngleMod = (rand() % 11) + 5;
- iPowerMod = (MAX_POWER / 8) + (rand() % (MAX_POWER / 8));
- if ((iOvershoot < 0) || (iAngle >= 170))
- // we need to raise distance urgently, so cancel bIsRaisingMod
- bIsRaisingMode = false;
- else
- // we need to lower distance urgently, so enter bIsRaisingMod
- bIsRaisingMode = true;
- }
-
- // before entering the step 4 modification parts, we could try a trick:
- if ( (abs(iLastOvershoot) < abs(iOvershoot))
- &&( ((iLastOvershoot < 0) && (iOvershoot > 0)) // be sure that the overshoots switches
- ||((iLastOvershoot > 0) && (iOvershoot < 0)) ) // between signed and unsigned
- &&( (abs(iLastAngle - iAngle) >= 2) // There has to be something to do or
- ||(abs(iLastPower - iPower) >=10) ) // we might waste all remaining attempts
- &&(abs(iLastOvershoot) < _global->screenWidth) // don't try it on wallhits, selfhits
- &&(abs(iOvershoot) < _global->screenWidth) )
- {
- // the current modification made the shot worse,
- // but switched between too short and too long,
- // so revert to half the modification:
- iAngleMod = (iLastAngle + iAngle) / 2; // We use the mod to save declaring
- iPowerMod = (iLastPower + iPower) / 2; // two new vars!
- iLastAngle = iAngle;
- iLastPower = iPower;
- iAngle = iAngleMod;
- iPower = iPowerMod;
- // Note: This trick won't work when both are too short or too long,
- // because then bots would never get over too high obstacles!
- }
- else
- {
- // No trick needed, we are getting nearer!
- iLastAngle = iAngle;
- iLastPower = iPower;
- iAngleMod = (int)((double)iAngleMod * focusRate * dAngleMul);
- iPowerMod = (int)((double)iPowerMod * focusRate * dPowerMul);
- // * a) short shots (overshoot < 0) - raise power
- if (iOvershoot < 0)
- {
- // If we are too short and have (char *)"overbend" in raising mode, it has to be cancelled!
- if (bIsRaisingMode && (iAngle > 180))
- bIsRaisingMode = false;
-
- if (!bIsRaisingMode)
- {
- // * i.: angle lowering mode - adjust the angle towards 45┬░ until it (or aAngle) is reached
- if (iAngle > iIdealAngle)
- {
- iAngle -= iAngleMod;
- if (iAngle < iIdealAngle) iAngle = iIdealAngle;
- }
- if (iAngle < iIdealAngle)
- {
- iAngle += iAngleMod;
- if (iAngle > iIdealAngle) iAngle = iIdealAngle;
- }
-
- // Apply as much power as is neccessary:
- iPower += iPowerMod - (abs(iLastAngle - iAngle) * 10);
-
- // check to see whether Raising mode should be entered:
- if ((iAngle == iIdealAngle) && (iAngle == iLastAngle))
- bIsRaisingMode = true;
- }
- else
- // * ii.:power raising mode - after the first positive overshoot is reached, the angle won't be changed any more
- iPower += (int)((double)iPowerMod * dAngleMul);
- }
- // * b) long shots (overshoot > 0) - lower power
- if (iOvershoot > 0)
- {
- if (bIsRaisingMode)
- {
- // * i.: angle raising mode - adjust the angle towards 89┬░ until it is reached
- if (iAngle > 178)
- iAngleMod = 1; // for small (char *)"overbends"
- iAngle += iAngleMod;
-
- // Apply as much power as is neccessary:
- iPower -= iPowerMod - (abs(iLastAngle - iAngle) * 10);
- }
- else
- // * ii.:power lowering mode - once the angle can't be changed any more, only power is changed
- iPower -= (int)((double)iPowerMod * dAngleMul);
- }
- }
-
- // last step: check iPower and iAngle:
- while ( (iPower >= MAX_POWER)
- ||(iPower <= (MAX_POWER / 20)))
- iPower = ((MAX_POWER / 2) + iPower) / 2;
- iPower -= iPower % 5;
-
- // check the angle, it must not be flatter than iAngleBarrirer!
- if (iAngle < iAngleBarrier)
- iAngle = iAngleBarrier;
-
- // Now flip the angle back if neccessary:
- if (aAngle > 180)
- iAngle = 180 + (180 - iAngle); // flip back!
-
- #ifdef DEBUG_AIM
- printf( (char *)" --> AngleMod: %3d PowerMod: %4d\n", (iAngle - iLastAngle), (iPower - iLastPower));
- #endif // DEBUG_AIM
- } // end of if(iOvershoot)
- }
-
- #ifdef DEBUG_AIM
- printf("looks like the end of the while loop in aiming\n");
- #endif
- // Record the best found values in tank if possible
- if (abs(iBestOvershoot) < tank->smallestOvershoot)
- {
- #ifdef DEBUG_AIM
- printf( (char *)" New best Overshoot: %5d\n", iBestOvershoot);
- #endif // DEBUG_AIM
- tank->smallestOvershoot = abs(iBestOvershoot);
- tank->bestAngle = iBestAngle;
- tank->bestPower = iBestPower;
- // Give the best ones back if possible
- if (iBestAngle != aAngle) aAngle = iBestAngle;
- if (iBestPower != aPower) aPower = iBestPower;
- }
- #ifdef DEBUG_AIM
- else
- cout << " No new best Overshoot..." << endl;
- #endif // DEBUG_AIM
- return(iBestOvershoot);
- }
-
- int PLAYER::adjustOvershoot(int &aOvershoot, double aReachedX, double aReachedY)
- {
- TANK *pTankHit = NULL; // For hitting quality analysis
- long int iOvershoot = aOvershoot; // To calculate with locally
- bool bIsDirectHit = true; // false for shaped charges and napalm is chosen
- bool bIsShaped = false; // true for shaped charges (special radius calculation needed!)
- int iDamage = weapon[tank->cw].damage;
- int iRadius = weapon[tank->cw].radius;
- int iSelfHits = 0; // Will be raised for every self- and team-hit and then returned
-
- if (iRadius < 10) iRadius = 10; // several things wouldn't function otherwise
- if (iDamage < 10) iDamage = 10; // if we didn't set minimum values
-
- if ((tank->cw >= SHAPED_CHARGE) && (tank->cw <= CUTTER))
- bIsShaped = true;
- if (bIsShaped || ((tank->cw >= SML_NAPALM) && (tank->cw <= LRG_NAPALM)))
- bIsDirectHit = false;
-
- /* --- Step 1: See whether a tank is hit: */
- for (int i = 0; i < 10; i++)
- {
- if (_global->players[i] && _global->players[i]->tank)
- {
- if ( (aReachedX > (_global->players[i]->tank->x - TANKWIDTH))
- &&(aReachedX < (_global->players[i]->tank->x + TANKWIDTH))
- &&(aReachedY > (_global->players[i]->tank->y))
- &&(aReachedY < (_global->players[i]->tank->y + TANKHEIGHT))
- &&(_global->players[i]->tank->l > 0))
- pTankHit = _global->players[i]->tank;
- }
- }
-
- /* --- Step 2: See whether the target tank is hit or in weapon radius: --- */
- // check these values in case of segfault
- if ( (_target) && (pTankHit) &&
- (pTankHit == _target) && bIsDirectHit && (pTankHit->player != this))
- // A Direct hit with a direct weapon is what we want, so give 0 back
- {
- iOvershoot = 0;
- #ifdef DEBUG_AIM
- cout << "ON TARGET! ";
- #endif // DEBUG_AIM
- }
- else
- {
- if (!iOvershoot)
- {
- if ( ( ((tank->x < _targetX) && (_targetX > aReachedX))
- ||((tank->x > _targetX) && (_targetX < aReachedX)) )
- &&(iOvershoot > 0)
- &&(iOvershoot < MAX_OVERSHOOT) )
- iOvershoot *= -1; // the shot is too short
- else
- iOvershoot = 1;
- }
- if (pTankHit)
- {
- // We *have* hit a tank, let's see to that it isn't us or a friend:
- if ((pTankHit == tank) || (pTankHit->player == this))
- {
- // Ourselves, not good!
- iOvershoot *= iRadius * iDamage;
- iSelfHits++;
- }
- else if (pTankHit->player->team == team)
- {
- // We hit someone of the same team, but only Jedi and SitH care, of course:
- if (team == TEAM_JEDI)
- iOvershoot *= iRadius * (defensive + 2) * focusRate;
- if (team == TEAM_SITH)
- iOvershoot *= iRadius * (-1 * (defensive - 2)) * focusRate;
- if (team != TEAM_NEUTRAL)
- iSelfHits++;
- }
- }
-
- /* --- Step 3: See, whether we, or a team member, is in blast radius and manipulate Overshoot accordingly --- */
- for (int i = 0; i < 10; i++)
- {
- if (_global->players[i] && _global->players[i]->tank && (_global->players[i]->tank != _target))
- {
- // _target is skipped, so we don't get wrong values when revenging on a team mate!
- int iX = _global->players[i]->tank->x;
- int iY = _global->players[i]->tank->y;
- int iRadiusDist;
- int iBlastDist = ABSDISTANCE(aReachedX, aReachedY, iX, iY);
-
- iRadiusDist = iRadius - iBlastDist;
- if (!iRadiusDist) iRadiusDist = 1;
-
- if (iBlastDist < iRadius)
- {
- // Is in Blast range. (maybe)
- if ( !bIsShaped
- ||(bIsShaped && (abs(iY - (int)aReachedY) <= (iRadius / 20))))
- {
- // Either no shaped charge or in radius
- if (_global->players[i]->tank == tank)
- {
- // Ourselves, not good!
- iOvershoot *= iRadiusDist * iDamage;
- iSelfHits++;
- }
- else if (_global->players[i]->team == team)
- {
- // We hit someone of the same team, but only Jedi and SitH care, of course:
- if (team == TEAM_JEDI)
- iOvershoot *= iRadiusDist * (defensive + 2) * focusRate;
- if (team == TEAM_SITH)
- iOvershoot *= iRadiusDist * (-1 * (defensive - 2)) * focusRate;
- if (team != TEAM_NEUTRAL)
- iSelfHits++;
- }
- }
- }
- }
- }
-
- // Be sure to not give a ridiculously high overshoot back:
- while (abs(iOvershoot) >= MAX_OVERSHOOT)
- iOvershoot /= 2;
- }
- aOvershoot = (int)iOvershoot;
-
- return(iSelfHits);
- }
-
- // If Napalm or a shaped weapon is chosen, the target has to be modified!
- int PLAYER::getAdjustedTargetX(TANK * aTarget)
- {
- int iTargetX, iTargetY;
- int iMinOffset = 0;
- int iMaxOffset = 1;
-
- if (aTarget)
- {
- iTargetX = aTarget->x;
- iTargetY = aTarget->y;
- }
- else if (_target)
- {
- iTargetX = _target->x;
- iTargetY = _target->y;
- }
- else // avoid segfault
- return ( rand() % _global->screenWidth ) ;
-
-
- // tank is dead, bail out to avoid segfault
- if (! tank)
- return iTargetX;
-
- if ( (tank->cw >= SHAPED_CHARGE) && (tank->cw <= CUTTER))
- {
- int iBestLeftOffset = 0;
- int iBestRightOffset = 0;
- int iLeftY = 0;
- int iBestLeftY = 0;
- int iRightY = 0;
- int iBestRightY = 0;
- iMinOffset = (int)((TANKWIDTH / 2) + ((double)TANKWIDTH * focusRate));
- iMaxOffset = iMinOffset + (TANKWIDTH * (((int)type + 1) / 2));
-
- for (int i = iMinOffset; i < iMaxOffset; i++)
- {
- // Get Y-Data:
- if ((iTargetX - i) > 1)
- iLeftY = _env->surface[iTargetX - i];
- if ((iTargetX + i) < (_global->screenWidth - 1))
- iRightY= _env->surface[iTargetX + i];
- // Check whether new Y-Data is better than what we have:
- if (abs(iLeftY - iTargetY) < abs(iBestLeftY - iTargetY))
- {
- iBestLeftOffset = i;
- iBestLeftY = iLeftY;
- }
- if (abs(iRightY - iTargetY) < abs(iBestRightY - iTargetY))
- {
- iBestRightOffset = i;
- iBestRightY = iRightY;
- }
- }
- // Now see whether we go left or right:
- if (abs(iBestLeftY - iTargetY) < abs(iBestRightY - iTargetY))
- // use left:
- iTargetX -= iBestLeftOffset;
- else
- // use right:
- iTargetX += iBestRightOffset;
- }
-
- if ( (tank->cw >= SML_NAPALM) && (tank->cw <= LRG_NAPALM))
- {
- // here we only check one side, the one to the wind:
- iMaxOffset = abs((int)((double)iMaxOffset * _env->wind * focusRate));
- iMinOffset = abs((int)(_env->wind * (double)TANKWIDTH * focusRate));
- int iSurfY = 0;
- int iBestY = 0;
- int iOffset = 0;
- int iBestOffset = 0;
- int iDirection = 0;
-
- if (_env->wind < 0) iDirection = 1; // for some reason I do not know, wind is
- if (_env->wind > 0) iDirection = -1; // used (char *)"the wrong way". (???)
-
- // Don't stretch the offset onto ourselves:
- if ( ((tank->x < iTargetX) && ((iTargetX - tank->x - (iDirection * iMaxOffset)) < (TANKWIDTH * 2)))
- ||((tank->x > iTargetX) && ((tank->x - iTargetX - (iDirection * iMaxOffset)) < (TANKWIDTH * 2))) )
- iMaxOffset = abs(iTargetX - (int)tank->x) - (TANKWIDTH * 2);
-
- // And don't allow a negative offset either (would be useless due to wind!)
- if (iMaxOffset < TANKWIDTH)
- iMaxOffset = TANKWIDTH;
-
- // But be sure iMinOffset is smaller than iMaxOffset:
- if (iMinOffset >= iMaxOffset)
- iMinOffset = iMaxOffset - (int)type;
-
- if (iDirection)
- {
- // Without wind there is nothing to do!
- for (int i = iMinOffset; i < iMaxOffset; i++)
- {
- iOffset = i * iDirection;
- // Get Y-Data:
- if (((iTargetX + iOffset) > 1) && (((iTargetX + iOffset) < (_global->screenWidth - 1))))
- iSurfY = _env->surface[iTargetX + iOffset];
-
- // Check whether new Y-Data is better than what we have:
- if ( ((iTargetY - iSurfY) < (iTargetY - iBestY))
- ||(!iBestY) )
- {
- iBestOffset = iOffset;
- iBestY = iSurfY;
- }
- }
- iTargetX += iBestOffset;
- }
- }
-
- return (iTargetX);
- }
-
- int PLAYER::calculateAttack(TANK *aTarget)
- {
- /* There are two general possibilities:
- * - aTarget provided:
- * This function was called from computerSelectTarget() and will only check if it is possible
- * to reach a target, aka try only once and give the overshoot back.
- * - default (aTarget is automatically NULL)
- * This function was called from computerControl() and will go forth and try to hit _target
- *
- * Outline:
- * --------
- * There are the following possibilities:
- * a) An Item or a laser is chosen
- * -> a direct angle will do!
- * b) We are burried
- * -> fire unburying tool at +/-60┬░ to the middle of the screen
- * c) Kamikaze
- * -> indicated by targetX/Y being tank.x/y
- * -> if shaped weapon is chosen, fire 45┬░ and power 100 to the side where y is nearest to tank.y
- * -> if napalm is chosen, fire 90┬░ and power 0
- * -> otherwise fire 90┬░ and power 250
- * d) Fire in non-boxed mode
- * -> normal calculation
- * e) Fire in non-boxed mode
- * -> extended power-control after normal calculation
- * -> if the target can't be reached while staying below the ceiling,
- * check for an obstacle than can be removed and do so if found.
- * --> boxed mode is included in non-boxed mode now. I hope it works as I intent!
- *
- * Update for dynamization: The previous target is recorded, aiming starts at the old values,
- * if the target didn't change. */
-
- int iXdistance, iYdistance;
- if (aTarget)
- {
- iXdistance = aTarget->x - tank->x;
- iYdistance = aTarget->y - tank->y;
- }
- else
- {
- iXdistance = _targetX - tank->x;
- iYdistance = _targetY - tank->y;
- }
-
- #ifdef DEBUG_AIM
- if (!aTarget && !iTargettingRound)
- {
- printf("\n-----------------------------------------------\n");
- if (_target)
- printf("%s is starting to aim at %s\n", getName(), _target->player->getName() );
- else
- printf("%s is aiming without a target!\n", getName());
- }
- #endif // DEBUG_AIM
-
- /* --- case a) An Item is chosen --- */
- if ( (!aTarget) && ( (tank->cw >= WEAPONS) ||
- ((tank->cw >= SML_LAZER) && (tank->cw <= LRG_LAZER))))
- {
- #ifdef DEBUG_AIM
- printf("About to calc direct angle.\n");
- #endif
- _targetAngle = calculateDirectAngle (iXdistance, iYdistance);
- _targetPower = MAX_POWER / 2;
-
- #ifdef DEBUG_AIM
- cout << "Direct " << _targetAngle - 90 << " for ";
- printf("Direct %d for %s\n", _targetAngle -90, (tank->cw < WEAPONS) ? weapon[tank->cw].name : item[tank->cw - WEAPONS].name);
- #endif // DEBUG_AIM
-
- iTargettingRound = retargetAttempts; // So it is done!
-
- return(0);
- }
-
-
- /* --- case b) We are burried --- */
- if (!aTarget &&(tank->howBuried () > BURIED_LEVEL))
- {
- // Angle is 60┬░ to the middle of the screen:
- int iAngleVariation = rand() % (20 - ((int)type * 3));
- iAngleVariation -= (int)((double)type / 1.5);
- if (tank->x <= _global->halfWidth)
- _targetAngle = 150 + iAngleVariation;
- else
- _targetAngle = 210 - iAngleVariation;
- #ifdef DEBUG_AIM
- printf("Freeing self with Angle %d and Power %d\n", _targetAngle, _targetPower);
- #endif // DEBUG_AIM
-
- iTargettingRound = retargetAttempts;
-
- return(0);
- }
-
- /* --- case c) Kamikaze --- */
- if (!aTarget && (_targetX == tank->x) && (_targetY == tank->y))
- {
- #ifdef DEBUG_AIM
- cout << "Going bye bye with ";
- if (tank->cw < WEAPONS)
- cout << weapon[tank->cw].name;
- else
- cout << item[tank->cw - WEAPONS].name;
- cout << " !!!" << endl;
- cout << "GERONNNNNIIIIIMOOOOOOOOOO !!!" << endl;
- #endif // DEBUG_AIM
-
- iTargettingRound = retargetAttempts;
-
- // For a nice bye bye we set angle/power directly
- if ((tank->cw >= SHAPED_CHARGE) && (tank->cw <= CUTTER))
- {
- // shaped weapons are a bit special:
- int iHLeft, iHRight;
- int iCount = 0;
- for (int i = TANKWIDTH; i <= (TANKWIDTH * 2); i++)
- {
- iCount++;
- iHLeft = _env->surface[(int)(tank->x - i)];
- iHRight= _env->surface[(int)(tank->x + i)];
- }
- iHRight /= iCount;
- iHLeft /= iCount;
- if (fabs(iHRight - tank->y) < fabs(iHLeft - tank->y))
- _targetAngle = 135;
- else
- _targetAngle = 225;
- _targetPower = MAX_POWER / 20;
- return(0);
- }
- // The other possibilities are easier:
- if ((tank->cw >= SML_NAPALM) && (tank->cw <= LRG_NAPALM))
- _targetPower = 0;
- else
- _targetPower = MAX_POWER / 8;
- _targetAngle = 180;
-
- return(0);
- }
-
- /* --- case d) Fire in non-boxed mode --- */
- int iRawAngle, iAdjAngle, iPower, iLastPower, iSavetyPower;
- int iOvershoot = MAX_OVERSHOOT;
- int iLastOvershoot = MAX_OVERSHOOT;
- int iBestOvershoot = MAX_OVERSHOOT;
-
- _targetX = getAdjustedTargetX(aTarget);
- calculateAttackValues(iXdistance, iYdistance, iRawAngle, iAdjAngle, iPower, false);
-
- if (!iTargettingRound)
- {
- // Initializiation, if this is the first try:
-
- if (!aTarget && (_oldTarget == _target) )
- {
- // Target didn't change, use last rounds values:
- tank->bestAngle = tank->a;
- tank->bestPower = tank->p;
- iRawAngle = tank->a;
- iAdjAngle = tank->a;
- iPower = tank->p;
- }
- else
- {
- // new target, new values:
- tank->bestAngle = 180;
- tank->bestPower = MAX_POWER;
- if (!aTarget)
- _oldTarget = _target;
- }
-
- tank->smallestOvershoot = MAX_OVERSHOOT + 1; // So there will be at least one recorded
-
- }
- else
- {
- // This is a follow-up round, get the last values back:
- iAdjAngle = _targetAngle;
- iPower = _targetPower;
- iBestOvershoot = tank->smallestOvershoot;
- }
-
- iLastPower= iPower;
-
- if (aTarget)
- {
- #ifdef DEBUG_AIM
- printf("Returning a range find.\n");
- #endif
- return(rangeFind(iAdjAngle, iPower));
- }
-
- // Now that we are here, normal handling is needed:
- #ifdef DEBUG_AIM
- if (!iTargettingRound)
- {
- printf("Intitial try:\n");
- printf("About to range find.\n");
- }
- #endif // DEBUG_AIM
-
- iOvershoot = rangeFind(iAdjAngle, iPower);
- iLastOvershoot = iBestOvershoot;
- if (abs(iOvershoot) < abs(iBestOvershoot))
- iBestOvershoot = iOvershoot;
-
- #ifdef DEBUG_AIM
- cout << "Overshoot: " << iOvershoot << " (best so far: " << iBestOvershoot << ")" << endl << endl;
- cout << iTargettingRound + 1 << ". re-try:";
- #endif // DEBUG_AIM
-
- // There is still an Overshoot, so see what we can do:
- if (abs(iLastOvershoot - iOvershoot) <= 1)
- {
- // rangeFind can't get a better version, so start completely different!
- int iAngleRange = 180;
- if (_env->current_wallType == WALL_STEEL)
- iAngleRange /= 2;
- if (_targetX > tank->x)
- // shoot basically to the right:
- iRawAngle = 90 + (rand() % iAngleRange);
- else
- // shoot basically to the left:
- iRawAngle = 270 - (rand() % iAngleRange);
- iAdjAngle = (iAdjAngle + iRawAngle) / 2;
- iPower = (iPower + (rand() % (MAX_POWER / 2))) / 2;
- #ifdef DEBUG_AIM
- cout << " Starting over! " << endl;
- #endif
- }
- else
- {
- // Just calculate anew:
- calculateAttackValues(iXdistance, iYdistance, iRawAngle, iAdjAngle, iPower, true);
- #ifdef DEBUG_AIM
- cout << " recalculating! " << endl;
- #endif
- }
-
- /* SavetyPower is as follows:
- * abs(iAdjAngle -180) changes the value to be between:
- * -> 0 : Straight upwards (originally 180)
- * -> 90 : Straight Left or right (originally 270/90)
- * 91 - abs(...) gives a value between 1 and 91 which is multiplied with the radius */
- iSavetyPower = ((91 - abs(iAdjAngle - 180)) * (weapon[tank->cw].radius * focusRate)) / focusRate;
- if (iSavetyPower > (MAX_POWER / 4)) iSavetyPower = MAX_POWER / 4;
- if (iPower < iSavetyPower) iPower = iSavetyPower;
-
- iLastOvershoot = iOvershoot;
- iOvershoot = rangeFind(iAdjAngle, iPower);
-
- if (abs(iOvershoot) < abs(iBestOvershoot))
- iBestOvershoot = iOvershoot;
-
- iTargettingRound++;
-
- // Do not count this attempt if the currently best overshoot is too high:
- if ((iBestOvershoot > _global->screenWidth) && (rand() % ((int)type * 2)))
- iTargettingRound--; // if ibestOvershoot is negative it will alwys be counted!
-
- if (tank->smallestOvershoot < (weapon[tank->cw].radius / (int)type))
- iTargettingRound = retargetAttempts;
-
- // Now the best Version has to be tested:
- iOvershoot = tank->smallestOvershoot; // Always a positive number!
- _targetAngle = tank->bestAngle; // Record for foolow-up rounds
- _targetPower = tank->bestPower; // Record for follow-up rounds
-
- if (iTargettingRound == retargetAttempts)
- {
- #ifdef DEBUG_AIM
- cout << " --- ---" << endl;
- cout << "Final Decision:" << endl;
- cout << "Angle : " << _targetAngle - 90 << endl;
- cout << "Power : " << _targetPower << endl;
- cout << "Overshoot: " << iBestOvershoot << endl;
- #endif // DEBUG_AIM
-
- if (iOvershoot > weapon[tank->cw].radius)
- {
- if (_targetAngle > 180)
- iRawAngle = _targetAngle + 10 + (rand() % ((int)type * 4));
- else
- iRawAngle = _targetAngle - (10 + (rand() % ((int)type * 4)));
-
- // There are three possibilities:
- if ( (iBestOvershoot < 0)
- &&(abs(_targetAngle - 180) <= (10 + ((iXdistance / _global->screenWidth) * 10)))
- &&(!tank->shootClearance(iRawAngle, weapon[tank->cw].radius * 1.5)))
- {
- // a) there is an obstacle in our way!
- tank->cw = getUnburyingTool();
- if (tank->cw < WEAPONS)
- {
- _targetAngle = iRawAngle;
- #ifdef DEBUG_AIM
- cout << "Revised Angle for unburying: " << _targetAngle - 90 << endl;
- #endif // DEBUG_AIM
- }
- }
- else
- {
- // b) the way is clear, so either try to switch weapon or fire away!
- if (iBestOvershoot < 0)
- {
- // As we are too short, see whether another weapon might be good
- if (nm[TREMOR]) tank->cw = TREMOR;
- if (nm[SHOCKWAVE]) tank->cw = SHOCKWAVE;
- if ((_targetY < (_global->screenHeight - iOvershoot)) && nm[BURROWER])
- tank->cw = BURROWER;
- if (nm[TECTONIC]) tank->cw = TECTONIC;
- if ((_targetY < (_global->screenHeight - iOvershoot)) && nm[PENETRATOR])
- tank->cw = PENETRATOR;
-
- // if it is boxed, it is better to teleport out!
- if ((_global->bIsBoxed) && (iOvershoot > (weapon[tank->cw].radius * 2)))
- {
- // no way, teleport out!
- if (ni[ITEM_TELEPORT]) tank->cw = ITEM_TELEPORT + WEAPONS;
- if (ni[ITEM_SWAPPER]) tank->cw = ITEM_SWAPPER + WEAPONS;
- }
- }
- else
- {
- // look for tectonis and their range
- if (nm[TREMOR] && (weapon[TREMOR].radius >= iOvershoot)) tank->cw = TREMOR;
- if (nm[SHOCKWAVE] && (weapon[SHOCKWAVE].radius >= iOvershoot)) tank->cw = SHOCKWAVE;
- if (nm[TECTONIC] && (weapon[TECTONIC].radius >= iOvershoot)) tank->cw = TECTONIC;
- }
-
- // c) we have no chance to reach anything!
- if (iOvershoot > _global->screenWidth)
- {
- // no way, teleport out!
- if (ni[ITEM_TELEPORT]) tank->cw = ITEM_TELEPORT + WEAPONS;
- if (ni[ITEM_SWAPPER]) tank->cw = ITEM_SWAPPER + WEAPONS;
- }
-
- }
- }
- else
- {
- // If we *are* hitting ok, Angle and Power need to be manipulated by errorMultiplier
- int iAngleMod, iPowerMod;
- int iAngleModLimit = (42 - (10 * ((int)type - 1))); // = 42, 32, 22, 12, 2 for useless -> deadly
- int iPowerModLimit = (210 - (50 * ((int)type - 1))); // = 210, 160, 110, 60, 10 for useless -> deadly
-
- iAngleMod = (rand() % 51) * errorMultiplier; // useless: 0 - 100, deadly: 0 - 2 (with 2 being very unlikely)
- if (iAngleMod > iAngleModLimit)
- iAngleMod = iAngleModLimit;
-
- iPowerMod = (rand() % 251) * errorMultiplier; // useless: 0 - 500, deadly: 0 - 10 (with 10 being *very* unlikely)
- if (iPowerMod > iPowerModLimit)
- iPowerMod = iPowerModLimit;
-
- // In boxed mode, errors have quite more impact and are therefore cut down to be only two thirds
- if (_global->bIsBoxed)
- {
- iAngleMod = (int)((double)iAngleMod / 3.0 * 2.0);
- iPowerMod = (int)((double)iPowerMod / 3.0 * 2.0);
- }
-
- // 25 % to get a flattening anglemod (aka nearing to the ground)
- if ((!(rand() % 4)) && (_targetAngle < 180))
- iAngleMod *= -1; // right side angle
- if ((rand() % 4) && (_targetAngle > 180))
- iAngleMod *= -1; // right side angle (other way round, because here a positive mod is flattening!
-
- // 25 % to get a lessening powermod. (it is more likely to overshoot than be too short!)
- if (!(rand() % 4)) // (and it leads to too many stupid self
- iPowerMod *= -1; // hits to allow negative PoerMod too often
-
- // The modification must not lead to a too stupid angle
- while ( iAngleMod
- &&( ( (_targetAngle < 180)
- &&( ((iAngleMod + _targetAngle) < 95)
- ||((iAngleMod + _targetAngle) > 175) ) )
- ||( (_targetAngle >=180)
- &&( ((iAngleMod + _targetAngle) > 265)
- ||((iAngleMod + _targetAngle) < 185) ) )
- ) ) iAngleMod /= 2;
- _targetAngle += iAngleMod;
-
- // Same applies for Power:
- while ( iPowerMod
- &&( ((iPowerMod + _targetPower) > MAX_POWER)
- ||((iPowerMod + _targetPower) < (MAX_POWER * 0.1)) ) )
- iPowerMod /= 2;
-
- // iPowerMod needs to be a multiplier of 5:
- iPowerMod -= iPowerMod % 5;
-
- _targetPower += iPowerMod;
-
- #ifdef DEBUG_AIM
- printf( "Error-Adjusting: Angle %3d Power %4d\n", iAngleMod, iPowerMod);
- #endif // DEBUG_AIM
- }
-
- #ifdef DEBUG_AIM
- cout << "HITTING " << _target->player->getName() << " ???" << endl << endl;
- #endif // DEBUG_AIM
-
- // Last one: if we are revenging, tell em!
- // Added _target check to avoid segfault
- if ( (tank->cw < WEAPONS) && (_target) // Wepaon selected?
- && (weapon[tank->cw].damage > 1) // One that delivers damage?
- && (revenge == _target->player) // And the target is our revengee?
- && ((rand() % ((int)DEADLY_PLAYER + 2 - (int)type))) ) // And we do really want to taunt?
- {
- FLOATTEXT *DIEText;
- char *my_text;
- my_text = selectRetaliationPhrase();
- DIEText = new FLOATTEXT (_global, _env, my_text, (int) tank->x, (int) tank->y - 30, color, CENTRE);
- if (my_text) free(my_text);
- if ( DIEText)
- {
- //DIEText->xv = 0;
- //DIEText->yv = -0.4;
- DIEText->set_speed(0.0, -0.4);
- DIEText->maxAge = 150;
- }
- else
- perror ( "player.cc: Failed allocating memory for DieText in calculateAttack().");
- }
- // As we might finish here because of a good overshoot, iTargettingRounds need to be maxed!
- iTargettingRound = retargetAttempts;
- }
- return(0);
- }
-
- void PLAYER::calculateAttackValues(int &aDistanceX, int aDistanceY, int &aRawAngle, int &aAdjAngle, int &aPower, bool aAllowFlip)
- {
- double dAirTime, dxTime;
- double dSlopeX, dSlopeY;
- double dAngleVariation, dPowerVariation;
- bool bIsWrapped = false; // Special handling for wrapped walls
-
- aDistanceX = _targetX - tank->x;
-
- /* --- Step 1: find the raw angle, aka the obstacle-free optimal angle --- */
-
- aRawAngle = (int)(atan2((double)aDistanceX, (double)(aDistanceY - abs (aDistanceX))) / PI * 180.0);
-
- // Bring in Range:
- if (aRawAngle < 0) aRawAngle = aRawAngle + 360;
- if (aRawAngle < 90) aRawAngle = 135;
- if (aRawAngle > 270) aRawAngle = 225;
-
- if ((_env->current_wallType == WALL_WRAP) && ((rand() % ((int)type + 1)) || !aAllowFlip))
- {
- // Note: We always wrap when possible and flipping not allowed, to have the shorter distance for sure!
- int iWrapDistance = 0;
-
- // if the distance through the wall is shorter, take it!
- if (tank->x < _targetX)
- iWrapDistance = -1 * (tank->x -1 + _global->screenWidth - 2 - _targetX);
- else
- iWrapDistance = tank->x -1 + _global->screenWidth - 2 - _targetX;
-
- if (abs(iWrapDistance) < abs(aDistanceX))
- {
- bIsWrapped = true;
- aDistanceX = iWrapDistance;
- aRawAngle = 180 + (180 - aRawAngle); // flip!
- }
- }
-
- // Angle variation for more flavour:
- dAngleVariation = (rand() % 21) * focusRate; // useless: 0-4, deadly: 0-20
- if (rand() % 2) dAngleVariation *= -1.0;
- while ( (dAngleVariation > 0.0)
- &&( ((aRawAngle > 180) && ( ((aRawAngle + dAngleVariation) < 190)
- ||((aRawAngle + dAngleVariation) > 260)))
- ||((aRawAngle < 180) && ( ((aRawAngle + dAngleVariation) > 170)
- ||((aRawAngle + dAngleVariation) < 100))) ) )
- dAngleVariation /= 2.0;
- aRawAngle += (int)dAngleVariation;
-
- // Maybe we could switch sides?
- if ((_env->current_wallType != WALL_STEEL) && (rand() % 2) && (rand() % (int)type) && aAllowFlip)
- {
- // Yes, we can! ( (char *)"Change is coming!" ;-) (b.h.o) )
- aRawAngle = 180 + (180 - aRawAngle); // This switches sides, yes. :-D
-
- if (tank->x < _targetX)
- {
- // We switch angle from right to left: (original distance is positive)
- switch (_env->current_wallType)
- {
- case WALL_RUBBER:
- // Add Distances to the wall to bounce from:
- aDistanceX += 2 * (tank->x - 1);
- break;
- case WALL_SPRING:
- // Spring walls add velocity, so adapt a bit
- aDistanceX += tank->x - 1 + ((tank->x - 1) * 0.75);
- break;
- case WALL_WRAP:
- // Distance is new: only distances from the wall:
- if (bIsWrapped)
- aDistanceX = tank->x -1 + _global->screenWidth - 2 - _targetX;
- else
- aDistanceX = -1 * (tank->x -1 + _global->screenWidth - 2 - _targetX);
- break;
- }
- }
- else
- {
- // We switch angle from left to right: (original distance is negative)
- switch (_env->current_wallType)
- {
- case WALL_RUBBER:
- // Add Distances to the wall to bounce from:
- aDistanceX += 2 * (_global->screenWidth - tank->x - 2);
- break;
- case WALL_SPRING:
- // Spring walls add velocity, so adapt a bit
- aDistanceX += _global->screenWidth - tank->x - 2 + ((_global->screenWidth - tank->x - 2) * 0.75);
- break;
- case WALL_WRAP:
- // Distance is new: only distances from the wall:
- if (bIsWrapped)
- aDistanceX = -1 * (tank->x -1 + _global->screenWidth - 2 - _targetX);
- else
- aDistanceX = tank->x -1 + _global->screenWidth - 2 - _targetX;
- break;
- }
- }
- }
-
- /* --- Step 2: Adjust the Angle given clearance --- */
-
- aAdjAngle = aRawAngle;
- while ((aAdjAngle < 180) && !(tank->shootClearance(aAdjAngle)))
- aAdjAngle++;
- while ((aAdjAngle > 180) && !(tank->shootClearance(aAdjAngle)))
- aAdjAngle--;
-
- /* --- Step 3: Find neccessary Power --- */
- dSlopeX = _global->slope[aAdjAngle][0];
- dSlopeY = _global->slope[aAdjAngle][1];
-
- if (dSlopeX != 0.0)
- dxTime = ((double)aDistanceX / fabs(dSlopeX));
- else
- // entirely down to the elements now
- dxTime = ((double)aDistanceX / 0.000001);
-
- // Low target, less power
- // xdistance proportional to sqrt(dy)
- dAirTime = fabs(dxTime) + (((double)aDistanceY * dSlopeY) * _env->gravity * (100.0 / _global->frames_per_second)) * 2.0;
-
- // Less airTime doesn't necessarily mean less power
- // Horizontal firing means more power needed even though
- // airTime is minimised
-
- aPower = (int)(sqrt (dAirTime * _env->gravity * (100.0 / _global->frames_per_second))) * 100;
-
- // Power variation for more flavour:
- dPowerVariation = (rand() % 51) * focusRate; // useless: 0-10, deadly: 0-50
- dPowerVariation -= (int)dPowerVariation % 5;
- if (rand() % 2) dPowerVariation *= -1.0;
- aPower += dPowerVariation;
-
- if (aPower > MAX_POWER) aPower = MAX_POWER;
- if (aPower < (MAX_POWER / 20)) aPower = MAX_POWER / 20;
-
- }
-
- int PLAYER::calculateDirectAngle (int dx, int dy)
- {
- double angle;
-
- angle = atan2 ((double)dx, (double)dy) / PI * 180;
- angle += (rand () % 40 - 20) * errorMultiplier;
-
- if (angle < 0)
- angle = angle + 360;
- if (angle < 90)
- angle = 90;
- else if (angle > 270)
- angle = 270;
-
- return ((int)angle);
- }
-
- TANK * PLAYER::computerSelectTarget (int aPreferredWeapon, bool aRotationMode)
- {
- int random_target;
- int attempts = 0;
- int max_attempts = (int)type * 3;
- TANK *best_target = NULL;
- int current_score = 0;
- int best_score = -1 * MAX_OVERSHOOT; // start with a loooooow score, so that even score<0 tanks can become best target
- TANK *current_tank = NULL;
- TANK *tankPool[10];
- int iMoneyNeed = getMoneyToSave() - money; // Are we in need for money?
- int target_count = 0;
-
- #ifdef DEBUG
- cout << " -> I need " << iMoneyNeed << " Credits *urgently*!" << endl;
- if (aPreferredWeapon < WEAPONS)
- cout << " - Searching target for " << weapon[aPreferredWeapon].name << endl;
- else
- cout << " - Searching target for " << item[aPreferredWeapon - WEAPONS].name << endl;
- #endif // DEBUG
- // find out how many tries we have to find a good target
-
- // Fill tankPool
- for (int i = 0; i < _global->numPlayers; i++)
- {
- if ( (_global->players[i]) && (_global->players[i]->tank) )
- {
- tankPool[i] = _global->players[i]->tank;
- target_count++;
- }
- else
- tankPool[i] = NULL;
- }
-
- if (target_count < 2) // just us left or nobody
- return NULL;
-
- // who do we want to shoot at?
- while (attempts < max_attempts)
- {
- // select random tank for target
- if (_global->numPlayers > 0)
- random_target = rand() % _global->numPlayers;
- else
- random_target = 0;
- current_tank = tankPool[random_target];
-
- if (current_tank)
- {
- current_score = 0;
- // only consider living tanks that are not us
-
- if ( (current_tank->l > 0) && (current_tank->player != this))
- {
- int iDamage = 0;
-
- if (weapon[aPreferredWeapon].numSubmunitions > 1)
- iDamage = (damageMultiplier
- * weapon[weapon[aPreferredWeapon].submunition].damage
- * (weapon[aPreferredWeapon].numSubmunitions / 3.0));
- else
- iDamage = (damageMultiplier
- * weapon[aPreferredWeapon].damage
- * weapon[aPreferredWeapon].spread);
-
- // compare the targets strength to ours
- int iDiffStrength = ( (tank->l + tank->sh) - (current_tank->l + current_tank->sh));
-
- current_score = iDiffStrength;
-
- if (iDiffStrength < 0)
- {
- // The target is stronger. Are we impressed?
- if ( (defensive < 0.0) && ( (rand() % ( (int) type + 1))))
- // No we aren't, add defensive-modified Strength
- // (The more offensive, the less impressed we are)
- current_score += (int) ( ( (defensive - 3.0) / 2.0) * (double) iDiffStrength);
- }
- else
- // the target is weaker, add points modified by how defensive we are
- current_score += (int) ( (double) iDiffStrength * ( (defensive + 3.0) / 2.0));
- #ifdef DEBUG
- cout << " (str)" << current_score;
- #endif
- // check to see if we are on the same team
- switch ( (int) team)
- {
- case TEAM_JEDI:
- if ((current_tank->player->team == TEAM_JEDI) && !aRotationMode)
- current_score -= 500 * (int) type;
- if ((current_tank->player->team == TEAM_JEDI) && aRotationMode)
- current_score -= MAX_OVERSHOOT; // no team consideration in rotation mode!
-
- if (current_tank->player->team == TEAM_SITH)
- current_score += -200.0 * (defensive - 2.0) * ( (double) type / 2.0);
- break;
-
- case TEAM_SITH:
- if ((current_tank->player->team == TEAM_SITH) && !aRotationMode)
- current_score -= 500 * (int) type;
- if ((current_tank->player->team == TEAM_SITH) && aRotationMode)
- current_score -= MAX_OVERSHOOT; // no team consideration in rotation mode!
-
- if (current_tank->player->team == TEAM_JEDI)
- current_score += -200.0 * (defensive - 2.0) * ( (double) type / 2.0);
- break;
- /*
- default:
- // Neutrals go rather for sith than jedi. (but not much)
- if (current_tank->player->team == TEAM_JEDI)
- current_score += -25.0 * (defensive - 2.0) * ( (double) type / 2.0);
-
- if (current_tank->player->team == TEAM_SITH)
- current_score += -50.0 * (defensive - 2.0) * ( (double) type / 2.0);
- */
- }
- #ifdef DEBUG
- cout << " (team)" << current_score;
- #endif // DEBUG
- // do we have a grudge against the target
- if (current_tank->player == revenge)
- {
- /*
- switch ( (int) team)
- {
- case TEAM_JEDI:
- // Revenge is a dark force!
- current_score += (50 * ( (defensive - 1.5) * -1.0));
- break;
- case TEAM_SITH:
- // Revenge means power!
- current_score += (200 * ( (defensive - 1.5) * -1.0));
- break;
- default:
- */
- current_score += (100 * ( (defensive - 1.5) * -1.0));
- // }
- }
- #ifdef DEBUG
- cout << " (rev)" << current_score;
- #endif // DEBUG
- // prefer targets further away when violent death is on
- if (_global->violent_death)
- {
- int distance;
- distance = (int) fabs (tank->x - current_tank->x);
-
- if (distance > _global->halfWidth)
- current_score += 100.0 * ( (defensive + 3.0) / 2.0);
- }
- #ifdef DEBUG
- cout << " (dis)" << current_score;
- #endif // DEBUG
- // Add some points if the target is more intelligent than we are (get'em DOWN!)
- // or substract if we are the better one. (Deal with the nutter later...)
- if ( (current_tank->player->type != HUMAN_PLAYER)
- && (current_tank->player->type < DEADLY_PLAYER))
- current_score += 50 * ( (int) current_tank->player->type - (int) type);
- else
- current_score += 50 * ( (int) DEADLY_PLAYER - (int) type);
- // Players, last player type and part time bots are counted as deadly bots.
- #ifdef DEBUG
- cout << " (typ)" << current_score;
- #endif // DEBUG
- // Add points for score difference if they have more than us
- // useless bot: 1 * diff * 60 --> 4 points difference would mean +240 score
- // deadly bot : 3 * diff * 60 --> 4 points difference would mean +720 score
- if (current_tank->player->score > score)
- current_score += ( (int) type + 1) / 2 * (current_tank->player->score - score) * 60;
-
- #ifdef DEBUG
- cout << " (scr)" << current_score;
- #endif // DEBUG
- if (aPreferredWeapon < WEAPONS)
- {
- // As we are wanting to fire a weapon, add points, if the damage is greater than the targets health
- // (if we are in need for money, but not on the same team)
- int iDamageDiff = iDamage - (current_tank->l + current_tank->sh);
-
- if ( (iDamageDiff > 0) && (iMoneyNeed > 0)
- &&( ( team == TEAM_NEUTRAL )
- ||((team != TEAM_NEUTRAL) && (current_tank->player->team != team)) ) )
- current_score += (double) iDamageDiff * (1.0 + ( (defensive + (double) type) / 10.0));
- #ifdef DEBUG
- cout << " (dmg)" << current_score;
- #endif // DEBUG
- // Check whether the target is buried, and substract points for non-burrower/-penetrator
- int iBurylevel = current_tank->howBuried();
-
- if (iBurylevel > BURIED_LEVEL)
- {
- // The target is burried!
- if ( ( (aPreferredWeapon < BURROWER) || (aPreferredWeapon > PENETRATOR))
- && ( (aPreferredWeapon < TREMOR) || (aPreferredWeapon > TECTONIC)))
- {
- // Napalm and shaped charges are absolutely useless
- if ( ( (aPreferredWeapon >= SML_NAPALM) && (aPreferredWeapon <= LRG_NAPALM))
- || ( (aPreferredWeapon >= SHAPED_CHARGE) && (aPreferredWeapon <= CUTTER)))
- current_score -= (int) type * 500; // Even the useless bot isn't *that* stupid
- else
- {
- // For all other weapons we go for the radius of the blast
- if (iBurylevel < weapon[aPreferredWeapon].radius)
- current_score *= 1.0 - ( ( (double) iBurylevel / (double) weapon[aPreferredWeapon].radius) / 2.0);
- else
- current_score -= (double) iDamage * (double) type * ( (double) defensive + 3.0);
- }
- }
- else
- // As we *want* to fire an appropriate weapon, the target looks rather nice to us!
- current_score += ( (double) (iBurylevel - BURIED_LEVEL) / ( ( (double) type + 1.0) / 2.0)) * (double) iDamage;
- #ifdef DEBUG
- cout << " (bur)" << current_score;
- #endif // DEBUG
- }
-
- // Finally, for weapons, see, if we can do good blast damage!
- if (type >= RANGEFINDER_PLAYER)
- {
- int iBlastBonus = 0;
- iBlastBonus = (int) ( (1.0 - ( (double) type / 10.0))
- * getBlastValue (current_tank, weapon[aPreferredWeapon].damage, aPreferredWeapon)
- * ( (defensive - 3.0) / -2.0));
-
- if (iBlastBonus > 0)
- {
- current_score += iBlastBonus;
- // if we need money, blast bonus is valued higher:
-
- if (iMoneyNeed > 0)
- current_score += iBlastBonus * ( (double) type / 2.0);
- }
- }
- }
-
- if (aRotationMode && ((team == TEAM_NEUTRAL) || (team != current_tank->player->team)))
- {
- // In rotationmode we try to actually reach the target with the preferred weapon
- tank->cw = aPreferredWeapon;
- _target = current_tank;
- _targetX = current_tank->x;
- _targetY = current_tank->y;
- iTargettingRound = 0;
- int iOvershoot = abs(calculateAttack(current_tank));
- if (iOvershoot > _global->screenWidth)
- current_score = -1 * MAX_OVERSHOOT; // Wall-Hit! Target is unreachable!
- else
- current_score -= iOvershoot; // substract overshoot!
- #ifdef DEBUG
- cout << " (rot)" << current_score;
- #endif // DEBUG
- }
-
- #ifdef DEBUG
- cout << " => " << current_score<< " : " << current_tank->player->getName() << endl;
- #endif // DEBUG
-
- // decide if this target is better than others
- if ((current_score > best_score) || (!aRotationMode && !best_target))
- {
- best_score = current_score;
- best_target = current_tank;
- }
- attempts++;
- }
- } // end of if we have a valid tank
- }
-
- if (best_target)
- {
- _target = best_target;
- if (_target)
- {
- _targetX= _target->x;
- _targetY= _target->y;
- #ifdef DEBUG
- cout << " -> " << best_target->player->getName() << " wins! (";
- cout << best_target->l << " life, " << best_target->sh << " shield)" << endl;
- #endif // DEBUG
- }
- }
- #ifdef DEBUG
- else
- cout << " -> Unable to find target!!!" << endl;
- #endif // DEBUG
-
- return (best_target);
- }
-
- int PLAYER::getUnburyingTool()
- {
- int iTool = 0; // small missile, if nothing else fits
- if (nm[LRG_LAZER]) iTool = LRG_LAZER;
- if (nm[MED_LAZER]) iTool = MED_LAZER;
- if (nm[SML_LAZER]) iTool = SML_LAZER;
- if (ni[ITEM_TELEPORT]) iTool = WEAPONS + ITEM_TELEPORT;
- if (ni[ITEM_SWAPPER]) iTool = WEAPONS + ITEM_SWAPPER;
- if (nm[HVY_RIOT_BOMB]) iTool = HVY_RIOT_BOMB;
- if (nm[RIOT_BOMB]) iTool = RIOT_BOMB;
- if (nm[RIOT_CHARGE]) iTool = RIOT_CHARGE;
- if (nm[RIOT_BLAST]) iTool = RIOT_BLAST;
- return(iTool);
- }
-
- int PLAYER::computerSelectItem ()
- {
- int current_weapon = 0; // Current Weapon (defaults to small missile)
- int iWeaponPool[15];
- int iPoolSize = (int)type * 3;
- int count = 0;
- // Initialize targetting:
- _target = NULL;
- _targetX= 0;
- _targetY= 0;
-
- #ifdef DEBUG
- cout << getName() << " : Starting target and weapon evaluation..." << endl;
- if (defensive < -0.9) cout << "(True Offensive)" << endl;
- if ((defensive >=-0.9) && (defensive < -0.75)) cout << "(Very Offensive)" << endl;
- if ((defensive >=-0.75) && (defensive < -0.25)) cout << "(Offensive)" << endl;
- if ((defensive >=-0.25) && (defensive < 0.00)) cout << "(Slightly Offensive)" << endl;
- if (defensive == 0.0) cout << "(Neutral)" << endl;
- if ((defensive >0.0) && (defensive <= 0.25)) cout << "(Slightly Defensive)" << endl;
- if ((defensive >0.25) && (defensive <= 0.75)) cout << "(Defensive)" << endl;
- if ((defensive >0.75) && (defensive <= 0.9)) cout << "(Very Defensive)" << endl;
- if (defensive > 0.9) cout << "(True Defensive)" << endl;
- cout << "----------------------------------" << endl;
- #endif // DEBUG
- // 1.: Preselection if buried
-
- if (tank->howBuried () > BURIED_LEVEL)
- {
- current_weapon = getUnburyingTool();
- #ifdef DEBUGctank
- if (current_weapon < WEAPONS)
- cout << "I have chosen a \"" << weapon[current_weapon].name << "\" to free myself first!" << endl;
- else
- cout << "I have chosen a \"" << item[current_weapon - WEAPONS].name << "\" to free myself first!" << endl;
- #endif // DEBUG
- }
- else
- {
- // 2.: Determine iPoolSize
- if (iPoolSize > 15)
- iPoolSize = 15; // Or part-time-bots would bust array size!
-
- // 3.: Fill iWeaponPool
- iWeaponPool[0] = 0; // The Small missile is always there!
- count = 1; // ...so start from second slot!
-
- while (count < iPoolSize)
- {
- int i = 0;
- // bots get a number of tries depending on their intelligence
- current_weapon = 0;
- while (!current_weapon && (i < ( (int) type * 2)))
- {
- current_weapon = Select_Random_Weapon();
- if (! current_weapon)
- current_weapon = Select_Random_Item();
- if ( (current_weapon >= THINGS) //should never occur, but make it sure!
- || ( (current_weapon < WEAPONS) && (!nm[current_weapon]))
- || ( (current_weapon >= WEAPONS) && (!ni[current_weapon - WEAPONS])))
- current_weapon = 0;
- i++;
- }
-
- if (!current_weapon && (count > 1))
- {
- // Slot 0 is allways the small missile, slot 1 is always the first thing the bot "things" of
- // "Last Resort" switching takes place from slot 2 on
- if (nm[MED_MIS]) current_weapon = MED_MIS;
-
- // Only bots with 4+ slots (rangefinder and up) revert to the large missile
- if ( (count > 2) && nm[LRG_MIS]) current_weapon = LRG_MIS;
- }
- iWeaponPool[count] = current_weapon;
- count++;
- }
-
- // 4a.: check if a dirtball is chosen
- if ( (iWeaponPool[1] >= DIRT_BALL) && (iWeaponPool[1] <= SUP_DIRT_BALL))
- current_weapon = iWeaponPool[1];
- else
- {
- // 4b.: Sort iWeaponPool, so that the most liked weapon is first
- // (...if the bot doesn't "forget" to sort ...)
- if (rand() % ( (int) type + 1))
- {
- bool bIsSorted = false;
- while (!bIsSorted)
- {
- bIsSorted = true;
- // The bot does only sort the first few weapons (type+1)
- // Stupid: first two, deadly: first 6
- for (int i = 1; i < ( (int) type + 1); i++)
- {
- if (_weaponPreference[iWeaponPool[i-1]] < _weaponPreference[iWeaponPool[i]])
- {
- bIsSorted = false;
- current_weapon = iWeaponPool[i-1];
- iWeaponPool[i-1] = iWeaponPool[i];
- iWeaponPool[i] = current_weapon;
- }
- }
- }
- }
- current_weapon = iWeaponPool[0]; // Most liked weapon, or, if not sorted, the small missile
- // Having the small missile here means, that the bot is selecting the most easy target.
- // Obviously that means, that the more stupid a bot is, the more often it will go for the easy strike.
- }
- #ifdef DEBUG
- for (int i = 0; i < iPoolSize; i++)
- {
- cout << i << ".: ";
- printf( "% 5d Pref - ", _weaponPreference[iWeaponPool[i]]);
- if (iWeaponPool[i] < WEAPONS)
- cout << "\"" << weapon[iWeaponPool[i]].name << "\"" << endl;
- else
- cout << "\"" << item[iWeaponPool[i] - WEAPONS].name << "\"" << endl;
- }
- #endif // DEBUG
- // if boxed mode is on, we have to try to find a target in rotation mode first!
- if ( _global->bIsBoxed && (current_weapon < WEAPONS)
- && ( (current_weapon < DIRT_BALL)
- ||(current_weapon > SUP_DIRT_BALL)))
- {
- int i = 0;
- while ((i < iPoolSize) && !_target)
- {
- _target = computerSelectTarget(iWeaponPool[i], true);
- i++;
- }
- if (_target)
- current_weapon = iWeaponPool[i - 1];
- }
- if (!_target) _target = computerSelectTarget(current_weapon);
- #ifdef OLD_GAMELOOP
- if (!_target && _oldTarget) _target = _oldTarget;
- #endif
- if (!_target) return (0); // If there is no target available, we have nothing more to do.
- _targetX = _target->x;
- _targetY = _target->y;
-
- // 5.: if a weapon is choosen, cycle through the pool
- // to find the best fitting one
- if ( (current_weapon < WEAPONS)
- && ( (current_weapon < DIRT_BALL)
- || (current_weapon > SUP_DIRT_BALL)))
- {
- int iBestWeapon = current_weapon;
- int iWeaponScore = 0; // Score of the currently used weapon
- int iBestWeapScr = -5000;// Best score calculated so far
- double dDamageMod = 0.0; // modifier for how close to targets health
- int iWeaponDamage = 0; // Calculate the (real) weapon damage
- int iTargetLife = _target->l + _target->sh;
- int iBurylevel = _target->howBuried();
- for (int i = 0; i < iPoolSize; i++)
- {
- current_weapon = iWeaponPool[i];
-
- // 1.: avoid trying to shoot below the tank's level with lasers
- if ( (_targetY >= tank->y) &&
- ( (current_weapon >= SML_LAZER) && (current_weapon <= LRG_LAZER)))
- iWeaponPool[i] = current_weapon = 0; // revert to small missile
- #ifdef DEBUG
- if (current_weapon < WEAPONS)
- cout << " -> \"" << weapon[current_weapon].name << "\" : ";
- else
- cout << " -> (ERROR!) \"" << item[current_weapon - WEAPONS].name << "\" : ";
- #endif // DEBUG
- // 2.: The closer the weapon damage is to the target health, the:
- // - more points added if it kills
- // - less points substracted if it doesn't kill
-
- // avoid trying to use weapons we do not have
- if ( ( current_weapon < 0) || (current_weapon >= WEAPONS) )
- current_weapon = 0;
-
- if (weapon[current_weapon].numSubmunitions > 1)
- iWeaponDamage = damageMultiplier
- * weapon[weapon[current_weapon].submunition].damage
- * (weapon[current_weapon].numSubmunitions / (defensive + 3.0)); // Clusters don't hit well (napalm?)
- else
- {
- if (weapon[current_weapon].spread > 1)
- iWeaponDamage = damageMultiplier
- * weapon[current_weapon].damage
- * (weapon[current_weapon].spread / (defensive + 2.0)); // Spreads *might* hit well, but do seldom
- else
- iWeaponDamage = damageMultiplier
- * weapon[current_weapon].damage;
- }
-
- // The more intelligent and defensive a bot is, the more (char *)"savety bonus" is granted:
- iWeaponDamage = (int) ( (double) iWeaponDamage / (1.0 + ( ( (double) type * (defensive + 2.0)) / 50.0)));
-
- // Examples:
- // Full offensive, useless: 1.0 + ((1.0 * 1.0) / 50.0) = 1.02 <== The damage is like 102% of the real damage
- // Full defensive, deadly : 1.0 + ((5.0 * 3.0) / 50.0) = 1.30 <== The damage is like 130% of the real damage
- #ifdef DEBUG
- cout << iWeaponDamage << " damage, ";
- #endif // DEBUG
- if ( iTargetLife > iWeaponDamage)
- {
- // The weapon is too weak, substract points. (Less if we are offensive)
- dDamageMod = (double) iWeaponDamage / (double) iTargetLife;
- if (defensive < 0)
- iWeaponScore = (10 - (int) type) * (int) ( (-10.0 * (1.1 + defensive)) / dDamageMod);
- else
- iWeaponScore = (10 - (int) type) * (int) (-10.0 / dDamageMod);
- #ifdef DEBUG
- cout << "weak: ";
- #endif // DEBUG
- /* Example calculations:
- Bot is deadly: (int)type = 5
- Weapon does 25% damage of the targets health: dDamegeMod = 0.25
- iWeaponScore = (10 - 5) * (-10 / 0.25) = 5 * -40 = -200
- Weapon does 50% damage of the targets health: dDamegeMod = 0.5
- iWeaponScore = (10 - 5) * (-10 / 0.5) = 5 * -20 = -100
- Weapon does 75% damage of the targets health: dDamegeMod = 0.75
- iWeaponScore = (10 - 5) * (-10 / 0.75) = 5 * -13,3 = -66,5
- Weapon does 95% damage of the targets health: dDamegeMod = 0.95
- iWeaponScore = (10 - 5) * (-10 / 0.95) = 5 * -10,5 = -52,5 */
- }
- else
- {
- // The weapon is strong enough, add points (More, if we are defensive)
- dDamageMod = (double) iTargetLife / (double) iWeaponDamage;
- if (defensive > 0)
- iWeaponScore = (int) type * (int) ( (100.0 * (1.0 + defensive)) * dDamageMod);
- else
- iWeaponScore = (int) type * (int) (100.0 * dDamageMod);
- #ifdef DEBUG
- cout << "strong: ";
- #endif // DEBUG
- /* Example calculations:
- Bot is deadly: (int)type = 5
- Weapon does 105% damage of the targets health: dDamegeMod = 0.95
- iWeaponScore = 5 * (100 * 0.95) = 5 * 95 = 475
- Weapon does 125% damage of the targets health: dDamegeMod = 0.8
- iWeaponScore = 5 * (100 * 0.8) = 5 * 80 = 400
- Weapon does 150% damage of the targets health: dDamegeMod = 0.67
- iWeaponScore = 5 * (100 * 0.67) = 5 * 67 = 335
- Weapon does 200% damage of the targets health: dDamegeMod = 0.5
- iWeaponScore = 5 * (100 * 0.5) = 5 * 50 = 250 */
- }
- #ifdef DEBUG
- cout << dDamageMod << " dMod -> ";
- #endif // DEBUG
- // 3.: Check if the way for a laser is clear if choosen
- if ( (current_weapon >= SML_LAZER) && (current_weapon <= LRG_LAZER))
- {
- int iXlow, iXhigh, iX, iY; // temp vars to calculate with
- int iRockAmount = 0; // How much mountain there is in between
-
- if (tank->x < _targetX)
- {
- iXlow = tank->x;
- iXhigh = _targetX;
- }
- else
- {
- iXlow = _targetX;
- iXhigh = tank->x;
- }
-
- for (iX = iXlow; iX < iXhigh; iX++)
- {
- iY = tank->y - ( (tank->y - _targetY) / (iXhigh - iX)); // y the laser will be on it's way
- if (_env->surface[iX] < iY)
- iRockAmount++; // Rock in the way!
- }
- iWeaponScore -= (int) type * iRockAmount * 10.0;
- }
-
- // 4.: If the target is burried, add points if we are using an adequate weapon
- if (iBurylevel > BURIED_LEVEL)
- {
- // The target is burried!
- if ( ( (current_weapon < BURROWER) || (current_weapon > PENETRATOR))
- && ( (current_weapon < TREMOR) || (current_weapon > TECTONIC)))
- {
- // Napalm and shaped charges are absolutely useless
- if ( ( (current_weapon >= SML_NAPALM) && (current_weapon <= LRG_NAPALM))
- || ( (current_weapon >= SHAPED_CHARGE) && (current_weapon <= CUTTER)))
- iWeaponScore -= (int) type * 500; // Even the useless bot isn't *that* stupid
- else
- {
- // For all other weapons we go for the radius of the blast
- if (iBurylevel < weapon[current_weapon].radius)
- iWeaponScore *= 1.0 - ( ( (double) iBurylevel / (double) weapon[current_weapon].radius) / 2.0);
- else
- iWeaponScore -= (double) iWeaponDamage * (double) type * ( (double) defensive + 3.0);
- }
- }
- else
- // As we *want* to fire an appropriate weapon, the target looks rather nice to us!
- iWeaponScore += ( (double) (iBurylevel - BURIED_LEVEL) / ( ( (double) type + 1.0) / 2.0)) * (double) iWeaponDamage;
- }
-
- // 5.: Substract points, if we are within the blast radius
- // 6.: Check, whether other tanks are in the blast radius, and add points according
- // to the additional damage delivered
- // Note: It seems to be paradox, that defensive bots add points for spread/cluster
- // weapons, but they a) buy onyl few of them and b) add only noticably points
- // if the collateral damage is enough to kill.
- if (type >= RANGEFINDER_PLAYER)
- iWeaponScore += getBlastValue (_target, iWeaponDamage, current_weapon, dDamageMod);
-
- // 7.: Try to hit this target with the weapon, and substract the overshoot
- if ((type >= RANGEFINDER_PLAYER) && (current_weapon < WEAPONS))
- {
- tank->cw = current_weapon;
- iTargettingRound = 0;
- iWeaponScore -= abs(calculateAttack(_target));
- }
-
- // 8.: Modify the Score by weapon preferences
- if (iWeaponScore > 0)
- iWeaponScore = (int) ( (double) iWeaponScore
- * (1.0
- + ( (double) _weaponPreference[current_weapon]
- / (double) MAX_WEAP_PROBABILITY)));
- // (it will only have a slight effect unless the prefs are really wide apart and the
- // original points very close to each other)
- #ifdef DEBUG
- cout << iWeaponScore << " points!" << endl;
- #endif // DEBUG
- // See if the choice fits better
- if (iWeaponScore > iBestWeapScr)
- {
- iBestWeapScr = iWeaponScore;
- iBestWeapon = current_weapon;
- }
- }
- current_weapon = iBestWeapon;
- }
- }
-
- // 6.: Kamikaze probability:
- // The more stupid and offensive a bot is, the more chance he has
- // to blow himself up when life is falling below a certain limit.
- if ( ( (tank->l + tank->sh) < 30)
- || ( ( (tank->l + tank->sh) < 50) && (tank->howBuried () > BURIED_LEVEL)))
- {
- // A buried bot needs to free himself first, leaving all others a free shot!
- double dBlowMod = defensive + 2.0; // gives 1.0 to 3.0
- dBlowMod *= (double) type / 2.0; // gives 1.0 to 9.0
- dBlowMod += 1.0; // gives 2.0 to 10.0
- // dBlowMod is now between 2.0 <== useless full offensive bot (50% chance)
- // and 10.0 <== deadly full defensive bots (10% chance)
-
- if (! (rand() % (int) dBlowMod))
- {
- // okay, I'm a goner, so BANZAAAAIIII! (maybe, check for a way first)
- int iWeaponDamage = 0;
- current_weapon = 0;
- if (nm[NUKE]) current_weapon = NUKE;
- if (nm[DTH_SPREAD]) current_weapon = DTH_SPREAD;
- if (nm[DTH_HEAD]) current_weapon = DTH_HEAD;
- if (nm[ARMAGEDDON]) current_weapon = ARMAGEDDON;
- if (nm[CUTTER]) current_weapon = CUTTER;
- if (ni[ITEM_VENGEANCE]) current_weapon = ITEM_VENGEANCE + WEAPONS;
- if (ni[ITEM_DYING_WRATH]) current_weapon = ITEM_DYING_WRATH + WEAPONS;
- if (ni[ITEM_FATAL_FURY]) current_weapon = ITEM_FATAL_FURY + WEAPONS;
- if (current_weapon < WEAPONS)
- iWeaponDamage = weapon[current_weapon].damage * damageMultiplier;
- else
- iWeaponDamage = weapon[ (int) item[current_weapon - WEAPONS].vals[0]].damage
- * item[current_weapon - WEAPONS].vals[1]
- * damageMultiplier;
-
- // Is something available, and do we take others with us?
- if ( (current_weapon > 0) && (getBlastValue (tank, iWeaponDamage, current_weapon) > 0.0))
- {
- // Yieha! Here we go!
- #ifdef DEBUG
- printf("I have chosen to go bye bye with a...\n");
- if (current_weapon < WEAPONS)
- printf("%s\n", weapon[current_weapon].name);
- else
- printf("%s\n", item[current_weapon - WEAPONS].name);
- #endif // DEBUG
- _targetX = tank->x;
- _targetY = tank->y;
- FLOATTEXT *kamikazeText;
- kamikazeText = new FLOATTEXT (_global, _env, selectKamikazePhrase(),
- (int) tank->x, (int) tank->y - 30, color, CENTRE);
- if ( kamikazeText)
- {
- // kamikazeText->xv = 0;
- // kamikazeText->yv = -0.4;
- kamikazeText->set_speed(0.0, -0.4);
- kamikazeText->maxAge = 300;
- }
- else
- perror ( "player.cc: Failed allocating memory for kamikazeText in computerSelectItem.");
- }
- }
- }
-
- #ifdef DEBUG
- if (_target)
- printf("Finished!\nShooting at %s\n", _target->player->getName() );
- else
- printf("Finished!\nI'll free myself.\n");
- if (current_weapon < WEAPONS)
- printf("Using weapon %s\n", weapon[current_weapon].name);
- else
- printf("Using item %s\n", item[current_weapon - WEAPONS].name);
- printf("=============================================\n");
- #endif // DEBUG
- tank->cw = current_weapon;
- _global->updateMenu = 1;
- return (current_weapon);
- }
-
- int PLAYER::computerControls ()
- {
- int status = 0;
- // At the most basic: select target, select weapon, aim and fire
- tank->requireUpdate ();
- if (_turnStage == SELECT_WEAPON)
- {
- computerSelectItem();
- iTargettingRound = 0;
- _turnStage = CALCULATE_ATTACK;
- _global->updateMenu = 1;
- }
- else if (_turnStage == SELECT_TARGET)
- {
- cout << "ERROR: _turnstage became SELECT_TARGET!" << endl;
- // Target is already chosen by computerSelectItem()
- _turnStage = SELECT_WEAPON;
- }
- else if (_turnStage == CALCULATE_ATTACK)
- {
-
- #ifdef DEBUG_AIM_SHOW
- _global->bASD = true; // Now it is allowed to be true
- #endif
-
- calculateAttack(NULL);
- _turnStage = AIM_WEAPON; // If the targetting wasn't finished, it will be done later!
-
- #ifdef DEBUG_AIM_SHOW
- _global->bASD = false; // And now it isn't!
- #endif
- }
- else if (_turnStage == AIM_WEAPON)
- {
- // First: Determine whether there are any updates to be done yet:
- bool bDoAngleUpdate = false;
- bool bDoPowerUpdate = false;
- if (iTargettingRound == retargetAttempts)
- {
- // Do both when finished aiming
- bDoAngleUpdate = true;
- bDoPowerUpdate = true;
- }
- else if (iTargettingRound && (abs(_targetAngle - tank->a) <= 90))
- // Only do Angle update if still aiming and difference isn't too high
- bDoAngleUpdate = true;
-
- if (bDoAngleUpdate && (_targetAngle > tank->a && tank->a < 270) )
- {
- // Left (If already aimed)
- tank->a++;
- _global->updateMenu = 1;
- }
- else if (bDoAngleUpdate && (_targetAngle < tank->a && tank->a > 90) )
- {
- // Right (if already aimed)
- tank->a--;
- _global->updateMenu = 1;
- }
- else if (bDoPowerUpdate && (_targetPower < (tank->p - 3) && tank->p > 0))
- {
- // Reduce power (if targetting is finished)
- tank->p -= 5;
- _global->updateMenu = 1;
- }
- else if (bDoPowerUpdate && (_targetPower > (tank->p + 3) && tank->p < MAX_POWER) )
- {
- // Increase Power (if targetting is finished)
- tank->p += 5;
- _global->updateMenu = 1;
- }
- else
- {
- // Targetting finished?
- if (iTargettingRound == retargetAttempts)
- _turnStage = FIRE_WEAPON; // Finished aiming, go ahead!
- else
- _turnStage = CALCULATE_ATTACK; // Not finished, do some more aiming
- }
- }
- #ifdef OLD_GAMELOOP
- else if (fi)
- {
- // if (fi) don't do any of the following
- }
- #endif
- else if (_turnStage == FIRE_WEAPON)
- {
- // tank->activateCurrentSelection ();
- _global->updateMenu = 1;
- #ifdef DEBUG
- printf("About to activate weapon.\n");
- #endif
- tank->simActivateCurrentSelection();
- if (type == VERY_PART_TIME_BOT)
- type = NETWORK_CLIENT;
- #ifdef DEBUG
- printf("Weapon was activated.\n");
- #endif
- gloating = false;
- _turnStage = 0;
- status = CONTROL_FIRE;
- }
- return (status);
- }
-
- int PLAYER::humanControls ()
- {
- int moved = 0;
- int status = 0;
-
- if (tank)
- {
- if (!_env->mouseclock && mouse_b & 1 && mouse_x >= 250
- && mouse_x < 378 && mouse_y >= 11 && mouse_y < 19)
- {
- _global->updateMenu = 1;
- if (tank->fs)
- {
- tank->sht++;
- }
- tank->fs = 1;
- if (tank->sht > SHIELDS - 1)
- {
- tank->sht = 0;
- }
- }
- if (!_env->mouseclock && mouse_b & 1 && mouse_x >= 250
- && mouse_x < 378 && mouse_y >= 21
- && mouse_y < 29 && tank->player->ni[tank->sht] > 0 && (tank->fs || tank->sh > 0))
- {
- _global->updateMenu = 1;
- tank->ds = tank->sht;
- tank->player->ni[tank->sht]--;
- tank->sh = (int)item[tank->sht].vals[SHIELD_ENERGY];
- }
-
- tank->requireUpdate ();
- }
-
- //Keyboard control
- if ( _env->stage == STAGE_AIM)
- {
- if (tank)
- {
- if ( (key[KEY_LEFT] || key[KEY_A]) && !ctrlUsedUp && tank->a < 270)
- {
- tank->a++;
- _global->updateMenu = 1;
- if (key_shifts & KB_CTRL_FLAG)
- ctrlUsedUp = TRUE;
- }
- if ( (key[KEY_RIGHT] || key[KEY_D]) && !ctrlUsedUp && tank->a > 90)
- {
- tank->a--;
- _global->updateMenu = 1;
- if (key_shifts & KB_CTRL_FLAG)
- ctrlUsedUp = TRUE;
- }
- if ( (key[KEY_DOWN] || key[KEY_S]) && !ctrlUsedUp && tank->p > 0)
- {
- tank->p -= 5;
- _global->updateMenu = 1;
- if (key_shifts & KB_CTRL_FLAG)
- ctrlUsedUp = TRUE;
- }
- if ( (key[KEY_UP] || key[KEY_W]) && !ctrlUsedUp && tank->p < MAX_POWER)
- {
- tank->p += 5;
- _global->updateMenu = 1;
- if (key_shifts & KB_CTRL_FLAG)
- ctrlUsedUp = TRUE;
- }
- if ( (key[KEY_PGUP] || key[KEY_R]) && !ctrlUsedUp && tank->p < MAX_POWER )
- {
- tank->p += 100;
- if (tank->p > MAX_POWER)
- tank->p = MAX_POWER;
- _global->updateMenu = 1;
- if (key_shifts & KB_CTRL_FLAG)
- ctrlUsedUp = TRUE;
- }
- if ( (key[KEY_PGDN] || key[KEY_F]) && !ctrlUsedUp && tank->p > 0)
- {
- tank->p -= 100;
- if (tank->p < 0)
- tank->p = 0;
- _global->updateMenu = 1;
- if (key_shifts & KB_CTRL_FLAG)
- ctrlUsedUp = TRUE;
- }
- }
- }
-
- #ifdef OLD_GAMELOOP
- if (k && !fi)
- #endif
- #ifdef NEW_GAMELOOP
- // if ( keypressed() )
- // k = readkey();
- // else
- // k = 0;
- if (! k)
- {
- if ( keypressed() )
- k = readkey();
- }
- if (k)
- #endif
- {
- status = CONTROL_PRESSED;
- if ( _env->stage == STAGE_AIM)
- {
- if (tank)
- {
- if (k >> 8 == KEY_N)
- {
- tank->a = 180;
- _global->updateMenu = 1;
- }
- if ( (k >> 8 == KEY_TAB) || (k >> 8 == KEY_C) )
- {
- _global->updateMenu = 1;
- while (1)
- {
- tank->cw++;
- if (tank->cw >= THINGS)
- tank->cw = 0;
- if (tank->cw < WEAPONS)
- {
- if (tank->player->nm[tank->cw])
- break;
- }
- else
- {
- if (item[tank->cw - WEAPONS].selectable && tank->player->ni[tank->cw - WEAPONS])
- break;
-
- }
- }
- //calcDamageMatrix (tank, tank->cw);
- //selectTarget ();
- changed_weapon = false;
- }
- if ( ( k >> 8 == KEY_BACKSPACE) || ( k >> 8 == KEY_Z) )
- {
- _global->updateMenu = 1;
- while (1)
- {
- tank->cw--;
- if (tank->cw < 0)
- tank->cw = THINGS - 1;
-
- if (tank->cw < WEAPONS)
- {
- if (tank->player->nm[tank->cw])
- break;
- }
- else
- {
- if (item[tank->cw - WEAPONS].selectable && tank->player->ni[tank->cw - WEAPONS])
- break;
-
- }
- }
- changed_weapon = false;
- }
-
- // put the tank under computer control
- if (k >> 8 == KEY_F10)
- {
- type = PART_TIME_BOT;
- setComputerValues();
- return (computerControls());
- }
-
- // move the tank
- if (k >> 8 == KEY_COMMA) // || (key >> 8 == KEY_H) )
- moved = tank->Move_Tank(DIR_LEFT);
- else if (k >> 8 == KEY_H)
- moved = tank->Move_Tank(DIR_LEFT);
-
- if (k >> 8 == KEY_STOP) // || (key >> 8 == KEY_J) )
- moved = tank->Move_Tank(DIR_RIGHT);
- else if (k >> 8 == KEY_J)
- moved = tank->Move_Tank(DIR_RIGHT);
- if (moved)
- _global->updateMenu = 1;
-
- if ((k >> 8 == KEY_SPACE) &&
- (((tank->cw < WEAPONS) && (tank->player->nm[tank->cw] > 0)) ||
- ((tank->cw < THINGS) && (tank->player->ni[tank->cw - WEAPONS] > 0))))
- {
- // tank->activateCurrentSelection ();
- tank->simActivateCurrentSelection();
- gloating = false;
- status = CONTROL_FIRE;
- }
- }
- }
- if ((_env->stage == STAGE_ENDGAME) && (k >> 8 == KEY_ENTER || k >> 8 == KEY_ESC || k >> 8 == KEY_SPACE))
- return (-1);
- }
- return (status);
- }
-
-
-
- // returns a static string to the player's team name
- char *PLAYER::Get_Team_Name()
- {
- char *team_name = "";
-
- switch ( (int) team)
- {
- case TEAM_JEDI:
- team_name = "Jedi";
- break;
- case TEAM_NEUTRAL:
- team_name = "Neutral";
- break;
- case TEAM_SITH:
- team_name = "Sith";
- break;
- }
-
- return team_name;
- }
-
-
-
- // Pick a weapon to fire at random.
- // The weapon number is returned. If no other
- // weapon is in inventory, then 0 - small missile is
- // used.
- int PLAYER::Select_Random_Weapon()
- {
- int index;
- int num_weapons = 0;
- int random_weapon;
-
- // count number of different weapons we have
- for (index = 1; index < WEAPONS; index++)
- {
- if ( nm[index] )
- num_weapons++;
- }
-
- // we have no weapons, use default
- if ( num_weapons == 0 )
- return 0;
-
- // pick a random offset from the bottom of the list
- random_weapon = (rand() % num_weapons) + 1;
-
- index = WEAPONS - 1;
- while ( (index) && (random_weapon) )
- {
- if ( nm[index] )
- random_weapon--;
- if (random_weapon)
- index--;
- }
-
- // If the (char *)"weapon" is a dirtball, skip it 75% of the time:
- if ( ( (index == DIRT_BALL) || (index == LRG_DIRT_BALL)) && (rand() % 4))
- return 0;
-
- // Skip if it is an unburying tool
- if ( (index == RIOT_CHARGE)
- || (index == RIOT_BOMB)
- || (index == RIOT_BLAST)
- || (index == HVY_RIOT_BOMB))
- return 0;
- return index;
- }
-
-
-
- // This function selects a random item for
- // the AI to use.
- // This item to use is returned. If no
- // item can be found, then the function
- // returns 0
- int PLAYER::Select_Random_Item()
- {
- int index, item_num;
- int num_items = 0;
- int random_item;
-
- // count the items we have
-
- for (index = WEAPONS; index < THINGS; index++)
- {
- item_num = index - WEAPONS;
-
- if ( (ni[item_num]) && (item[item_num].selectable))
- num_items++;
- } // end of counting items
-
- if (! num_items)
- return 0;
-
- // if we have an item, there is still a 75% chance
- // that we may not use it
- if (rand() % 4)
- return 0;
-
- // pick a random offset from the bottom of the list
- random_item = (rand() % num_items) + 1;
-
- index = THINGS - 1;
-
- item_num = index - WEAPONS;
-
- while (item_num && random_item)
- {
- if ( (ni[item_num]) && (item[item_num].selectable))
- random_item--;
-
- if (random_item)
- {
- index--;
- item_num = index - WEAPONS;
- }
- }
-
- if ((item_num == ITEM_TELEPORT) || (item_num == ITEM_SWAPPER))
- index = 0;
-
- // // do not use teleport without a parachute
- // if ( (item_num == ITEM_TELEPORT) && (! ni[ITEM_PARACHUTE]))
- // index = 0;
-
- // do not self destruct
- if ( (item_num == ITEM_VENGEANCE)
- || (item_num == ITEM_DYING_WRATH)
- || (item_num == ITEM_FATAL_FURY))
- index = 0;
-
- if (index > 0)
- index = item_num + WEAPONS;
- return index;
- }
-
- // This function takes one off the player's time to fire.
- // If the player runs out of time, the function returns TRUE.
- // If the player has time left, or no time clock is being used,
- // then the function returns FALSE.
- int PLAYER::Reduce_Time_Clock()
- {
- if (! time_left_to_fire) // not using clock
- return FALSE;
-
- time_left_to_fire--;
- if (! time_left_to_fire) // ran out of time
- {
- tank->shots_fired++; // pretend we fired
- time_left_to_fire = _global->max_fire_time;
- return TRUE;
- }
- return FALSE;
- }
-
- // The damage provided is not the weapon damage, but what the bot calculated!
- int PLAYER::getBlastValue (TANK * aTarget, int aDamage, int aWeapon, double aDamageMod)
- {
- int iHealth = 0; // Health of the evaluated tank
- int iTeam; // Team of the evaluated tank
- double dDistance; // Distance of the target to the evaluated tank
- double dXdist, dYdist; // needed to calculate dDistance
- int iDmgValue = 0; // The value of the blast
- int iBlastDamage = 0; // The damage the weapon is doing right there
- double dTeamMod = 1.0;// Teams react differently on TAs
- int iWeapon = 0; // Items (self destruc) count as CUTTERS for their blast radius!
- TANK * cOppTank;
-
- if (aWeapon < WEAPONS)
- iWeapon = aWeapon;
- else
- iWeapon = CUTTER;
-
- for (int i = 0; i < _global->numPlayers; i++)
- {
- cOppTank = _global->players[i]->tank;
- if (cOppTank && aTarget)
- {
- if (this == _global->players[i])
- // If we'd hit ourself, life is valued twice.
- iHealth = (cOppTank->l / 2) + cOppTank->sh;
- else
- iHealth = cOppTank->l + cOppTank->sh;
- iTeam = _global->players[i]->team;
- // Only count living targets that are not the official target
- if ( (iHealth > 0) && (cOppTank != aTarget))
- {
- dXdist = fabs (aTarget->x - cOppTank->x) - (TANKWIDTH / 2);
- dYdist = fabs (aTarget->y - cOppTank->y) - (TANKHEIGHT / 2);
- if (dXdist < 0) dXdist = 1.0;
- if (dYdist < 0) dYdist = 0.0;
- if ( (aWeapon >= SHAPED_CHARGE)
- && (aWeapon <= CUTTER)
- && ( (dYdist >= (weapon[iWeapon].radius / 20))
- || (dXdist <= (TANKWIDTH / 2))))
- dDistance = 2.0 * (double) weapon[iWeapon].radius; // out of the way!
- else
- dDistance = ABSDISTANCE(dXdist, dYdist, 0, 0) - ( (double) type * (defensive + 2.0));
-
- // Now see whether the tank is in weapon blast range:
- if (dDistance <= weapon[iWeapon].radius)
- {
- // Yep, it is!
- iBlastDamage = (int) ( (double) aDamage * (1.0 - ( (dDistance / (double) weapon[iWeapon].radius) / 2.0)));
- if ( ( (team != TEAM_NEUTRAL) && (team == iTeam))
- || (this == _global->players[i]))
- {
- // it is a fellow team mate, or ourself
- // teams act differently
- switch ( (int) team)
- {
- case TEAM_JEDI:
- dTeamMod = ( (double) type / 2.0) + 1.5; // 2.0 up to 4.0
- break;
- case TEAM_SITH:
- dTeamMod = ( (double) type / 4.0) + 0.75; // 1.0 up to 2.0
- break;
- default:
- dTeamMod = (double) type; // neutrals won't like to hit themselves at all!
- }
-
- if (iBlastDamage > iHealth)
- // We would kill our mate, or cost us too much health!
- iDmgValue -= (int) (dTeamMod * ( (double) aDamage / aDamageMod) * ( (defensive + 3.0) / 2.0));
- else
- // We don't kill, but count the blast at least
- iDmgValue -= (int) (dTeamMod * ( (double) iBlastDamage / aDamageMod) * ( (defensive + 3.0) / 2.0));
-
- // Note: The /=aDamageMod results in blast damage counted worse, if the aDamageMod is low
- }
- else
- {
- // Just Someone... see if we kill them
- if (iBlastDamage > iHealth)
- // Woohoo!
- iDmgValue += (int) ( ( (1.0 + (double) type / 10))
- * (double) (iBlastDamage + iHealth)
- * ( (defensive + 3.0) / 2.0));
- else
- // Well, at least...
- iDmgValue += (int) ( ( (1.0 + (double) type / 10))
- * aDamageMod
- * (double) (iBlastDamage)
- * ( (defensive - 3.0) / -2.0));
- }
- }
- }
- }
- }
- #ifdef DEBUG
- if ((iDmgValue != 0) && (aDamageMod != 1.0))
- cout << "(blast: " << iDmgValue << ") ";
- #endif
- return (iDmgValue);
- }
-
- int PLAYER::getMoneyToSave()
- {
- double dMoneyToSave = 0.0;
- int iAvgPref = 0;
- int iPrefLimit = 0;
- int iPrefCount = 0;
- int iInvCount = 0;
- int iInvMoney = 0;
- int iMaxCost = 0;
-
- // if the preferences are exceptionally low, a div by 0 might occur, so it has to be dynamized:
- for (int i = 0; i < WEAPONS; i++)
- {
- if (_weaponPreference[i] > iPrefLimit)
- iPrefLimit = (iPrefLimit + _weaponPreference[i]) / 2;
- }
- // Now it is guaranteed, that iPrefCount and iAvgPref will result in values > 0!
- for (int i = 0; i < WEAPONS; i++)
- {
- if (_weaponPreference[i] > iPrefLimit)
- {
- iPrefCount++;
- iAvgPref += _weaponPreference[i];
- }
- }
- // we are running into divide by zero here.
- if (iPrefCount < 1)
- iPrefCount = 1;
- iAvgPref /= iPrefCount; // Now the average of all relevant weapon preferences
-
- // Now we search for everything over this average and count costs and inventory value:
- for (int i = 0; i < WEAPONS; i++)
- {
- if (_weaponPreference[i] > iAvgPref)
- {
- // This weapon is (char *)"wanted"
- dMoneyToSave += weapon[i].cost;
- if (weapon[i].cost > iMaxCost)
- iMaxCost = weapon[i].cost;
- if (nm[i])
- {
- // We have (some) if this already!
- iInvCount++;
- iInvMoney += (int) ( ( (double) nm[i] / (double) weapon[i].amt) * (double) weapon[i].cost);
- }
- }
- }
-
- // The Maximum amount is (type * 2) times the most expensive weapon we like:
- iMaxCost *= (int) type * 2;
- // As of writing this, this means a maximum of:
- // Armageddon: 100,000 credits
- // Deadly Bot: (int)type == 5
- // iMaxCost = 100,000 * 5 * 2 = 1,000,000 credits maximum!
-
- dMoneyToSave = (double)iMaxCost * 0.25;
- #ifdef DEBUG
- printf( (char *)"% 4d Prefs, worth % 6d\n", iPrefCount, iAvgPref);
- printf( (char *)"% 4d Items, worth % 6d\n", iInvCount, iInvMoney);
- printf( (char *)"MoneyToSave: % 6d\n", (int)dMoneyToSave);
- printf( (char *)"Money: % 6d -> MaxCost: %6d\n", (int)money, iMaxCost);
- #endif // DEBUG
- // Whenever dMoneyToSave is less than the money owned, iBoostItemsBought is resetted
- if (money > (int)dMoneyToSave)
- iBoostItemsBought = 0; // Let's go!
- return ( (int) dMoneyToSave);
- }
-
-
-
- // if we have some shield strength at the end of the round, then
- // reclaim this shield back into our inventory
-
- int PLAYER::Reclaim_Shield()
- {
- if (! tank)
- return FALSE;
- if (! last_shield_used)
- return FALSE;
-
- if (tank->sh > 0)
- ni[last_shield_used] += 1;
-
- last_shield_used = 0;
- return TRUE;
- }
-
-
-
- #ifdef NETWORK
-
-
- // Removes the newline character and anything after it
- // from a string.
- void PLAYER::Trim_Newline(char *line)
- {
- int index = 0;
-
- while ( line[index] )
- {
- if ( (line[index] == '\n') || (line[index] == '\r') )
- line[index] = '\0';
- else
- index++;
- }
- }
-
-
- // This function checks for incoming data from a client.
- // If data is coming in, we put the incoming data in the net_command
- // variable. If the socket connection is broken, then we will
- // close the socket and hand control over to the AI.
- int PLAYER::Get_Network_Command()
- {
- int status;
-
- status = Check_For_Incoming_Data(server_socket);
- if (status)
- {
- // we have something coming down the pipe
- memset(net_command, '\0', NET_COMMAND_SIZE); // clear buffer
- status = read(server_socket, net_command, NET_COMMAND_SIZE);
- if (! status) // connection is broken
- {
- close(server_socket);
- type = DEADLY_PLAYER;
- printf("%s lost network connection. Returning control to AI.\n", _name);
- return FALSE;
- }
- else // we got data
- {
- net_command[NET_COMMAND_SIZE - 1] = '\0';
- Trim_Newline(net_command);
- }
- }
- return TRUE;
- }
-
-
- // This function gets called during a round when a networked
- // player gets to act. The function checks to see if anything
- // is in the net_command variable. If there is, it handles
- // the request. If not, the function returns.
- //
- // We should have some time keeping in here before this goes live
- // to avoid hanging the game.
-
- int PLAYER::Execute_Network_Command(int my_turn)
- {
- char buffer[NET_COMMAND_SIZE];
- static int player_index = -1;
- static int fire_delay = 0, net_delay = 0;
-
- if (my_turn)
- {
- fire_delay++;
- // if enough time has passed, we give up and turn control over to the AI
- if (fire_delay >= NET_DELAY)
- {
- setComputerValues();
- type = VERY_PART_TIME_BOT;
- fire_delay = 0;
- return ( computerControls() );
- }
- }
-
- if (! net_command[0])
- {
- net_delay++;
- /*
- if (my_delay >= NET_DELAY)
- {
- my_delay = 0;
- setComputerValues();
- type = VERY_PART_TIME_BOT;
- strcpy(buffer, "PING");
- write(server_socket, buffer, strlen(buffer));
- return (computerControls());
- }
- else*/
- if (net_delay >= NET_DELAY_SHORT)
- {
- // prompt the client to respond
- strcpy(buffer, "PING");
- write(server_socket, buffer, strlen(buffer));
- return 0;
- }
- return 0;
- } // we did not get a command to process
-
- else
- net_delay = 0; // we got something, so reset timer
-
- if (! strncmp(net_command, "VERSION", 7) )
- {
- sprintf(buffer, "SERVERVERSION %s", VERSION);
- write(server_socket, buffer, strlen(buffer) );
- }
- else if (! strncmp(net_command, "CLOSE", 5) )
- {
- close(server_socket);
- type = DEADLY_PLAYER;
- }
- else if (! strncmp(net_command, "BOXED", 5) )
- {
- char buffer[32];
- if (_global->bIsBoxed)
- strcpy(buffer, "BOXED 1");
- else
- strcpy(buffer, "BOXED 0");
- write(server_socket, buffer, strlen(buffer));
- }
- else if (! strncmp(net_command, "GOSSIP", 6) )
- {
- snprintf(_global->tank_status, 128, "%s", & (net_command[7]) );
- _global->updateMenu = TRUE;
- }
- else if (! strncmp(net_command, "HEALTH", 6) )
- {
- int tank_index;
- char buffer[64];
-
- sscanf( &(net_command[7]), "%d", &tank_index );
- if ( (tank_index >= 0) && (tank_index < _global->numPlayers) )
- {
- if (_global->players[tank_index]->tank)
- {
- snprintf(buffer, 64, "HEALTH %d %d %d %d", tank_index,
- _global->players[tank_index]->tank->l,
- _global->players[tank_index]->tank->sh,
- _global->players[tank_index]->tank->sht);
- write(server_socket, buffer, strlen(buffer));
- }
- }
-
- }
- else if (! strncmp(net_command, "ITEM", 4) )
- {
- char buffer[32];
- int item_index;
- sscanf( &(net_command[5]), "%d", &item_index);
- if ( (item_index >= 0) && (item_index < ITEMS) )
- {
- sprintf(buffer, "ITEM %d %d", item_index, ni[item_index]);
- write(server_socket, buffer, strlen(buffer));
- }
- }
- else if (! strncmp(net_command, "MOVE", 4) )
- {
- if (! my_turn) return 0;
- if (tank)
- {
- if (strstr(net_command, "LEFT") )
- tank->Move_Tank(DIR_LEFT);
- else
- tank->Move_Tank(DIR_RIGHT);
- _global->updateMenu = 1;
- }
- }
- else if (! strncmp(net_command, "FIRE", 4) )
- {
- int angle = 180, power = 1000, item = 0;
- if (! my_turn) return 0;
- sscanf( & (net_command[5]), "%d %d %d", &item, &angle, &power);
- fire_delay = 0;
- if (tank)
- {
- if (item >= THINGS) item = 0;
- tank->cw = item;
- if (item < WEAPONS)
- {
- if (nm[tank->cw] < 1)
- tank->cw = 0;
- }
- else // item
- {
- if ( ni[tank->cw - WEAPONS] < 1)
- tank->cw = 0;
- }
- tank->a = angle;
- if (tank->a > 270) tank->a = 270;
- else if (tank->a < 90) tank->a = 90;
- tank->p = power;
- if (tank->p > 2000) tank->p = 2000;
- else if (tank->p < 0) tank->p = 0;
- tank->simActivateCurrentSelection();
- gloating = false;
- net_command[0] = '\0';
- return CONTROL_FIRE;
- }
- }
-
- // find out which player this is
- else if (! strncmp(net_command, "WHOAMI", 6) )
- {
- bool found = false;
- char buffer[128];
-
- while ( (player_index < _global->numPlayers) && (! found) )
- {
- if ( _global->players[player_index] == this )
- {
- found = true;
- sprintf(buffer, "YOUARE %d", player_index);
- }
- else
- player_index++;
- }
- // check to see if something went very wrong
- if (! found)
- strcpy(buffer, "YOUARE -1");
- write(server_socket, buffer, strlen(buffer));
- }
- // return wind speed
- else if (! strncmp(net_command, "WIND", 4) )
- {
- char buffer[64];
- sprintf(buffer, "WIND %f", _env->wind);
- write(server_socket, buffer, strlen(buffer));
- }
-
- // find out how many players we have
- else if (! strncmp(net_command, "NUMPLAYERS", 10) )
- {
- char buffer[32];
- sprintf(buffer, "NUMPLAYERS %d", _global->numPlayers);
- write(server_socket, buffer, strlen(buffer));
- }
- else if (! strncmp(net_command, "PLAYERNAME", 10) )
- {
- int my_number;
- char buffer[128];
- sscanf( &(net_command[11]), "%d", &my_number);
- if ( (my_number >= 0) && (my_number < _global->numPlayers) )
- {
- sprintf(buffer, "PLAYERNAME %d %s", my_number, _global->players[my_number]->getName() );
- write(server_socket, buffer, strlen(buffer));
- }
- }
-
- // how many rounds are we playing
- else if (! strncmp(net_command, "ROUNDS", 6) )
- {
- char buffer[64];
- sprintf(buffer, "ROUNDS %d %d", (int) _global->rounds, _global->currentround);
- write(server_socket, buffer, strlen(buffer));
- }
- // send back the position of each tank
- else if (! strncmp(net_command, "TANKPOSITION", 12) )
- {
- char buffer[64];
- int count;
-
- sscanf( &(net_command[13]), "%d", &count);
- if ( (count >= 0) && (count < _global->numPlayers) )
- {
- if (_global->players[count]->tank)
- {
- sprintf(buffer, "TANKPOSITION %d %d %d", count, (int) _global->players[count]->tank->x,
- (int) _global->players[count]->tank->y);
- write(server_socket, buffer, strlen(buffer));
- }
- }
- }
-
- // send back the surface height of the dirt
- else if (! strncmp(net_command, "SURFACE", 7) )
- {
- char buffer[32];
- int x;
-
- sscanf( &(net_command[8]), "%d", &x);
- if ( (x >= 0) && (x < _global->screenWidth) )
- {
- sprintf(buffer, "SURFACE %d %d", x, _env->surface[x]);
- write(server_socket, buffer, strlen(buffer));
- }
- }
- else if (! strncmp(net_command, "SCREEN", 6) )
- {
- char buffer[64];
- sprintf(buffer, "SCREEN %d %d", _global->screenWidth, _global->screenHeight);
- write(server_socket, buffer, strlen(buffer));
- }
- else if (! strncmp(net_command, "TEAMS", 5) )
- {
- int count;
- char buffer[32];
-
- sscanf( &(net_command[6]), "%d", &count);
- if ( (count < _global->numPlayers) && (count >= 0) )
- {
- sprintf(buffer, "TEAM %d %d", count, (int) _global->players[count]->team);
- write(server_socket, buffer, strlen(buffer));
- }
- }
- else if (! strncmp(net_command, "WALLTYPE", 8) )
- {
- char buffer[32];
- sprintf(buffer, "WALLTYPE %d", _env->current_wallType);
- write(server_socket, buffer, strlen(buffer));
- }
- else if (! strncmp(net_command, "WEAPON", 6) )
- {
- char buffer[32];
- int weapon_number;
- sscanf( &(net_command[7]), "%d", &weapon_number);
- if ( (weapon_number >= 0) && (weapon_number < WEAPONS) )
- {
- sprintf(buffer, "WEAPON %d %d", weapon_number, nm[weapon_number]);
- write(server_socket, buffer, strlen(buffer));
- }
- }
-
- net_command[0] = '\0';
- return 0;
- }
-
- #endif
-
-
-
-
-