summaryrefslogtreecommitdiff
path: root/openttd.c
diff options
context:
space:
mode:
Diffstat (limited to 'openttd.c')
-rw-r--r--openttd.c1415
1 files changed, 1415 insertions, 0 deletions
diff --git a/openttd.c b/openttd.c
new file mode 100644
index 000000000..619c964f8
--- /dev/null
+++ b/openttd.c
@@ -0,0 +1,1415 @@
+#include "stdafx.h"
+#include "string.h"
+#include "table/strings.h"
+#include "debug.h"
+#include "strings.h"
+#include "map.h"
+#include "tile.h"
+
+#define VARDEF
+#include "openttd.h"
+#include "mixer.h"
+#include "spritecache.h"
+#include "gfx.h"
+#include "gui.h"
+#include "station.h"
+#include "vehicle.h"
+#include "viewport.h"
+#include "window.h"
+#include "player.h"
+#include "command.h"
+#include "town.h"
+#include "industry.h"
+#include "news.h"
+#include "engine.h"
+#include "sound.h"
+#include "economy.h"
+#include "fileio.h"
+#include "hal.h"
+#include "airport.h"
+#include "ai.h"
+#include "console.h"
+#include "screenshot.h"
+#include "network.h"
+#include "signs.h"
+#include "depot.h"
+#include "waypoint.h"
+
+#include <stdarg.h>
+
+void GenerateWorld(int mode, uint log_x, uint log_y);
+void CallLandscapeTick(void);
+void IncreaseDate(void);
+void RunOtherPlayersLoop(void);
+void DoPaletteAnimations(void);
+void MusicLoop(void);
+void ResetMusic(void);
+void InitializeStations(void);
+void DeleteAllPlayerStations(void);
+
+extern void SetDifficultyLevel(int mode, GameOptions *gm_opt);
+extern void DoStartupNewPlayer(bool is_ai);
+extern void ShowOSErrorBox(const char *buf);
+
+bool LoadSavegame(const char *filename);
+
+extern void HalGameLoop(void);
+
+uint32 _pixels_redrawn;
+bool _dbg_screen_rect;
+bool disable_computer; // We should get ride of this thing.. is only used for a debug-cheat
+static byte _os_version = 0;
+
+/* TODO: usrerror() for errors which are not of an internal nature but
+ * caused by the user, i.e. missing files or fatal configuration errors.
+ * Post-0.4.0 since Celestar doesn't want this in SVN before. --pasky */
+
+void CDECL error(const char *s, ...) {
+ va_list va;
+ char buf[512];
+ va_start(va, s);
+ vsprintf(buf, s, va);
+ va_end(va);
+
+ ShowOSErrorBox(buf);
+ if (_video_driver)
+ _video_driver->stop();
+
+ assert(0);
+ exit(1);
+}
+
+void CDECL ShowInfoF(const char *str, ...)
+{
+ va_list va;
+ char buf[1024];
+ va_start(va, str);
+ vsprintf(buf, str, va);
+ va_end(va);
+ ShowInfo(buf);
+}
+
+char * CDECL str_fmt(const char *str, ...)
+{
+ char buf[4096];
+ va_list va;
+ int len;
+ char *p;
+
+ va_start(va, str);
+ len = vsprintf(buf, str, va);
+ va_end(va);
+ p = malloc(len + 1);
+ if (p)
+ memcpy(p, buf, len + 1);
+ return p;
+}
+
+
+// NULL midi driver
+static const char *NullMidiStart(const char * const *parm) { return NULL; }
+static void NullMidiStop(void) {}
+static void NullMidiPlaySong(const char *filename) {}
+static void NullMidiStopSong(void) {}
+static bool NullMidiIsSongPlaying(void) { return true; }
+static void NullMidiSetVolume(byte vol) {}
+
+const HalMusicDriver _null_music_driver = {
+ NullMidiStart,
+ NullMidiStop,
+ NullMidiPlaySong,
+ NullMidiStopSong,
+ NullMidiIsSongPlaying,
+ NullMidiSetVolume,
+};
+
+// NULL video driver
+static void *_null_video_mem;
+static const char *NullVideoStart(const char * const *parm)
+{
+ _screen.width = _screen.pitch = _cur_resolution[0];
+ _screen.height = _cur_resolution[1];
+ _null_video_mem = malloc(_cur_resolution[0]*_cur_resolution[1]);
+ return NULL;
+}
+static void NullVideoStop(void) { free(_null_video_mem); }
+static void NullVideoMakeDirty(int left, int top, int width, int height) {}
+static int NullVideoMainLoop(void)
+{
+ int i = 1000;
+ do {
+ GameLoop();
+ _screen.dst_ptr = _null_video_mem;
+ UpdateWindows();
+ } while (--i);
+ return ML_QUIT;
+}
+
+static bool NullVideoChangeRes(int w, int h) { return false; }
+static void NullVideoFullScreen(bool fs) {}
+
+const HalVideoDriver _null_video_driver = {
+ NullVideoStart,
+ NullVideoStop,
+ NullVideoMakeDirty,
+ NullVideoMainLoop,
+ NullVideoChangeRes,
+ NullVideoFullScreen,
+};
+
+// NULL sound driver
+static const char *NullSoundStart(const char * const *parm) { return NULL; }
+static void NullSoundStop(void) {}
+const HalSoundDriver _null_sound_driver = {
+ NullSoundStart,
+ NullSoundStop,
+};
+
+enum {
+ DF_PRIORITY_MASK = 0xf,
+};
+
+typedef struct {
+ const DriverDesc *descs;
+ const char *name;
+ void *var;
+} DriverClass;
+
+static DriverClass _driver_classes[] = {
+ {_video_driver_descs, "video", &_video_driver},
+ {_sound_driver_descs, "sound", &_sound_driver},
+ {_music_driver_descs, "music", &_music_driver},
+};
+
+static const DriverDesc *GetDriverByName(const DriverDesc *dd, const char *name)
+{
+ do {
+ if (!strcmp(dd->name, name))
+ return dd;
+ } while ((++dd)->name);
+ return NULL;
+}
+
+static const DriverDesc *ChooseDefaultDriver(const DriverDesc *dd)
+{
+ const DriverDesc *best = NULL;
+ int best_pri = -1;
+ do {
+ if ((int)(dd->flags&DF_PRIORITY_MASK) > best_pri && _os_version >= (byte)dd->flags) {
+ best_pri = dd->flags&DF_PRIORITY_MASK;
+ best = dd;
+ }
+ } while ((++dd)->name);
+ return best;
+}
+
+
+void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize)
+{
+ FILE *in;
+ byte *mem;
+ size_t len;
+
+ in = fopen(filename, "rb");
+ if (in == NULL)
+ return NULL;
+
+ fseek(in, 0, SEEK_END);
+ len = ftell(in);
+ fseek(in, 0, SEEK_SET);
+ if (len > maxsize || (mem = malloc(len + 1)) == NULL) {
+ fclose(in);
+ return NULL;
+ }
+ mem[len] = 0;
+ if (fread(mem, len, 1, in) != 1) {
+ fclose(in);
+ free(mem);
+ return NULL;
+ }
+ fclose(in);
+
+ *lenp = len;
+ return mem;
+}
+
+void LoadDriver(int driver, const char *name)
+{
+ const DriverClass *dc = &_driver_classes[driver];
+ const DriverDesc *dd;
+ const void **var;
+ const void *drv;
+ const char *err;
+ char *parm;
+ char buffer[256];
+ const char *parms[32];
+
+ parms[0] = NULL;
+
+ if (!*name) {
+ dd = ChooseDefaultDriver(dc->descs);
+ } else {
+ // Extract the driver name and put parameter list in parm
+ ttd_strlcpy(buffer, name, sizeof(buffer));
+ parm = strchr(buffer, ':');
+ if (parm) {
+ uint np = 0;
+ // Tokenize the parm.
+ do {
+ *parm++ = 0;
+ if (np < lengthof(parms) - 1)
+ parms[np++] = parm;
+ while (*parm != 0 && *parm != ',')
+ parm++;
+ } while (*parm == ',');
+ parms[np] = NULL;
+ }
+ dd = GetDriverByName(dc->descs, buffer);
+ if (dd == NULL)
+ error("No such %s driver: %s\n", dc->name, buffer);
+ }
+ var = dc->var;
+ if (*var != NULL) ((const HalCommonDriver*)*var)->stop();
+ *var = NULL;
+ drv = dd->drv;
+ if ((err=((const HalCommonDriver*)drv)->start(parms)) != NULL)
+ error("Unable to load driver %s(%s). The error was: %s\n", dd->name, dd->longname, err);
+ *var = drv;
+}
+
+static void showhelp(void)
+{
+ char buf[4096], *p;
+ const DriverClass *dc = _driver_classes;
+ const DriverDesc *dd;
+ int i;
+
+ p = strecpy(buf,
+ "Command line options:\n"
+ " -v drv = Set video driver (see below)\n"
+ " -s drv = Set sound driver (see below)\n"
+ " -m drv = Set music driver (see below)\n"
+ " -r res = Set resolution (for instance 800x600)\n"
+ " -h = Display this help text\n"
+ " -t date = Set starting date\n"
+ " -d [[fac=]lvl[,...]]= Debug mode\n"
+ " -l lng = Select Language\n"
+ " -e = Start Editor\n"
+ " -g [savegame] = Start new/save game immediately\n"
+ " -G seed = Set random seed\n"
+ " -n [ip#player:port] = Start networkgame\n"
+ " -D = Start dedicated server\n"
+ #if !defined(__MORPHOS__) && !defined(__AMIGA__)
+ " -f = Fork into the background (dedicated only)\n"
+ #endif
+ " -i = Force to use the DOS palette (use this if you see a lot of pink)\n"
+ " -p #player = Player as #player (deprecated) (network only)\n"
+ " -c config_file = Use 'config_file' instead of 'openttd.cfg'\n",
+ lastof(buf)
+ );
+
+ for(i=0; i!=lengthof(_driver_classes); i++,dc++) {
+ p += sprintf(p, "List of %s drivers:\n", dc->name);
+ dd = dc->descs;
+ do {
+ p += sprintf(p, "%10s: %s\n", dd->name, dd->longname);
+ } while ((++dd)->name);
+ }
+
+ ShowInfo(buf);
+}
+
+
+const char *GetDriverParam(const char * const *parm, const char *name)
+{
+ const char *p;
+ int len = strlen(name);
+ while ((p = *parm++) != NULL) {
+ if (!strncmp(p,name,len)) {
+ if (p[len] == '=') return p + len + 1;
+ if (p[len] == 0) return p + len;
+ }
+ }
+ return NULL;
+}
+
+bool GetDriverParamBool(const char * const *parm, const char *name)
+{
+ const char *p = GetDriverParam(parm, name);
+ return p != NULL;
+}
+
+int GetDriverParamInt(const char * const *parm, const char *name, int def)
+{
+ const char *p = GetDriverParam(parm, name);
+ return p != NULL ? atoi(p) : def;
+}
+
+typedef struct {
+ char *opt;
+ int numleft;
+ char **argv;
+ const char *options;
+ char *cont;
+} MyGetOptData;
+
+static void MyGetOptInit(MyGetOptData *md, int argc, char **argv, const char *options)
+{
+ md->cont = NULL;
+ md->numleft = argc;
+ md->argv = argv;
+ md->options = options;
+}
+
+static int MyGetOpt(MyGetOptData *md)
+{
+ char *s,*r,*t;
+
+ if ((s=md->cont) != NULL)
+ goto md_continue_here;
+
+ while(true) {
+ if (--md->numleft < 0)
+ return -1;
+
+ s = *md->argv++;
+ if (*s == '-') {
+md_continue_here:;
+ s++;
+ if (*s != 0) {
+ // Found argument, try to locate it in options.
+ if (*s == ':' || (r = strchr(md->options, *s)) == NULL) {
+ // ERROR!
+ return -2;
+ }
+ if (r[1] == ':') {
+ // Item wants an argument. Check if the argument follows, or if it comes as a separate arg.
+ if (!*(t = s + 1)) {
+ // It comes as a separate arg. Check if out of args?
+ if (--md->numleft < 0 || *(t = *md->argv) == '-') {
+ // Check if item is optional?
+ if (r[2] != ':')
+ return -2;
+ md->numleft++;
+ t = NULL;
+ } else {
+ md->argv++;
+ }
+ }
+ md->opt = t;
+ md->cont = NULL;
+ return *s;
+ }
+ md->opt = NULL;
+ md->cont = s;
+ return *s;
+ }
+ } else {
+ // This is currently not supported.
+ return -2;
+ }
+ }
+}
+
+
+static void ParseResolution(int res[2], char *s)
+{
+ char *t = strchr(s, 'x');
+ if (t == NULL) {
+ ShowInfoF("Invalid resolution '%s'", s);
+ return;
+ }
+
+ res[0] = clamp(strtoul(s, NULL, 0), 64, MAX_SCREEN_WIDTH);
+ res[1] = clamp(strtoul(t + 1, NULL, 0), 64, MAX_SCREEN_HEIGHT);
+}
+
+static void InitializeDynamicVariables(void)
+{
+ /* Dynamic stuff needs to be initialized somewhere... */
+ _station_sort = NULL;
+ _vehicle_sort = NULL;
+ _town_sort = NULL;
+ _industry_sort = NULL;
+}
+
+static void UnInitializeDynamicVariables(void)
+{
+ /* Dynamic stuff needs to be free'd somewhere... */
+ CleanPool(&_town_pool);
+ CleanPool(&_industry_pool);
+ CleanPool(&_station_pool);
+ CleanPool(&_vehicle_pool);
+ CleanPool(&_sign_pool);
+ CleanPool(&_order_pool);
+
+ free(_station_sort);
+ free(_vehicle_sort);
+ free(_town_sort);
+ free(_industry_sort);
+}
+
+static void UnInitializeGame(void)
+{
+ UnInitWindowSystem();
+ UnInitNewgrEngines();
+
+ free(_config_file);
+}
+
+static void LoadIntroGame(void)
+{
+ char filename[256];
+
+ _game_mode = GM_MENU;
+ CLRBITS(_display_opt, DO_TRANS_BUILDINGS); // don't make buildings transparent in intro
+ _opt_ptr = &_opt_newgame;
+
+ GfxLoadSprites();
+ LoadStringWidthTable();
+
+ // Setup main window
+ ResetWindowSystem();
+ SetupColorsAndInitialWindow();
+
+ // Generate a world.
+ sprintf(filename, "%sopntitle.dat", _path.data_dir);
+ if (SaveOrLoad(filename, SL_LOAD) != SL_OK) {
+#if defined SECOND_DATA_DIR
+ sprintf(filename, "%sopntitle.dat", _path.second_data_dir);
+ if (SaveOrLoad(filename, SL_LOAD) != SL_OK)
+#endif
+ GenerateWorld(1, 6, 6); // if failed loading, make empty world.
+ }
+
+ _pause = 0;
+ _local_player = 0;
+ MarkWholeScreenDirty();
+
+ // Play main theme
+ if (_music_driver->is_song_playing()) ResetMusic();
+}
+
+extern void DedicatedFork(void);
+extern void CheckExternalFiles(void);
+
+int ttd_main(int argc, char* argv[])
+{
+ MyGetOptData mgo;
+ int i;
+ bool network = false;
+ char *network_conn = NULL;
+ char *language = NULL;
+ const char *optformat;
+ char musicdriver[16], sounddriver[16], videodriver[16];
+ int resolution[2] = {0,0};
+ uint startdate = -1;
+ bool dedicated;
+
+ musicdriver[0] = sounddriver[0] = videodriver[0] = 0;
+
+ _game_mode = GM_MENU;
+ _switch_mode = SM_MENU;
+ _switch_mode_errorstr = INVALID_STRING_ID;
+ _dedicated_forks = false;
+ dedicated = false;
+ _config_file = NULL;
+
+ // The last param of the following function means this:
+ // a letter means: it accepts that param (e.g.: -h)
+ // a ':' behind it means: it need a param (e.g.: -m<driver>)
+ // a '::' behind it means: it can optional have a param (e.g.: -d<debug>)
+ #if !defined(__MORPHOS__) && !defined(__AMIGA__) && !defined(WIN32)
+ optformat = "m:s:v:hDfn::l:eit:d::r:g::G:p:c:";
+ #else
+ optformat = "m:s:v:hDn::l:eit:d::r:g::G:p:c:"; // no fork option
+ #endif
+
+ MyGetOptInit(&mgo, argc-1, argv+1, optformat);
+ while ((i = MyGetOpt(&mgo)) != -1) {
+ switch(i) {
+ case 'm': ttd_strlcpy(musicdriver, mgo.opt, sizeof(musicdriver)); break;
+ case 's': ttd_strlcpy(sounddriver, mgo.opt, sizeof(sounddriver)); break;
+ case 'v': ttd_strlcpy(videodriver, mgo.opt, sizeof(videodriver)); break;
+ case 'D': {
+ sprintf(musicdriver,"null");
+ sprintf(sounddriver,"null");
+ sprintf(videodriver,"dedicated");
+ dedicated = true;
+ } break;
+ case 'f': {
+ _dedicated_forks = true;
+ }; break;
+ case 'n': {
+ network = true;
+ if (mgo.opt)
+ // Optional, you can give an IP
+ network_conn = mgo.opt;
+ else
+ network_conn = NULL;
+ } break;
+ case 'r': ParseResolution(resolution, mgo.opt); break;
+ case 'l': {
+ language = mgo.opt;
+ } break;
+ case 't': {
+ startdate = atoi(mgo.opt);
+ } break;
+ case 'd': {
+#if defined(WIN32)
+ CreateConsole();
+#endif
+ if (mgo.opt)
+ SetDebugString(mgo.opt);
+ } break;
+ case 'e': _switch_mode = SM_EDITOR; break;
+ case 'i': _use_dos_palette = true; break;
+ case 'g':
+ if (mgo.opt) {
+ strcpy(_file_to_saveload.name, mgo.opt);
+ _switch_mode = SM_LOAD;
+ } else
+ _switch_mode = SM_NEWGAME;
+ break;
+ case 'G':
+ _random_seeds[0][0] = atoi(mgo.opt);
+ break;
+ case 'p': {
+ int i = atoi(mgo.opt);
+ // Play as an other player in network games
+ if (IS_INT_INSIDE(i, 1, MAX_PLAYERS)) _network_playas = i;
+ break;
+ }
+ case 'c':
+ _config_file = strdup(mgo.opt);
+ break;
+ case -2:
+ case 'h':
+ showhelp();
+ return 0;
+ }
+ }
+
+ DeterminePaths();
+ CheckExternalFiles();
+
+#ifdef UNIX
+ // We must fork here, or we'll end up without some resources we need (like sockets)
+ if (_dedicated_forks)
+ DedicatedFork();
+#endif
+
+ LoadFromConfig();
+ CheckConfig();
+ LoadFromHighScore();
+
+ // override config?
+ if (musicdriver[0]) ttd_strlcpy(_ini_musicdriver, musicdriver, sizeof(_ini_musicdriver));
+ if (sounddriver[0]) ttd_strlcpy(_ini_sounddriver, sounddriver, sizeof(_ini_sounddriver));
+ if (videodriver[0]) ttd_strlcpy(_ini_videodriver, videodriver, sizeof(_ini_videodriver));
+ if (resolution[0]) { _cur_resolution[0] = resolution[0]; _cur_resolution[1] = resolution[1]; }
+ if (startdate != (uint)-1) _patches.starting_date = startdate;
+
+ if (_dedicated_forks && !dedicated)
+ _dedicated_forks = false;
+
+ // enumerate language files
+ InitializeLanguagePacks();
+
+ // initialize screenshot formats
+ InitializeScreenshotFormats();
+
+ // initialize airport state machines
+ InitializeAirports();
+
+ /* initialize all variables that are allocated dynamically */
+ InitializeDynamicVariables();
+
+ // Sample catalogue
+ DEBUG(misc, 1) ("Loading sound effects...");
+ _os_version = GetOSVersion();
+ MxInitialize(11025);
+ SoundInitialize("sample.cat");
+
+ // This must be done early, since functions use the InvalidateWindow* calls
+ InitWindowSystem();
+
+ GfxLoadSprites();
+ LoadStringWidthTable();
+
+ DEBUG(misc, 1) ("Loading drivers...");
+ LoadDriver(SOUND_DRIVER, _ini_sounddriver);
+ LoadDriver(MUSIC_DRIVER, _ini_musicdriver);
+ LoadDriver(VIDEO_DRIVER, _ini_videodriver); // load video last, to prevent an empty window while sound and music loads
+ _savegame_sort_order = 1; // default sorting of savegames is by date, newest first
+
+#ifdef ENABLE_NETWORK
+ // initialize network-core
+ NetworkStartUp();
+#endif /* ENABLE_NETWORK */
+
+ _opt_ptr = &_opt_newgame;
+
+ /* XXX - ugly hack, if diff_level is 9, it means we got no setting from the config file */
+ if (_opt_newgame.diff_level == 9)
+ SetDifficultyLevel(0, &_opt_newgame);
+
+ // initialize the ingame console
+ IConsoleInit();
+ InitializeGUI();
+ IConsoleCmdExec("exec scripts/autoexec.scr 0");
+
+ InitPlayerRandoms();
+
+ GenerateWorld(1, 6, 6); // Make the viewport initialization happy
+
+#ifdef ENABLE_NETWORK
+ if ((network) && (_network_available)) {
+ if (network_conn != NULL) {
+ const char *port = NULL;
+ const char *player = NULL;
+ uint16 rport;
+
+ rport = NETWORK_DEFAULT_PORT;
+
+ ParseConnectionString(&player, &port, network_conn);
+
+ if (player != NULL) _network_playas = atoi(player);
+ if (port != NULL) rport = atoi(port);
+
+ LoadIntroGame();
+ _switch_mode = SM_NONE;
+ NetworkClientConnectGame(network_conn, rport);
+ }
+ }
+#endif /* ENABLE_NETWORK */
+
+ while (_video_driver->main_loop() == ML_SWITCHDRIVER) {}
+
+ JoinOTTDThread();
+ IConsoleFree();
+
+#ifdef ENABLE_NETWORK
+ if (_network_available) {
+ // Shut down the network and close any open connections
+ NetworkDisconnect();
+ NetworkUDPClose();
+ NetworkShutDown();
+ }
+#endif /* ENABLE_NETWORK */
+
+ _video_driver->stop();
+ _music_driver->stop();
+ _sound_driver->stop();
+
+ SaveToConfig();
+ SaveToHighScore();
+
+ // uninitialize airport state machines
+ UnInitializeAirports();
+
+ /* uninitialize variables that are allocated dynamic */
+ UnInitializeDynamicVariables();
+
+ /* Close all and any open filehandles */
+ FioCloseAll();
+ UnInitializeGame();
+
+ return 0;
+}
+
+static void ShowScreenshotResult(bool b)
+{
+ if (b) {
+ SetDParam(0, STR_SPEC_SCREENSHOT_NAME);
+ ShowErrorMessage(INVALID_STRING_ID, STR_031B_SCREENSHOT_SUCCESSFULLY, 0, 0);
+ } else {
+ ShowErrorMessage(INVALID_STRING_ID, STR_031C_SCREENSHOT_FAILED, 0, 0);
+ }
+
+}
+
+static void MakeNewGame(void)
+{
+ _game_mode = GM_NORMAL;
+
+ // Copy in game options
+ _opt_ptr = &_opt;
+ memcpy(_opt_ptr, &_opt_newgame, sizeof(GameOptions));
+
+ GfxLoadSprites();
+
+ // Reinitialize windows
+ ResetWindowSystem();
+ LoadStringWidthTable();
+
+ SetupColorsAndInitialWindow();
+
+ // Randomize world
+ GenerateWorld(0, _patches.map_x, _patches.map_y);
+
+ // In a dedicated server, the server does not play
+ if (_network_dedicated) {
+ _local_player = OWNER_SPECTATOR;
+ } else {
+ // Create a single player
+ DoStartupNewPlayer(false);
+
+ _local_player = 0;
+ }
+
+ MarkWholeScreenDirty();
+}
+
+static void MakeNewEditorWorld(void)
+{
+ _game_mode = GM_EDITOR;
+
+ // Copy in game options
+ _opt_ptr = &_opt;
+ memcpy(_opt_ptr, &_opt_newgame, sizeof(GameOptions));
+
+ GfxLoadSprites();
+
+ // Re-init the windowing system
+ ResetWindowSystem();
+
+ // Create toolbars
+ SetupColorsAndInitialWindow();
+
+ // Startup the game system
+ GenerateWorld(1, _patches.map_x, _patches.map_y);
+
+ _local_player = OWNER_NONE;
+ MarkWholeScreenDirty();
+}
+
+void StartupPlayers(void);
+void StartupDisasters(void);
+
+/**
+ * Start Scenario starts a new game based on a scenario.
+ * Eg 'New Game' --> select a preset scenario
+ * This starts a scenario based on your current difficulty settings
+ */
+static void StartScenario(void)
+{
+ _game_mode = GM_NORMAL;
+
+ // invalid type
+ if (_file_to_saveload.mode == SL_INVALID) {
+ printf("Savegame is obsolete or invalid format: %s\n", _file_to_saveload.name);
+ ShowErrorMessage(_error_message, STR_4009_GAME_LOAD_FAILED, 0, 0);
+ _game_mode = GM_MENU;
+ return;
+ }
+
+ GfxLoadSprites();
+
+ // Reinitialize windows
+ ResetWindowSystem();
+ LoadStringWidthTable();
+
+ SetupColorsAndInitialWindow();
+
+ // Load game
+ if (SaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode) != SL_OK) {
+ LoadIntroGame();
+ ShowErrorMessage(_error_message, STR_4009_GAME_LOAD_FAILED, 0, 0);
+ }
+
+ _opt_ptr = &_opt;
+ memcpy(&_opt_ptr->diff, &_opt_newgame.diff, sizeof(GameDifficulty));
+ _opt.diff_level = _opt_newgame.diff_level;
+
+ // Inititalize data
+ StartupPlayers();
+ StartupEngines();
+ StartupDisasters();
+
+ _local_player = 0;
+
+ MarkWholeScreenDirty();
+}
+
+bool SafeSaveOrLoad(const char *filename, int mode, int newgm)
+{
+ byte ogm = _game_mode;
+ int r;
+
+ _game_mode = newgm;
+ r = SaveOrLoad(filename, mode);
+ if (r == SL_REINIT) {
+ if (ogm == GM_MENU)
+ LoadIntroGame();
+ else if (ogm == GM_EDITOR)
+ MakeNewEditorWorld();
+ else
+ MakeNewGame();
+ return false;
+ } else if (r != SL_OK) {
+ _game_mode = ogm;
+ return false;
+ } else
+ return true;
+}
+
+void SwitchMode(int new_mode)
+{
+ _in_state_game_loop = true;
+
+#ifdef ENABLE_NETWORK
+ // If we are saving something, the network stays in his current state
+ if (new_mode != SM_SAVE) {
+ // If the network is active, make it not-active
+ if (_networking) {
+ if (_network_server && (new_mode == SM_LOAD || new_mode == SM_NEWGAME)) {
+ NetworkReboot();
+ NetworkUDPClose();
+ } else {
+ NetworkDisconnect();
+ NetworkUDPClose();
+ }
+ }
+
+ // If we are a server, we restart the server
+ if (_is_network_server) {
+ // But not if we are going to the menu
+ if (new_mode != SM_MENU) {
+ NetworkServerStart();
+ } else {
+ // This client no longer wants to be a network-server
+ _is_network_server = false;
+ }
+ }
+ }
+#endif /* ENABLE_NETWORK */
+
+ switch (new_mode) {
+ case SM_EDITOR: /* Switch to scenario editor */
+ MakeNewEditorWorld();
+ break;
+
+ case SM_NEWGAME: /* New Game --> 'Random game' */
+#ifdef ENABLE_NETWORK
+ if (_network_server)
+ snprintf(_network_game_info.map_name, 40, "Random");
+#endif /* ENABLE_NETWORK */
+ MakeNewGame();
+ break;
+
+ case SM_START_SCENARIO: /* New Game --> Choose one of the preset scenarios */
+ StartScenario();
+ break;
+
+ case SM_LOAD: { /* Load game, Play Scenario */
+ _opt_ptr = &_opt;
+
+ _error_message = INVALID_STRING_ID;
+ if (!SafeSaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_NORMAL)) {
+ LoadIntroGame();
+ ShowErrorMessage(_error_message, STR_4009_GAME_LOAD_FAILED, 0, 0);
+ } else {
+ _local_player = 0;
+ DoCommandP(0, 0, 0, NULL, CMD_PAUSE); // decrease pause counter (was increased from opening load dialog)
+#ifdef ENABLE_NETWORK
+ if (_network_server)
+ snprintf(_network_game_info.map_name, 40, "Loaded game");
+#endif /* ENABLE_NETWORK */
+ }
+ break;
+ }
+
+ case SM_LOAD_SCENARIO: { /* Load scenario from scenario editor */
+ int i;
+
+ if (SafeSaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_EDITOR)) {
+ _opt_ptr = &_opt;
+
+ _local_player = OWNER_NONE;
+ _generating_world = true;
+ // delete all players.
+ for (i = 0; i != MAX_PLAYERS; i++) {
+ ChangeOwnershipOfPlayerItems(i, 0xff);
+ _players[i].is_active = false;
+ }
+ _generating_world = false;
+ // delete all stations owned by a player
+ DeleteAllPlayerStations();
+
+#ifdef ENABLE_NETWORK
+ if (_network_server)
+ snprintf(_network_game_info.map_name, 40, "Loaded scenario");
+#endif /* ENABLE_NETWORK */
+ } else
+ ShowErrorMessage(INVALID_STRING_ID, STR_4009_GAME_LOAD_FAILED, 0, 0);
+
+ break;
+ }
+
+
+ case SM_MENU: /* Switch to game intro menu */
+ LoadIntroGame();
+ break;
+
+ case SM_SAVE: /* Save game */
+ if (SaveOrLoad(_file_to_saveload.name, SL_SAVE) != SL_OK)
+ ShowErrorMessage(INVALID_STRING_ID, STR_4007_GAME_SAVE_FAILED, 0, 0);
+ else
+ DeleteWindowById(WC_SAVELOAD, 0);
+ break;
+
+ case SM_GENRANDLAND: /* Generate random land within scenario editor */
+ GenerateWorld(2, _patches.map_x, _patches.map_y);
+ // XXX: set date
+ _local_player = OWNER_NONE;
+ MarkWholeScreenDirty();
+ break;
+ }
+
+ if (_switch_mode_errorstr != INVALID_STRING_ID)
+ ShowErrorMessage(INVALID_STRING_ID,_switch_mode_errorstr,0,0);
+
+ _in_state_game_loop = false;
+}
+
+
+// State controlling game loop.
+// The state must not be changed from anywhere
+// but here.
+// That check is enforced in DoCommand.
+void StateGameLoop(void)
+{
+ // dont execute the state loop during pause
+ if (_pause) return;
+
+ _in_state_game_loop = true;
+ // _frame_counter is increased somewhere else when in network-mode
+ // Sidenote: _frame_counter is ONLY used for _savedump in non-MP-games
+ // Should that not be deleted? If so, the next 2 lines can also be deleted
+ if (!_networking)
+ _frame_counter++;
+
+ if (_savedump_path[0] && (uint)_frame_counter >= _savedump_first && (uint)(_frame_counter -_savedump_first) % _savedump_freq == 0 ) {
+ char buf[100];
+ sprintf(buf, "%s%.5d.sav", _savedump_path, _frame_counter);
+ SaveOrLoad(buf, SL_SAVE);
+ if ((uint)_frame_counter >= _savedump_last) exit(1);
+ }
+
+ if (_game_mode == GM_EDITOR) {
+ RunTileLoop();
+ CallVehicleTicks();
+ CallLandscapeTick();
+ CallWindowTickEvent();
+ NewsLoop();
+ } else {
+ // All these actions has to be done from OWNER_NONE
+ // for multiplayer compatibility
+ uint p = _current_player;
+ _current_player = OWNER_NONE;
+
+ AnimateAnimatedTiles();
+ IncreaseDate();
+ RunTileLoop();
+ CallVehicleTicks();
+ CallLandscapeTick();
+
+ // To bad the AI does not work in multiplayer, because states are not saved
+ // perfectly
+ if (!disable_computer && !_networking)
+ RunOtherPlayersLoop();
+
+ CallWindowTickEvent();
+ NewsLoop();
+ _current_player = p;
+ }
+
+ _in_state_game_loop = false;
+}
+
+static void DoAutosave(void)
+{
+ char buf[200];
+
+ if (_patches.keep_all_autosave && _local_player != OWNER_SPECTATOR) {
+ const Player *p = DEREF_PLAYER(_local_player);
+ char *s;
+ sprintf(buf, "%s%s", _path.autosave_dir, PATHSEP);
+
+ SetDParam(0, p->name_1);
+ SetDParam(1, p->name_2);
+ SetDParam(2, _date);
+ s = (char*)GetString(buf + strlen(_path.autosave_dir) + strlen(PATHSEP), STR_4004);
+ strcpy(s, ".sav");
+ } else { /* Save a maximum of 15 autosaves */
+ int n = _autosave_ctr;
+ _autosave_ctr = (_autosave_ctr + 1) & 15;
+ sprintf(buf, "%s%sautosave%d.sav", _path.autosave_dir, PATHSEP, n);
+ }
+
+ DEBUG(misc, 2) ("Autosaving to %s", buf);
+ if (SaveOrLoad(buf, SL_SAVE) != SL_OK)
+ ShowErrorMessage(INVALID_STRING_ID, STR_AUTOSAVE_FAILED, 0, 0);
+}
+
+static void ScrollMainViewport(int x, int y)
+{
+ if (_game_mode != GM_MENU) {
+ Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
+ assert(w);
+
+ WP(w,vp_d).scrollpos_x += x << w->viewport->zoom;
+ WP(w,vp_d).scrollpos_y += y << w->viewport->zoom;
+ }
+}
+
+static const int8 scrollamt[16][2] = {
+ { 0, 0},
+ {-2, 0}, // 1:left
+ { 0,-2}, // 2:up
+ {-2,-1}, // 3:left + up
+ { 2, 0}, // 4:right
+ { 0, 0}, // 5:left + right
+ { 2,-1}, // 6:right + up
+ { 0,-2}, // 7:left + right + up = up
+ { 0 ,2}, // 8:down
+ {-2 ,1}, // 9:down+left
+ { 0, 0}, // 10:impossible
+ {-2, 0}, // 11:left + up + down = left
+ { 2, 1}, // 12:down+right
+ { 0, 2}, // 13:left + right + down = down
+ { 0,-2}, // 14:left + right + up = up
+ { 0, 0}, // 15:impossible
+};
+
+static void HandleKeyScrolling(void)
+{
+ if (_dirkeys && !_no_scroll) {
+ int factor = _shift_pressed ? 50 : 10;
+ ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
+ }
+}
+
+void GameLoop(void)
+{
+ int m;
+
+ // autosave game?
+ if (_do_autosave) {
+ _do_autosave = false;
+ DoAutosave();
+ RedrawAutosave();
+ }
+
+ // handle scrolling of the main window
+ if (_dirkeys) HandleKeyScrolling();
+
+ // make a screenshot?
+ if ((m=_make_screenshot) != 0) {
+ _make_screenshot = 0;
+ switch(m) {
+ case 1: // make small screenshot
+ UndrawMouseCursor();
+ ShowScreenshotResult(MakeScreenshot());
+ break;
+ case 2: // make large screenshot
+ ShowScreenshotResult(MakeWorldScreenshot(-(int)MapMaxX() * 32, 0, MapMaxX() * 64, MapSizeY() * 32, 0));
+ break;
+ }
+ }
+
+ // switch game mode?
+ if ((m=_switch_mode) != SM_NONE) {
+ _switch_mode = SM_NONE;
+ SwitchMode(m);
+ }
+
+ IncreaseSpriteLRU();
+ InteractiveRandom();
+
+ if (_scroller_click_timeout > 3)
+ _scroller_click_timeout -= 3;
+ else
+ _scroller_click_timeout = 0;
+
+ _caret_timer += 3;
+ _timer_counter+=8;
+ CursorTick();
+
+#ifdef ENABLE_NETWORK
+ // Check for UDP stuff
+ NetworkUDPGameLoop();
+
+ if (_networking) {
+ // Multiplayer
+ NetworkGameLoop();
+ } else {
+ if (_network_reconnect > 0 && --_network_reconnect == 0) {
+ // This means that we want to reconnect to the last host
+ // We do this here, because it means that the network is really closed
+ NetworkClientConnectGame(_network_last_host, _network_last_port);
+ }
+ // Singleplayer
+ StateGameLoop();
+ }
+#else
+ StateGameLoop();
+#endif /* ENABLE_NETWORK */
+
+ if (!_pause && _display_opt&DO_FULL_ANIMATION)
+ DoPaletteAnimations();
+
+ if (!_pause || _cheats.build_in_pause.value)
+ MoveAllTextEffects();
+
+ InputLoop();
+
+ MusicLoop();
+}
+
+void BeforeSaveGame(void)
+{
+ Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
+
+ if (w != NULL) {
+ _saved_scrollpos_x = WP(w,vp_d).scrollpos_x;
+ _saved_scrollpos_y = WP(w,vp_d).scrollpos_y;
+ _saved_scrollpos_zoom = w->viewport->zoom;
+ }
+}
+
+static void ConvertTownOwner(void)
+{
+ uint tile;
+
+ for (tile = 0; tile != MapSize(); tile++) {
+ if (IsTileType(tile, MP_STREET)) {
+ if ((_map5[tile] & 0xF0) == 0x10 && _map3_lo[tile] & 0x80)
+ _map3_lo[tile] = OWNER_TOWN;
+
+ if (_map_owner[tile] & 0x80)
+ _map_owner[tile] = OWNER_TOWN;
+ } else if (IsTileType(tile, MP_TUNNELBRIDGE)) {
+ if (_map_owner[tile] & 0x80)
+ _map_owner[tile] = OWNER_TOWN;
+ }
+ }
+}
+
+// before savegame version 4, the name of the company determined if it existed
+static void CheckIsPlayerActive(void)
+{
+ Player *p;
+ FOR_ALL_PLAYERS(p) {
+ if (p->name_1 != 0) {
+ p->is_active = true;
+ }
+ }
+}
+
+// since savegame version 4.1, exclusive transport rights are stored at towns
+static void UpdateExclusiveRights(void)
+{
+ Town *t;
+ FOR_ALL_TOWNS(t) if (t->xy != 0) {
+ t->exclusivity=(byte)-1;
+ }
+
+ /* FIXME old exclusive rights status is not being imported (stored in s->blocked_months_obsolete)
+ could be implemented this way:
+ 1.) Go through all stations
+ Build an array town_blocked[ town_id ][ player_id ]
+ that stores if at least one station in that town is blocked for a player
+ 2.) Go through that array, if you find a town that is not blocked for
+ one player, but for all others, then give him exclusivity.
+ */
+}
+
+const byte convert_currency[] = {
+ 0, 1, 12, 8, 3,
+ 10, 14, 19, 4, 5,
+ 9, 11, 13, 6, 17,
+ 16, 22, 21, 7, 15,
+ 18, 2, 20, };
+
+// since savegame version 4.2 the currencies are arranged differently
+static void UpdateCurrencies(void)
+{
+ _opt.currency = convert_currency[_opt.currency];
+}
+
+/* Up to revision 1413 the invisible tiles at the southern border have not been
+ * MP_VOID, even though they should have. This is fixed by this function
+ */
+static void UpdateVoidTiles(void)
+{
+ uint i;
+
+ for (i = 0; i < MapMaxY(); ++i)
+ SetTileType(i * MapSizeX() + MapMaxX(), MP_VOID);
+ for (i = 0; i < MapSizeX(); ++i)
+ SetTileType(MapSizeX() * MapMaxY() + i, MP_VOID);
+}
+
+// since savegame version 6.0 each sign has an "owner", signs without owner (from old games are set to 255)
+static void UpdateSignOwner(void)
+{
+ SignStruct *ss;
+ FOR_ALL_SIGNS(ss) {
+ ss->owner = OWNER_NONE; // no owner
+ }
+}
+
+extern void UpdateOldAircraft( void );
+extern void UpdateOilRig( void );
+
+bool AfterLoadGame(uint version)
+{
+ Window *w;
+ ViewPort *vp;
+
+ // in version 2.1 of the savegame, town owner was unified.
+ if (version <= 0x200) {
+ ConvertTownOwner();
+ }
+
+ // from version 4.1 of the savegame, exclusive rights are stored at towns
+ if (version <= 0x400) {
+ UpdateExclusiveRights();
+ }
+
+ // from version 4.2 of the savegame, currencies are in a different order
+ if (version <= 0x401) {
+ UpdateCurrencies();
+ }
+
+ // from version 6.0 of the savegame, signs have an "owner"
+ if (version <= 0x600) {
+ UpdateSignOwner();
+ }
+
+ /* In old version there seems to be a problem that water is owned by
+ OWNER_NONE, not OWNER_WATER.. I can't replicate it for the current
+ (0x402) version, so I just check when versions are older, and then
+ walk through the whole map.. */
+ if (version <= 0x402) {
+ TileIndex tile = TILE_XY(0,0);
+ uint w = MapSizeX();
+ uint h = MapSizeY();
+
+ BEGIN_TILE_LOOP(tile_cur, w, h, tile)
+ if (IsTileType(tile_cur, MP_WATER) && _map_owner[tile_cur] >= MAX_PLAYERS)
+ _map_owner[tile_cur] = OWNER_WATER;
+ END_TILE_LOOP(tile_cur, w, h, tile)
+ }
+
+ // convert road side to my format.
+ if (_opt.road_side) _opt.road_side = 1;
+
+ // Load the sprites
+ GfxLoadSprites();
+
+ // Update current year
+ SetDate(_date);
+
+ // reinit the landscape variables (landscape might have changed)
+ InitializeLandscapeVariables(true);
+
+ // Update all vehicles
+ AfterLoadVehicles();
+
+ // Update all waypoints
+ if (version < 0x0C00)
+ FixOldWaypoints();
+
+ UpdateAllWaypointSigns();
+
+ // in version 2.2 of the savegame, we have new airports
+ if (version <= 0x201) {
+ UpdateOldAircraft();
+ }
+
+ UpdateAllStationVirtCoord();
+
+ // Setup town coords
+ AfterLoadTown();
+ UpdateAllSignVirtCoords();
+
+ // make sure there is a town in the game
+ if (_game_mode == GM_NORMAL && !ClosestTownFromTile(0, (uint)-1))
+ {
+ _error_message = STR_NO_TOWN_IN_SCENARIO;
+ return false;
+ }
+
+ // Initialize windows
+ ResetWindowSystem();
+ SetupColorsAndInitialWindow();
+
+ w = FindWindowById(WC_MAIN_WINDOW, 0);
+
+ WP(w,vp_d).scrollpos_x = _saved_scrollpos_x;
+ WP(w,vp_d).scrollpos_y = _saved_scrollpos_y;
+
+ vp = w->viewport;
+ vp->zoom = _saved_scrollpos_zoom;
+ vp->virtual_width = vp->width << vp->zoom;
+ vp->virtual_height = vp->height << vp->zoom;
+
+
+ // in version 4.0 of the savegame, is_active was introduced to determine
+ // if a player does exist, rather then checking name_1
+ if (version <= 0x400) {
+ CheckIsPlayerActive();
+ }
+
+ // the void tiles on the southern border used to belong to a wrong class.
+ if (version <= 0x402)
+ UpdateVoidTiles();
+
+ // If Load Scenario / New (Scenario) Game is used,
+ // a player does not exist yet. So create one here.
+ // 1 exeption: network-games. Those can have 0 players
+ // But this exeption is not true for network_servers!
+ if (!_players[0].is_active && (!_networking || (_networking && _network_server)))
+ DoStartupNewPlayer(false);
+
+ DoZoomInOutWindow(ZOOM_NONE, w); // update button status
+ MarkWholeScreenDirty();
+
+ //In 5.1, Oilrigs have been moved (again)
+ if (version <= 0x500) {
+ UpdateOilRig();
+ }
+
+ if (version <= 0x600) {
+ BEGIN_TILE_LOOP(tile, MapSizeX(), MapSizeY(), 0) {
+ if (IsTileType(tile, MP_HOUSE)) {
+ _map3_hi[tile] = _map2[tile];
+ //XXX magic
+ SetTileType(tile, MP_VOID);
+ _map2[tile] = ClosestTownFromTile(tile,(uint)-1)->index;
+ SetTileType(tile, MP_HOUSE);
+ } else if (IsTileType(tile, MP_STREET)) {
+ //XXX magic
+ SetTileType(tile, MP_VOID);
+ _map3_hi[tile] |= (_map2[tile] << 4);
+ if ( _map_owner[tile] == OWNER_TOWN)
+ _map2[tile] = ClosestTownFromTile(tile,(uint)-1)->index;
+ else
+ _map2[tile] = 0;
+ SetTileType(tile, MP_STREET);
+ }
+ } END_TILE_LOOP(tile, MapSizeX(), MapSizeY(), 0);
+ }
+
+ if (version < 0x900) {
+ Town *t;
+ FOR_ALL_TOWNS(t) {
+ UpdateTownMaxPass(t);
+ }
+ }
+
+ return true;
+}