/* * XPilot NG, a multiplayer space war game. * * Copyright (C) 1991-2001 by * * Bjørn Stabell * Ken Ronny Schouten * Bert Gijsbers * Dick Balaska * * 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 */ /* Robot code originally submitted by Maurice Abraham. */ #include "xpserver.h" #define DEFAULT_ROBOT_TYPE "default" /* * Array of different robots which are used * when we cannot read a robot configuration file. * * Each robot has a robot driver, * a name as seen by the human players, * some optional configuration string, * a usage count, * and a shipshape. */ static robot_t DefaultRobots[] = { { DEFAULT_ROBOT_TYPE, "Mad Max", "94 20", 0, "(SH: 15,0 7,1 7,2 2,4 -1,11 -3,11 -2,3 -8,6 -8,-6 -2,-3 -3,-11 " "-1,-11 2,-4 7,-2 7,-1)" "(EN: -8,0)(MG: 15,0)(LL: -8,-6)(RL: -8,6)(MR: 15,0)" }, { DEFAULT_ROBOT_TYPE, "Blackie", "10 90", 0, "(SH: 15,0 6,2 -2,3 -1,4 -2,5 -10,8 -13,8 -13,1 -15,0 -13,-1 " "-13,-8 -10,-8 -2,-5 -1,-4 -2,-3 6,-2)" "(EN: -13,0)(MG: 15,0)(LL: -13,8)(RL: -13,-8)(MR: 15,0)" }, { DEFAULT_ROBOT_TYPE, "Kryten", "70 40", 0, "(SH: 15,0 0,8 -8,0 0,-8)" "(EN: 0,0)(MG: 15,0)(LL: 0,8)(RL: 0,-8)(MR: 15,0)" }, { DEFAULT_ROBOT_TYPE, "Marvin", "30 70", 0, "(SH: 10,0 10,7 5,14 -5,14 -10,7 -10,-7 -5,-14 5,-14 10,-7 10,0 " "5,5 2,7 5,0 2,-7 5,-5)" "(EN: -10,0)(MG: 10,0)(LL: -10,7)(RL: -10,-7)(MR: 10,0)" }, { DEFAULT_ROBOT_TYPE, "R2D2", "50 60", 0, "(SH: 15,0 14,1 -1,2 -2,9 0,10 -4,10 -7,2 -8,2 -8,-2 -7,-2 -4,-10 " "0,-10 -2,-9 -1,-2 14,-1)" "(EN: -7,-2)(MG: 15,0)(LL: -8,-2)(RL: -7,-2)(MR: 15,0)" }, { DEFAULT_ROBOT_TYPE, "C3PO", "60 50", 0, "(SH: 10,0 0,5 0,15 15,10 0,15 -15,10 0,15 0,5 -7,0 0,-5 0,-15 " "-15,-10 0,-15 15,-10 0,-15 0,-5)" "(EN: 0,0)(MG: 10,0)(LL: 0,5)(RL: 0,-5)(MR: 10,0)" }, { DEFAULT_ROBOT_TYPE, "K9", "50 50", 0, "(SH: 15,0 15,5 5,5 5,-5 15,-5 15,0 -15,0 -15,5 5,5 5,-5 -15,-5 " "-15,-8 -15,8 -15,0)" "(EN: 15,0)(MG: 15,0)(LL: 15,0)(RL: 15,0)(MR: 15,0)" }, { DEFAULT_ROBOT_TYPE, "Robby", "45 55", 0, "(SH: 15,0 0,12 -9,8 -9,-8 0,-12)" "(EN: -9,0)(MG: 15,0)(LL: -9,8)(RL: -9,-8)(MR: 15,0)" }, { DEFAULT_ROBOT_TYPE, "Mickey", "05 95", 0, "(SH: 5,-1 8,-5 7,-9 4,-11 -1,-10 -5,-6 -8,-10 -8,10 -5,6 -1,10 " "4,11 7,9 8,5 5,1 0,0)" "(EN: -8,0)(MG: 5,-1)(LL: -8,-10)(RL: -8,10)(MR: 5,-1)" }, { DEFAULT_ROBOT_TYPE, "Hermes", "15 85", 0, "(SH: 10,1 12,8 -11,8 -10,3 -7,0 -5,2 -7,0 -10,-1 -10,-3 -13,-4 " "-13,-7 -15,-8 -15,-13 -5,-5 -2,-4 5,-2)" "(EN: -15,-10)(MG: 10,1)(LL: -15,-13)(RL: -15,-8)(MR: 10,1)" }, { DEFAULT_ROBOT_TYPE, "Pan", "60 60", 0, "(SH: 15,-1 15,0 5,0 5,-1 5,9 -15,9 -15,-4 -5,-7 -3,-8 -7,-8 -5,-7 " "5,-4 5,-1 -15,-1)" "(EN: -15,2)(MG: 15,-1)(LL: -15,-4)(RL: -15,9)(MR: 15,-1)" }, { DEFAULT_ROBOT_TYPE, "Azurion", "40 30", 0, "(SH: 15,0 0,2 -9,8 -3,0 -9,-8 0,-2)" "(EN: -3,0)(MG: 15,0)(LL: -9,8)(RL: -9,-8)(MR: 15,0)" }, { DEFAULT_ROBOT_TYPE, "Droidion", "60 30", 0, "(SH: 10,0 5,9 -6,9 -11,0 -6,-9 5,-9 10,0)" "(EN: -6,0)(MG: 10,0)(LL: -6,9)(RL: -6,-9)(MR: 10,0)" }, { DEFAULT_ROBOT_TYPE, "Terminator", "80 40", 0, "(SH: 15,0 0,2 -9,8 -3,0 -9,-8 0,-2)" "(EN: -3,0)(MG: 15,0)(LL: -9,8)(RL: -9,-8)(MR: 15,0)" }, { DEFAULT_ROBOT_TYPE, "Sniper", "30 90", 0, "(SH: 15,0 4,2 -2,8 -4,7 -3,2 -8,5 -8,2 -6,1 -6,-1 -8,-2 -8,-5 " "-3,-2 -4,-7 -2,-8 4,-2)" "(EN: -6,0)(MG: 15,0)(LL: -8,2)(RL: -8,-2)(MR: 15,0)" }, { DEFAULT_ROBOT_TYPE, "Slugger", "40 40", 0, "(SH: 15,0 13,1 3,2 -1,8 -3,8 -3,2 -5,2 -8,5 -8,-5 -5,-2 -3,-2 " "-3,-8 -1,-8 3,-2 13,-1 15,0)" "(EN: -8,0)(MG: 15,0)(LL: -8,-5)(RL: -8,5)(MR: 15,0)" }, { DEFAULT_ROBOT_TYPE, "Uzi", "95 5", 0, "(SH: 15,3 7,3 7,-8 3,-8 3,1 -2,1 -3,-1 -5,-1 -14,-5 -15,2 -3,4 " "-1,8 0,6 13,6 14,8 15,6)" "(EN: -14,-1)(MG: 15,3)(LL: -15,2)(RL: -14,-5)(MR: 15,3)" }, { DEFAULT_ROBOT_TYPE, "Capone", "80 50", 0, "(SH: 14,0 2,2 0,8 1,8 -3,8 -3,2 -8,4 -7,1 -7,-1 -8,-4 -3,-2 " "-3,-8 1,-8 0,-8 2,-2)" "(EN: -7,0)(MG: 14,0)(LL: -8,-4)(RL: -8,4)(MR: 14,0)" }, { DEFAULT_ROBOT_TYPE, "Tanx", "40 70", 0, "(SH: 15,1 2,0 1,-2 6,-3 6,-5 3,-8 -10,-8 -13,-6 -13,-3 -10,-2 " "-11,2 -7,2 -7,8 -7,2 1,2 2,1)" "(EN: -13,-4)(MG: 15,1)(LL: -13,-6)(RL: -13,-3)(MR: 15,1)" }, { DEFAULT_ROBOT_TYPE, "Chrome Star", "60 60", 0, "(SH: 10,0 -10,6 2,-10 2,10 -10,-6 10,0)" "(EN: -6,0)(MG: 10,0)(LL: -10,6)(RL: -10,-6)(MR: 10,0)" }, { DEFAULT_ROBOT_TYPE, "Bully", "80 10", 0, "(SH: 11,0 12,-3 9,-3 8,-2 -5,-5 -9,-11 -14,-14 -5,-3 -5,3 -14,14 " "-9,11 -5,5 8,2 9,3 12,3)" "(EN: -5,0)(MG: 11,0)(LL: -14,-14)(RL: -14,14)(MR: 11,0)" }, { DEFAULT_ROBOT_TYPE, "Metal Hero", "40 45", 0, "(SH: 15,5 12,-2 9,-2 10,-1 -8,-1 -4,-1 1,-3 -13,-9 -9,0 -15,8 " "1,3 -4,1 -8,1 -8,-1 -8,1 11,1)" "(EN: -9,0)(MG: 15,5)(LL: -13,-9)(RL: -15,8)(MR: 15,5)" }, { DEFAULT_ROBOT_TYPE, "Aurora", "60 55", 0, "(SH: 15,0 -1,3 -3,5 -3,9 7,10 -12,10 -6,9 -6,4 -8,0 -6,-4 -6,-9 " "-12,-10 7,-10 -3,-9 -3,-5 -1,-3)" "(EN: -8,0)(MG: 15,0)(LL: -12,10)(RL: -12,-10)(MR: 15,0)" }, { DEFAULT_ROBOT_TYPE, "Dalt Wisney", "30 75", 0, "(SH: 14,0 7,-4 0,-1 -5,-4 2,-8 0,-10 -14,-10 -5,-7 -14,0 -5,7 " "-14,10 0,10 2,8 -5,4 0,1 7,4)" "(EN: -14,0)(MG: 14,0)(LL: -14,10)(RL: -14,-10)(MR: 14,0)" }, { DEFAULT_ROBOT_TYPE, "Psycho", "65 55", 0, "(SH: 8,0 5,8 3,12 0,15 0,0 -8,3 -8,-3 0,0 0,-15 3,-12 5,-8)" "(EN: -8,0)(MG: 8,0)(LL: -8,3)(RL: -8,-3)(MR: 8,0)" }, { DEFAULT_ROBOT_TYPE, "Gorgon", "30 40", 0, "(SH: 15,0 5,2 3,8 2,2 -9,2 -10,4 -12,2 -14,4 -14,-4 -12,-2 -10,-4 " "-9,-2 2,-2 3,-8 5,-2)" "(EN: -14,0)(MG: 15,0)(LL: -14,4)(RL: -14,-4)(MR: 15,0)" }, { DEFAULT_ROBOT_TYPE, "Pompel", "50 50", 0, "(SH: 15,0 14,4 10,5 5,2 -7,3 -7,6 5,8 -9,8 -9,-8 5,-8 -7,-6 " "-7,-3 5,-2 10,-5 14,-4)" "(EN: -9,0)(MG: 15,0)(LL: -9,8)(RL: -9,-8)(MR: 15,0)" }, { DEFAULT_ROBOT_TYPE, "Pilt", "50 50", 0, "(SH: 15,0 13,-2 9,-3 3,-3 -3,-3 -5,-2 -13,-2 -15,-3 -15,3 -13,2 " "-5,2 -3,3 -3,-8 -3,8 -3,3 8,3)" "(EN: -15,0)(MG: 15,0)(LL: -15,3)(RL: -15,-3)(MR: 15,0)" }, { DEFAULT_ROBOT_TYPE, "Sparky", "20 40", 0, "(SH:)" "(EN: -8,0)(MG: 14,0)(LL: -8,8)(RL: -8,-8)(MR: 14,0)" }, { DEFAULT_ROBOT_TYPE, "Cobra", "85 60", 0, "(SH: 8,0 8,-6 6,-8 0,-7 5,-6 -14,-4 5,-2 0,-1 5,0 0,1 5,2 " "-14,4 5,6 0,7 6,8 8,6 8,0)" "(EN: 5,0)(MG: 8,0)(LL: -14,-4)(RL: -14,4)(MR: 8,0)" }, { DEFAULT_ROBOT_TYPE, "Falcon", "70 20", 0, "(SH: 14,2 14,4 2,10 -5,10 -10,8 -12,3 -12,-3 -10,-8 -5,-10 9,-11 " "10,-8 7,-8 14,-4 14,-2 4,-2 4,2)" "(EN: -12,0)(MG: 14,2)(LL: -12,3)(RL: -12,-3)(MR: 14,2)" }, { DEFAULT_ROBOT_TYPE, "Boson", "25 35", 0, "(SH: 15,0 10,-5 4,-8 7,-2 7,2 4,8 6,0 4,-8 -10,-8 -10,8 -10,-8 " "-15,-7 -15,7 -10,8 4,8 10,5)" "(EN: -15,0)(MG: 15,0)(LL: -15,-7)(RL: -15,7)(MR: 15,0)" }, { DEFAULT_ROBOT_TYPE, "Blazy", "40 40", 0, "(SH: 4,0 2,4 -5,11 10,12 -8,12 -4,6 -2,0 -4,-6 -8,-12 10,-12 " "-5,-11 2,-4)" "(EN: -2,0)(MG: 4,0)(LL: -8,12)(RL: -8,-12)(MR: 4,0)" }, { DEFAULT_ROBOT_TYPE, "Pixie", "15 93", 0, "(SH: 15,0 7,4 11,1 -4,3 3,5 -7,10 -9,2 -9,-2 -7,-10 3,-5 -4,-3 " "11,-1 7,-4)" "(EN: -9,0)(MG: 15,0)(LL: -9,2)(RL: -9,-2)(MR: 15,0)" }, { DEFAULT_ROBOT_TYPE, "Wimpy", "5 98", 0, "(SH: 3,0 6,5 8,10 5,11 1,10 -1,8 -4,9 -8,6 -5,0 -8,-6 -4,-9 " "-1,-8 1,-10 5,-11 8,-10 6,-5)" "(EN: -5,0)(MG: 3,0)(LL: -8,-6)(RL: -8,6)(MR: 3,0)" }, { DEFAULT_ROBOT_TYPE, "Bonnie", "30 40", 0, "(SH: 15,3 5,3 5,1 4,-1 0,-1 -2,-8 -8,-8 -5,3 -6,6 -8,7 -7,8 -4,7 " "10,7 12,8 14,8 15,7 15,3)" "(EN: -6,0)(MG: 15,3)(LL: -8,7)(RL: -8,-8)(MR: 15,3)" }, { DEFAULT_ROBOT_TYPE, "Clyde", "40 45", 0, "(SH: 14,0 5,5 6,2 0,2 0,8 -13,8 -13,4 -4,4 -6,0 -4,-4 -13,-4 " "-13,-8 0,-8 0,-2 6,-2 5,-5)" "(EN: -6,0)(MG: 14,0)(LL: -13,8)(RL: -13,-8)(MR: 14,0)" }, { DEFAULT_ROBOT_TYPE, "Neuro", "70 70", 0, "(SH: 12,-7 12,-12 5,-12 2,-10 1,-5 -9,-4 -11,2 -8,8 -3,11 3,11 " "9,8 11,2 13,0 12,-3 12,-7 7,-7)" "(EN: -8,2)(MG: 12,-7)(LL: -8,8)(RL: -9,-4)(MR: 12,-7)" }, }; int NumRobots = 0; /* number of currently playing robots. */ static int MAX_ROBOTS = 0; /* number of different robot parameters. */ static robot_t *Robots; /* array of robot parameters. */ /* * Function prototype for robot type setup routines. * Add your own robot type setup routine here. */ extern int Robot_default_setup(robot_type_t *type_ptr); /* * Array to store function pointers to robot type setup routines. * The default robot type should be first. * Add your own robot type setup routine here too. */ static struct robot_setup { int (*setup_func)(robot_type_t *type_ptr); } robot_type_setups[] = { { Robot_default_setup }, }; /* * Array of the different robot types available. */ static int num_robot_types = NELEM(robot_type_setups); static robot_type_t robot_types[NELEM(robot_type_setups)]; void Parse_robot_file(void) { if (options.robotFile && *options.robotFile) { FILE *fp = fopen(options.robotFile, "r"); if (fp) { char buf[1024]; char name_buf[MAX_NAME_LEN]; char ship_buf[2*MSG_LEN]; char type_buf[MAX_NAME_LEN]; char para_buf[MSG_LEN]; int got_name = 0; int num_robs = 0; int max_robs = 0; robot_t *robs = 0; /* * Fill in some default values. */ strlcpy(ship_buf, "(15,0)(-9,8)(-9,-8)", sizeof ship_buf); strlcpy(type_buf, DEFAULT_ROBOT_TYPE, sizeof type_buf); strlcpy(para_buf, "", sizeof para_buf); while (fp) { int end_of_record = 0; if (!fgets(buf, sizeof buf, fp)) { end_of_record = 1; fclose(fp); fp = NULL; } else if (*buf == '\n') end_of_record = 1; else { size_t size = 0; int key = 0; char *dst = 0; if (!strncmp(buf, "name:", 5)) { dst = name_buf; size = sizeof name_buf; key = 1; got_name = 1; } else if (!strncmp(buf, "ship:", 5)) { dst = ship_buf; size = sizeof ship_buf; key = 2; } else if (!strncmp(buf, "type:", 5)) { dst = type_buf; size = sizeof type_buf; key = 3; } else if (!strncmp(buf, "para:", 5)) { dst = para_buf; size = sizeof para_buf; key = 4; } if (key > 0) { char *ptr = strchr(buf, ':') + 1; while (isspace(*ptr)) ptr++; strlcpy(dst, ptr, size); ptr = &dst[strlen(dst)]; while (--ptr >= dst && isspace(*ptr)) *ptr = '\0'; } } if (end_of_record && got_name) { got_name = 0; if (num_robs == max_robs) { if (max_robs == 0) { max_robs = 10; robs = XMALLOC(robot_t, max_robs); } else { max_robs += 10; robs = XREALLOC(robot_t, robs, max_robs); } if (!robs) { error("Not enough memory to parse robotsfile"); fclose(fp); break; } } strlcpy(robs[num_robs].driver, type_buf, sizeof robs[num_robs].driver); strlcpy(robs[num_robs].name, name_buf, sizeof robs[num_robs].name); strlcpy(robs[num_robs].config, para_buf, sizeof robs[num_robs].config); strlcpy(robs[num_robs].shape, ship_buf, sizeof robs[num_robs].shape); robs[num_robs].used = 0; num_robs++; } } if (num_robs > 0) { Robots = robs; MAX_ROBOTS = num_robs; } } } if (MAX_ROBOTS == 0) { Robots = &DefaultRobots[0]; MAX_ROBOTS = NELEM(DefaultRobots); } #ifdef DEVELOPMENT if (getenv("XPILOTS_DUMP_ROBOTS_TO_ROBOT_FILE") != NULL) { if (options.robotFile && *options.robotFile) { FILE *fp = fopen(options.robotFile, "w"); if (fp) { int i; for (i = 0; i < MAX_ROBOTS; i++) { fprintf(fp, "type:\t%s\n" "para:\t%s\n" "ship:\t%s\n" "name:\t%s\n" "\n", Robots[i].driver, Robots[i].config, Robots[i].shape, Robots[i].name); } fclose(fp); } } } #endif } /* * First time initialization of all the robot stuff. */ void Robot_init(void) { int i, result, n; /* * For each robot driver call its initialization function. * If this function returns 0 then remember this robot driver. */ n = 0; for (i = 0; i < num_robot_types; i++) { memset(&robot_types[n], 0, sizeof(robot_type_t)); result = (*robot_type_setups[i].setup_func)(&robot_types[n]); if (result == 0) n++; } num_robot_types = n; Parse_robot_file(); if (options.robotTeam < 0 || options.robotTeam >= MAX_TEAMS) options.robotTeam = 0; } static void Robot_talks(enum robot_talk_t says_what, char *robot_name, const char *other_name) { /* * Insert your own witty messages here and remove the silly ones. */ static const char *enter_msgs[] = { "%s just can't stand you anymore.", "%s has come to give you a hard time.", "%s is looking for trouble.", "%s has a very loose trigger finger.", "Have fear, %s is here.", "Prepare to die by the hands of %s.", "%s is untouchable.", "%s is in a gruesome mood.", "%s is in a killing mood.", "%s wants you for dessert.", "%s vows to torment you in this life and the next.", "%s has no sense of humour.", "%s is back from the Sirius wars, and he's in a violent mood.", }; static const char *leave_msgs[] = { "That's it, I've had enough. :( I'm outta here. [%s]", "Later people. It's been fun. [%s]", "Gotta go, ... er ... this ... er ... lab is closing. [%s]", "Er... Oh! It it really that late? I gotta go. [%s].", "I'm signing off now. Bye! [%s]", "Gotta go... Er... I have some work to be done. [%s]", "This sucks! :( I'm going back to cyberspace. [%s]", "I've taken enough beating for today. [%s]", "Oh man. Playing with humans sucks. [%s]", "You can't beat us robots; we always return, stronger than ever! [%s]", "Geez, this just isn't my lucky day. See ya some other time. [%s]", "Wow, this game is just killing me. :( [%s]", "I'll be back when you stop cheating! :-( [%s]", }; static const char *kill_msgs[] = { "Have some %s. Have some! [%s]", "You want some more %s? [%s]", "%s lost his stuff again. That's just tooooooo bad. :) [%s]", "%s: did you like that one? [%s]", "Face it %s, you just can't compete with me. [%s]", "%s, my grandmother plays better than you. [%s]", "Hey %s, go play chess instead. [%s]", "I think Darwin would've said you're too unfit to survive %s :) [%s]", "Oh my, what colourful explosions you make %s. :) [%s]", "Hey %s, maybe its time you upgraded that old 386. [%s]", }; static const char *war_msgs[] = { "UNBELIEVABLE, me shot down by %s?!?! This means war [%s]", "People like %s just piss me off. [%s]", "Nice %s. But now its my turn. [%s]", "Red alert... target: %s. [%s]", "$%#^@#$^#$% That's the last time you do that %s! [%s]", "Enough's enough! It's only room enough for one of us %s here. [%s]", "I'm sorry %s, but you must... DIE!!!!! [%s]", "Jihad! Die %s! Die! [%s]", "%s will be assimilated [%s]", }; static int next_msg = -1; const char **msgsp; int two, i, n; if (options.robotsTalk != true && says_what != ROBOT_TALK_ENTER) return; switch (says_what) { case ROBOT_TALK_ENTER: msgsp = enter_msgs; n = NELEM(enter_msgs); two = 1; break; case ROBOT_TALK_LEAVE: msgsp = leave_msgs; n = NELEM(leave_msgs); two = 1; break; case ROBOT_TALK_KILL: msgsp = kill_msgs; n = NELEM(kill_msgs); two = 2; break; case ROBOT_TALK_WAR: msgsp = war_msgs; n = NELEM(war_msgs); two = 2; break; default: return; } if (next_msg == -1) next_msg = (int)(rfrac() * 997); if (++next_msg > 997) next_msg = 0; i = next_msg % n; if (two == 2) Set_message_f(msgsp[i], other_name, robot_name); else Set_message_f(msgsp[i], robot_name); } static void Robot_create(void) { player_t *robot; robot_t *rob; team_t *teamp = NULL; int i, num, most_used, least_used; robot_data_t *data, *new_data; robot_type_t *rob_type; if (peek_ID() == 0) return; if ((new_data = XMALLOC(robot_data_t, 1)) == NULL) { error("malloc robot_data"); return; } new_data->private_data = NULL; most_used = 0; for (i = 0; i < MAX_ROBOTS; i++) { if (Robots[i].used > Robots[most_used].used) most_used = i; } for (i = 0; i < NumPlayers; i++) { player_t *pl_i = Player_by_index(i); if (Player_is_robot(pl_i)) { data = (robot_data_t *)pl_i->robot_data_ptr; if (Robots[data->robots_ind].used < Robots[most_used].used) Robots[data->robots_ind].used = Robots[most_used].used; } } least_used = 0; for (i = 0; i < MAX_ROBOTS; i++) { if (Robots[i].used < Robots[least_used].used) least_used = i; } num = (int)(rfrac() * MAX_ROBOTS); while (Robots[num].used > Robots[least_used].used) { if (++num >= MAX_ROBOTS) num = 0; } rob = &Robots[num]; rob->used++; new_data->robots_ind = num; new_data->robot_types_ind = 0; for (i = 1; i < num_robot_types; i++) { if (!strcmp(robot_types[i].name, rob->driver)) new_data->robot_types_ind = i; } rob_type = &robot_types[new_data->robot_types_ind]; Init_player(NumPlayers, options.allowShipShapes ? Parse_shape_str(rob->shape) : NULL, PL_TYPE_ROBOT); robot = Player_by_index(NumPlayers); robot->robot_data_ptr = new_data; strlcpy(robot->name, rob->name, MAX_CHARS); strlcpy(robot->username, options.robotUserName, MAX_CHARS); strlcpy(robot->hostname, options.robotHostName, MAX_CHARS); robot->turnspeed = MAX_PLAYER_TURNSPEED; robot->turnspeed_s = MAX_PLAYER_TURNSPEED; robot->turnresistance = 0.12; robot->turnresistance_s = 0.12; robot->power = MAX_PLAYER_POWER; robot->power_s = MAX_PLAYER_POWER; robot->check = 0; if (BIT(world->rules->mode, TEAM_PLAY)) { robot->team = Pick_team(PL_TYPE_ROBOT); teamp = Team_by_index(robot->team); assert(teamp); /* if teamplay, can't have TEAM_NOT_SET */ teamp->NumMembers++; teamp->NumRobots++; } Pick_startpos(robot); (*rob_type->robot_create)(robot, rob->config); Go_home(robot); request_ID(); NumPlayers++; NumRobots++; Rank_get_saved_score(robot); for (i = 0; i < NumPlayers - 1; i++) { player_t *pl_i = Player_by_index(i); if (pl_i->conn != NULL) { Send_player(pl_i->conn, robot->id); Send_base(pl_i->conn, robot->id, robot->home_base->ind); } } Robot_talks(ROBOT_TALK_ENTER, robot->name, ""); if (options.logRobots) xpprintf("%s %s (%d, %s) starts at startpos %d.\n", showtime(), robot->name, NumPlayers, robot->username, robot->home_base->ind); if (NumPlayers == 1) { if (options.maxRoundTime > 0) roundtime = options.maxRoundTime * FPS; else roundtime = -1; Set_message_f("Player entered. Delaying 0 seconds until next %s.", (BIT(world->rules->mode, TIMING) ? "race" : "round")); } updateScores = true; } void Robot_destroy(player_t *pl) { robot_type_t *rob_type = &robot_types[pl->robot_data_ptr->robot_types_ind]; (*rob_type->robot_destroy)(pl); XFREE(pl->robot_data_ptr); } void Robot_delete(player_t *pl, bool kicked) { int i; if (pl == NULL) { player_t *low_pl = NULL; double low_score = (double)LONG_MAX; /* * Find the robot with the lowest score. */ for (i = 0; i < NumPlayers; i++) { player_t *pl_i = Player_by_index(i); if (!Player_is_robot(pl_i)) continue; if (Get_Score(pl_i) < low_score) { low_pl = pl_i; low_score = Get_Score(low_pl); } } if (low_pl) pl = low_pl; } if (pl) { if (kicked) Set_message_f("%s upset the gods and was kicked out of the game.", pl->name); Delete_player(pl); } } /* * Ask a robot for an alliance */ void Robot_invite(player_t *pl, player_t *inviter) { robot_type_t *rob_type = &robot_types[pl->robot_data_ptr->robot_types_ind]; (*rob_type->robot_invite)(pl, inviter); } /* * Turn on a war lock. */ static void Robot_set_war(player_t *pl, int victim_id) { robot_type_t *rob_type = &robot_types[pl->robot_data_ptr->robot_types_ind]; (*rob_type->robot_set_war)(pl, victim_id); } /* * Turn off a war lock. * The only time when this can be called is if * a player a robot has war on leaves the game. */ void Robot_reset_war(player_t *pl) { Robot_set_war(pl, NO_ID); } /* * Someone has programmed a robot (using ECM) to seek some player. */ void Robot_program(player_t *pl, int victim_id) { Robot_set_war(pl, victim_id); } /* * Return the id of the player this robot has war on. * If the robot is not in peace mode then return -1. */ int Robot_war_on_player(player_t *pl) { robot_type_t *rob_type = &robot_types[pl->robot_data_ptr->robot_types_ind]; return (*rob_type->robot_war_on_player)(pl); } /* * A robot has killed someone. * Or a robot has been killed by someone. * Maybe this is enough reason for the killed robot to change * its behavior with respect to the player it has been killed by. */ void Robot_war(player_t *pl, player_t *kp) { if (kp->id == pl->id) return; if (Player_is_robot(kp)) { Robot_talks(ROBOT_TALK_KILL, kp->name, pl->name); Robot_set_war(kp, NO_ID); } if (Player_is_robot(pl) && rfrac() * 100.0 < Get_Score(kp) - Get_Score(pl) && !Players_are_teammates(pl, kp) && !Players_are_allies(pl, kp)) { Robot_talks(ROBOT_TALK_WAR, pl->name, kp->name); if (Robot_war_on_player(pl) != kp->id) { sound_play_all(DECLARE_WAR_SOUND); Robot_set_war(pl, kp->id); } } } /* * A robot starts on its homebase. */ void Robot_go_home(player_t *pl) { robot_type_t *rob_type = &robot_types[pl->robot_data_ptr->robot_types_ind]; (*rob_type->robot_go_home)(pl); } /* * Someone sends a message to a robot. * The format of the message is: "This is the real message [receiver]:[sender]" */ void Robot_message(player_t *pl, const char *message) { robot_type_t *rob_type = &robot_types[pl->robot_data_ptr->robot_types_ind]; (*rob_type->robot_message)(pl, message); } /* * A robot plays this frame. */ static void Robot_play(player_t *pl) { robot_type_t *rob_type = &robot_types[pl->robot_data_ptr->robot_types_ind]; (*rob_type->robot_play)(pl); } /* * Check if robot is still considered good enough to continue playing. * Return false if robot continues playing, * return true if robot leaves the game. */ static bool Robot_check_leave(player_t *pl) { bool leave = false; if (!options.robotsLeave) return false; if (options.robotLeaveLife > 0 && pl->pl_deaths_since_join >= options.robotLeaveLife) { Set_message_f("%s retired.", pl->name); leave = true; } if (leave) { Robot_talks(ROBOT_TALK_LEAVE, pl->name, ""); Robot_delete(pl, false); return true; } return false; } /* * On each round we call the robot type round ticker. */ static void Robot_round_tick(void) { int i; if (NumRobots > 0) { for (i = 0; i < num_robot_types; i++) (*robot_types[i].robot_round_tick)(); } } /* * Update tanks here. */ static void Tank_play(player_t *pl) { int t = frame_loops % (TANK_NOTHRUST_TIME + TANK_THRUST_TIME); if (t == 0) Thrust(pl, true); else if (t == TANK_THRUST_TIME) Thrust(pl, false); } /* * Update robots. If 'tick' is true, robot AI routines will be called. */ void Robot_update(bool tick) { int i; static double new_robot_delay; int num_playing_ships, num_any_ships; num_any_ships = NumPlayers + login_in_progress; num_playing_ships = num_any_ships - NumPseudoPlayers; if ((num_playing_ships < options.maxRobots || NumRobots < options.minRobots) && num_playing_ships < Num_bases() && num_any_ships < NUM_IDS && NumRobots < MAX_ROBOTS && !(BIT(world->rules->mode, TEAM_PLAY) && options.restrictRobots && world->teams[options.robotTeam].NumMembers >= world->teams[options.robotTeam].NumBases)) { new_robot_delay += timeStep; if (new_robot_delay >= ROBOT_CREATE_DELAY) { Robot_create(); new_robot_delay = 0; } } else { new_robot_delay = 0; if (NumRobots > 0) { if ((num_playing_ships > Num_bases()) || (num_any_ships > NUM_IDS) || (num_playing_ships > options.maxRobots && NumRobots > options.minRobots)) Robot_delete(NULL, false); } } if (!tick) return; if (NumRobots <= 0 && NumPseudoPlayers <= 0) return; Robot_round_tick(); for (i = 0; i < NumPlayers; i++) { player_t *pl = Player_by_index(i); if (Player_is_tank(pl)) { Tank_play(pl); continue; } if (!Player_is_robot(pl)) /* Ignore non-robots. */ continue; if (!Player_is_alive(pl)) { if (Robot_check_leave(pl)) i--; continue; } /* * Let the robot code control this robot. */ Robot_play(pl); } }