/* * Copyright (C) 2003 Tim Martin * * 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 #include #include #include #include #include #include #include #include /* so strptime gets picked up */ #define _XOPEN_SOURCE #include #include "main.h" #include "protocol.h" #include "map.h" #include "landvalue.h" #include "government.h" #include "population.h" #include "player.h" #include "game.h" #include "senkenconfig.h" #include "utils.h" #include "game.h" #include "companies.h" #include "investment.h" #include "utilities.h" #include "traffic.h" #define MAX_ARGS 10 #define DEFAULT_MAPSIZE 100 game_t *game = NULL; map_t *map = NULL; population_t *population = NULL; companies_t *companies = NULL; extern struct timeval game_speed; tiles_t *tiles = NULL; extern int g_population_change_month; extern time_t CurrentGameTime; static int cmd_populationstats(connection_t *conn); static int cmd_finances(connection_t *conn); static int cmd_setspeed(connection_t *conn, char *speedstr); static int cmd_spotinfo(connection_t *conn, char **argv, int argc); static int cmd_borrow(connection_t *conn, char **argv, int argc); static int cmd_listgames(connection_t *conn); static int cmd_stats(connection_t *conn); static int cmd_selectgame(connection_t *conn, char **argv, int argc); static int cmd_getgoal(connection_t *conn); static int cmd_txn_start(connection_t *conn); static int cmd_txn_commit(connection_t *conn); static int cmd_txn_abort(connection_t *conn); static int cmd_listdebts(connection_t *conn); static int cmd_payback(connection_t *conn, char **argv, int argc); static int cmd_settax(connection_t *conn, char **argv, int argc); static int cmd_gettax(connection_t *conn, char **argv, int argc); static int cmd_gethappiness(connection_t *conn); static void listdebts_cb(player_t *player, int num, int amount, int term, int payments_made, float rate, void *rock); typedef struct set_op_s { int x; int y; int connected; mapspot_t spot; } set_op_t; static void obj_changed(map_t *map, int x, int y, mapobj_t old, mapobj_t newobj, void *rock) { maptype_t type = map_item_gettype(newobj); switch (type) { case MAPTYPE_HOUSE: case MAPTYPE_EMPTY: case MAPTYPE_WATER: case MAPTYPE_ZONING: case MAPTYPE_ENTERTAINMENT: /* xxx */ break; default: if (type == MAPTYPE_POWER) { if (game) { game->power_excess += (-1 * tiles_getpoweruse(tiles, newobj)); } } if (type == MAPTYPE_WATERUTIL) { if (game) { int prod = -1 * tiles_getwateruse(tiles, newobj); if (prod > 0) { game->water_excess += prod; } } } if ((companies) && (tiles_getnumemploy(tiles, newobj) > 0)) { companies_add(companies, x, y, newobj); } break; } } static map_t * make_map(tiles_t *tiles, player_t *player, int sizex, int sizey, int *startx, int *starty) { map_t *ret; int r; const char *land = config_getstring("initial_land_type", "grassland"); mapobj_t landobj; landobj = map_item_name2obj((char *)land); if (landobj == MAPOBJ_INVALID) { printf("Invalid initial land type: %s\n", land); landobj = map_item_emptytype(); } map_init(tiles, sizex, sizey, landobj, &ret); map_set_obj_changed_cb(ret, &obj_changed, NULL); government_randomize(ret, tiles, player, startx, starty); r = government_initial_player_land(ret, tiles, player, startx, starty); if (r) { return NULL; } return ret; } /* * Utility function. Make into a args structure */ static int make_args(char *str, char **argv, int maxargs, int *argc) { int numargs = 0; while (str) { char *end; end = strchr(str,' '); if (end) { *end = '\0'; end++; } argv[numargs++] = str; str = end; } *argc = numargs; argv[numargs] = NULL; return 0; } /* * A connection wishes to leave */ static int cmd_quit(connection_t *conn) { connection_write(conn, 1, "* BYE\r\n"); connection_close(conn); return 0; } static int showboard(connection_t *conn) { int i; int sizex; int sizey; char *data; sizex = map_get_sizex(map); sizey = map_get_sizey(map); connection_write(conn, 1, "* BOARDSIZE %d %d\r\n",sizex, sizey); data = malloc(4 * sizex + 1); if (!data) return -1; /* give the layout of the board */ for (i = 0; i < sizey; i++) { int j; for (j = 0; j < sizex; j++) { mapspot_t spot; map_get_spot(map, j, i, &spot); memcpy(&data[j*4],map_spot2string(&spot), 4); } data[j*4] = '\0'; connection_write(conn, 0, "* BOARDLINE %d %s\r\n",i,data); } connection_write(conn, 0, "* REPOSITION %d %d\r\n", game->map_startx, game->map_starty); free(data); return 0; } static void * load_tile(char *filename, int request_width, int h, int *eh, void *rock) { /* return non-NULL */ return (void *)1; } static int cmd_refreshboard(connection_t *conn, char **argv, int argc) { if (argc != 0) return -1; showboard(conn); connection_write(conn, 1, "OK\r\n"); return 0; } static int set_spot(connection_t *conn, int x, int y, mapspot_t *spot, int give_okno) { player_t *player = connection_getplayer(conn); int r; r = map_set_spot(map, player, x, y, 0, 0, spot); if (r) { if (give_okno) connection_write(conn, 1, "NO Setting map spot failed\r\n"); } else { connections_send_to_all("* BOARDSPOT %d %d %s\r\n",x,y,map_spot2string(spot)); connection_write(conn, 0, "* PLAYERINFO CASH %d\r\n",player_getmoney(player)); if (give_okno) connection_write(conn, 1, "OK\r\n"); if (spot->mapobj == MAPOBJ_ACTION_DEMOLISH) { companies_remove(companies, x, y); population_fire_everybody(population, x, y); population_evict_everybody(population, x, y); } } return 0; } static int cmd_set(connection_t *conn, char **argv, int argc) { int x; int y; mapspot_t spot; if (argc != 3) return -1; x = atoi(argv[0]); y = atoi(argv[1]); map_string2spot(argv[2], &spot); /* * If transaction active just save this for later */ if (connection_txn_active) { set_op_t *op = calloc(1, sizeof(set_op_t)); int r; op->x = x; op->y = y; op->spot = spot; r = connection_txn_add(conn, op); if (r) { connection_write(conn, 1, "NO Error adding operation to transaction list\r\n"); } else { connection_write(conn, 1, "OK\r\n"); } return r; } else { return set_spot(conn, x, y, &spot, 1); } } static int cmd_login(connection_t *conn, char **argv, int argc) { int playernum; player_t *player; if (connection_setplayer(conn, argv[0])) { connection_write(conn, 1, "NO Error creating player\r\n"); return -1; } player = connection_getplayer(conn); playernum = player_getnum(player); connection_write(conn, 0, "* PLAYERINFO NUMBER %d\r\n",playernum); connection_write(conn, 0, "* PLAYERINFO CASH %d\r\n",player_getmoney(player)); connection_write(conn, 1, "OK\r\n"); return 0; } /* * Handle a line of protocol */ int protocol_handle_line(connection_t *conn, char *line) { int len; char *argv[MAX_ARGS+1]; int argc; int r = 0; /* remove any newline */ len = strlen(line); if (line[len-1] == '\n') { len--; } if (line[len-1] == '\r') { len--; } line[len] = '\0'; make_args(line, argv, MAX_ARGS, &argc); if (strcasecmp(argv[0],"quit")==0) { r = cmd_quit(conn); } else if (strcasecmp(argv[0],"refreshboard")==0) { r = cmd_refreshboard(conn, &argv[1], argc-1); } else if (strcasecmp(argv[0],"set")==0) { r = cmd_set(conn, &argv[1], argc-1); } else if (strcasecmp(argv[0], "login")==0) { r = cmd_login(conn, &argv[1], argc-1); } else if (strcasecmp(argv[0], "populationstats") == 0) { r = cmd_populationstats(conn); } else if (strcasecmp(argv[0], "finances") == 0) { r = cmd_finances(conn); } else if (strcasecmp(argv[0], "setspeed") == 0) { r = cmd_setspeed(conn, argv[1]); } else if (strcasecmp(argv[0], "spotinfo") == 0) { r = cmd_spotinfo(conn, &argv[1], argc-1); } else if (strcasecmp(argv[0], "borrow") == 0) { r = cmd_borrow(conn, &argv[1], argc-1); } else if (strcasecmp(argv[0], "listdebts") == 0) { r = cmd_listdebts(conn); } else if (strcasecmp(argv[0], "payback") == 0) { r = cmd_payback(conn, &argv[1], argc-1); } else if (strcasecmp(argv[0], "listgames") == 0) { r = cmd_listgames(conn); } else if (strcasecmp(argv[0], "selectgame") == 0) { r = cmd_selectgame(conn, &argv[1], argc-1); } else if (strcasecmp(argv[0], "getgoal") == 0) { r = cmd_getgoal(conn); } else if (strcasecmp(argv[0], "gethappiness") == 0) { r = cmd_gethappiness(conn); } else if (strcasecmp(argv[0], "txn_start") == 0) { r = cmd_txn_start(conn); } else if (strcasecmp(argv[0], "txn_commit") == 0) { r = cmd_txn_commit(conn); } else if (strcasecmp(argv[0], "txn_abort") == 0) { r = cmd_txn_abort(conn); } else if (strcasecmp(argv[0], "stats") == 0) { r = cmd_stats(conn); } else if (strcasecmp(argv[0], "gettax") == 0) { r = cmd_gettax(conn, &argv[1], argc-1); } else if (strcasecmp(argv[0], "settax") == 0) { r = cmd_settax(conn, &argv[1], argc-1); } else if (strcasecmp(argv[0], "noop") == 0) { connection_write(conn, 1, "OK\r\n"); } else if (strcasecmp(argv[0], "exit") == 0) { exit(0); /* xxx only in debugging */ } else { connection_write(conn, 1,"Command '%s' not understood\r\n",line); r = -1; } return r; } static int interest_income_day(player_t *player) { int cash; float interest; cash = player_getmoney(player); /* XXX 5% yearly interest rate */ interest = (((float)cash) * .05)/365.; player_addmoney(player, INCOME_INTEREST, interest); return interest; } static void update_money(connection_t *conn, player_t *player, void *rock) { connection_write(conn, 0, "* PLAYERINFO CASH %d\r\n",player_getmoney(player)); } static void update_monies(void) { connections_enumerate(&update_money, NULL); } static void goal_succeeded(void) { connections_send_to_all("* GAMEOVER \"Goal met!\"\r\n"); game->active = 0; } static void goal_failed(void) { connections_send_to_all("* GAMEOVER \"You failed!\"\r\n"); game->active = 0; } static int all_goals_met(player_t *player) { int goal_population; int goal_production; int goal_profit; int goal_gdp; if (config_getswitch("goal_justplay", 0)) { return 0; } /* * Goal population */ goal_population = config_getint("goal_population", -1); if (goal_population > -1) { if (game->totalpop < goal_population) { return 0; } } /* * Goal production */ goal_production = config_getint("goal_production", -1); if (goal_production > -1) { if (population_getproduction(population) < goal_production) { return 0; } } /* * Goal profit */ goal_profit = config_getint("goal_profit", -1); if (goal_profit > -1) { if (player_lastmonth_profit(player) < goal_profit) { return 0; } } /* * Goal GDP */ goal_gdp = config_getint("goal_gdp", -1); if (goal_gdp > -1) { if (game->month_gdp < goal_gdp) { return 0; } } return 1; } /* * Check to see if any player has reached the goals */ static void check_goals(time_t curtime, player_t *player) { char *datestr; struct tm tm; time_t goaltime = 0; if (all_goals_met(player)) { goal_succeeded(); return; } /* * See if time has run out */ datestr = (char *)config_getstring("goal_deadline",NULL); if (datestr) { memset(&tm, 0, sizeof(struct tm)); strptime(datestr,"%m/%d/%Y", &tm); goaltime = mktime(&tm); } if ((goaltime > 0) && (curtime >= goaltime)) { goal_failed(); return; } } static void year_elapsed(int initial) { if (!initial) { players_clearout_year(); } population_age1year(population, map); } static void update_population(int days, int initial) { g_population_change_month += population_imigrate(population, days); g_population_change_month -= population_exigrate(population, map, days); g_population_change_month += population_born(population, days); /* * New building */ investment_invest(population, map, days, initial); } static void week_elapsed(int initial) { /* * Now people change their positions in life */ population_movehousing(population, map); population_changejobs(population, map); if (!initial) { population_calculate_happiness(population); } } static void month_elapsed(time_t curtime, int initial) { int i; if (initial) return; players_clearout_month(); /* * Service debt */ for (i = 1; i < MAX_NUM_PLAYERS; i++) { if (player_isactive(i)) { player_servicedebt(player_get(i)); } } /* * Salaries */ population_paysalaries(population, map); /* * Rent income */ population_payrent(population, map); /* * Everyone has to pay upkeep */ population_payupkeep(population, map); population_property_tax(population); /* * Business revenue */ population_revenue(population, map); /* * Utilities */ utilities_calculate(map, population); /* * The amount of money each player has probably changed */ update_monies(); population_patronize(population, map); companies_out_of_business(companies); for (i = 1; i < MAX_NUM_PLAYERS; i++) { if (player_isactive(i)) { check_goals(curtime, player_get(i)); } } population_newmonth(); /* * Calculate traffic */ traffic_calculate(); } static void day_elapsed(int initial) { int i; /* * Interest income */ if (!initial) { for (i = 1; i < MAX_NUM_PLAYERS; i++) { if (player_isactive(i)) { interest_income_day(player_get(i)); } } } update_population(1, initial); } struct season_item_s { char *name; int def_avgtemp; int def_variance; }; struct season_item_s season_table[] = { {"winter", 40, 20}, {"spring", 60, 20}, {"summer", 80, 20}, {"fall", 60, 20}, }; struct weather_chance_s { char *name; int def_chance; }; struct weather_chance_s weather_types[] = { {"rain", 15}, {"cloudy", 15}, {"partcloudy", 15}, {"sunny", 55}, {NULL, 0}, }; static void give_weather(int mon) { int season = mon % 3; char key[256]; int avgtemp; int variance; int temp; int i; char *weather; /* * Get the temperature */ snprintf(key, sizeof(key)-1,"%s_avgtemp", season_table[season].name); avgtemp = config_getint(key, season_table[season].def_avgtemp); snprintf(key, sizeof(key)-1,"%s_tempvar", season_table[season].name); variance = config_getint(key, season_table[season].def_variance); temp = avgtemp - variance + rand()%(variance*2); /* * Find weather type */ for (i = 0; ; i++) { int chance; if (weather_types[i+1].name == NULL) { weather = weather_types[i].name; break; } snprintf(key, sizeof(key)-1,"%s_chance", weather_types[i].name); chance = config_getint(key, weather_types[i].def_chance); if (rand()%100 < chance) { weather = weather_types[i].name; break; } } connections_send_to_all("* WEATHER %d %s\r\n", temp, weather); } void pass_time(time_t curtime, int initial) { struct tm *tm; tm = gmtime(&curtime); give_weather(tm->tm_mon); if (tm->tm_wday == 1) week_elapsed(initial); if (tm->tm_mday == 1) month_elapsed(curtime, initial); if (tm->tm_yday == 1) year_elapsed(initial); day_elapsed(initial); } static void run_days(int days) { time_t t = CurrentGameTime - (days * SECS_IN_DAY); int i; utilities_calculate(map, population); for (i = 0; i < days; i++) { pass_time(t, 1); t += SECS_IN_DAY; } } static int cmd_populationstats(connection_t *conn) { int i; if (!population) { connection_write(conn, 0, "NO Game hasn't started yet\r\n"); return -1; } for (i = 0; i < MAXAGE; i++) { popgroup_t *group; int housed = 0; int working = 0; int k; long long tot_income = 0; long long tot_happy = 0; int avgincome = 0; int avghappy = 0; group = population_getgroup(population, i); for (k = 0; k < group->size; k++) { person_t *person = &group->data[k]; if (person->livex >= 0) housed++; if (person->workx >= 0) { tot_income += companies_getsalary(companies, person->workx, person->worky, person->worklevel); working++; } tot_happy += person->happy; } if (group->size) { avgincome = (int)(tot_income/group->size); avghappy = (int)(tot_happy/group->size); } connection_write(conn, 0, "* POPULATION %d %d SIZE=%d INCOME=%d HOUSED=%d WORKING=%d HAPPINESS=%d\r\n", i, 0, group->size, avgincome, housed, working, avghappy); } connection_write(conn, 1, "OK\r\n"); return 0; } static void write_finance_type(connection_t *conn, player_t *player, char *name, income_types type) { connection_write(conn, 0, " %s=%d,%d,%d,%d,%d", name, player_getincometype(player, RANGE_CURRENT_MONTH, type), player_getincometype(player, RANGE_LAST_MONTH, type), player_getincometype(player, RANGE_CURRENT_YEAR, type), player_getincometype(player, RANGE_LAST_YEAR, type), player_getincometype(player, RANGE_ALLTIME, type)); } static int cmd_finances(connection_t *conn) { player_t *player = connection_getplayer(conn); int i; connection_write(conn, 0, "* FINANCES"); for (i = 0; i < INCOME_NUMTYPES; i++) { struct balance_item *item = &balance_sheet_items[i]; write_finance_type(conn, player, item->string, item->type); } connection_write(conn, 0, "\r\n"); player_loans_enumerate(player, &listdebts_cb, conn); connection_write(conn, 1, "OK\r\n"); return 0; } static void set_speed(int speed) { switch (speed) { case SPEED_FASTEST: game_speed.tv_sec = 0; game_speed.tv_usec = 500000; break; case SPEED_FAST: game_speed.tv_sec = 1; game_speed.tv_usec = 0; break; case SPEED_NORMAL: game_speed.tv_sec = 1; game_speed.tv_usec = 500000; break; case SPEED_SLOW: game_speed.tv_sec = 3; game_speed.tv_usec = 0; break; default: case SPEED_PAUSED: game_speed.tv_sec = -1; game_speed.tv_usec = -1; break; } } static int cmd_setspeed(connection_t *conn, char *speedstr) { int speed = atoi(speedstr); set_speed(speed); connection_write(conn, 1, "OK\r\n"); return 0; } static int cmd_spotinfo(connection_t *conn, char **argv, int argc) { int x; int y; int live; int maxlive; labor_t *work; labor_t *maxwork; mapobj_t obj; maptype_t kind; int owner; char moneystr[20]; int power; int water; int capacity; if (argc != 2) return -1; x = atoi(argv[0]); y = atoi(argv[1]); connection_write(conn, 0, "* SPOTINFO"); obj = map_get_type(map, x, y); kind = map_item_gettype(obj); connection_write(conn, 0, " NAME=%s", map_item_getname(obj)); connection_write(conn, 0, " WHERE=%d,%d", x, y); owner = map_get_owner(map, x, y); connection_write(conn, 0, " OWNER=\"%s\"", player_getname(owner)); connection_write(conn, 0, " LANDVALUE=%s", prettyprint_money(government_calculate_onelandvalue(map, tiles, x, y), moneystr, sizeof(moneystr)-1)); connection_write(conn, 0, " UPKEEP=%s", prettyprint_money(map_get_upkeep(map, x, y)/12, moneystr, sizeof(moneystr)-1)); maxwork = companies_maxemploy(companies, x, y); work = companies_employ(companies, x, y); if (maxwork > 0) { int i; for (i = 0; i < LABOR_NUMGROUPS; i++) { if (maxwork->workers[i] > 0) { connection_write(conn, 0, " WORK=\"%d of %d %s\"", work->workers[i], maxwork->workers[i], labor_table[i].name); } } connection_write(conn, 0, " LABORCOST=%s", prettyprint_money(companies_gettotalsalary(companies, x, y)/12, moneystr, sizeof(moneystr)-1)); } power = tiles_getpoweruse(tiles, obj); if (map_isflagset(map, x, y, MAPFLAG_NOPOWER)) { connection_write(conn, 0, " NOPOWER=yes"); } else if (power > 0) { connection_write(conn, 0, " POWERUSE=%d", power); } else if (power < 0) { connection_write(conn, 0, " POWER_PRODUCTION=%d", utilities_one_power_production(x, y, obj)); } water = tiles_getwateruse(tiles, obj); if (map_isflagset(map, x, y, MAPFLAG_NOWATER)) { connection_write(conn, 0, " NOWATER=yes"); } else if (water > 0) { connection_write(conn, 0, " WATERUSE=%d", water); } else if (water < 0) { connection_write(conn, 0, " WATER_PRODUCTION=%d", -1 * water); } capacity = tiles_getcapacity(tiles, obj); if (capacity > 0) { connection_write(conn, 0, " CAPACITY=%d", capacity); } switch (kind) { case MAPTYPE_SCHOOL: map_live_info(map, x, y, &live, &maxlive); connection_write(conn, 0, " ATTENDANCE=\"%d (max %d)\"", live, maxlive); break; case MAPTYPE_HOUSE: map_live_info(map, x, y, &live, &maxlive); connection_write(conn, 0, " LIVE=\"%d (max %d)\"", live, maxlive); connection_write(conn, 0, " RENT=%s", prettyprint_money(map_getrent(map, x, y)/12, moneystr, sizeof(moneystr)-1)); break; case MAPTYPE_POWER: case MAPTYPE_WATERUTIL: case MAPTYPE_POLICE: case MAPTYPE_FIRE: case MAPTYPE_HOSPITAL: case MAPTYPE_ROAD: case MAPTYPE_WATER: case MAPTYPE_ZONING: case MAPTYPE_EMPTY: case MAPTYPE_LANDFILL: break; case MAPTYPE_ENTERTAINMENT: case MAPTYPE_FARM: case MAPTYPE_COMMERCIAL: case MAPTYPE_OFFICE: case MAPTYPE_INDUSTRIAL: connection_write(conn, 0, " REVENUE=%s", prettyprint_money(companies_get_revenue(companies, x, y)/12, moneystr, sizeof(moneystr)-1)); connection_write(conn, 0, " PATRONS=%d", map_get_patrons(map, x, y)); connection_write(conn, 0, " LAST_PROFIT=%s", prettyprint_money(companies_get_lastprofit(companies, x, y), moneystr, sizeof(moneystr)-1)); connection_write(conn, 0, " CASH_RESERVES=%s", prettyprint_money(companies_get_cash(companies, x, y), moneystr, sizeof(moneystr)-1)); break; } /*xxx height */ connection_write(conn, 0, "\r\n"); connection_write(conn, 1, "OK\r\n"); return 0; } static int asset_owner(int owner, void *rock) { int *playernump = (int *)rock; if (*playernump == owner) return 1; return 0; } static void asset_count(map_t *map, int mapx, int mapy, int owner, mapobj_t obj, void *rock) { int *countp = (int *)rock; int landvalue = government_calculate_onelandvalue(map, tiles, mapx, mapy); int cost = tiles_cost(tiles, obj); (*countp) += landvalue + cost; } static int asset_value(player_t *player) { int num = player_getnum(player); int count = 0; map_iterate(map, MAP_ITERATE_NORMAL, 0, 0, 0, NULL, NULL, &asset_owner, &num, &asset_count, &count); return count; } static int player_variable_cb(char *name, void *rock) { player_t *player = (player_t *)rock; if (strcasecmp(name, "networth") == 0) { return player_getmoney(player) + /* cash */ asset_value(player) - player_total_borrowed(player); } else { printf("player variable unknown: %s\n", name); return 0; } } static int cmd_borrow(connection_t *conn, char **argv, int argc) { int amount; int debt_ceiling; player_t *player = connection_getplayer(conn); if (argc != 1) return -1; amount = atoi(argv[0]); debt_ceiling = config_getfunction_evaluate("debt_ceiling", &player_variable_cb, player, 0); if (player_total_borrowed(player) + amount > debt_ceiling) { connection_write(conn, 1, "NO Borrowing limit exceeded\r\n"); } else { player_addloan(player, amount, 12*30, .07); /* xxx */ connection_write(conn, 0, "* PLAYERINFO CASH %d\r\n",player_getmoney(player)); connection_write(conn, 1, "OK\r\n"); } return 0; } static void listdebts_cb(player_t *player, int num, int amount, int term, int payments_made, float rate, void *rock) { connection_t *conn = (connection_t *)rock; connection_write(conn, 0, "* LOAN %d %d %d %d %f\r\n", num, amount, term, payments_made, rate); } static int cmd_listdebts(connection_t *conn) { player_t *player = connection_getplayer(conn); player_loans_enumerate(player, &listdebts_cb, conn); connection_write(conn, 1, "OK\r\n"); return 0; } static int cmd_payback(connection_t *conn, char **argv, int argc) { player_t *player = connection_getplayer(conn); int loan_num; int r; if (argc != 1) { connection_write(conn, 1, "NO Wrong number of arguments\r\n"); return 0; } loan_num = atoi(argv[0]); r = player_payoff_loan(player, loan_num); if (r) { connection_write(conn, 1, "NO Error paying off loan\r\n"); } else { connection_write(conn, 1, "OK\r\n"); } return 0; } static int listgames(connection_t *conn, char *dir) { DIR *dirp; struct dirent *dp; int cnt = 0; dirp = opendir(dir); if (!dirp) return 0; while ((dp = readdir(dirp)) != NULL) { int len = strlen(dp->d_name); if (len < 5) continue; if (strcasecmp(dp->d_name + len - 5, ".game") != 0) continue; connection_write(conn, 0, "* GAME %s\r\n", dp->d_name); cnt++; } (void)closedir(dirp); return cnt; } static int cmd_listgames(connection_t *conn) { if (!listgames(conn, "../games/.")) { if (!listgames(conn, GAMES_DIRECTORY "/.")) { listgames(conn, "."); } } connection_write(conn, 1, "OK\r\n"); return 0; } static int cmd_selectgame(connection_t *conn, char **argv, int argc) { char *name; int mapsizex; int mapsizey; int r; char *datestr; player_t *player = connection_getplayer(conn); int cash; char path[MAXPATHLEN+1]; if (argc != 1) { connection_write(conn, 1, "NO Wrong number of arguments\r\n"); return 0; } name = argv[0]; snprintf(path, sizeof(path)-1,"../games/%s", name); r = config_init(path); if (r == ENOENT) { snprintf(path, sizeof(path)-1,GAMES_DIRECTORY "/%s", name); r = config_init(path); if (r == ENOENT) { snprintf(path, sizeof(path)-1,"%s", name); r = config_init(path); } } if (r) { if (r == ENOENT) { connection_write(conn, 1, "NO No game by name '%s' doesn't exist\r\n", name); } else { connection_write(conn, 1, "NO Error loading game\r\n"); } return 0; } game = calloc(1, sizeof(game_t)); mapsizex = config_getint("mapsizex", DEFAULT_MAPSIZE); mapsizey = config_getint("mapsizey", DEFAULT_MAPSIZE); r = tiles_init(&tiles, "../img/tiles.data", &load_tile, NULL); if (r) { r = tiles_init(&tiles, IMG_DIRECTORY "tiles.data", &load_tile, NULL); } if (r) { connection_write(conn, 1, "NO Error loading tiles\r\n"); return r; } companies_init(&companies); map = make_map(tiles, player, mapsizex, mapsizey, &game->map_startx, &game->map_starty); if (!map) { connection_write(conn, 1, "NO Error creating map\r\n"); return r; } /* * Default tax rates are natural ones */ game->tax_rates[TAX_TYPE_INCOME] = config_getint("nominal_income_taxrate", 10); game->tax_rates[TAX_TYPE_PROPERTY] = config_getint("nominal_property_taxrate", 1); game->tax_rates[TAX_TYPE_BUS_INCOME] = config_getint("nominal_bus_income_taxrate", 10); game->tax_rates[TAX_TYPE_SALES] = config_getint("nominal_sales_taxrate", 5); game->min_working_age = config_getint("min_work_age", 18); population_init(&population); datestr = (char *)config_getstring("start_date","1/1/2000"); set_time_str(datestr); game->active = 1; run_days(config_getint("initial_run_days", 0)); game->government_player = player_getnum(player); /* xxx */ game->month_gdp = 0; set_speed(SPEED_NORMAL); connection_write(conn, 0, "* SPEED %d\r\n", SPEED_NORMAL); cash = config_getint("initial_player_money", 1000000); player_setmoney(player, cash); connection_write(conn, 0, "* PLAYERINFO CASH %d\r\n",player_getmoney(player)); connection_write(conn, 1, "OK\r\n"); return 0; } static int cmd_getgoal(connection_t *conn) { char *goal; goal = (char *)config_getstring("goal_description", NULL); if (goal) { connection_write(conn, 0, "* VALUE \"%s\"\r\n", goal); connection_write(conn, 1, "OK\r\n"); } else { connection_write(conn, 1, "NO No goal description found\r\n"); } return 0; } static int cmd_gethappiness(connection_t *conn) { int i; char buf[8096]; char *p = buf; char *eob = buf + sizeof(buf) - 1; int total = game->totalpop; for (i = 0; i < NUM_REASONS; i++) { int reason = game->reasons_last[i]; float percentage = ((float)(abs(reason)*100))/((float)total); if (percentage > 100.0) { percentage = 100.0; } if (reason >= 0) { p += snprintf(p, eob - p, "%s - %.2f%% cool with that\n", population_reason_string(i), percentage); } else { p += snprintf(p, eob - p, "%s - %.2f%% totally pissed\n", population_reason_string(i), percentage); } } connection_write(conn, 0, "* VALUE {%d+}\r\n%s\r\n", strlen(buf),buf); connection_write(conn, 1, "OK\r\n"); return 0; } static char* biggest_complaint(void) { int i; int minreason = -1; int minreason_num = 0; for (i = 0; i < NUM_REASONS; i++) { int reason = game->reasons_last[i]; if (reason < minreason_num) { minreason = i; minreason_num = reason; } } if (minreason == -1) { return "No complaints"; } else { return population_reason_string(minreason); } } static int cmd_txn_start(connection_t *conn) { int r; r = connection_txn_start(conn); if (r) { connection_write(conn, 1, "NO Failed starting transaction\r\n"); } else { connection_write(conn, 1, "OK\r\n"); } return 0; } struct can_s { int failed; int cost; int connect_added; int connect_failed; mapspot_list_t *connect_list; }; static int is_connected(int x, int y, int owner, mapspot_list_t *connect_list) { if (map_is_connected(map, x, y, owner)) return 1; if (mapspot_is_in_list(connect_list, x - 1, y )) return 1; if (mapspot_is_in_list(connect_list, x + 1, y )) return 1; if (mapspot_is_in_list(connect_list, x , y - 1)) return 1; if (mapspot_is_in_list(connect_list, x , y + 1)) return 1; return 0; } static int connects(int newowner, int x, int y, mapspot_list_t *connect_list) { if (newowner == 7) return 1; if (newowner != 0) { return is_connected(x, y, newowner, connect_list); } else { return 1; /* xxx */ } } static void can_connect_op(void *opp, void *rock) { struct can_s *can = (struct can_s *)rock; set_op_t *op = (set_op_t *)opp; if (op->connected) return; if (connects(op->spot.owner, op->x, op->y, can->connect_list)) { can->connect_added++; mapspot_list_add(&can->connect_list, op->x, op->y, NULL); op->connected = 1; } else { can->connect_failed++; } } static void calc_cost_op(void *opp, void *rock) { int *cost = (int *)rock; set_op_t *op = (set_op_t *)opp; (*cost) += map_set_spot_cost(map, op->x, op->y, &op->spot); } static void do_op(void *opp, void *rock) { set_op_t *op = (set_op_t *)opp; connection_t *conn = (connection_t *)rock; set_spot(conn, op->x, op->y, &op->spot, 0); } static int cmd_txn_commit(connection_t *conn) { struct can_s can; int r = 0; int cost = 0; player_t *player = connection_getplayer(conn); memset(&can, 0, sizeof(struct can_s)); /* * Check if connecting works */ if (config_getswitch("land_mustconnect", 0)) { do { can.connect_added = 0; can.connect_failed = 0; connection_txn_enumerate(conn, &can_connect_op, &can); } while (can.connect_added > 0); if (can.connect_failed > 0) { printf("Something doesn't connect\n"); goto done; } } /* * See if have enough money */ connection_txn_enumerate(conn, &calc_cost_op, &cost); if (!player_canafford(player, cost)) { printf("Can't afford %d!\n", cost); r = -1; goto done; } connection_txn_enumerate(conn, &do_op, conn); done: connection_txn_clear(conn); if (r) { connection_write(conn, 1, "NO Failed starting transaction\r\n"); } else { connection_write(conn, 1, "OK\r\n"); } return r; } static int cmd_txn_abort(connection_t *conn) { connection_txn_clear(conn); connection_write(conn, 1, "OK\r\n"); return 0; } static float getpower_percent(game_t *game) { int tot = game->power_numyes + game->power_numno; if (tot == 0) return 100.0; return ((float)(game->power_numyes*100))/((float)(tot)); } static float getwater_percent(game_t *game) { int tot = game->water_numyes + game->water_numno; if (tot == 0) return 100.0; return ((float)(game->water_numyes*100))/((float)(tot)); } static int cmd_stats(connection_t *conn) { char moneystr[20]; connection_write(conn, 0, "* STAT POPULATION %d\r\n", game->totalpop); connection_write(conn, 0, "* STAT POP_CHANGE %d\r\n", g_population_change_month); connection_write(conn, 0, "* STAT AVG_HAPPY %.2f\r\n", game->avghappy); connection_write(conn, 0, "* STAT HOMELESS %.2f%%\r\n", population_homeless_rate(population) * 100.0); connection_write(conn, 0, "* STAT HOMES_AVAIL %d\r\n", game->vacancies); connection_write(conn, 0, "* STAT UNEMPLOYED %.2f%%\r\n", population_unemployment_rate(population) * 100.0); connection_write(conn, 0, "* STAT JOBS_AVAIL %d\r\n", labor_count(&game->jobsavail)); connection_write(conn, 0, "* STAT FOOD_PRODUCTION %d\r\n", population_getproduction(population)); connection_write(conn, 0, "* STAT GDP %s\r\n", prettyprint_money(game->month_gdp, moneystr, sizeof(moneystr)-1)); /* connection_write(conn, 0, "* STAT INVESTMENT_FUNDS_AVAILABLE %s\r\n", prettyprint_money(game->total_to_invest, moneystr, sizeof(moneystr)-1));*/ connection_write(conn, 0, "* STAT BIGGEST_COMPLAINT \"%s\"\r\n", biggest_complaint()); connection_write(conn, 0, "* STAT POWERED %.1f%%\r\n", getpower_percent(game)); connection_write(conn, 0, "* STAT EXCESS_POWER %d\r\n", game->power_excess); connection_write(conn, 0, "* STAT WATER %.1f%%\r\n", getwater_percent(game)); connection_write(conn, 0, "* STAT STORED_WATER %d\r\n", game->water_excess); if (game->garbage_excess > 500) { connection_write(conn, 0, "* STAT EXCESS_GARBAGE %d\r\n", game->garbage_excess); } connection_write(conn, 1, "OK\r\n"); return 0; } static int cmd_settax(connection_t *conn, char **argv, int argc) { char *name; int rate; int i; if (argc != 2) { connection_write(conn, 1, "NO Wrong number of arguments\r\n"); return 0; } name = argv[0]; rate = atoi(argv[1]); for (i = 0; i < NUM_TAX_TYPES; i++) { if (strcasecmp(name, tax_types[i]) == 0) { game->tax_rates[i] = rate; } } connection_write(conn, 1, "OK\r\n"); return 0; } static int cmd_gettax(connection_t *conn, char **argv, int argc) { int i; char *name; if (argc != 1) { connection_write(conn, 1, "NO Wrong number of arguments\r\n"); return 0; } name = argv[0]; for (i = 0; i < NUM_TAX_TYPES; i++) { if (strcasecmp(name, tax_types[i]) == 0) { connection_write(conn, 0, "* TAXRATE %d\r\n", game->tax_rates[i]); } } connection_write(conn, 1, "OK\r\n"); return 0; }