/************************************************************************ * dopewars.c dopewars - general purpose routines and init * * Copyright (C) 1998-2005 Ben Webb * * Email: benwebb@users.sf.net * * WWW: http://dopewars.sourceforge.net/ * * * * 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. * ************************************************************************/ #define _GNU_SOURCE #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_GETOPT_LONG #include #endif #include #include #include #include #include "configfile.h" #include "convert.h" #include "dopewars.h" #include "admin.h" #include "log.h" #include "message.h" #include "nls.h" #include "serverside.h" #include "sound.h" #include "tstring.h" #include "AIPlayer.h" #include "util.h" #include "winmain.h" #ifdef CURSES_CLIENT #include "curses_client/curses_client.h" #endif #ifdef GUI_CLIENT #include "gui_client/gtk_client.h" #endif #ifdef GUI_SERVER #include "gtkport/gtkport.h" #endif int ClientSock, ListenSock; gboolean Network, Client, Server, WantAntique = FALSE, UseSounds = TRUE; /* * dopewars acting as standalone TCP server: * Network=Server=TRUE Client=FALSE * dopewars acting as client, connecting to standalone server: * Network=Client=TRUE Server=FALSE * dopewars in single-player or antique mode: * Network=Server=Client=FALSE */ unsigned Port = 7902; gboolean Sanitized, ConfigVerbose, DrugValue, Antique = FALSE; gchar *HiScoreFile = NULL, *ServerName = NULL; gchar *ServerMOTD = NULL, *BindAddress = NULL, *PlayerName = NULL; struct DATE StartDate = { 1, 12, 1984 }; #ifdef CYGWIN gboolean MinToSysTray = TRUE; #else gboolean Daemonize = TRUE; #endif #ifdef CYGWIN #define SNDPATH "sounds\\19.5degs\\" #else #define SNDPATH DPDATADIR"/dopewars/" #endif gchar *WebBrowser = NULL; gint ConfigErrors = 0; int NumLocation = 0, NumGun = 0, NumCop = 0, NumDrug = 0, NumSubway = 0; int NumPlaying = 0, NumStoppedTo = 0; int DebtInterest = 10, BankInterest = 5; Player Noone; int LoanSharkLoc, BankLoc, GunShopLoc, RoughPubLoc; int DrugSortMethod = DS_ATOZ; int FightTimeout = 5, IdleTimeout = 14400, ConnectTimeout = 300; int MaxClients = 20, AITurnPause = 5; price_t StartCash = 2000, StartDebt = 5500; GSList *ServerList = NULL; GScannerConfig ScannerConfig = { " \t\n", /* Ignore these characters */ /* Valid characters for starting an identifier */ G_CSET_a_2_z "_" G_CSET_A_2_Z, /* Valid characters for continuing an identifier */ G_CSET_a_2_z "._-0123456789" G_CSET_A_2_Z, "#\n", /* Single line comments start with # and * end with \n */ FALSE, /* Are symbols case sensitive? */ TRUE, /* Ignore C-style comments? */ TRUE, /* Ignore single-line comments? */ TRUE, /* Treat C-style comments as single tokens * - do not break into words? */ TRUE, /* Read identifiers as tokens? */ TRUE, /* Read single-character identifiers as * 1-character strings? */ TRUE, /* Allow the parsing of NULL as the * G_TOKEN_IDENTIFIER_NULL ? */ FALSE, /* Allow symbols (defined by * g_scanner_scope_add_symbol) ? */ TRUE, /* Allow binary numbers in 0b1110 format ? */ TRUE, /* Allow octal numbers in C-style e.g. 034 ? */ FALSE, /* Allow floats? */ TRUE, /* Allow hex numbers in C-style e.g. 0xFF ? */ TRUE, /* Allow hex numbers in $FF format ? */ TRUE, /* Allow '' strings (no escaping) ? */ TRUE, /* Allow "" strings (\ escapes parsed) ? */ TRUE, /* Convert octal, binary and hex to int? */ FALSE, /* Convert ints to floats? */ FALSE, /* Treat all identifiers as strings? */ TRUE, /* Leave single characters (e.g. {,=) * unchanged, instead of returning * G_TOKEN_CHAR ? */ FALSE, /* Replace read symbols with the token * given by their value, instead of * G_TOKEN_SYMBOL ? */ FALSE /* scope_0_fallback... */ }; struct LOCATION StaticLocation, *Location = NULL; struct DRUG StaticDrug, *Drug = NULL; struct GUN StaticGun, *Gun = NULL; struct COP StaticCop, *Cop = NULL; struct NAMES Names = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; struct SOUNDS Sounds = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; /* N.B. The slightly over-enthusiastic comments here are for the benefit * of translators ;) */ struct NAMES DefaultNames = { /* Name of a single bitch - if you need to use different words for * "bitch" depending on where in the sentence it occurs (e.g. subject or * object) then read doc/i18n.html about the %tde (etc.) notation. N.B. * This notation can be used for most of the translatable strings in * dopewars. */ N_("bitch"), /* Word used for two or more bitches */ N_("bitches"), /* Word used for a single gun */ N_("gun"), /* Word used for two or more guns */ N_("guns"), /* Word used for a single drug */ N_("drug"), /* Word used for two or more drugs */ N_("drugs"), /* String for displaying the game date or turn number. This is passed * to the strftime() function, with the exception that %T is used to * mean the turn number rather than the calendar date. */ N_("%m-%d-%Y"), /* Names of the loan shark, the bank, the gun shop, and the pub, * respectively */ N_("the Loan Shark"), N_("the Bank"), N_("Dan\'s House of Guns"), N_("the pub") }; struct CURRENCY Currency = { NULL, TRUE }; struct PRICES Prices = { 20000, 10000 }; struct BITCH Bitch = { 50000, 150000 }; #ifdef NETWORKING struct METASERVER MetaServer = { FALSE, NULL, 0, NULL, 0, NULL, NULL, NULL, NULL, FALSE, NULL, NULL, NULL, NULL }; struct METASERVER DefaultMetaServer = { TRUE, "dopewars.sourceforge.net", 80, "", 8080, "/metaserver.php", "", "", "dopewars server", FALSE, "", "", "", "" }; SocksServer Socks = { NULL, 0, 0, FALSE, NULL, NULL, NULL }; gboolean UseSocks; #endif int NumTurns = 31; int PlayerArmour = 100, BitchArmour = 50; struct LOG Log; struct GLOBALS Globals[] = { /* The following strings are the helptexts for all the options that can * be set in a dopewars configuration file, or in the server. See * doc/configfile.html for more detailed explanations. */ {&Port, NULL, NULL, NULL, NULL, "Port", N_("Network port to connect to"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 65535}, {NULL, NULL, NULL, &HiScoreFile, NULL, "HiScoreFile", N_("Name of the high score file"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &ServerName, NULL, "Server", N_("Name of the server to connect to"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &ServerMOTD, NULL, "ServerMOTD", N_("Server's welcome message of the day"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &BindAddress, NULL, "BindAddress", N_("Network address for the server to listen on"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, #ifdef NETWORKING {NULL, &UseSocks, NULL, NULL, NULL, "Socks.Active", N_("TRUE if a SOCKS server should be used for networking"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, #ifndef CYGWIN {NULL, &Socks.numuid, NULL, NULL, NULL, "Socks.NumUID", N_("TRUE if numeric user IDs should be used for SOCKS4"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, #endif {NULL, NULL, NULL, &Socks.user, NULL, "Socks.User", N_("If not blank, the username to use for SOCKS4"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Socks.name, NULL, "Socks.Name", N_("The hostname of a SOCKS server to use"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {&Socks.port, NULL, NULL, NULL, NULL, "Socks.Port", N_("The port number of a SOCKS server to use"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 65535}, {&Socks.version, NULL, NULL, NULL, NULL, "Socks.Version", N_("The version of the SOCKS protocol to use (4 or 5)"), NULL, NULL, 0, "", NULL, NULL, FALSE, 4, 5}, {NULL, NULL, NULL, &Socks.authuser, NULL, "Socks.Auth.User", N_("Username for SOCKS5 authentication"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Socks.authpassword, NULL, "Socks.Auth.Password", N_("Password for SOCKS5 authentication"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, &MetaServer.Active, NULL, NULL, NULL, "MetaServer.Active", N_("TRUE if server should report to a metaserver"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &MetaServer.Name, NULL, "MetaServer.Name", N_("Metaserver name to report/get server details to/from"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {&MetaServer.Port, NULL, NULL, NULL, NULL, "MetaServer.Port", N_("Port for metaserver communication"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 65535}, {NULL, NULL, NULL, &MetaServer.ProxyName, NULL, "MetaServer.ProxyName", N_("Name of a proxy for metaserver communication"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {&MetaServer.ProxyPort, NULL, NULL, NULL, NULL, "MetaServer.ProxyPort", N_("Port for communicating with the proxy server"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 65535}, {NULL, NULL, NULL, &MetaServer.Path, NULL, "MetaServer.Path", N_("Path of the script on the metaserver"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &MetaServer.LocalName, NULL, "MetaServer.LocalName", N_("Preferred hostname of your server machine"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &MetaServer.Password, NULL, "MetaServer.Password", N_("Authentication for LocalName with the metaserver"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &MetaServer.Comment, NULL, "MetaServer.Comment", N_("Server description, reported to the metaserver"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, &MetaServer.UseSocks, NULL, NULL, NULL, "MetaServer.UseSocks", N_("If TRUE, use SOCKS for metaserver communication"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &MetaServer.authuser, NULL, "MetaServer.Auth.User", N_("Username for HTTP Basic authentication"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &MetaServer.authpassword, NULL, "MetaServer.Auth.Password", N_("Password for HTTP Basic authentication"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &MetaServer.proxyuser, NULL, "MetaServer.Proxy.User", N_("Username for HTTP Basic proxy authentication"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &MetaServer.proxypassword, NULL, "MetaServer.Proxy.Password", N_("Password for HTTP Basic proxy authentication"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, #endif /* NETWORKING */ #ifdef CYGWIN {NULL, &MinToSysTray, NULL, NULL, NULL, "MinToSysTray", N_("If TRUE, the server minimizes to the System Tray"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, #else {NULL, &Daemonize, NULL, NULL, NULL, "Daemonize", N_("If TRUE, the server runs in the background"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &WebBrowser, NULL, "WebBrowser", N_("The command used to start your web browser"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, #endif {&NumTurns, NULL, NULL, NULL, NULL, "NumTurns", N_("No. of game turns (if 0, game never ends)"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1}, {&StartDate.day, NULL, NULL, NULL, NULL, "StartDate.Day", N_("Day of the month on which the game starts"), NULL, NULL, 0, "", NULL, NULL, FALSE, 1, 31}, {&StartDate.month, NULL, NULL, NULL, NULL, "StartDate.Month", N_("Month in which the game starts"), NULL, NULL, 0, "", NULL, NULL, FALSE, 1, 12}, {&StartDate.year, NULL, NULL, NULL, NULL, "StartDate.Year", N_("Year in which the game starts"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1}, {NULL, NULL, NULL, &Currency.Symbol, NULL, "Currency.Symbol", N_("The currency symbol (e.g. $)"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, &Currency.Prefix, NULL, NULL, NULL, "Currency.Prefix", N_("If TRUE, the currency symbol precedes prices"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Log.File, NULL, "Log.File", N_("File to write log messages to"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {&Log.Level, NULL, NULL, NULL, NULL, "Log.Level", N_("Controls the number of log messages produced"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 5}, {NULL, NULL, NULL, &Log.Timestamp, NULL, "Log.Timestamp", N_("strftime() format string for log timestamps"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, &Sanitized, NULL, NULL, NULL, "Sanitized", N_("Random events are sanitized"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, &DrugValue, NULL, NULL, NULL, "DrugValue", N_("TRUE if the value of bought drugs should be saved"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, &ConfigVerbose, NULL, NULL, NULL, "ConfigVerbose", N_("Be verbose in processing config file"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {&NumLocation, NULL, NULL, NULL, NULL, "NumLocation", N_("Number of locations in the game"), (void **)(&Location), NULL, sizeof(struct LOCATION), "", NULL, ResizeLocations, FALSE, 1, -1}, {&NumCop, NULL, NULL, NULL, NULL, "NumCop", N_("Number of types of cop in the game"), (void **)(&Cop), NULL, sizeof(struct COP), "", NULL, ResizeCops, FALSE, 0, -1}, {&NumGun, NULL, NULL, NULL, NULL, "NumGun", N_("Number of guns in the game"), (void **)(&Gun), NULL, sizeof(struct GUN), "", NULL, ResizeGuns, FALSE, 0, -1}, {&NumDrug, NULL, NULL, NULL, NULL, "NumDrug", N_("Number of drugs in the game"), (void **)(&Drug), NULL, sizeof(struct DRUG), "", NULL, ResizeDrugs, FALSE, 1, -1}, {&LoanSharkLoc, NULL, NULL, NULL, NULL, "LoanShark", N_("Location of the Loan Shark"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {&BankLoc, NULL, NULL, NULL, NULL, "Bank", N_("Location of the bank"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {&GunShopLoc, NULL, NULL, NULL, NULL, "GunShop", N_("Location of the gun shop"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {&RoughPubLoc, NULL, NULL, NULL, NULL, "RoughPub", N_("Location of the pub"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {&DebtInterest, NULL, NULL, NULL, NULL, "DebtInterest", N_("Daily interest rate on the loan shark debt"), NULL, NULL, 0, "", NULL, NULL, FALSE, -100, -200}, {&BankInterest, NULL, NULL, NULL, NULL, "BankInterest", N_("Daily interest rate on your bank balance"), NULL, NULL, 0, "", NULL, NULL, FALSE, -100, -200}, {NULL, NULL, NULL, &Names.LoanSharkName, NULL, "LoanSharkName", N_("Name of the loan shark"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Names.BankName, NULL, "BankName", N_("Name of the bank"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Names.GunShopName, NULL, "GunShopName", N_("Name of the gun shop"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Names.RoughPubName, NULL, "RoughPubName", N_("Name of the pub"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, &UseSounds, NULL, NULL, NULL, "UseSounds", N_("TRUE if sounds should be enabled"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Sounds.FightHit, NULL, "Sounds.FightHit", N_("Sound file played for a gun \"hit\""), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Sounds.FightMiss, NULL, "Sounds.FightMiss", N_("Sound file played for a gun \"miss\""), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Sounds.FightReload, NULL, "Sounds.FightReload", N_("Sound file played when guns are reloaded"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Sounds.EnemyBitchKilled, NULL, "Sounds.EnemyBitchKilled", N_("Sound file played when an enemy bitch/deputy is killed"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Sounds.BitchKilled, NULL, "Sounds.BitchKilled", N_("Sound file played when one of your bitches is killed"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Sounds.EnemyKilled, NULL, "Sounds.EnemyKilled", N_("Sound file played when another player or cop is killed"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Sounds.Killed, NULL, "Sounds.Killed", N_("Sound file played when you are killed"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Sounds.EnemyFailFlee, NULL, "Sounds.EnemyFailFlee", N_("Sound file played when a player tries to escape, but fails"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Sounds.FailFlee, NULL, "Sounds.FailFlee", N_("Sound file played when you try to escape, but fail"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Sounds.EnemyFlee, NULL, "Sounds.EnemyFlee", N_("Sound file played when a player successfully escapes"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Sounds.Flee, NULL, "Sounds.Flee", N_("Sound file played when you successfully escape"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Sounds.Jet, NULL, "Sounds.Jet", N_("Sound file played on arriving at a new location"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Sounds.TalkToAll, NULL, "Sounds.TalkToAll", N_("Sound file played when a player sends a public chat message"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Sounds.TalkPrivate, NULL, "Sounds.TalkPrivate", N_("Sound file played when a player sends a private chat message"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Sounds.JoinGame, NULL, "Sounds.JoinGame", N_("Sound file played when a player joins the game"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Sounds.LeaveGame, NULL, "Sounds.LeaveGame", N_("Sound file played when a player leaves the game"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Sounds.StartGame, NULL, "Sounds.StartGame", N_("Sound file played at the start of the game"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Sounds.EndGame, NULL, "Sounds.EndGame", N_("Sound file played at the end of the game"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {&DrugSortMethod, NULL, NULL, NULL, NULL, "DrugSortMethod", N_("Sort key for listing available drugs"), NULL, NULL, 0, "", NULL, NULL, FALSE, 1, 4}, {&FightTimeout, NULL, NULL, NULL, NULL, "FightTimeout", N_("No. of seconds in which to return fire"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1}, {&IdleTimeout, NULL, NULL, NULL, NULL, "IdleTimeout", N_("Players are disconnected after this many seconds"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1}, {&ConnectTimeout, NULL, NULL, NULL, NULL, "ConnectTimeout", N_("Time in seconds for connections to be made or broken"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1}, {&MaxClients, NULL, NULL, NULL, NULL, "MaxClients", N_("Maximum number of TCP/IP connections"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1}, {&AITurnPause, NULL, NULL, NULL, NULL, "AITurnPause", N_("Seconds between turns of AI players"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1}, {NULL, NULL, &StartCash, NULL, NULL, "StartCash", N_("Amount of cash that each player starts with"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1}, {NULL, NULL, &StartDebt, NULL, NULL, "StartDebt", N_("Amount of debt that each player starts with"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1}, {NULL, NULL, NULL, &StaticLocation.Name, NULL, "Name", N_("Name of each location"), (void **)(&Location), &StaticLocation, sizeof(struct LOCATION), "Location", &NumLocation, NULL, FALSE, 0, -1}, {&(StaticLocation.PolicePresence), NULL, NULL, NULL, NULL, "PolicePresence", N_("Police presence at each location (%)"), (void **)(&Location), &StaticLocation, sizeof(struct LOCATION), "Location", &NumLocation, NULL, FALSE, 0, 100}, {&(StaticLocation.MinDrug), NULL, NULL, NULL, NULL, "MinDrug", N_("Minimum number of drugs at each location"), (void **)(&Location), &StaticLocation, sizeof(struct LOCATION), "Location", &NumLocation, NULL, FALSE, 1, -1}, {&(StaticLocation.MaxDrug), NULL, NULL, NULL, NULL, "MaxDrug", N_("Maximum number of drugs at each location"), (void **)(&Location), &StaticLocation, sizeof(struct LOCATION), "Location", &NumLocation, NULL, FALSE, 1, -1}, {&PlayerArmour, NULL, NULL, NULL, NULL, "PlayerArmour", N_("% resistance to gunshots of each player"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 100}, {&BitchArmour, NULL, NULL, NULL, NULL, "BitchArmour", N_("% resistance to gunshots of each bitch"), NULL, NULL, 0, "", NULL, NULL, FALSE, 1, 100}, {NULL, NULL, NULL, &StaticCop.Name, NULL, "Name", N_("Name of each cop"), (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &StaticCop.DeputyName, NULL, "DeputyName", N_("Name of each cop's deputy"), (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &StaticCop.DeputiesName, NULL, "DeputiesName", N_("Name of each cop's deputies"), (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop, NULL, FALSE, 0, 0}, {&StaticCop.Armour, NULL, NULL, NULL, NULL, "Armour", N_("% resistance to gunshots of each cop"), (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop, NULL, FALSE, 1, 100}, {&StaticCop.DeputyArmour, NULL, NULL, NULL, NULL, "DeputyArmour", N_("% resistance to gunshots of each deputy"), (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop, NULL, FALSE, 1, 100}, {&StaticCop.AttackPenalty, NULL, NULL, NULL, NULL, "AttackPenalty", N_("Attack penalty relative to a player"), (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop, NULL, FALSE, 0, 100}, {&StaticCop.DefendPenalty, NULL, NULL, NULL, NULL, "DefendPenalty", N_("Defend penalty relative to a player"), (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop, NULL, FALSE, 0, 100}, {&StaticCop.MinDeputies, NULL, NULL, NULL, NULL, "MinDeputies", N_("Minimum number of accompanying deputies"), (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop, NULL, FALSE, 0, -1}, {&StaticCop.MaxDeputies, NULL, NULL, NULL, NULL, "MaxDeputies", N_("Maximum number of accompanying deputies"), (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop, NULL, FALSE, 0, -1}, {&StaticCop.GunIndex, NULL, NULL, NULL, NULL, "GunIndex", N_("Zero-based index of the gun that cops are armed with"), (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop, NULL, FALSE, 0, -1}, {&StaticCop.CopGun, NULL, NULL, NULL, NULL, "CopGun", N_("Number of guns that each cop carries"), (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop, NULL, FALSE, 0, -1}, {&StaticCop.DeputyGun, NULL, NULL, NULL, NULL, "DeputyGun", N_("Number of guns that each deputy carries"), (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop, NULL, FALSE, 0, -1}, {NULL, NULL, NULL, &StaticDrug.Name, NULL, "Name", N_("Name of each drug"), (void **)(&Drug), &StaticDrug, sizeof(struct DRUG), "Drug", &NumDrug, NULL, FALSE, 0, 0}, {NULL, NULL, &(StaticDrug.MinPrice), NULL, NULL, "MinPrice", N_("Minimum normal price of each drug"), (void **)(&Drug), &StaticDrug, sizeof(struct DRUG), "Drug", &NumDrug, NULL, FALSE, 1, -1}, {NULL, NULL, &(StaticDrug.MaxPrice), NULL, NULL, "MaxPrice", N_("Maximum normal price of each drug"), (void **)(&Drug), &StaticDrug, sizeof(struct DRUG), "Drug", &NumDrug, NULL, FALSE, 1, -1}, {NULL, &(StaticDrug.Cheap), NULL, NULL, NULL, "Cheap", N_("TRUE if this drug can be specially cheap"), (void **)(&Drug), &StaticDrug, sizeof(struct DRUG), "Drug", &NumDrug, NULL, FALSE, 0, 0}, {NULL, &(StaticDrug.Expensive), NULL, NULL, NULL, "Expensive", N_("TRUE if this drug can be specially expensive"), (void **)(&Drug), &StaticDrug, sizeof(struct DRUG), "Drug", &NumDrug, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &StaticDrug.CheapStr, NULL, "CheapStr", N_("Message displayed when this drug is specially cheap"), (void **)(&Drug), &StaticDrug, sizeof(struct DRUG), "Drug", &NumDrug, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Drugs.ExpensiveStr1, NULL, "Drugs.ExpensiveStr1", N_("Format string used for expensive drugs 50% of time"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Drugs.ExpensiveStr2, NULL, "Drugs.ExpensiveStr2", N_("Format string used for expensive drugs 50% of time"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {&(Drugs.CheapDivide), NULL, NULL, NULL, NULL, "Drugs.CheapDivide", N_("Divider for drug price when it's specially cheap"), NULL, NULL, 0, "", NULL, NULL, FALSE, 1, -1}, {&(Drugs.ExpensiveMultiply), NULL, NULL, NULL, NULL, "Drugs.ExpensiveMultiply", N_("Multiplier for specially expensive drug prices"), NULL, NULL, 0, "", NULL, NULL, FALSE, 1, -1}, {NULL, NULL, NULL, &StaticGun.Name, NULL, "Name", N_("Name of each gun"), (void **)(&Gun), &StaticGun, sizeof(struct GUN), "Gun", &NumGun, NULL, FALSE, 0, 0}, {NULL, NULL, &(StaticGun.Price), NULL, NULL, "Price", N_("Price of each gun"), (void **)(&Gun), &StaticGun, sizeof(struct GUN), "Gun", &NumGun, NULL, FALSE, 0, 0}, {&(StaticGun.Space), NULL, NULL, NULL, NULL, "Space", N_("Space taken by each gun"), (void **)(&Gun), &StaticGun, sizeof(struct GUN), "Gun", &NumGun, NULL, FALSE, 0, -1}, {&(StaticGun.Damage), NULL, NULL, NULL, NULL, "Damage", N_("Damage done by each gun"), (void **)(&Gun), &StaticGun, sizeof(struct GUN), "Gun", &NumGun, NULL, FALSE, 0, -1}, {NULL, NULL, NULL, &Names.Bitch, NULL, "Names.Bitch", N_("Word used to denote a single \"bitch\""), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Names.Bitches, NULL, "Names.Bitches", N_("Word used to denote two or more \"bitches\""), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Names.Gun, NULL, "Names.Gun", N_("Word used to denote a single gun or equivalent"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Names.Guns, NULL, "Names.Guns", N_("Word used to denote two or more guns"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Names.Drug, NULL, "Names.Drug", N_("Word used to denote a single drug or equivalent"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Names.Drugs, NULL, "Names.Drugs", N_("Word used to denote two or more drugs"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, NULL, &Names.Date, NULL, "Names.Date", N_("strftime() format string for displaying the game turn"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0}, {NULL, NULL, &Prices.Spy, NULL, NULL, "Prices.Spy", N_("Cost for a bitch to spy on the enemy"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1}, {NULL, NULL, &Prices.Tipoff, NULL, NULL, "Prices.Tipoff", N_("Cost for a bitch to tipoff the cops to an enemy"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1}, {NULL, NULL, &Bitch.MinPrice, NULL, NULL, "Bitch.MinPrice", N_("Minimum price to hire a bitch"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1}, {NULL, NULL, &Bitch.MaxPrice, NULL, NULL, "Bitch.MaxPrice", N_("Maximum price to hire a bitch"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1}, {NULL, NULL, NULL, NULL, &SubwaySaying, "SubwaySaying", N_("List of things which you overhear on the subway"), NULL, NULL, 0, "", &NumSubway, ResizeSubway, FALSE, 0, 0}, {&NumSubway, NULL, NULL, NULL, NULL, "NumSubwaySaying", N_("Number of subway sayings"), NULL, NULL, 0, "", NULL, ResizeSubway, FALSE, 0, -1}, {NULL, NULL, NULL, NULL, &Playing, "Playing", N_("List of songs which you can hear playing"), NULL, NULL, 0, "", &NumPlaying, ResizePlaying, FALSE, 0, 0}, {&NumPlaying, NULL, NULL, NULL, NULL, "NumPlaying", N_("Number of playing songs"), NULL, NULL, 0, "", NULL, ResizePlaying, FALSE, 0, -1}, {NULL, NULL, NULL, NULL, &StoppedTo, "StoppedTo", N_("List of things which you can stop to do"), NULL, NULL, 0, "", &NumStoppedTo, ResizeStoppedTo, FALSE, 0, 0}, {&NumStoppedTo, NULL, NULL, NULL, NULL, "NumStoppedTo", N_("Number of things which you can stop to do"), NULL, NULL, 0, "", NULL, ResizeStoppedTo, FALSE, 0, -1} }; const int NUMGLOB = sizeof(Globals) / sizeof(Globals[0]); char **Playing = NULL; char *DefaultPlaying[] = { /* Default list of songs that you can hear playing (N.B. this can be * overridden in the configuration file with the "Playing" variable) - * look for "You hear someone playing %s" to see how these are used. */ N_("`Are you Experienced` by Jimi Hendrix"), N_("`Cheeba Cheeba` by Tone Loc"), N_("`Comin` in to Los Angeles` by Arlo Guthrie"), N_("`Commercial` by Spanky and Our Gang"), N_("`Late in the Evening` by Paul Simon"), N_("`Light Up` by Styx"), N_("`Mexico` by Jefferson Airplane"), N_("`One toke over the line` by Brewer & Shipley"), N_("`The Smokeout` by Shel Silverstein"), N_("`White Rabbit` by Jefferson Airplane"), N_("`Itchycoo Park` by Small Faces"), N_("`White Punks on Dope` by the Tubes"), N_("`Legend of a Mind` by the Moody Blues"), N_("`Eight Miles High` by the Byrds"), N_("`Acapulco Gold` by Riders of the Purple Sage"), N_("`Kicks` by Paul Revere & the Raiders"), N_("the Nixon tapes"), N_("`Legalize It` by Mojo Nixon & Skid Roper") }; char **StoppedTo = NULL; char *DefaultStoppedTo[] = { /* Default list of things which you can "stop to do" (random events that * cost you a little money). These can be overridden with the "StoppedTo" * variable in the configuration file. See the later string "You stopped * to %s." to see how these strings are used. */ N_("have a beer"), N_("smoke a joint"), N_("smoke a cigar"), N_("smoke a Djarum"), N_("smoke a cigarette") }; struct COP DefaultCop[] = { /* Name of the first police officer to attack you */ {N_("Officer Hardass"), /* Name of a single deputy of the first police officer */ N_("deputy"), /* Word used for more than one deputy of the first police officer */ N_("deputies"), 4, 3, 30, 30, 2, 8, 0, 1, 1}, /* Ditto, for the other police officers */ {N_("Officer Bob"), N_("deputy"), N_("deputies"), 15, 4, 30, 20, 4, 10, 0, 2, 1}, {N_("Agent Smith"), N_("cop"), N_("cops"), 50, 6, 20, 20, 6, 18, 1, 3, 2} }; struct GUN DefaultGun[] = { /* The names of the default guns */ {N_("Baretta"), 3000, 4, 5}, {N_(".38 Special"), 3500, 4, 9}, {N_("Ruger"), 2900, 4, 4}, {N_("Saturday Night Special"), 3100, 4, 7} }; struct DRUG DefaultDrug[] = { /* The names of the default drugs, and the messages displayed when they * are specially cheap or expensive */ {N_("Acid"), 1000, 4400, TRUE, FALSE, N_("The market is flooded with cheap home-made acid!")}, {N_("Cocaine"), 15000, 29000, FALSE, TRUE, ""}, {N_("Hashish"), 480, 1280, TRUE, FALSE, N_("The Marrakesh Express has arrived!")}, {N_("Heroin"), 5500, 13000, FALSE, TRUE, ""}, {N_("Ludes"), 11, 60, TRUE, FALSE, N_("Rival drug dealers raided a pharmacy and are selling cheap ludes!")}, {N_("MDA"), 1500, 4400, FALSE, FALSE, ""}, {N_("Opium"), 540, 1250, FALSE, TRUE, ""}, {N_("PCP"), 1000, 2500, FALSE, FALSE, ""}, {N_("Peyote"), 220, 700, FALSE, FALSE, ""}, {N_("Shrooms"), 630, 1300, FALSE, FALSE, ""}, {N_("Speed"), 90, 250, FALSE, TRUE, ""}, {N_("Weed"), 315, 890, TRUE, FALSE, N_("Columbian freighter dusted the Coast Guard! " "Weed prices have bottomed out!")} }; #define NUMDRUG (sizeof(DefaultDrug)/sizeof(DefaultDrug[0])) struct LOCATION DefaultLocation[] = { /* The names of the default locations */ {N_("Bronx"), 10, NUMDRUG / 2 + 1, NUMDRUG}, {N_("Ghetto"), 5, NUMDRUG / 2 + 2, NUMDRUG}, {N_("Central Park"), 15, NUMDRUG / 2, NUMDRUG}, {N_("Manhattan"), 90, NUMDRUG / 2 - 2, NUMDRUG - 2}, {N_("Coney Island"), 20, NUMDRUG / 2, NUMDRUG}, {N_("Brooklyn"), 70, NUMDRUG / 2 - 2, NUMDRUG - 1}, {N_("Queens"), 50, NUMDRUG / 2, NUMDRUG}, {N_("Staten Island"), 20, NUMDRUG / 2, NUMDRUG} }; struct DRUGS Drugs = { NULL, NULL, 0, 0 }; struct DRUGS DefaultDrugs = { /* Messages displayed for drug busts, etc. */ N_("Cops made a big %tde bust! Prices are outrageous!"), N_("Addicts are buying %tde at ridiculous prices!"), 4, 4 }; char **SubwaySaying = NULL; char *DefaultSubwaySaying[] = { /* Default list of things which the "lady on the subway" can tell you * (N.B. can be overridden with the "SubwaySaying" config. file * variable). Look for "the lady next to you" to see how these strings * are used. */ N_("Wouldn\'t it be funny if everyone suddenly quacked at once?"), N_("The Pope was once Jewish, you know"), N_("I\'ll bet you have some really interesting dreams"), N_("So I think I\'m going to Amsterdam this year"), N_("Son, you need a yellow haircut"), N_("I think it\'s wonderful what they\'re doing with incense these days"), N_("I wasn\'t always a woman, you know"), N_("Does your mother know you\'re a dope dealer?"), N_("Are you high on something?"), N_("Oh, you must be from California"), N_("I used to be a hippie, myself"), N_("There\'s nothing like having lots of money"), N_("You look like an aardvark!"), N_("I don\'t believe in Ronald Reagan"), N_("Courage! Bush is a noodle!"), N_("Haven\'t I seen you on TV?"), N_("I think hemorrhoid commercials are really neat!"), N_("We\'re winning the war for drugs!"), N_("A day without dope is like night"), /* xgettext:no-c-format */ N_("We only use 20% of our brains, so why not burn out the other 80%"), N_("I\'m soliciting contributions for Zombies for Christ"), N_("I\'d like to sell you an edible poodle"), N_("Winners don\'t do drugs... unless they do"), N_("Kill a cop for Christ!"), N_("I am the walrus!"), N_("Jesus loves you more than you will know"), N_("I feel an unaccountable urge to dye my hair blue"), N_("Wasn\'t Jane Fonda wonderful in Barbarella"), N_("Just say No... well, maybe... ok, what the hell!"), N_("Would you like a jelly baby?"), N_("Drugs can be your friend!") }; static gboolean SetConfigValue(int GlobalIndex, int StructIndex, gboolean IndexGiven, Converter *conv, GScanner *scanner); /* * Returns a random integer not less than bot and less than top. */ int brandom(int bot, int top) { return (int)((float)(top - bot) * rand() / (RAND_MAX + 1.0)) + bot; } /* * Returns a random price not less than bot and less than top. */ price_t prandom(price_t bot, price_t top) { return (price_t)((float)(top - bot) * rand() / (RAND_MAX + 1.0)) + bot; } /* * Returns the total numbers of players in the list starting at "First"; * players still in the process of connecting or leaving, and those that * are actually cops (server-created internal AI players) are ignored. */ int CountPlayers(GSList *First) { GSList *list; Player *Play; int count = 0; for (list = First; list; list = g_slist_next(list)) { Play = (Player *)list->data; if (strlen(GetPlayerName(Play)) > 0 && !IsCop(Play)) count++; } return count; } /* * Adds the new Player structure "NewPlayer" to the linked list * pointed to by "First", and initialises all fields. Returns the new * start of the list. If this function is called by the server, then * it should pass the file descriptor of the socket used to * communicate with the client player. */ GSList *AddPlayer(int fd, Player *NewPlayer, GSList *First) { Player *tmp; GSList *list; list = First; NewPlayer->ID = 0; /* Generate a unique player ID, if we're the server (clients get their * IDs from the server, so don't need to generate IDs) */ if (Server) { while (list) { tmp = (Player *)list->data; if (tmp->ID == NewPlayer->ID) { NewPlayer->ID++; list = First; } else { list = g_slist_next(list); } } } NewPlayer->Name = NULL; SetPlayerName(NewPlayer, NULL); NewPlayer->IsAt = 0; NewPlayer->EventNum = E_NONE; NewPlayer->FightTimeout = NewPlayer->ConnectTimeout = NewPlayer->IdleTimeout = 0; NewPlayer->Guns = (Inventory *)g_malloc0(NumGun * sizeof(Inventory)); NewPlayer->Drugs = (Inventory *)g_malloc0(NumDrug * sizeof(Inventory)); InitList(&(NewPlayer->SpyList)); InitList(&(NewPlayer->TipList)); NewPlayer->Turn = 1; NewPlayer->date = g_date_new_dmy(StartDate.day, StartDate.month, StartDate.year); NewPlayer->Cash = StartCash; NewPlayer->Debt = StartDebt; NewPlayer->Bank = 0; NewPlayer->Bitches.Carried = 8; NewPlayer->CopIndex = 0; NewPlayer->Health = 100; NewPlayer->CoatSize = 100; NewPlayer->Flags = 0; #if NETWORKING InitNetworkBuffer(&NewPlayer->NetBuf, '\n', '\r', UseSocks ? &Socks : NULL); if (Server) BindNetworkBufferToSocket(&NewPlayer->NetBuf, fd); #endif InitAbilities(NewPlayer); NewPlayer->FightArray = NULL; NewPlayer->Attacking = NULL; return g_slist_append(First, (gpointer)NewPlayer); } /* * Returns TRUE only if the given player has properly connected (i.e. has * a valid name). */ gboolean IsConnectedPlayer(Player *play) { return (play && play->Name && play->Name[0]); } /* * Redimensions the Gun and Drug lists for "Play". */ void UpdatePlayer(Player *Play) { Play->Guns = (Inventory *)g_realloc(Play->Guns, NumGun * sizeof(Inventory)); Play->Drugs = (Inventory *)g_realloc(Play->Drugs, NumDrug * sizeof(Inventory)); } /* * Removes the Player structure pointed to by "Play" from the linked * list starting at "First". The client socket is freed if called * from the server. The new start of the list is returned. */ GSList *RemovePlayer(Player *Play, GSList *First) { g_assert(Play); g_assert(First); First = g_slist_remove(First, (gpointer)Play); #if NETWORKING if (!IsCop(Play)) ShutdownNetworkBuffer(&Play->NetBuf); #endif ClearList(&(Play->SpyList)); ClearList(&(Play->TipList)); g_date_free(Play->date); g_free(Play->Name); g_free(Play->Guns); g_free(Play->Drugs); g_free(Play); return First; } /* * Copies player "Src" to player "Dest". */ void CopyPlayer(Player *Dest, Player *Src) { if (!Dest || !Src) return; Dest->Turn = Src->Turn; Dest->Cash = Src->Cash; Dest->Debt = Src->Debt; Dest->Bank = Src->Bank; Dest->Health = Src->Health; ClearInventory(Dest->Guns, Dest->Drugs); AddInventory(Dest->Guns, Src->Guns, NumGun); AddInventory(Dest->Drugs, Src->Drugs, NumDrug); Dest->CoatSize = Src->CoatSize; Dest->IsAt = Src->IsAt; g_free(Dest->Name); Dest->Name = g_strdup(Src->Name); Dest->Bitches.Carried = Src->Bitches.Carried; Dest->Flags = Src->Flags; } gboolean IsCop(Player *Play) { return (Play->CopIndex > 0); } char *GetPlayerName(Player *Play) { if (Play->Name) return Play->Name; else return ""; } void SetPlayerName(Player *Play, char *Name) { if (Play->Name) g_free(Play->Name); if (!Name) Play->Name = g_strdup(""); else Play->Name = g_strdup(Name); } /* * Searches the linked list starting at "First" for a Player structure * with the given ID. Returns a pointer to this structure, or NULL if * no match can be found. */ Player *GetPlayerByID(guint ID, GSList *First) { GSList *list; Player *Play; for (list = First; list; list = g_slist_next(list)) { Play = (Player *)list->data; if (Play->ID == ID) return Play; } return NULL; } /* * Searches the linked list starting at "First" for a Player structure * with the name "Name". Returns a pointer to this structure, or NULL * if no match can be found. */ Player *GetPlayerByName(char *Name, GSList *First) { GSList *list; Player *Play; if (Name == NULL || Name[0] == 0) return &Noone; for (list = First; list; list = g_slist_next(list)) { Play = (Player *)list->data; if (!IsCop(Play) && strcmp(GetPlayerName(Play), Name) == 0) return Play; } return NULL; } /* * Forms a price based on the string representation in "buf". */ price_t strtoprice(char *buf) { guint i, buflen, FracNum; gchar digit, suffix; gboolean minus, InFrac; price_t val = 0; minus = FALSE; if (!buf || !buf[0]) return 0; buflen = strlen(buf); suffix = buf[buflen - 1]; suffix = toupper(suffix); if (suffix == 'M') FracNum = 6; else if (suffix == 'K') FracNum = 3; else FracNum = 0; for (i = 0, InFrac = FALSE; i < buflen && (!InFrac || FracNum > 0); i++) { digit = buf[i]; if (digit == '.' || digit == ',') { InFrac = TRUE; } else if (digit >= '0' && digit <= '9') { if (InFrac) FracNum--; val *= 10; val += (digit - '0'); } else if (digit == '-') minus = TRUE; } for (i = 0; i < FracNum; i++) val *= 10; if (minus) val = -val; return val; } /* * Prints "price" directly into a dynamically-allocated string buffer * and returns a pointer to this buffer. It is the responsbility of * the user to g_free this buffer when it is finished with. */ gchar *pricetostr(price_t price) { GString *PriceStr; gchar *NewBuffer; price_t absprice; if (price < 0) absprice = -price; else absprice = price; PriceStr = g_string_new(NULL); while (absprice != 0) { g_string_prepend_c(PriceStr, '0' + (absprice % 10)); absprice /= 10; if (absprice == 0) { if (price < 0) g_string_prepend_c(PriceStr, '-'); } } NewBuffer = PriceStr->str; /* Free the string structure, but not the actual char array */ g_string_free(PriceStr, FALSE); return NewBuffer; } /* * Takes the number in "price" and prints it into a dynamically-allocated * string, adding commas to split up thousands, and adding a currency * symbol to the start. Returns a pointer to the string, which must be * g_free'd by the user when it is finished with. */ gchar *FormatPrice(price_t price) { GString *PriceStr; gchar *NewBuffer; char thou[10]; gboolean First = TRUE; price_t absprice; PriceStr = g_string_new(NULL); if (price < 0) absprice = -price; else absprice = price; while (First || absprice > 0) { if (absprice >= 1000) sprintf(thou, "%03d", (int)(absprice % 1000l)); else sprintf(thou, "%d", (int)(price % 1000l)); price /= 1000l; absprice /= 1000l; if (!First) g_string_prepend_c(PriceStr, ','); g_string_prepend(PriceStr, thou); First = FALSE; } if (Currency.Prefix) g_string_prepend(PriceStr, Currency.Symbol); else g_string_append(PriceStr, Currency.Symbol); NewBuffer = PriceStr->str; /* Free the string structure only, not the char data */ g_string_free(PriceStr, FALSE); return NewBuffer; } /* * Returns the total number of guns being carried by "Play". */ int TotalGunsCarried(Player *Play) { int i, c; c = 0; for (i = 0; i < NumGun; i++) c += Play->Guns[i].Carried; return c; } /* * Capitalises the first character of "string" and writes the resultant * string into a dynamically-allocated copy; the user must g_free this * string (a pointer to which is returned) when it is no longer needed. */ gchar *InitialCaps(gchar *string) { gchar *buf; if (!string) return NULL; buf = g_strdup(string); if (strlen(buf) >= 1) buf[0] = toupper(buf[0]); return buf; } /* * Returns TRUE if "string" starts with a vowel. */ char StartsWithVowel(char *string) { int c; if (!string || strlen(string) < 1) return FALSE; c = toupper(string[0]); return (c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U'); } /* * Reads a NULL-terminated string into the buffer "buf" from file "fp". * buf is sized to hold the string; this is a dynamic string and must be * freed by the calling routine. Returns 0 on success, EOF on failure. */ int read_string(FILE *fp, char **buf) { int c; GString *text; text = g_string_new(""); do { c = fgetc(fp); if (c != EOF && c != 0) g_string_append_c(text, (char)c); } while (c != EOF && c != 0); *buf = text->str; /* Free the GString, but not the actual data text->str */ g_string_free(text, FALSE); if (c == EOF) return EOF; else return 0; } /* * This function simply clears the given inventories "Guns" * and "Drugs" if they are non-NULL. */ void ClearInventory(Inventory *Guns, Inventory *Drugs) { int i; if (Guns) for (i = 0; i < NumGun; i++) { Guns[i].Carried = 0; Guns[i].TotalValue = 0; } if (Drugs) for (i = 0; i < NumDrug; i++) { Drugs[i].Carried = 0; Drugs[i].TotalValue = 0; } } /* * Returns TRUE only if "Guns" and "Drugs" contain no objects. */ char IsInventoryClear(Inventory *Guns, Inventory *Drugs) { int i; if (Guns) for (i = 0; i < NumGun; i++) if (Guns[i].Carried > 0) return FALSE; if (Drugs) for (i = 0; i < NumDrug; i++) if (Drugs[i].Carried > 0) return FALSE; return TRUE; } /* * Adds inventory "Add" into the contents of inventory "Cumul" * Each inventory is of length "Length". * N.B. TotalValue is not modified, as it is assumed that the * new items are free (if this is not the case it must be * handled elsewhere). */ void AddInventory(Inventory *Cumul, Inventory *Add, int Length) { int i; for (i = 0; i < Length; i++) Cumul[i].Carried += Add[i].Carried; } /* * Given the lists of "Guns" and "Drugs" (which the given player "Play" * must have sufficient room to carry) updates the player's space to * reflect carrying them. */ void ChangeSpaceForInventory(Inventory *Guns, Inventory *Drugs, Player *Play) { int i; if (Guns) for (i = 0; i < NumGun; i++) { Play->CoatSize -= Guns[i].Carried * Gun[i].Space; } if (Drugs) for (i = 0; i < NumDrug; i++) { Play->CoatSize -= Drugs[i].Carried; } } /* * Discards items from "Guns" and/or "Drugs" (if non-NULL) if necessary * such that player "Play" is able to carry them all. The cheapest * objects are discarded. */ void TruncateInventoryFor(Inventory *Guns, Inventory *Drugs, Player *Play) { int i, Total, CheapIndex; int CheapestGun; Total = 0; if (Guns) for (i = 0; i < NumGun; i++) Total += Guns[i].Carried; Total += TotalGunsCarried(Play); while (Guns && Total > Play->Bitches.Carried + 2) { CheapIndex = -1; for (i = 0; i < NumGun; i++) if (Guns[i].Carried && (CheapIndex == -1 || Gun[i].Price <= Gun[CheapIndex].Price)) { CheapIndex = i; } i = Total - Play->Bitches.Carried - 2; if (Guns[CheapIndex].Carried > i) { Guns[CheapIndex].Carried -= i; Total -= i; } else { Total -= Guns[CheapIndex].Carried; Guns[CheapIndex].Carried = 0; } } Total = Play->CoatSize; if (Guns) for (i = 0; i < NumGun; i++) Total -= Guns[i].Carried * Gun[i].Space; if (Drugs) for (i = 0; i < NumDrug; i++) Total -= Drugs[i].Carried; while (Total < 0) { CheapestGun = -1; CheapIndex = -1; if (Guns) for (i = 0; i < NumGun; i++) if (Guns[i].Carried && (CheapIndex == -1 || Gun[i].Price <= Gun[CheapIndex].Price)) { CheapIndex = i; CheapestGun = Gun[i].Price / Gun[i].Space; } if (Drugs) for (i = 0; i < NumDrug; i++) if (Drugs[i].Carried && (CheapIndex == -1 || (CheapestGun == -1 && Drug[i].MinPrice <= Drug[CheapIndex].MinPrice) || (CheapestGun >= 0 && Drug[i].MinPrice <= CheapestGun))) { CheapIndex = i; CheapestGun = -1; } if (Guns && CheapestGun >= 0) { Guns[CheapIndex].Carried--; Total += Gun[CheapIndex].Space; } else { if (Drugs && Drugs[CheapIndex].Carried >= -Total) { Drugs[CheapIndex].TotalValue = Drugs[CheapIndex].TotalValue * (Drugs[CheapIndex].Carried + Total) / Drugs[CheapIndex].Carried; Drugs[CheapIndex].Carried += Total; Total = 0; } else { Total += Drugs[CheapIndex].Carried; Drugs[CheapIndex].Carried = 0; Drugs[CheapIndex].TotalValue = 0; } } } } /* * Returns an index into the drugs array of a random drug that "Play" is * carrying at least "amount" of. If no suitable drug is found after 5 * attempts, returns -1. */ int IsCarryingRandom(Player *Play, int amount) { int i, ind; for (i = 0; i < 5; i++) { ind = brandom(0, NumDrug); if (Play->Drugs[ind].Carried >= amount) { return ind; } } return -1; } /* * Returns an index into the "Drugs" array maintained by player "Play" * of the next available drug after "OldIndex", following the current * sort method (defined globally as "DrugSortMethod"). */ int GetNextDrugIndex(int OldIndex, Player *Play) { int i, MaxIndex; MaxIndex = -1; for (i = 0; i < NumDrug; i++) { if (Play->Drugs[i].Price != 0 && i != OldIndex && i != MaxIndex && (MaxIndex == -1 || (DrugSortMethod == DS_ATOZ && g_strcasecmp(Drug[MaxIndex].Name, Drug[i].Name) > 0) || (DrugSortMethod == DS_ZTOA && g_strcasecmp(Drug[MaxIndex].Name, Drug[i].Name) < 0) || (DrugSortMethod == DS_CHEAPFIRST && Play->Drugs[MaxIndex].Price > Play->Drugs[i].Price) || (DrugSortMethod == DS_CHEAPLAST && Play->Drugs[MaxIndex].Price < Play->Drugs[i].Price)) && (OldIndex == -1 || (DrugSortMethod == DS_ATOZ && g_strcasecmp(Drug[OldIndex].Name, Drug[i].Name) <= 0) || (DrugSortMethod == DS_ZTOA && g_strcasecmp(Drug[OldIndex].Name, Drug[i].Name) >= 0) || (DrugSortMethod == DS_CHEAPFIRST && Play->Drugs[OldIndex].Price <= Play->Drugs[i].Price) || (DrugSortMethod == DS_CHEAPLAST && Play->Drugs[OldIndex].Price >= Play->Drugs[i].Price))) { MaxIndex = i; } } return MaxIndex; } /* * A DopeList is akin to a Vector class; it is a list of DopeEntry * structures, which can be dynamically extended or compressed. This * function initialises the newly-created list pointed to by "List" * (A DopeEntry contains a Player pointer and a counter, and is used * by the server to keep track of tipoffs and spies.) */ void InitList(DopeList *List) { List->Data = NULL; List->Number = 0; } /* * Clears the list pointed to by "List". */ void ClearList(DopeList *List) { g_free(List->Data); InitList(List); } /* * Adds a new DopeEntry (pointed to by "NewEntry") to the list "List". * A copy of NewEntry is placed into the list, so the original * structure pointed to by NewEntry can be reused. */ void AddListEntry(DopeList *List, DopeEntry *NewEntry) { if (!NewEntry || !List) return; List->Number++; List->Data = (DopeEntry *)g_realloc(List->Data, List->Number * sizeof(DopeEntry)); g_memmove(&(List->Data[List->Number - 1]), NewEntry, sizeof(DopeEntry)); } /* * Removes the DopeEntry at index "Index" from list "List". */ void RemoveListEntry(DopeList *List, int Index) { if (!List || Index < 0 || Index >= List->Number) return; if (Index < List->Number - 1) { g_memmove(&(List->Data[Index]), &(List->Data[Index + 1]), (List->Number - 1 - Index) * sizeof(DopeEntry)); } List->Number--; List->Data = (DopeEntry *)g_realloc(List->Data, List->Number * sizeof(DopeEntry)); if (List->Number == 0) List->Data = NULL; } /* * Returns the index of the DopeEntry matching "Play" in list "List" * or -1 if this is not found. */ int GetListEntry(DopeList *List, Player *Play) { int i; for (i = List->Number - 1; i >= 0; i--) { if (List->Data[i].Play == Play) return i; } return -1; } /* * Removes (if it exists) the DopeEntry in list "List" matching "Play". */ void RemoveListPlayer(DopeList *List, Player *Play) { RemoveListEntry(List, GetListEntry(List, Play)); } /* * Similar to RemoveListPlayer, except that if the list contains "Play" more * than once, all the matching entries are removed, not just the first. */ void RemoveAllEntries(DopeList *List, Player *Play) { int i; do { i = GetListEntry(List, Play); if (i >= 0) RemoveListEntry(List, i); } while (i >= 0); } void ResizeLocations(int NewNum) { int i; if (NewNum < NumLocation) for (i = NewNum; i < NumLocation; i++) { g_free(Location[i].Name); } Location = g_realloc(Location, sizeof(struct LOCATION) * NewNum); if (NewNum > NumLocation) { memset(&Location[NumLocation], 0, (NewNum - NumLocation) * sizeof(struct LOCATION)); for (i = NumLocation; i < NewNum; i++) { Location[i].Name = g_strdup(""); } } NumLocation = NewNum; } void ResizeCops(int NewNum) { int i; if (NewNum < NumCop) for (i = NewNum; i < NumCop; i++) { g_free(Cop[i].Name); g_free(Cop[i].DeputyName); g_free(Cop[i].DeputiesName); } Cop = g_realloc(Cop, sizeof(struct COP) * NewNum); if (NewNum > NumCop) { memset(&Cop[NumCop], 0, (NewNum - NumCop) * sizeof(struct COP)); for (i = NumCop; i < NewNum; i++) { Cop[i].Name = g_strdup(""); Cop[i].DeputyName = g_strdup(""); Cop[i].DeputiesName = g_strdup(""); } } NumCop = NewNum; } void ResizeGuns(int NewNum) { int i; if (NewNum < NumGun) for (i = NewNum; i < NumGun; i++) { g_free(Gun[i].Name); } Gun = g_realloc(Gun, sizeof(struct GUN) * NewNum); if (NewNum > NumGun) { memset(&Gun[NumGun], 0, (NewNum - NumGun) * sizeof(struct GUN)); for (i = NumGun; i < NewNum; i++) { Gun[i].Name = g_strdup(""); } } NumGun = NewNum; } void ResizeDrugs(int NewNum) { int i; if (NewNum < NumDrug) for (i = NewNum; i < NumDrug; i++) { g_free(Drug[i].Name); g_free(Drug[i].CheapStr); } Drug = g_realloc(Drug, sizeof(struct DRUG) * NewNum); if (NewNum > NumDrug) { memset(&Drug[NumDrug], 0, (NewNum - NumDrug) * sizeof(struct DRUG)); for (i = NumDrug; i < NewNum; i++) { Drug[i].Name = g_strdup(""); Drug[i].CheapStr = g_strdup(""); } } NumDrug = NewNum; } void ResizeSubway(int NewNum) { int i; if (NewNum < NumSubway) for (i = NewNum; i < NumSubway; i++) { g_free(SubwaySaying[i]); } SubwaySaying = g_realloc(SubwaySaying, sizeof(char *) * NewNum); if (NewNum > NumSubway) for (i = NumSubway; i < NewNum; i++) { SubwaySaying[i] = g_strdup(""); } NumSubway = NewNum; } void ResizePlaying(int NewNum) { int i; if (NewNum < NumPlaying) for (i = NewNum; i < NumPlaying; i++) { g_free(Playing[i]); } Playing = g_realloc(Playing, sizeof(char *) * NewNum); if (NewNum > NumPlaying) for (i = NumPlaying; i < NewNum; i++) { Playing[i] = g_strdup(""); } NumPlaying = NewNum; } void ResizeStoppedTo(int NewNum) { int i; if (NewNum < NumStoppedTo) for (i = NewNum; i < NumStoppedTo; i++) { g_free(StoppedTo[i]); } StoppedTo = g_realloc(StoppedTo, sizeof(char *) * NewNum); if (NewNum > NumStoppedTo) for (i = NumStoppedTo; i < NewNum; i++) { StoppedTo[i] = g_strdup(""); } NumStoppedTo = NewNum; } /* * Sets the dynamically-sized string pointed to by *dest to a copy of * "src" - src can safely be freed or reused afterwards. Any existing * string in "dest" is freed. The function returns immediately if src * and *dest are already the same. */ void AssignName(gchar **dest, gchar *src) { if (*dest == src) return; g_free(*dest); *dest = g_strdup(src); } void CopyNames(struct NAMES *dest, struct NAMES *src) { AssignName(&dest->Bitch, _(src->Bitch)); AssignName(&dest->Bitches, _(src->Bitches)); AssignName(&dest->Gun, _(src->Gun)); AssignName(&dest->Guns, _(src->Guns)); AssignName(&dest->Drug, _(src->Drug)); AssignName(&dest->Drugs, _(src->Drugs)); AssignName(&dest->Date, _(src->Date)); AssignName(&dest->LoanSharkName, _(src->LoanSharkName)); AssignName(&dest->BankName, _(src->BankName)); AssignName(&dest->GunShopName, _(src->GunShopName)); AssignName(&dest->RoughPubName, _(src->RoughPubName)); } #ifdef NETWORKING void CopyMetaServer(struct METASERVER *dest, struct METASERVER *src) { dest->Active = src->Active; dest->Port = src->Port; dest->ProxyPort = src->ProxyPort; dest->UseSocks = src->UseSocks; AssignName(&dest->Name, src->Name); AssignName(&dest->ProxyName, src->ProxyName); AssignName(&dest->Path, src->Path); AssignName(&dest->LocalName, src->LocalName); AssignName(&dest->Password, src->Password); AssignName(&dest->Comment, src->Comment); AssignName(&dest->authuser, src->authuser); AssignName(&dest->authpassword, src->authpassword); AssignName(&dest->proxyuser, src->proxyuser); AssignName(&dest->proxypassword, src->proxypassword); } #endif void CopyLocation(struct LOCATION *dest, struct LOCATION *src) { AssignName(&dest->Name, _(src->Name)); dest->PolicePresence = src->PolicePresence; dest->MinDrug = src->MinDrug; dest->MaxDrug = src->MaxDrug; } void CopyCop(struct COP *dest, struct COP *src) { AssignName(&dest->Name, _(src->Name)); AssignName(&dest->DeputyName, _(src->DeputyName)); AssignName(&dest->DeputiesName, _(src->DeputiesName)); dest->Armour = src->Armour; dest->DeputyArmour = src->DeputyArmour; dest->AttackPenalty = src->AttackPenalty; dest->DefendPenalty = src->DefendPenalty; dest->MinDeputies = src->MinDeputies; dest->MaxDeputies = src->MaxDeputies; dest->GunIndex = src->GunIndex; dest->CopGun = src->CopGun; dest->DeputyGun = src->DeputyGun; } void CopyGun(struct GUN *dest, struct GUN *src) { AssignName(&dest->Name, _(src->Name)); dest->Price = src->Price; dest->Space = src->Space; dest->Damage = src->Damage; } void CopyDrug(struct DRUG *dest, struct DRUG *src) { AssignName(&dest->Name, src->Name[0] ? _(src->Name) : ""); dest->MinPrice = src->MinPrice; dest->MaxPrice = src->MaxPrice; dest->Cheap = src->Cheap; dest->Expensive = src->Expensive; AssignName(&dest->CheapStr, src->CheapStr[0] ? _(src->CheapStr) : ""); } void CopyDrugs(struct DRUGS *dest, struct DRUGS *src) { AssignName(&dest->ExpensiveStr1, _(src->ExpensiveStr1)); AssignName(&dest->ExpensiveStr2, _(src->ExpensiveStr2)); dest->CheapDivide = src->CheapDivide; dest->ExpensiveMultiply = src->ExpensiveMultiply; } static struct PRICES BackupPrices; static struct NAMES BackupNames; static struct DRUG *BackupDrug = NULL; static struct GUN *BackupGun = NULL; static struct LOCATION *BackupLocation = NULL; static struct CURRENCY BackupCurrency = { NULL, TRUE }; static gint NumBackupDrug = 0, NumBackupGun = 0, NumBackupLocation = 0; void BackupConfig(void) { gint i; BackupPrices.Spy = Prices.Spy; BackupPrices.Tipoff = Prices.Tipoff; AssignName(&BackupCurrency.Symbol, Currency.Symbol); BackupCurrency.Prefix = Currency.Prefix; CopyNames(&BackupNames, &Names); /* Free existing backups of guns, drugs, and locations */ for (i = 0; i < NumBackupGun; i++) g_free(BackupGun[i].Name); g_free(BackupGun); for (i = 0; i < NumBackupDrug; i++) { g_free(BackupDrug[i].Name); g_free(BackupDrug[i].CheapStr); } g_free(BackupDrug); for (i = 0; i < NumBackupLocation; i++) g_free(BackupLocation[i].Name); g_free(BackupLocation); NumBackupGun = NumGun; BackupGun = g_new0(struct GUN, NumGun); for (i = 0; i < NumGun; i++) CopyGun(&BackupGun[i], &Gun[i]); NumBackupDrug = NumDrug; BackupDrug = g_new0(struct DRUG, NumDrug); for (i = 0; i < NumDrug; i++) CopyDrug(&BackupDrug[i], &Drug[i]); NumBackupLocation = NumLocation; BackupLocation = g_new0(struct LOCATION, NumLocation); for (i = 0; i < NumLocation; i++) CopyLocation(&BackupLocation[i], &Location[i]); } void RestoreConfig(void) { gint i; Prices.Spy = BackupPrices.Spy; Prices.Tipoff = BackupPrices.Tipoff; CopyNames(&Names, &BackupNames); AssignName(&Currency.Symbol, BackupCurrency.Symbol); Currency.Prefix = BackupCurrency.Prefix; ResizeGuns(NumBackupGun); for (i = 0; i < NumGun; i++) CopyGun(&Gun[i], &BackupGun[i]); ResizeDrugs(NumBackupDrug); for (i = 0; i < NumDrug; i++) CopyDrug(&Drug[i], &BackupDrug[i]); ResizeLocations(NumBackupLocation); for (i = 0; i < NumLocation; i++) CopyLocation(&Location[i], &BackupLocation[i]); } void ScannerErrorHandler(GScanner *scanner, gchar *msg, gint error) { g_print("%s\n", msg); } /* * On Windows systems, check the current config file referenced by "scanner" * for a UTF-8 header. If one is found, "conv" and "encoding" are set * for UTF-8 encoding. */ static void CheckConfigHeader(GScanner *scanner, Converter *conv, gchar **encoding) { #ifdef CYGWIN GTokenType token; token = g_scanner_peek_next_token(scanner); if (token == (guchar)'\357') { /* OK; assume this is a Windows-style \357 \273 \277 UTF-8 header */ if (g_scanner_get_next_token(scanner) != (guchar)'\357' || g_scanner_get_next_token(scanner) != (guchar)'\273' || g_scanner_get_next_token(scanner) != (guchar)'\277') { return; } Conv_SetCodeset(conv, "UTF-8"); if (encoding) { g_free(*encoding); *encoding = g_strdup("UTF-8"); } } #endif } /* * Read a configuration file given by "FileName" */ static gboolean ReadConfigFile(char *FileName, gchar **encoding) { FILE *fp; Converter *conv; GScanner *scanner; fp = fopen(FileName, "r"); if (fp) { conv = Conv_New(); if (encoding) { *encoding = NULL; } scanner = g_scanner_new(&ScannerConfig); scanner->input_name = FileName; scanner->msg_handler = ScannerErrorHandler; g_scanner_input_file(scanner, fileno(fp)); CheckConfigHeader(scanner, conv, encoding); while (!g_scanner_eof(scanner)) { if (!ParseNextConfig(scanner, conv, encoding, FALSE)) { ConfigErrors++; g_scanner_error(scanner, _("Unable to process configuration file %s, line %d"), FileName, g_scanner_cur_line(scanner)); } } g_scanner_destroy(scanner); Conv_Free(conv); fclose(fp); return TRUE; } else { return FALSE; } } gboolean ParseNextConfig(GScanner *scanner, Converter *conv, gchar **encoding, gboolean print) { GTokenType token; gchar *ID1, *ID2; gulong ind = 0; int GlobalIndex; gboolean IndexGiven = FALSE; ID1 = ID2 = NULL; token = g_scanner_get_next_token(scanner); if (token == G_TOKEN_EOF) return TRUE; if (token != G_TOKEN_IDENTIFIER) { g_scanner_unexp_token(scanner, G_TOKEN_IDENTIFIER, NULL, NULL, NULL, NULL, FALSE); return FALSE; } if (g_strcasecmp(scanner->value.v_identifier, "include") == 0) { token = g_scanner_get_next_token(scanner); if (token == G_TOKEN_STRING) { if (!ReadConfigFile(scanner->value.v_string, NULL)) { g_scanner_error(scanner, _("Unable to open file %s"), scanner->value.v_string); } return TRUE; } else { g_scanner_unexp_token(scanner, G_TOKEN_STRING, NULL, NULL, NULL, NULL, FALSE); return FALSE; } } else if (g_strcasecmp(scanner->value.v_identifier, "encoding") == 0) { token = g_scanner_get_next_token(scanner); if (token == G_TOKEN_STRING) { Conv_SetCodeset(conv, scanner->value.v_string); if (encoding) { g_free(*encoding); *encoding = g_strdup(scanner->value.v_string); } return TRUE; } else { g_scanner_unexp_token(scanner, G_TOKEN_STRING, NULL, NULL, NULL, NULL, FALSE); return FALSE; } } ID1 = g_strdup(scanner->value.v_identifier); token = g_scanner_get_next_token(scanner); if (token == G_TOKEN_LEFT_BRACE) { token = g_scanner_get_next_token(scanner); if (token != G_TOKEN_INT) { g_scanner_unexp_token(scanner, G_TOKEN_INT, NULL, NULL, NULL, NULL, FALSE); return FALSE; } ind = scanner->value.v_int; IndexGiven = TRUE; token = g_scanner_get_next_token(scanner); if (token != G_TOKEN_RIGHT_BRACE) { g_scanner_unexp_token(scanner, G_TOKEN_RIGHT_BRACE, NULL, NULL, NULL, NULL, FALSE); return FALSE; } token = g_scanner_get_next_token(scanner); if (token == '.') { token = g_scanner_get_next_token(scanner); if (token != G_TOKEN_IDENTIFIER) { g_scanner_unexp_token(scanner, G_TOKEN_IDENTIFIER, NULL, NULL, NULL, NULL, FALSE); return FALSE; } ID2 = g_strdup(scanner->value.v_identifier); token = g_scanner_get_next_token(scanner); } } GlobalIndex = GetGlobalIndex(ID1, ID2); g_free(ID1); g_free(ID2); if (GlobalIndex == -1) return FALSE; if (token == G_TOKEN_EOF) { PrintConfigValue(GlobalIndex, (int)ind, IndexGiven, scanner); return TRUE; } else if (token == G_TOKEN_EQUAL_SIGN) { if (CountPlayers(FirstServer) > 0) { g_warning(_("Configuration can only be changed interactively " "when no\nplayers are logged on. Wait for all " "players to log off, or remove\nthem with the " "push or kill commands, and try again.")); } else { if (SetConfigValue(GlobalIndex, (int)ind, IndexGiven, conv, scanner) && print) { PrintConfigValue(GlobalIndex, (int)ind, IndexGiven, scanner); } } return TRUE; } else { return FALSE; } return FALSE; } int GetGlobalIndex(gchar *ID1, gchar *ID2) { int i; const int NumGlob = sizeof(Globals) / sizeof(Globals[0]); if (!ID1) return -1; for (i = 0; i < NumGlob; i++) { if (g_strcasecmp(ID1, Globals[i].Name) == 0 && !Globals[i].NameStruct[0]) { /* Just a bog-standard ID1=value */ return i; } if (g_strcasecmp(ID1, Globals[i].NameStruct) == 0 && ID2 && g_strcasecmp(ID2, Globals[i].Name) == 0 && Globals[i].StructStaticPt && Globals[i].StructListPt) { /* ID1[index].ID2=value */ return i; } } return -1; } static void *GetGlobalPointer(int GlobalIndex, int StructIndex) { void *ValPt = NULL; if (Globals[GlobalIndex].IntVal) { ValPt = (void *)Globals[GlobalIndex].IntVal; } else if (Globals[GlobalIndex].PriceVal) { ValPt = (void *)Globals[GlobalIndex].PriceVal; } else if (Globals[GlobalIndex].BoolVal) { ValPt = (void *)Globals[GlobalIndex].BoolVal; } else if (Globals[GlobalIndex].StringVal) { ValPt = (void *)Globals[GlobalIndex].StringVal; } if (!ValPt) return NULL; if (Globals[GlobalIndex].StructStaticPt && Globals[GlobalIndex].StructListPt) { return (char *)ValPt - (char *)Globals[GlobalIndex].StructStaticPt + (char *)*(Globals[GlobalIndex].StructListPt) + (StructIndex - 1) * Globals[GlobalIndex].LenStruct; } else { return ValPt; } } gchar **GetGlobalString(int GlobalIndex, int StructIndex) { g_assert(Globals[GlobalIndex].StringVal); return (gchar **)GetGlobalPointer(GlobalIndex, StructIndex); } gint *GetGlobalInt(int GlobalIndex, int StructIndex) { g_assert(Globals[GlobalIndex].IntVal); return (gint *)GetGlobalPointer(GlobalIndex, StructIndex); } price_t *GetGlobalPrice(int GlobalIndex, int StructIndex) { g_assert(Globals[GlobalIndex].PriceVal); return (price_t *)GetGlobalPointer(GlobalIndex, StructIndex); } gboolean *GetGlobalBoolean(int GlobalIndex, int StructIndex) { g_assert(Globals[GlobalIndex].BoolVal); return (gboolean *)GetGlobalPointer(GlobalIndex, StructIndex); } gchar ***GetGlobalStringList(int GlobalIndex, int StructIndex) { g_assert(Globals[GlobalIndex].StringList); return (gchar ***)GetGlobalPointer(GlobalIndex, StructIndex); } gboolean CheckMaxIndex(GScanner *scanner, int GlobalIndex, int StructIndex, gboolean IndexGiven) { if (!Globals[GlobalIndex].MaxIndex || (Globals[GlobalIndex].StringList && !IndexGiven) || (IndexGiven && StructIndex >= 1 && StructIndex <= *(Globals[GlobalIndex].MaxIndex))) { return TRUE; } /* Error message displayed when you try to set, for example, * Drug[10].Name when NumDrug<10 (%s="Drug" and %d=10 in this example) */ g_scanner_error(scanner, _("Index into %s array should be between 1 and %d"), (Globals[GlobalIndex].NameStruct && Globals[GlobalIndex]. NameStruct[0]) ? Globals[GlobalIndex]. NameStruct : Globals[GlobalIndex].Name, *(Globals[GlobalIndex].MaxIndex)); return FALSE; } void PrintConfigValue(int GlobalIndex, int StructIndex, gboolean IndexGiven, GScanner *scanner) { gchar *GlobalName; int i; if (!CheckMaxIndex(scanner, GlobalIndex, StructIndex, IndexGiven)) return; if (Globals[GlobalIndex].NameStruct[0]) { GlobalName = g_strdup_printf("%s[%d].%s", Globals[GlobalIndex].NameStruct, StructIndex, Globals[GlobalIndex].Name); } else GlobalName = Globals[GlobalIndex].Name; if (Globals[GlobalIndex].IntVal) { /* Display of a numeric config. file variable - e.g. "NumDrug is 6" */ g_print(_("%s is %d\n"), GlobalName, *GetGlobalInt(GlobalIndex, StructIndex)); } else if (Globals[GlobalIndex].BoolVal) { /* Display of a boolean config. file variable - e.g. "DrugValue is * TRUE" */ g_print(_("%s is %s\n"), GlobalName, *GetGlobalBoolean(GlobalIndex, StructIndex) ? "TRUE" : "FALSE"); } else if (Globals[GlobalIndex].PriceVal) { /* Display of a price config. file variable - e.g. "Bitch.MinPrice is * $200" */ dpg_print(_("%s is %P\n"), GlobalName, *GetGlobalPrice(GlobalIndex, StructIndex)); } else if (Globals[GlobalIndex].StringVal) { /* Display of a string config. file variable - e.g. "LoanSharkName is * \"the loan shark\"" */ g_print(_("%s is \"%s\"\n"), GlobalName, *GetGlobalString(GlobalIndex, StructIndex)); } else if (Globals[GlobalIndex].StringList) { if (IndexGiven) { /* Display of an indexed string list config. file variable - e.g. * "StoppedTo[1] is have a beer" */ g_print(_("%s[%d] is %s\n"), GlobalName, StructIndex, (*(Globals[GlobalIndex].StringList))[StructIndex - 1]); } else { GString *text; text = g_string_new(""); /* Display of the first part of an entire string list config. file * variable - e.g. "StoppedTo is { " (followed by "have a beer", * "smoke a joint" etc.) */ g_string_sprintf(text, _("%s is { "), GlobalName); if (Globals[GlobalIndex].MaxIndex) { for (i = 0; i < *(Globals[GlobalIndex].MaxIndex); i++) { if (i > 0) g_string_append(text, ", "); g_string_sprintfa(text, "\"%s\"", (*(Globals[GlobalIndex].StringList))[i]); } } g_string_append(text, " }\n"); g_print("%s", text->str); g_string_free(text, TRUE); } } if (Globals[GlobalIndex].NameStruct[0]) g_free(GlobalName); } static gboolean SetConfigValue(int GlobalIndex, int StructIndex, gboolean IndexGiven, Converter *conv, GScanner *scanner) { gchar *GlobalName, *tmpstr; GTokenType token; int IntVal, NewNum; Player *tmp; GSList *list, *StartList; gboolean parsed; if (!CheckMaxIndex(scanner, GlobalIndex, StructIndex, IndexGiven)) return FALSE; if (Globals[GlobalIndex].NameStruct[0]) { GlobalName = g_strdup_printf("%s[%d].%s", Globals[GlobalIndex].NameStruct, StructIndex, Globals[GlobalIndex].Name); } else { GlobalName = Globals[GlobalIndex].Name; } if (Globals[GlobalIndex].IntVal) { gboolean minus = FALSE; /* GScanner doesn't understand negative numbers, so we need to * explicitly check for a prefixed minus sign */ token = g_scanner_get_next_token(scanner); if (token == '-') { minus = TRUE; token = g_scanner_get_next_token(scanner); } if (token == G_TOKEN_INT) { IntVal = (int)scanner->value.v_int; if (minus) { IntVal = -IntVal; } if (IntVal < Globals[GlobalIndex].MinVal) { g_scanner_warn(scanner, _("%s can be no smaller than %d - ignoring!"), GlobalName, Globals[GlobalIndex].MinVal); return FALSE; } if (Globals[GlobalIndex].MaxVal > Globals[GlobalIndex].MinVal && IntVal > Globals[GlobalIndex].MaxVal) { g_scanner_warn(scanner, _("%s can be no larger than %d - ignoring!"), GlobalName, Globals[GlobalIndex].MaxVal); return FALSE; } if (Globals[GlobalIndex].ResizeFunc) { (*(Globals[GlobalIndex].ResizeFunc)) (IntVal); /* Displayed, for example, when you set NumDrug=10 to allow * Drug[10].Name etc. to be set */ if (ConfigVerbose) g_print(_("Resized structure list to %d elements\n"), IntVal); for (list = FirstClient; list; list = g_slist_next(list)) { tmp = (Player *)list->data; UpdatePlayer(tmp); } for (list = FirstServer; list; list = g_slist_next(list)) { tmp = (Player *)list->data; UpdatePlayer(tmp); } } *GetGlobalInt(GlobalIndex, StructIndex) = IntVal; } else { g_scanner_unexp_token(scanner, G_TOKEN_INT, NULL, NULL, NULL, NULL, FALSE); return FALSE; } } else if (Globals[GlobalIndex].BoolVal) { scanner->config->cset_identifier_first = G_CSET_a_2_z "01" G_CSET_A_2_Z; scanner->config->cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z; token = g_scanner_get_next_token(scanner); scanner->config->cset_identifier_first = G_CSET_a_2_z "_" G_CSET_A_2_Z; scanner->config->cset_identifier_nth = G_CSET_a_2_z "._0123456789" G_CSET_A_2_Z; parsed = FALSE; if (token == G_TOKEN_IDENTIFIER) { if (g_strcasecmp(scanner->value.v_identifier, "TRUE") == 0 || strcmp(scanner->value.v_identifier, "1") == 0) { parsed = TRUE; *GetGlobalBoolean(GlobalIndex, StructIndex) = TRUE; } else if (g_strcasecmp(scanner->value.v_identifier, "FALSE") == 0 || strcmp(scanner->value.v_identifier, "0") == 0) { parsed = TRUE; *GetGlobalBoolean(GlobalIndex, StructIndex) = FALSE; } } if (!parsed) { g_scanner_unexp_token(scanner, G_TOKEN_NONE, NULL, NULL, NULL, _("expected a boolean value (one of 0, FALSE, " "1, TRUE)"), FALSE); return FALSE; } } else if (Globals[GlobalIndex].PriceVal) { token = g_scanner_get_next_token(scanner); if (token == G_TOKEN_INT) { *GetGlobalPrice(GlobalIndex, StructIndex) = (price_t)scanner->value.v_int; } else { g_scanner_unexp_token(scanner, G_TOKEN_INT, NULL, NULL, NULL, NULL, FALSE); return FALSE; } } else if (Globals[GlobalIndex].StringVal) { scanner->config->identifier_2_string = TRUE; scanner->config->cset_identifier_first = G_CSET_a_2_z "._0123456789" G_CSET_A_2_Z G_CSET_LATINS G_CSET_LATINC; scanner->config->cset_identifier_nth = G_CSET_a_2_z " ._0123456789" G_CSET_A_2_Z G_CSET_LATINS G_CSET_LATINC; token = g_scanner_get_next_token(scanner); if (token == G_TOKEN_STRING) { tmpstr = Conv_ToInternal(conv, scanner->value.v_string, -1); AssignName(GetGlobalString(GlobalIndex, StructIndex), tmpstr); g_free(tmpstr); } else if (token == G_TOKEN_IDENTIFIER) { tmpstr = Conv_ToInternal(conv, scanner->value.v_identifier, -1); AssignName(GetGlobalString(GlobalIndex, StructIndex), tmpstr); g_free(tmpstr); } else { g_scanner_unexp_token(scanner, G_TOKEN_STRING, NULL, NULL, NULL, NULL, FALSE); } scanner->config->identifier_2_string = FALSE; scanner->config->cset_identifier_first = G_CSET_a_2_z "_" G_CSET_A_2_Z; scanner->config->cset_identifier_nth = G_CSET_a_2_z "._0123456789" G_CSET_A_2_Z; } else if (Globals[GlobalIndex].StringList) { token = g_scanner_get_next_token(scanner); if (IndexGiven) { if (token == G_TOKEN_STRING) { tmpstr = Conv_ToInternal(conv, scanner->value.v_string, -1); AssignName(&(*(Globals[GlobalIndex].StringList))[StructIndex - 1], tmpstr); g_free(tmpstr); } else { g_scanner_unexp_token(scanner, G_TOKEN_STRING, NULL, NULL, NULL, NULL, FALSE); return FALSE; } } else { StartList = NULL; if (token != G_TOKEN_LEFT_CURLY) { g_scanner_unexp_token(scanner, G_TOKEN_LEFT_CURLY, NULL, NULL, NULL, NULL, FALSE); return FALSE; } NewNum = 0; do { token = g_scanner_get_next_token(scanner); if (token == G_TOKEN_STRING) { tmpstr = g_strdup(scanner->value.v_string); NewNum++; StartList = g_slist_append(StartList, tmpstr); } else if (token != G_TOKEN_RIGHT_CURLY && token != G_TOKEN_COMMA) { g_scanner_unexp_token(scanner, G_TOKEN_STRING, NULL, NULL, NULL, NULL, FALSE); return FALSE; } } while (token != G_TOKEN_RIGHT_CURLY); (*Globals[GlobalIndex].ResizeFunc) (NewNum); NewNum = 0; for (list = StartList; list; NewNum++, list = g_slist_next(list)) { AssignName(&(*(Globals[GlobalIndex].StringList))[NewNum], (char *)list->data); g_free(list->data); } g_slist_free(StartList); } } if (Globals[GlobalIndex].NameStruct[0]) g_free(GlobalName); Globals[GlobalIndex].Modified = TRUE; return TRUE; } /* * Returns the URL of the directory containing local HTML documentation. */ gchar *GetDocRoot(void) { gchar *path; #ifdef CYGWIN gchar *bindir; bindir = GetBinaryDir(); path = g_strdup_printf("file://%s\\", bindir); g_free(bindir); #else path = g_strdup_printf("file://%s/doc/%s-%s/", DPDATADIR, PACKAGE, VERSION); #endif return path; } /* * Returns the URL of the index file for the local HTML documentation. */ gchar *GetDocIndex(void) { gchar *file, *root; root = GetDocRoot(); file = g_strdup_printf("%sindex.html", root); g_free(root); return file; } /* * Returns the pathname of the global (all users) configuration file, * as a dynamically-allocated string that must be later freed. On * error, NULL is returned. */ gchar *GetGlobalConfigFile(void) { #ifdef CYGWIN gchar *bindir, *conf = NULL; /* Global configuration is in the same directory as the dopewars binary */ bindir = GetBinaryDir(); if (bindir) { conf = g_strdup_printf("%s/dopewars-config.txt", bindir); g_free(bindir); } return conf; #else return g_strdup("/etc/dopewars"); #endif } /* * Returns the pathname of the local (per-user) configuration file, * as a dynamically-allocated string that must be later freed. On * error, NULL is returned. */ gchar *GetLocalConfigFile(void) { #ifdef CYGWIN return g_strdup("dopewars-config.txt"); #else gchar *home, *conf = NULL; /* Local config is in the user's home directory */ home = getenv("HOME"); if (home) { conf = g_strdup_printf("%s/.dopewars", home); } return conf; #endif } /* * Sets up data - such as the location of the high score file - to * hard-coded internal values, and then processes the global and * user-specific configuration files. */ static void SetupParameters(GSList *extraconfigs, gboolean antique) { gchar *conf; GSList *list; int i, defloc; DrugValue = TRUE; Sanitized = ConfigVerbose = FALSE; g_free(Currency.Symbol); /* The currency symbol */ Currency.Symbol = g_strdup(_("$")); Currency.Prefix = (strcmp("Currency.Prefix=TRUE", /* Translate this to "Currency.Prefix=FALSE" if you want your currency * symbol to follow all prices. */ _("Currency.Prefix=TRUE")) == 0); /* Set hard-coded default values */ AssignName(&ServerName, "localhost"); AssignName(&ServerMOTD, ""); AssignName(&BindAddress, ""); AssignName(&WebBrowser, "/usr/bin/mozilla"); AssignName(&Sounds.FightHit, SNDPATH"colt.wav"); AssignName(&Sounds.EnemyBitchKilled, SNDPATH"shotdown.wav"); AssignName(&Sounds.BitchKilled, SNDPATH"losebitch.wav"); AssignName(&Sounds.EnemyKilled, SNDPATH"shotdown.wav"); AssignName(&Sounds.Killed, SNDPATH"die.wav"); AssignName(&Sounds.EnemyFlee, SNDPATH"run.wav"); AssignName(&Sounds.Flee, SNDPATH"run.wav"); AssignName(&Sounds.Jet, SNDPATH"train.wav"); AssignName(&Sounds.TalkPrivate, SNDPATH"murmur.wav"); AssignName(&Sounds.TalkToAll, SNDPATH"message.wav"); AssignName(&Sounds.EndGame, SNDPATH"bye.wav"); LoanSharkLoc = DEFLOANSHARK; BankLoc = DEFBANK; if (antique) { GunShopLoc = RoughPubLoc = 0; } else { GunShopLoc = DEFGUNSHOP; RoughPubLoc = DEFROUGHPUB; } CopyNames(&Names, &DefaultNames); CopyDrugs(&Drugs, &DefaultDrugs); #ifdef NETWORKING CopyMetaServer(&MetaServer, &DefaultMetaServer); AssignName(&Socks.name, "socks"); Socks.port = 1080; Socks.version = 4; g_free(Socks.user); g_free(Socks.authuser); g_free(Socks.authpassword); Socks.user = g_strdup(""); Socks.numuid = FALSE; Socks.authuser = g_strdup(""); Socks.authpassword = g_strdup(""); UseSocks = FALSE; #endif defloc = sizeof(DefaultLocation) / sizeof(DefaultLocation[0]); g_assert(defloc >= 6); if (antique) { defloc = 6; } ResizeLocations(defloc); for (i = 0; i < NumLocation; i++) CopyLocation(&Location[i], &DefaultLocation[i]); ResizeCops(sizeof(DefaultCop) / sizeof(DefaultCop[0])); for (i = 0; i < NumCop; i++) CopyCop(&Cop[i], &DefaultCop[i]); ResizeGuns(sizeof(DefaultGun) / sizeof(DefaultGun[0])); for (i = 0; i < NumGun; i++) CopyGun(&Gun[i], &DefaultGun[i]); ResizeDrugs(sizeof(DefaultDrug) / sizeof(DefaultDrug[0])); for (i = 0; i < NumDrug; i++) CopyDrug(&Drug[i], &DefaultDrug[i]); ResizeSubway(sizeof(DefaultSubwaySaying) / sizeof(DefaultSubwaySaying[0])); for (i = 0; i < NumSubway; i++) { AssignName(&SubwaySaying[i], _(DefaultSubwaySaying[i])); } ResizePlaying(sizeof(DefaultPlaying) / sizeof(DefaultPlaying[0])); for (i = 0; i < NumPlaying; i++) { AssignName(&Playing[i], _(DefaultPlaying[i])); } ResizeStoppedTo(sizeof(DefaultStoppedTo) / sizeof(DefaultStoppedTo[0])); for (i = 0; i < NumStoppedTo; i++) { AssignName(&StoppedTo[i], _(DefaultStoppedTo[i])); } /* Replace nasty null pointers with null strings */ for (i = 0; i < NUMGLOB; ++i) { if (Globals[i].StringVal && !*Globals[i].StringVal) { *Globals[i].StringVal = g_strdup(""); } } /* Now read in the global configuration file */ conf = GetGlobalConfigFile(); if (conf) { ReadConfigFile(conf, NULL); g_free(conf); } /* Next, try the local configuration file */ conf = GetLocalConfigFile(); if (conf) { ReadConfigFile(conf, &LocalCfgEncoding); g_free(conf); } /* Finally, any configuration files named on the command line */ for (list = extraconfigs; list; list = g_slist_next(list)) { ReadConfigFile(list->data, NULL); } } void GetDateString(GString *str, Player *Play) { gchar buf[200], *turn, *pt; turn = g_strdup_printf("%d", Play->Turn); g_string_assign(str, Names.Date); while ((pt = strstr(str->str, "%T")) != NULL) { int ind = pt - str->str; g_string_erase(str, ind, 2); g_string_insert(str, ind, turn); } g_date_strftime(buf, sizeof(buf), str->str, Play->date); g_string_assign(str, buf); g_free(turn); } static void PluginHelp(void) { gchar *plugins; #ifdef HAVE_GETOPT_LONG g_print(_(" -u, --plugin=FILE use sound plugin \"FILE\"\n" " ")); #else g_print(_(" -u file use sound plugin \"file\"\n" " ")); #endif plugins = GetPluginList(); g_print(_("(%s available)\n"), plugins); g_free(plugins); } void HandleHelpTexts(gboolean fullhelp) { g_print(_("dopewars version %s\n"), VERSION); if (!fullhelp) { return; } g_print( #ifdef HAVE_GETOPT_LONG /* Usage information, printed when the user runs "dopewars -h" * (version with support for GNU long options) */ _("Usage: dopewars [OPTION]...\n\ Drug dealing game based on \"Drug Wars\" by John E. Dell\n\ -b, --no-color, \"black and white\" - i.e. do not use pretty colours\n\ --no-colour (by default colours are used where available)\n\ -n, --single-player be boring and don't connect to any available dopewars\n\ servers (i.e. single player mode)\n\ -a, --antique \"antique\" dopewars - keep as closely to the original\n\ version as possible (no networking)\n\ -f, --scorefile=FILE specify a file to use as the high score table (by\n\ default %s/dopewars.sco is used)\n\ -o, --hostname=ADDR specify a hostname where the server for multiplayer\n\ dopewars can be found\n\ -s, --public-server run in server mode (note: see the -A option for\n\ configuring a server once it\'s running)\n\ -S, --private-server run a \"private\" server (do not notify the metaserver)\n\ -p, --port=PORT specify the network port to use (default: 7902)\n\ -g, --config-file=FILE specify the pathname of a dopewars configuration file;\n\ this file is read immediately when the -g option\n\ is encountered\n\ -r, --pidfile=FILE maintain pid file \"FILE\" while running the server\n\ -l, --logfile=FILE write log information to \"FILE\"\n\ -A, --admin connect to a locally-running server for administration\n\ -c, --ai-player create and run a computer player\n\ -w, --windowed-client force the use of a graphical (windowed)\n\ client (GTK+ or Win32)\n\ -t, --text-client force the use of a text-mode client (curses) (by\n\ default, a windowed client is used when possible)\n\ -P, --player=NAME set player name to \"NAME\"\n\ -C, --convert=FILE convert an \"old format\" score file to the new format\n"), DPSCOREDIR); PluginHelp(); g_print(_(" -h, --help display this help information\n\ -v, --version output version information and exit\n\n\ dopewars is Copyright (C) Ben Webb 1998-2005, and released under the GNU GPL\n\ Report bugs to the author at benwebb@users.sf.net\n")); #else /* Usage information, printed when the user runs "dopewars -h" * (short options only version) */ _("Usage: dopewars [OPTION]...\n\ Drug dealing game based on \"Drug Wars\" by John E. Dell\n\ -b \"black and white\" - i.e. do not use pretty colours\n\ (by default colours are used where the terminal supports them)\n\ -n be boring and don't connect to any available dopewars servers\n\ (i.e. single player mode)\n\ -a \"antique\" dopewars - keep as closely to the original version as\n\ possible (no networking)\n\ -f file specify a file to use as the high score table\n\ (by default %s/dopewars.sco is used)\n\ -o addr specify a hostname where the server for multiplayer dopewars\n\ can be found\n\ -s run in server mode (note: see the -A option for configuring a\n\ server once it\'s running)\n\ -S run a \"private\" server (i.e. do not notify the metaserver)\n\ -p port specify the network port to use (default: 7902)\n\ -g file specify the pathname of a dopewars configuration file; this file\n\ is read immediately when the -g option is encountered\n\ -r file maintain pid file \"file\" while running the server\n\ -l file write log information to \"file\"\n\ -c create and run a computer player\n\ -w force the use of a graphical (windowed) client (GTK+ or Win32)\n\ -t force the use of a text-mode client (curses)\n\ (by default, a windowed client is used when possible)\n\ -P name set player name to \"name\"\n\ -C file convert an \"old format\" score file to the new format\n\ -A connect to a locally-running server for administration\n"), DPSCOREDIR); PluginHelp(); g_print(_(" -h display this help information\n\ -v output version information and exit\n\n\ dopewars is Copyright (C) Ben Webb 1998-2005, and released under the GNU GPL\n\ Report bugs to the author at benwebb@users.sf.net\n")); #endif } struct CMDLINE *ParseCmdLine(int argc, char *argv[]) { int c; struct CMDLINE *cmdline = g_new0(struct CMDLINE, 1); static const gchar *options = "anbchvf:o:sSp:g:r:wtC:l:NAu:P:"; #ifdef HAVE_GETOPT_LONG static const struct option long_options[] = { {"no-color", no_argument, NULL, 'b'}, {"no-colour", no_argument, NULL, 'b'}, {"single-player", no_argument, NULL, 'n'}, {"antique", no_argument, NULL, 'a'}, {"scorefile", required_argument, NULL, 'f'}, {"hostname", required_argument, NULL, 'o'}, {"public-server", no_argument, NULL, 's'}, {"private-server", no_argument, NULL, 'S'}, {"port", required_argument, NULL, 'p'}, {"configfile", required_argument, NULL, 'g'}, {"pidfile", required_argument, NULL, 'r'}, {"ai-player", no_argument, NULL, 'c'}, {"windowed-client", no_argument, NULL, 'w'}, {"text-client", no_argument, NULL, 't'}, {"player", required_argument, NULL, 'P'}, {"convert", required_argument, NULL, 'C'}, {"logfile", required_argument, NULL, 'l'}, {"admin", no_argument, NULL, 'A'}, {"plugin", required_argument, NULL, 'u'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'v'}, {0, 0, 0, 0} }; #endif cmdline->scorefile = cmdline->servername = cmdline->pidfile = cmdline->logfile = cmdline->plugin = cmdline->convertfile = cmdline->playername = NULL; cmdline->configs = NULL; cmdline->colour = cmdline->network = TRUE; cmdline->client = CLIENT_AUTO; do { #ifdef HAVE_GETOPT_LONG c = getopt_long(argc, argv, options, long_options, NULL); #else c = getopt(argc, argv, options); #endif switch (c) { case 'n': cmdline->network = FALSE; break; case 'b': cmdline->colour = FALSE; break; case 'c': cmdline->ai = TRUE; break; case 'a': cmdline->antique = TRUE; cmdline->network = FALSE; break; case 'v': cmdline->version = TRUE; break; case 'h': case 0: case '?': cmdline->help = TRUE; break; case 'f': AssignName(&cmdline->scorefile, optarg); break; case 'o': AssignName(&cmdline->servername, optarg); break; case 's': cmdline->server = TRUE; cmdline->notifymeta = TRUE; break; case 'S': cmdline->server = TRUE; cmdline->notifymeta = FALSE; break; case 'p': cmdline->setport = TRUE; cmdline->port = atoi(optarg); break; case 'g': cmdline->configs = g_slist_append(cmdline->configs, g_strdup(optarg)); break; case 'r': AssignName(&cmdline->pidfile, optarg); break; case 'l': AssignName(&cmdline->logfile, optarg); break; case 'u': AssignName(&cmdline->plugin, optarg); break; case 'w': cmdline->client = CLIENT_WINDOW; break; case 't': cmdline->client = CLIENT_CURSES; break; case 'P': AssignName(&cmdline->playername, optarg); break; case 'C': AssignName(&cmdline->convertfile, optarg); cmdline->convert = TRUE; break; case 'A': cmdline->admin = TRUE; break; } } while (c != -1); return cmdline; } void FreeCmdLine(struct CMDLINE *cmdline) { GSList *list; g_free(cmdline->scorefile); g_free(cmdline->servername); g_free(cmdline->pidfile); g_free(cmdline->logfile); g_free(cmdline->plugin); g_free(cmdline->convertfile); g_free(cmdline->playername); for (list = cmdline->configs; list; list = g_slist_next(list)) { g_free(list->data); } g_slist_free(list); g_free(cmdline); } static gchar *priv_hiscore = NULL; /* * Does general startup stuff (command line, dropping privileges, * and high score init; config files are handled later) */ struct CMDLINE *GeneralStartup(int argc, char *argv[]) { /* First, open the hard-coded high score file with possibly * elevated privileges */ priv_hiscore = g_strdup_printf("%s/dopewars.sco", DPSCOREDIR); HiScoreFile = g_strdup(priv_hiscore); OpenHighScoreFile(); DropPrivileges(); /* Initialise variables */ Log.File = g_strdup(""); Log.Level = 2; Log.Timestamp = g_strdup("[%H:%M:%S] "); srand((unsigned)time(NULL)); Noone.Name = g_strdup("Noone"); Server = Client = Network = FALSE; return ParseCmdLine(argc, argv); } void InitConfiguration(struct CMDLINE *cmdline) { ConfigErrors = 0; SetupParameters(cmdline->configs, cmdline->antique); if (cmdline->scorefile) { AssignName(&HiScoreFile, cmdline->scorefile); } if (cmdline->servername) { AssignName(&ServerName, cmdline->servername); } if (cmdline->playername) { AssignName(&PlayerName, cmdline->playername); } if (cmdline->pidfile) { AssignName(&PidFile, cmdline->pidfile); } if (cmdline->logfile) { AssignName(&Log.File, cmdline->logfile); OpenLog(); } if (cmdline->setport) { Port = cmdline->port; } #ifdef NETWORKING if (cmdline->server) { MetaServer.Active = cmdline->notifymeta; } #endif WantAntique = cmdline->antique; if (!cmdline->version && !cmdline->help && !cmdline->ai && !cmdline->convert && !cmdline->admin) { /* Open a user-specified high score file with no privileges, if one * was given */ if (strcmp(priv_hiscore, HiScoreFile) != 0) { CloseHighScoreFile(); OpenHighScoreFile(); } } else { CloseHighScoreFile(); } } /* * Removes any ^ or \n characters from the given string, which is * modified in place. */ void StripTerminators(gchar *str) { guint i; if (str) { for (i = 0; i < strlen(str); i++) { switch(str[i]) { case '^': case '\n': str[i] = '~'; break; default: break; } } } } #ifndef CYGWIN #if NETWORKING && !GUI_SERVER static void ServerLogMessage(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) { GString *text; text = GetLogString(log_level, message); if (text) { fprintf(Log.fp ? Log.fp : stdout, "%s\n", text->str); g_string_free(text, TRUE); } } #endif #ifndef CURSES_CLIENT /* * Stub function to report an error if the Curses client is requested and * it isn't compiled in. */ void CursesLoop(struct CMDLINE *cmdline) { g_print(_("No curses client available - rebuild the binary passing the\n" "--enable-curses-client option to configure, or use a windowed\n" "client (if available) instead!\n")); } #endif #ifndef GUI_CLIENT /* * Stub function to report an error if the GTK+ client is requested and * it isn't compiled in. */ #ifdef CYGWIN gboolean GtkLoop(HINSTANCE hInstance, HINSTANCE hPrevInstance, struct CMDLINE *cmdline, gboolean ReturnOnFail) #else gboolean GtkLoop(int *argc, char **argv[], struct CMDLINE *cmdline, gboolean ReturnOnFail) #endif { if (!ReturnOnFail) { g_print(_("No graphical client available - rebuild the binary\n" "passing the --enable-gui-client option to configure, or\n" "use the curses client (if available) instead!\n")); } return FALSE; } #endif static void DefaultLogMessage(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) { GString *text; text = GetLogString(log_level, message); if (text) { g_print("%s\n", text->str); g_string_free(text, TRUE); } } /* * Standard program entry - Win32 uses WinMain() instead, in winmain.c */ int main(int argc, char *argv[]) { struct CMDLINE *cmdline; #ifdef ENABLE_NLS setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); #endif WantUTF8Errors(FALSE); g_log_set_handler(NULL, LogMask(), DefaultLogMessage, NULL); cmdline = GeneralStartup(argc, argv); if (cmdline->logfile) { AssignName(&Log.File, cmdline->logfile); } OpenLog(); SoundInit(); if (cmdline->version || cmdline->help) { HandleHelpTexts(cmdline->help); } else if (cmdline->admin) { #ifdef NETWORKING AdminServer(cmdline); #else g_print(_("This binary has been compiled without networking " "support, and thus cannot run\nin admin mode. " "Recompile passing --enable-networking to the " "configure script.\n")); #endif } else if (cmdline->convert) { ConvertHighScoreFile(cmdline->convertfile); } else { InitNetwork(); if (cmdline->server) { #ifdef NETWORKING #ifdef GUI_SERVER Server = TRUE; gtk_set_locale(); gtk_init(&argc, &argv); GuiServerLoop(cmdline, FALSE); #else g_log_set_handler(NULL, LogMask(), ServerLogMessage, NULL); ServerLoop(cmdline); #endif /* GUI_SERVER */ #else g_print(_("This binary has been compiled without networking " "support, and thus cannot run\nin server mode. " "Recompile passing --enable-networking to the " "configure script.\n")); #endif /* NETWORKING */ } else if (cmdline->ai) { AIPlayerLoop(cmdline); } else switch (cmdline->client) { case CLIENT_AUTO: if (!GtkLoop(&argc, &argv, cmdline, TRUE)) CursesLoop(cmdline); break; case CLIENT_WINDOW: GtkLoop(&argc, &argv, cmdline, FALSE); break; case CLIENT_CURSES: CursesLoop(cmdline); break; } #ifdef NETWORKING StopNetworking(); #endif } FreeCmdLine(cmdline); CloseLog(); CloseHighScoreFile(); g_free(PidFile); g_free(Log.File); SoundClose(); return 0; } #endif /* CYGWIN */