tron@2186: /* $Id$ */ tron@2186: belugas@6677: /** @file openttd.cpp */ belugas@6677: truelight@0: #include "stdafx.h" rubidium@5838: #define VARDEF tron@1317: #include "string.h" tron@507: #include "table/strings.h" tron@1299: #include "debug.h" tron@2171: #include "driver.h" tron@2162: #include "saveload.h" tron@1309: #include "strings.h" tron@679: #include "map.h" tron@1209: #include "tile.h" tron@3144: #include "void_map.h" rubidium@5838: #include "helpers.hpp" truelight@0: Darkvater@1891: #include "openttd.h" tron@3367: #include "bridge_map.h" tron@2163: #include "functions.h" tron@1496: #include "mixer.h" tron@1349: #include "spritecache.h" Darkvater@4912: #include "strings.h" truelight@0: #include "gfx.h" tron@2340: #include "gfxinit.h" truelight@0: #include "gui.h" truelight@0: #include "station.h" tron@3367: #include "station_map.h" tron@3558: #include "town_map.h" tron@3367: #include "tunnel_map.h" truelight@0: #include "vehicle.h" truelight@0: #include "viewport.h" truelight@0: #include "window.h" truelight@0: #include "player.h" truelight@0: #include "command.h" truelight@0: #include "town.h" truelight@0: #include "industry.h" truelight@0: #include "news.h" truelight@0: #include "engine.h" truelight@0: #include "sound.h" truelight@0: #include "economy.h" truelight@0: #include "fileio.h" truelight@0: #include "hal.h" truelight@0: #include "airport.h" celestar@6519: #include "aircraft.h" dominik@126: #include "console.h" tron@430: #include "screenshot.h" rubidium@5720: #include "network/network.h" truelight@988: #include "signs.h" truelight@1313: #include "depot.h" truelight@1542: #include "waypoint.h" truelight@2395: #include "ai/ai.h" bjarni@2855: #include "train.h" KUDr@3900: #include "yapf/yapf.h" rubidium@3889: #include "settings.h" truelight@4300: #include "genworld.h" rubidium@4261: #include "date.h" truelight@4328: #include "clear_map.h" peter1138@5108: #include "fontcache.h" peter1138@6947: #include "newgrf.h" peter1138@5228: #include "newgrf_config.h" maedhros@6658: #include "newgrf_house.h" rubidium@6516: #include "player_face.h" truelight@0: celestar@5573: #include "bridge_map.h" celestar@5573: #include "clear_map.h" celestar@5573: #include "rail_map.h" celestar@5573: #include "road_map.h" celestar@5573: #include "water_map.h" rubidium@5687: #include "industry_map.h" truelight@6583: #include "unmovable_map.h" celestar@5573: truelight@0: #include truelight@0: rubidium@6573: void CallLandscapeTick(); rubidium@6573: void IncreaseDate(); rubidium@6573: void DoPaletteAnimations(); rubidium@6573: void MusicLoop(); rubidium@6573: void ResetMusic(); truelight@0: truelight@0: extern void SetDifficultyLevel(int mode, GameOptions *gm_opt); rubidium@5838: extern Player* DoStartupNewPlayer(bool is_ai); truelight@0: extern void ShowOSErrorBox(const char *buf); truelight@0: pasky@1436: /* TODO: usrerror() for errors which are not of an internal nature but pasky@1436: * caused by the user, i.e. missing files or fatal configuration errors. pasky@1436: * Post-0.4.0 since Celestar doesn't want this in SVN before. --pasky */ pasky@1436: belugas@4171: void CDECL error(const char *s, ...) tron@2639: { truelight@0: va_list va; truelight@0: char buf[512]; tron@2639: truelight@0: va_start(va, s); Darkvater@5170: vsnprintf(buf, lengthof(buf), s, va); truelight@0: va_end(va); truelight@193: truelight@0: ShowOSErrorBox(buf); tron@2639: if (_video_driver != NULL) _video_driver->stop(); truelight@0: truelight@0: assert(0); truelight@0: exit(1); truelight@0: } truelight@0: truelight@0: void CDECL ShowInfoF(const char *str, ...) truelight@0: { truelight@0: va_list va; truelight@0: char buf[1024]; truelight@0: va_start(va, str); Darkvater@5170: vsnprintf(buf, lengthof(buf), str, va); truelight@0: va_end(va); truelight@0: ShowInfo(buf); truelight@0: } truelight@0: truelight@0: tron@1310: void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize) truelight@0: { truelight@0: FILE *in; tron@1105: byte *mem; truelight@0: size_t len; truelight@0: truelight@0: in = fopen(filename, "rb"); tron@2639: if (in == NULL) return NULL; truelight@0: truelight@0: fseek(in, 0, SEEK_END); truelight@0: len = ftell(in); truelight@0: fseek(in, 0, SEEK_SET); KUDr@5860: if (len > maxsize || (mem = MallocT(len + 1)) == NULL) { truelight@0: fclose(in); truelight@0: return NULL; truelight@0: } tron@1105: mem[len] = 0; truelight@0: if (fread(mem, len, 1, in) != 1) { truelight@0: fclose(in); truelight@0: free(mem); truelight@0: return NULL; truelight@0: } truelight@0: fclose(in); truelight@0: truelight@0: *lenp = len; truelight@0: return mem; truelight@0: } truelight@0: rubidium@5838: extern const char _openttd_revision[]; rubidium@6573: static void showhelp() truelight@0: { truelight@0: char buf[4096], *p; truelight@0: Darkvater@5170: p = buf; truelight@2831: Darkvater@5170: p += snprintf(p, lengthof(buf), "OpenTTD %s\n", _openttd_revision); Darkvater@5170: p = strecpy(p, truelight@2831: "\n" truelight@2831: "\n" truelight@0: "Command line options:\n" truelight@543: " -v drv = Set video driver (see below)\n" Darkvater@6136: " -s drv = Set sound driver (see below) (param bufsize,hz)\n" truelight@543: " -m drv = Set music driver (see below)\n" truelight@543: " -r res = Set resolution (for instance 800x600)\n" truelight@543: " -h = Display this help text\n" rubidium@4285: " -t year = Set starting year\n" pasky@1440: " -d [[fac=]lvl[,...]]= Debug mode\n" truelight@543: " -e = Start Editor\n" truelight@543: " -g [savegame] = Start new/save game immediately\n" truelight@543: " -G seed = Set random seed\n" bjarni@5943: #if defined(ENABLE_NETWORK) rubidium@5676: " -n [ip:port#player] = Start networkgame\n" rubidium@5759: " -D [ip][:port] = Start dedicated server\n" truelight@6210: " -l ip[:port] = Redirect DEBUG()\n" tron@4077: #if !defined(__MORPHOS__) && !defined(__AMIGA__) && !defined(WIN32) truelight@704: " -f = Fork into the background (dedicated only)\n" tron@4077: #endif bjarni@5943: #endif /* ENABLE_NETWORK */ truelight@2831: " -i = Force to use the DOS palette\n" truelight@2831: " (use this if you see a lot of pink)\n" truelight@2831: " -c config_file = Use 'config_file' instead of 'openttd.cfg'\n" Darkvater@5827: " -x = Do not automatically save to config file on exit\n", Darkvater@5170: lastof(buf) truelight@0: ); truelight@0: Darkvater@5170: p = GetDriverList(p, lastof(buf)); truelight@0: Darkvater@5974: /* ShowInfo put output to stderr, but version information should go Darkvater@5974: * to stdout; this is the only exception */ Darkvater@5974: #if !defined(WIN32) && !defined(WIN64) Darkvater@5974: printf("%s\n", buf); Darkvater@5974: #else truelight@0: ShowInfo(buf); Darkvater@5974: #endif truelight@0: } truelight@0: truelight@0: rubidium@5838: struct MyGetOptData { truelight@0: char *opt; truelight@0: int numleft; truelight@0: char **argv; truelight@0: const char *options; rubidium@5838: const char *cont; truelight@0: rubidium@5838: MyGetOptData(int argc, char **argv, const char *options) rubidium@5838: { rubidium@5838: opt = NULL; rubidium@5838: numleft = argc; rubidium@5838: this->argv = argv; rubidium@5838: this->options = options; rubidium@5838: cont = NULL; rubidium@5838: } rubidium@5838: }; truelight@0: truelight@0: static int MyGetOpt(MyGetOptData *md) truelight@0: { rubidium@5838: const char *s,*r,*t; truelight@0: tron@2639: s = md->cont; tron@2639: if (s != NULL) truelight@0: goto md_continue_here; truelight@0: tron@2639: for (;;) { tron@2639: if (--md->numleft < 0) return -1; truelight@0: truelight@0: s = *md->argv++; truelight@0: if (*s == '-') { truelight@0: md_continue_here:; truelight@0: s++; truelight@0: if (*s != 0) { belugas@6677: /* Found argument, try to locate it in options. */ truelight@0: if (*s == ':' || (r = strchr(md->options, *s)) == NULL) { belugas@6677: /* ERROR! */ truelight@0: return -2; truelight@0: } truelight@0: if (r[1] == ':') { belugas@6677: /* Item wants an argument. Check if the argument follows, or if it comes as a separate arg. */ truelight@0: if (!*(t = s + 1)) { belugas@6677: /* It comes as a separate arg. Check if out of args? */ truelight@0: if (--md->numleft < 0 || *(t = *md->argv) == '-') { belugas@6677: /* Check if item is optional? */ truelight@0: if (r[2] != ':') truelight@0: return -2; truelight@0: md->numleft++; truelight@0: t = NULL; truelight@0: } else { truelight@0: md->argv++; truelight@0: } truelight@0: } rubidium@5838: md->opt = (char*)t; truelight@0: md->cont = NULL; truelight@0: return *s; truelight@0: } truelight@0: md->opt = NULL; truelight@0: md->cont = s; truelight@0: return *s; truelight@0: } truelight@0: } else { belugas@6677: /* This is currently not supported. */ truelight@0: return -2; truelight@0: } truelight@0: } truelight@0: } truelight@0: truelight@0: belugas@4171: static void ParseResolution(int res[2], const char *s) truelight@0: { rubidium@5838: const char *t = strchr(s, 'x'); truelight@0: if (t == NULL) { truelight@0: ShowInfoF("Invalid resolution '%s'", s); truelight@0: return; truelight@0: } truelight@0: Darkvater@1764: res[0] = clamp(strtoul(s, NULL, 0), 64, MAX_SCREEN_WIDTH); Darkvater@1764: res[1] = clamp(strtoul(t + 1, NULL, 0), 64, MAX_SCREEN_HEIGHT); truelight@193: } truelight@0: rubidium@6573: static void InitializeDynamicVariables() truelight@919: { truelight@919: /* Dynamic stuff needs to be initialized somewhere... */ truelight@1267: _town_sort = NULL; truelight@919: _industry_sort = NULL; truelight@919: } truelight@919: Darkvater@5910: rubidium@6573: static void UnInitializeGame() truelight@919: { Darkvater@5910: UnInitWindowSystem(); Darkvater@5910: Darkvater@5910: /* Uninitialize airport state machines */ Darkvater@5910: UnInitializeAirports(); Darkvater@5910: Darkvater@5910: /* Uninitialize variables that are allocated dynamically */ tron@4983: CleanPool(&_Town_pool); tron@4976: CleanPool(&_Industry_pool); tron@4980: CleanPool(&_Station_pool); tron@4972: CleanPool(&_Vehicle_pool); tron@4979: CleanPool(&_Sign_pool); tron@4977: CleanPool(&_Order_pool); truelight@1260: tron@4277: free((void*)_town_sort); tron@4277: free((void*)_industry_sort); Darkvater@1482: Darkvater@1482: free(_config_file); Darkvater@1474: } truelight@919: rubidium@6573: static void LoadIntroGame() truelight@543: { truelight@543: char filename[256]; Darkvater@1500: truelight@543: _game_mode = GM_MENU; peter1138@6923: peter1138@6923: /* Clear transparency options */ peter1138@6923: _transparent_opt = 0; peter1138@6923: Darkvater@1500: _opt_ptr = &_opt_newgame; peter1138@5228: ResetGRFConfig(false); truelight@543: belugas@6677: /* Setup main window */ Darkvater@1474: ResetWindowSystem(); truelight@543: SetupColorsAndInitialWindow(); truelight@543: belugas@6677: /* Generate a world. */ Darkvater@5296: snprintf(filename, lengthof(filename), "%sopntitle.dat", _paths.data_dir); tron@3033: #if defined SECOND_DATA_DIR tron@921: if (SaveOrLoad(filename, SL_LOAD) != SL_OK) { Darkvater@5296: snprintf(filename, lengthof(filename), "%sopntitle.dat", _paths.second_data_dir); tron@3033: } bjarni@561: #endif tron@3033: if (SaveOrLoad(filename, SL_LOAD) != SL_OK) { tron@3033: GenerateWorld(GW_EMPTY, 64, 64); // if failed loading, make empty world. truelight@4300: WaitTillGeneratedWorld(); tron@921: } truelight@543: truelight@6557: _pause_game = 0; rubidium@5838: SetLocalPlayer(PLAYER_FIRST); truelight@4336: /* Make sure you can't scroll in the menu */ truelight@4336: _scrolling_viewport = 0; truelight@4336: _cursor.fix_at = false; truelight@543: MarkWholeScreenDirty(); truelight@543: belugas@6677: /* Play main theme */ truelight@543: if (_music_driver->is_song_playing()) ResetMusic(); truelight@543: } truelight@543: truelight@2290: #if defined(UNIX) && !defined(__MORPHOS__) rubidium@6573: extern void DedicatedFork(); truelight@2290: #endif truelight@704: belugas@4171: int ttd_main(int argc, char *argv[]) truelight@0: { truelight@0: int i; tron@1010: const char *optformat; Darkvater@6136: char musicdriver[32], sounddriver[32], videodriver[32]; truelight@0: int resolution[2] = {0,0}; rubidium@4293: Year startyear = INVALID_YEAR; truelight@4300: uint generation_seed = GENERATE_NEW_SEED; bjarni@5943: bool save_config = true; bjarni@5943: #if defined(ENABLE_NETWORK) Darkvater@3058: bool dedicated = false; Darkvater@3058: bool network = false; Darkvater@3058: char *network_conn = NULL; truelight@6210: char *debuglog_conn = NULL; rubidium@5759: char *dedicated_host = NULL; rubidium@5759: uint16 dedicated_port = 0; bjarni@5943: #endif /* ENABLE_NETWORK */ tron@1406: Darkvater@6136: musicdriver[0] = sounddriver[0] = videodriver[0] = '\0'; truelight@0: truelight@0: _game_mode = GM_MENU; truelight@0: _switch_mode = SM_MENU; darkvater@172: _switch_mode_errorstr = INVALID_STRING_ID; truelight@704: _dedicated_forks = false; Darkvater@1482: _config_file = NULL; truelight@0: belugas@6677: /* The last param of the following function means this: belugas@6677: * a letter means: it accepts that param (e.g.: -h) belugas@6677: * a ':' behind it means: it need a param (e.g.: -m) belugas@6677: * a '::' behind it means: it can optional have a param (e.g.: -d) */ truelight@6210: optformat = "m:s:v:hD::n::eit:d::r:g::G:c:xl:" tron@4077: #if !defined(__MORPHOS__) && !defined(__AMIGA__) && !defined(WIN32) tron@4077: "f" tron@4077: #endif tron@4077: ; bjarni@770: rubidium@5838: MyGetOptData mgo(argc-1, argv+1, optformat); rubidium@5838: truelight@0: while ((i = MyGetOpt(&mgo)) != -1) { tron@2951: switch (i) { truelight@0: case 'm': ttd_strlcpy(musicdriver, mgo.opt, sizeof(musicdriver)); break; truelight@0: case 's': ttd_strlcpy(sounddriver, mgo.opt, sizeof(sounddriver)); break; truelight@0: case 'v': ttd_strlcpy(videodriver, mgo.opt, sizeof(videodriver)); break; bjarni@5943: #if defined(ENABLE_NETWORK) Darkvater@3058: case 'D': Darkvater@3058: strcpy(musicdriver, "null"); Darkvater@3058: strcpy(sounddriver, "null"); Darkvater@3058: strcpy(videodriver, "dedicated"); Darkvater@3058: dedicated = true; Darkvater@6136: if (mgo.opt != NULL) { rubidium@5759: /* Use the existing method for parsing (openttd -n). rubidium@5759: * However, we do ignore the #player part. */ rubidium@5759: const char *temp = NULL; rubidium@5759: const char *port = NULL; rubidium@5759: ParseConnectionString(&temp, &port, mgo.opt); Darkvater@6136: if (!StrEmpty(mgo.opt)) dedicated_host = mgo.opt; rubidium@5759: if (port != NULL) dedicated_port = atoi(port); rubidium@5759: } Darkvater@3058: break; Darkvater@3058: case 'f': _dedicated_forks = true; break; Darkvater@3058: case 'n': Darkvater@3058: network = true; Darkvater@3059: network_conn = mgo.opt; // optional IP parameter, NULL if unset Darkvater@3058: break; truelight@6210: case 'l': truelight@6210: debuglog_conn = mgo.opt; truelight@6210: break; bjarni@5943: #endif /* ENABLE_NETWORK */ truelight@0: case 'r': ParseResolution(resolution, mgo.opt); break; rubidium@4285: case 't': startyear = atoi(mgo.opt); break; truelight@0: case 'd': { truelight@0: #if defined(WIN32) truelight@0: CreateConsole(); truelight@0: #endif tron@2639: if (mgo.opt != NULL) SetDebugString(mgo.opt); truelight@0: } break; truelight@0: case 'e': _switch_mode = SM_EDITOR; break; dominik@614: case 'i': _use_dos_palette = true; break; truelight@193: case 'g': tron@2639: if (mgo.opt != NULL) { truelight@0: strcpy(_file_to_saveload.name, mgo.opt); truelight@0: _switch_mode = SM_LOAD; tron@4077: } else { truelight@0: _switch_mode = SM_NEWGAME; truelight@6635: /* Give a random map */ truelight@6635: generation_seed = InteractiveRandom(); tron@4077: } truelight@0: break; truelight@4300: case 'G': generation_seed = atoi(mgo.opt); break; Darkvater@3058: case 'c': _config_file = strdup(mgo.opt); break; rubidium@5758: case 'x': save_config = false; break; truelight@0: case -2: tron@2026: case 'h': truelight@0: showhelp(); truelight@0: return 0; truelight@0: } truelight@0: } truelight@0: rubidium@6643: DeterminePaths(argv[0]); dominik@961: CheckExternalFiles(); truelight@704: truelight@2290: #if defined(UNIX) && !defined(__MORPHOS__) belugas@6677: /* We must fork here, or we'll end up without some resources we need (like sockets) */ truelight@704: if (_dedicated_forks) truelight@704: DedicatedFork(); truelight@704: #endif truelight@704: truelight@0: LoadFromConfig(); Darkvater@1688: CheckConfig(); darkvater@983: LoadFromHighScore(); truelight@0: belugas@6677: /* override config? */ Darkvater@6136: if (!StrEmpty(musicdriver)) ttd_strlcpy(_ini_musicdriver, musicdriver, sizeof(_ini_musicdriver)); Darkvater@6136: if (!StrEmpty(sounddriver)) ttd_strlcpy(_ini_sounddriver, sounddriver, sizeof(_ini_sounddriver)); Darkvater@6136: if (!StrEmpty(videodriver)) ttd_strlcpy(_ini_videodriver, videodriver, sizeof(_ini_videodriver)); Darkvater@6136: if (resolution[0] != 0) { _cur_resolution[0] = resolution[0]; _cur_resolution[1] = resolution[1]; } rubidium@4293: if (startyear != INVALID_YEAR) _patches_newgame.starting_year = startyear; truelight@4300: if (generation_seed != GENERATE_NEW_SEED) _patches_newgame.generation_seed = generation_seed; truelight@0: bjarni@5943: #if defined(ENABLE_NETWORK) rubidium@5759: if (dedicated_host) snprintf(_network_server_bind_ip_host, NETWORK_HOSTNAME_LENGTH, "%s", dedicated_host); rubidium@5759: if (dedicated_port) _network_server_port = dedicated_port; tron@2951: if (_dedicated_forks && !dedicated) _dedicated_forks = false; bjarni@5943: #endif /* ENABLE_NETWORK */ truelight@704: belugas@6677: /* enumerate language files */ truelight@0: InitializeLanguagePacks(); truelight@0: belugas@6677: /* initialize screenshot formats */ truelight@0: InitializeScreenshotFormats(); truelight@0: belugas@6677: /* initialize airport state machines */ dominik@105: InitializeAirports(); truelight@193: truelight@919: /* initialize all variables that are allocated dynamically */ truelight@919: InitializeDynamicVariables(); truelight@919: truelight@2395: /* start the AI */ truelight@2395: AI_Initialize(); truelight@2395: belugas@6677: /* Sample catalogue */ Darkvater@5568: DEBUG(misc, 1, "Loading sound effects..."); tron@1496: MxInitialize(11025); tron@1496: SoundInitialize("sample.cat"); truelight@0: peter1138@5108: /* Initialize FreeType */ peter1138@5108: InitFreeType(); peter1138@5108: belugas@6677: /* This must be done early, since functions use the InvalidateWindow* calls */ truelight@139: InitWindowSystem(); truelight@0: peter1138@5166: /* Initialize game palette */ peter1138@5166: GfxInitPalettes(); peter1138@5166: Darkvater@5568: DEBUG(driver, 1, "Loading drivers..."); truelight@0: LoadDriver(SOUND_DRIVER, _ini_sounddriver); truelight@0: LoadDriver(MUSIC_DRIVER, _ini_musicdriver); truelight@0: LoadDriver(VIDEO_DRIVER, _ini_videodriver); // load video last, to prevent an empty window while sound and music loads tron@2526: _savegame_sort_order = SORT_BY_DATE | SORT_DESCENDING; truelight@0: belugas@6677: /* restore saved music volume */ glx@3678: _music_driver->set_volume(msf.music_vol); glx@3678: Darkvater@4830: NetworkStartUp(); // initialize network-core truelight@543: truelight@6210: #if defined(ENABLE_NETWORK) truelight@6210: if (debuglog_conn != NULL && _network_available) { truelight@6210: const char *not_used = NULL; truelight@6210: const char *port = NULL; truelight@6210: uint16 rport; truelight@6210: truelight@6210: rport = NETWORK_DEFAULT_DEBUGLOG_PORT; truelight@6210: truelight@6210: ParseConnectionString(¬_used, &port, debuglog_conn); truelight@6210: if (port != NULL) rport = atoi(port); truelight@6210: truelight@6210: NetworkStartDebugLog(debuglog_conn, rport); truelight@6210: } truelight@6210: #endif /* ENABLE_NETWORK */ truelight@6210: peter1138@5228: ScanNewGRFFiles(); peter1138@5228: Darkvater@1500: _opt_ptr = &_opt_newgame; peter1138@5228: ResetGRFConfig(false); truelight@193: Darkvater@1500: /* XXX - ugly hack, if diff_level is 9, it means we got no setting from the config file */ tron@2951: if (_opt_newgame.diff_level == 9) SetDifficultyLevel(0, &_opt_newgame); truelight@0: truelight@4300: /* Make sure _patches is filled with _patches_newgame if we switch to a game directly */ truelight@4300: if (_switch_mode != SM_NONE) { tron@5024: _opt = _opt_newgame; truelight@4300: UpdatePatches(); truelight@4300: } truelight@4300: belugas@6677: /* initialize the ingame console */ truelight@139: IConsoleInit(); Darkvater@3312: _cursor.in_window = true; darkvater@1246: InitializeGUI(); dominik@644: IConsoleCmdExec("exec scripts/autoexec.scr 0"); truelight@656: truelight@2828: GenerateWorld(GW_EMPTY, 64, 64); // Make the viewport initialization happy truelight@4300: WaitTillGeneratedWorld(); truelight@1268: truelight@543: #ifdef ENABLE_NETWORK tron@2951: if (network && _network_available) { truelight@543: if (network_conn != NULL) { tron@1329: const char *port = NULL; tron@1329: const char *player = NULL; truelight@543: uint16 rport; truelight@543: truelight@543: rport = NETWORK_DEFAULT_PORT; Darkvater@4878: _network_playas = PLAYER_NEW_COMPANY; truelight@543: truelight@543: ParseConnectionString(&player, &port, network_conn); truelight@543: Darkvater@4861: if (player != NULL) { rubidium@5838: _network_playas = (PlayerID)atoi(player); Darkvater@4878: Darkvater@4880: if (_network_playas != PLAYER_SPECTATOR) { Darkvater@4880: _network_playas--; Darkvater@4878: if (!IsValidPlayer(_network_playas)) return false; Darkvater@4878: } Darkvater@4861: } truelight@543: if (port != NULL) rport = atoi(port); truelight@543: truelight@543: LoadIntroGame(); truelight@543: _switch_mode = SM_NONE; truelight@543: NetworkClientConnectGame(network_conn, rport); tron@1109: } truelight@543: } truelight@543: #endif /* ENABLE_NETWORK */ truelight@543: tron@2228: _video_driver->main_loop(); truelight@139: tron@2285: WaitTillSaved(); truelight@139: IConsoleFree(); truelight@0: Darkvater@4830: if (_network_available) NetworkShutDown(); // Shut down the network and close any open connections truelight@0: truelight@0: _video_driver->stop(); truelight@0: _music_driver->stop(); truelight@0: _sound_driver->stop(); truelight@0: rubidium@5758: /* only save config if we have to */ rubidium@5758: if (save_config) { rubidium@5758: SaveToConfig(); rubidium@5758: SaveToHighScore(); rubidium@5758: } truelight@0: Darkvater@5910: /* Reset windowing system and free config file */ Darkvater@5910: UnInitializeGame(); truelight@919: truelight@2395: /* stop the AI */ truelight@2395: AI_Uninitialize(); truelight@2395: darkvater@1036: /* Close all and any open filehandles */ darkvater@1036: FioCloseAll(); darkvater@1036: truelight@0: return 0; truelight@0: } truelight@0: rubidium@6573: void HandleExitGameRequest() rubidium@4548: { rubidium@4548: if (_game_mode == GM_MENU) { // do not ask to quit on the main screen rubidium@4548: _exit_game = true; rubidium@4548: } else if (_patches.autosave_on_exit) { rubidium@4548: DoExitSave(); rubidium@4548: _exit_game = true; rubidium@4548: } else { rubidium@4548: AskExitGame(); rubidium@4548: } rubidium@4548: } rubidium@4548: rubidium@4548: Darkvater@2380: /** Mutex so that only one thread can communicate with the main program Darkvater@2380: * at any given time */ truelight@4323: static ThreadMsg _message = MSG_OTTD_NO_MESSAGE; Darkvater@2380: rubidium@6573: static inline void OTTD_ReleaseMutex() {_message = MSG_OTTD_NO_MESSAGE;} rubidium@6573: static inline ThreadMsg OTTD_PollThreadEvent() {return _message;} Darkvater@2380: Darkvater@2380: /** Called by running thread to execute some action in the main game. Darkvater@2380: * It will stall as long as the mutex is not freed (handled) by the game */ Darkvater@2380: void OTTD_SendThreadMessage(ThreadMsg msg) Darkvater@2380: { Darkvater@2383: if (_exit_game) return; truelight@4323: while (_message != MSG_OTTD_NO_MESSAGE) CSleep(10); Darkvater@2380: Darkvater@2380: _message = msg; Darkvater@2380: } Darkvater@2380: Darkvater@2380: Darkvater@2380: /** Handle the user-messages sent to us Darkvater@2380: * @param message message sent Darkvater@2380: */ tron@2817: static void ProcessSentMessage(ThreadMsg message) Darkvater@2380: { Darkvater@2380: switch (message) { Darkvater@2380: case MSG_OTTD_SAVETHREAD_DONE: SaveFileDone(); break; Darkvater@2380: case MSG_OTTD_SAVETHREAD_ERROR: SaveFileError(); break; Darkvater@2380: default: NOT_REACHED(); Darkvater@2380: } Darkvater@2380: Darkvater@2380: OTTD_ReleaseMutex(); // release mutex so that other threads, messages can be handled Darkvater@2380: } Darkvater@2380: truelight@0: static void ShowScreenshotResult(bool b) truelight@0: { truelight@0: if (b) { ludde@2055: SetDParamStr(0, _screenshot_name); truelight@0: ShowErrorMessage(INVALID_STRING_ID, STR_031B_SCREENSHOT_SUCCESSFULLY, 0, 0); truelight@0: } else { truelight@0: ShowErrorMessage(INVALID_STRING_ID, STR_031C_SCREENSHOT_FAILED, 0, 0); truelight@0: } truelight@0: truelight@0: } truelight@0: rubidium@6573: static void MakeNewGameDone() truelight@4300: { truelight@4300: /* In a dedicated server, the server does not play */ truelight@4300: if (_network_dedicated) { rubidium@5564: SetLocalPlayer(PLAYER_SPECTATOR); truelight@4300: return; truelight@4300: } truelight@4300: truelight@4300: /* Create a single player */ truelight@4300: DoStartupNewPlayer(false); truelight@4300: rubidium@5838: SetLocalPlayer(PLAYER_FIRST); truelight@4300: _current_player = _local_player; peter1138@4661: DoCommandP(0, (_patches.autorenew << 15 ) | (_patches.autorenew_months << 16) | 4, _patches.autorenew_money, NULL, CMD_SET_AUTOREPLACE); truelight@4300: KUDr@5116: SettingsDisableElrail(_patches.disable_elrails); KUDr@5116: truelight@4300: MarkWholeScreenDirty(); truelight@4300: } truelight@4300: truelight@4300: static void MakeNewGame(bool from_heightmap) truelight@0: { truelight@0: _game_mode = GM_NORMAL; truelight@0: peter1138@5313: ResetGRFConfig(true); maedhros@6658: ResetHouseIDMapping(); peter1138@5313: truelight@4300: GenerateWorldSetCallback(&MakeNewGameDone); truelight@4300: GenerateWorld(from_heightmap ? GW_HEIGHTMAP : GW_NEWGAME, 1 << _patches.map_x, 1 << _patches.map_y); truelight@4300: } truelight@0: rubidium@6573: static void MakeNewEditorWorldDone() truelight@4300: { rubidium@5564: SetLocalPlayer(OWNER_NONE); truelight@0: truelight@0: MarkWholeScreenDirty(); truelight@0: } truelight@0: rubidium@6573: static void MakeNewEditorWorld() truelight@0: { truelight@0: _game_mode = GM_EDITOR; truelight@0: peter1138@5313: ResetGRFConfig(true); peter1138@5313: truelight@4300: GenerateWorldSetCallback(&MakeNewEditorWorldDone); truelight@2828: GenerateWorld(GW_EMPTY, 1 << _patches.map_x, 1 << _patches.map_y); truelight@0: } truelight@0: rubidium@6573: void StartupPlayers(); rubidium@6573: void StartupDisasters(); rubidium@6573: extern void StartupEconomy(); dominik@116: Darkvater@1500: /** Darkvater@1500: * Start Scenario starts a new game based on a scenario. Darkvater@1500: * Eg 'New Game' --> select a preset scenario Darkvater@1501: * This starts a scenario based on your current difficulty settings Darkvater@1500: */ rubidium@6573: static void StartScenario() truelight@0: { truelight@0: _game_mode = GM_NORMAL; truelight@0: belugas@6677: /* invalid type */ truelight@0: if (_file_to_saveload.mode == SL_INVALID) { Darkvater@5568: DEBUG(sl, 0, "Savegame is obsolete or invalid format: '%s'", _file_to_saveload.name); Darkvater@2749: ShowErrorMessage(INVALID_STRING_ID, STR_4009_GAME_LOAD_FAILED, 0, 0); truelight@0: _game_mode = GM_MENU; truelight@0: return; truelight@0: } truelight@0: belugas@6677: /* Reinitialize windows */ Darkvater@1474: ResetWindowSystem(); truelight@0: truelight@0: SetupColorsAndInitialWindow(); truelight@0: peter1138@5313: ResetGRFConfig(true); peter1138@5313: belugas@6677: /* Load game */ truelight@0: if (SaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode) != SL_OK) { truelight@0: LoadIntroGame(); Darkvater@2749: ShowErrorMessage(INVALID_STRING_ID, STR_4009_GAME_LOAD_FAILED, 0, 0); truelight@0: } truelight@0: Darkvater@1501: _opt_ptr = &_opt; tron@5024: _opt_ptr->diff = _opt_newgame.diff; Darkvater@1501: _opt.diff_level = _opt_newgame.diff_level; Darkvater@1500: belugas@6677: /* Inititalize data */ Darkvater@3891: StartupEconomy(); dominik@115: StartupPlayers(); dominik@115: StartupEngines(); dominik@115: StartupDisasters(); dominik@115: rubidium@5838: SetLocalPlayer(PLAYER_FIRST); bjarni@2293: _current_player = _local_player; peter1138@4661: DoCommandP(0, (_patches.autorenew << 15 ) | (_patches.autorenew_months << 16) | 4, _patches.autorenew_money, NULL, CMD_SET_AUTOREPLACE); truelight@0: truelight@0: MarkWholeScreenDirty(); truelight@0: } truelight@0: truelight@543: bool SafeSaveOrLoad(const char *filename, int mode, int newgm) truelight@0: { truelight@0: byte ogm = _game_mode; truelight@0: truelight@0: _game_mode = newgm; tron@3024: switch (SaveOrLoad(filename, mode)) { tron@3024: case SL_OK: return true; tron@3024: tron@3024: case SL_REINIT: tron@3024: switch (ogm) { tron@3024: case GM_MENU: LoadIntroGame(); break; tron@3024: case GM_EDITOR: MakeNewEditorWorld(); break; truelight@4300: default: MakeNewGame(false); break; tron@3024: } tron@3024: return false; tron@3024: tron@3024: default: tron@3024: _game_mode = ogm; tron@3024: return false; tron@2639: } truelight@0: } truelight@0: truelight@543: void SwitchMode(int new_mode) truelight@0: { truelight@543: #ifdef ENABLE_NETWORK belugas@6677: /* If we are saving something, the network stays in his current state */ truelight@543: if (new_mode != SM_SAVE) { belugas@6677: /* If the network is active, make it not-active */ truelight@543: if (_networking) { truelight@543: if (_network_server && (new_mode == SM_LOAD || new_mode == SM_NEWGAME)) { truelight@543: NetworkReboot(); rubidium@5870: NetworkUDPCloseAll(); truelight@543: } else { truelight@543: NetworkDisconnect(); rubidium@5870: NetworkUDPCloseAll(); truelight@543: } truelight@543: } truelight@543: belugas@6677: /* If we are a server, we restart the server */ truelight@543: if (_is_network_server) { belugas@6677: /* But not if we are going to the menu */ truelight@543: if (new_mode != SM_MENU) { truelight@543: NetworkServerStart(); truelight@543: } else { belugas@6677: /* This client no longer wants to be a network-server */ truelight@543: _is_network_server = false; truelight@543: } truelight@543: } truelight@543: } truelight@543: #endif /* ENABLE_NETWORK */ truelight@543: Darkvater@1500: switch (new_mode) { Darkvater@1500: case SM_EDITOR: /* Switch to scenario editor */ truelight@0: MakeNewEditorWorld(); truelight@0: break; truelight@0: Darkvater@1500: case SM_NEWGAME: /* New Game --> 'Random game' */ truelight@630: #ifdef ENABLE_NETWORK tron@2951: if (_network_server) { tron@2989: snprintf(_network_game_info.map_name, lengthof(_network_game_info.map_name), "Random Map"); tron@2951: } truelight@630: #endif /* ENABLE_NETWORK */ truelight@4300: MakeNewGame(false); truelight@0: break; truelight@0: Darkvater@1500: case SM_START_SCENARIO: /* New Game --> Choose one of the preset scenarios */ tron@2951: #ifdef ENABLE_NETWORK tron@2951: if (_network_server) { tron@2989: snprintf(_network_game_info.map_name, lengthof(_network_game_info.map_name), "%s (Loaded scenario)", _file_to_saveload.title); tron@2951: } tron@2951: #endif /* ENABLE_NETWORK */ truelight@543: StartScenario(); truelight@543: break; truelight@543: Darkvater@1500: case SM_LOAD: { /* Load game, Play Scenario */ Darkvater@1500: _opt_ptr = &_opt; peter1138@5228: ResetGRFConfig(true); truelight@0: truelight@0: if (!SafeSaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_NORMAL)) { truelight@942: LoadIntroGame(); Darkvater@2749: ShowErrorMessage(INVALID_STRING_ID, STR_4009_GAME_LOAD_FAILED, 0, 0); truelight@0: } else { Darkvater@5197: /* Update the local player for a loaded game. It is either always Darkvater@5197: * player #1 (eg 0) or in the case of a dedicated server a spectator */ rubidium@5838: SetLocalPlayer(_network_dedicated ? PLAYER_SPECTATOR : PLAYER_FIRST); truelight@0: DoCommandP(0, 0, 0, NULL, CMD_PAUSE); // decrease pause counter (was increased from opening load dialog) truelight@630: #ifdef ENABLE_NETWORK tron@2951: if (_network_server) { tron@2989: snprintf(_network_game_info.map_name, lengthof(_network_game_info.map_name), "%s (Loaded game)", _file_to_saveload.title); tron@2951: } truelight@630: #endif /* ENABLE_NETWORK */ truelight@0: } truelight@0: break; truelight@0: } truelight@0: truelight@4300: case SM_START_HEIGHTMAP: /* Load a heightmap and start a new game from it */ truelight@4300: #ifdef ENABLE_NETWORK truelight@4300: if (_network_server) { truelight@4300: snprintf(_network_game_info.map_name, lengthof(_network_game_info.map_name), "%s (Heightmap)", _file_to_saveload.title); truelight@4300: } truelight@4300: #endif /* ENABLE_NETWORK */ truelight@4300: MakeNewGame(true); truelight@4300: break; truelight@4300: truelight@4300: case SM_LOAD_HEIGHTMAP: /* Load heightmap from scenario editor */ rubidium@5564: SetLocalPlayer(OWNER_NONE); truelight@4300: truelight@4300: GenerateWorld(GW_HEIGHTMAP, 1 << _patches.map_x, 1 << _patches.map_y); truelight@4300: MarkWholeScreenDirty(); truelight@4300: break; truelight@4300: Darkvater@1500: case SM_LOAD_SCENARIO: { /* Load scenario from scenario editor */ tron@2634: if (SafeSaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_EDITOR)) { Darkvater@1500: _opt_ptr = &_opt; truelight@0: rubidium@5564: SetLocalPlayer(OWNER_NONE); rubidium@4537: _patches_newgame.starting_year = _cur_year; tron@2639: } else { truelight@0: ShowErrorMessage(INVALID_STRING_ID, STR_4009_GAME_LOAD_FAILED, 0, 0); tron@2639: } truelight@0: break; truelight@0: } truelight@0: Darkvater@1500: case SM_MENU: /* Switch to game intro menu */ truelight@0: LoadIntroGame(); truelight@0: break; truelight@0: Darkvater@1500: case SM_SAVE: /* Save game */ tron@2639: if (SaveOrLoad(_file_to_saveload.name, SL_SAVE) != SL_OK) { truelight@0: ShowErrorMessage(INVALID_STRING_ID, STR_4007_GAME_SAVE_FAILED, 0, 0); tron@2639: } else { truelight@0: DeleteWindowById(WC_SAVELOAD, 0); tron@2639: } truelight@0: break; truelight@0: Darkvater@1500: case SM_GENRANDLAND: /* Generate random land within scenario editor */ rubidium@5564: SetLocalPlayer(OWNER_NONE); tron@2951: GenerateWorld(GW_RANDOM, 1 << _patches.map_x, 1 << _patches.map_y); belugas@6677: /* XXX: set date */ truelight@0: MarkWholeScreenDirty(); truelight@0: break; truelight@0: } truelight@193: tron@2951: if (_switch_mode_errorstr != INVALID_STRING_ID) { tron@2951: ShowErrorMessage(INVALID_STRING_ID, _switch_mode_errorstr, 0, 0); tron@2951: } truelight@0: } truelight@0: truelight@0: belugas@6677: /* State controlling game loop. belugas@6677: * The state must not be changed from anywhere belugas@6677: * but here. belugas@6677: * That check is enforced in DoCommand. */ rubidium@6573: void StateGameLoop() truelight@0: { belugas@6677: /* dont execute the state loop during pause */ truelight@6557: if (_pause_game) return; truelight@4300: if (IsGeneratingWorld()) return; darkvater@213: truelight@0: if (_game_mode == GM_EDITOR) { truelight@0: RunTileLoop(); truelight@0: CallVehicleTicks(); truelight@0: CallLandscapeTick(); truelight@0: CallWindowTickEvent(); truelight@0: NewsLoop(); truelight@0: } else { belugas@6677: /* All these actions has to be done from OWNER_NONE belugas@6677: * for multiplayer compatibility */ tron@2498: PlayerID p = _current_player; signde@206: _current_player = OWNER_NONE; signde@206: truelight@0: AnimateAnimatedTiles(); truelight@0: IncreaseDate(); truelight@0: RunTileLoop(); truelight@0: CallVehicleTicks(); truelight@0: CallLandscapeTick(); truelight@0: truelight@2395: AI_RunGameLoop(); truelight@0: truelight@0: CallWindowTickEvent(); truelight@0: NewsLoop(); signde@206: _current_player = p; truelight@0: } truelight@0: } truelight@0: rubidium@6573: static void DoAutosave() truelight@0: { truelight@0: char buf[200]; truelight@193: truelight@6228: #if defined(PSP) truelight@6228: /* Autosaving in networking is too time expensive for the PSP */ truelight@6228: if (_networking) truelight@6228: return; truelight@6228: #endif /* PSP */ truelight@6228: Darkvater@4848: if (_patches.keep_all_autosave && _local_player != PLAYER_SPECTATOR) { celestar@1962: const Player *p = GetPlayer(_local_player); Darkvater@4912: char* s = buf; Darkvater@4912: Darkvater@5296: s += snprintf(buf, lengthof(buf), "%s%s", _paths.autosave_dir, PATHSEP); Darkvater@1500: tron@534: SetDParam(0, p->name_1); tron@534: SetDParam(1, p->name_2); tron@534: SetDParam(2, _date); Darkvater@4912: s = GetString(s, STR_4004, lastof(buf)); Darkvater@4912: strecpy(s, ".sav", lastof(buf)); belugas@6677: } else { // generate a savegame name and number according to _patches.max_num_autosaves Darkvater@5296: snprintf(buf, lengthof(buf), "%s%sautosave%d.sav", _paths.autosave_dir, PATHSEP, _autosave_ctr); bjarni@2672: bjarni@2672: _autosave_ctr++; bjarni@2672: if (_autosave_ctr >= _patches.max_num_autosaves) { belugas@6677: /* we reached the limit for numbers of autosaves. We will start over */ bjarni@2672: _autosave_ctr = 0; bjarni@2672: } truelight@0: } truelight@0: Darkvater@5568: DEBUG(sl, 2, "Autosaving to '%s'", buf); truelight@0: if (SaveOrLoad(buf, SL_SAVE) != SL_OK) truelight@0: ShowErrorMessage(INVALID_STRING_ID, STR_AUTOSAVE_FAILED, 0, 0); truelight@0: } truelight@0: Darkvater@1397: static void ScrollMainViewport(int x, int y) Darkvater@1397: { rubidium@4536: if (_game_mode != GM_MENU) { Darkvater@1397: Window *w = FindWindowById(WC_MAIN_WINDOW, 0); Darkvater@1397: assert(w); Darkvater@1397: Darkvater@1397: WP(w,vp_d).scrollpos_x += x << w->viewport->zoom; Darkvater@1397: WP(w,vp_d).scrollpos_y += y << w->viewport->zoom; Darkvater@1397: } Darkvater@1397: } Darkvater@1397: Darkvater@1397: static const int8 scrollamt[16][2] = { rubidium@4344: { 0, 0}, belugas@6677: {-2, 0}, ///< 1 : left belugas@6677: { 0, -2}, ///< 2 : up belugas@6677: {-2, -1}, ///< 3 : left + up belugas@6677: { 2, 0}, ///< 4 : right belugas@6677: { 0, 0}, ///< 5 : left + right belugas@6677: { 2, -1}, ///< 6 : right + up belugas@6677: { 0, -2}, ///< 7 : left + right + up = up belugas@6677: { 0 ,2}, ///< 8 : down belugas@6677: {-2 ,1}, ///< 9 : down+left belugas@6677: { 0, 0}, ///< 10 : impossible belugas@6677: {-2, 0}, ///< 11 : left + up + down = left belugas@6677: { 2, 1}, ///< 12 : down+right belugas@6677: { 0, 2}, ///< 13 : left + right + down = down belugas@6677: { 0, -2}, ///< 14 : left + right + up = up belugas@6677: { 0, 0}, ///< 15 : impossible Darkvater@1397: }; Darkvater@1397: rubidium@6573: static void HandleKeyScrolling() Darkvater@1397: { Darkvater@1397: if (_dirkeys && !_no_scroll) { Darkvater@1397: int factor = _shift_pressed ? 50 : 10; Darkvater@1397: ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor); Darkvater@1397: } Darkvater@1397: } dominik@1199: rubidium@6573: void GameLoop() truelight@0: { Darkvater@2380: ThreadMsg message; Darkvater@2380: Darkvater@2380: if ((message = OTTD_PollThreadEvent()) != 0) ProcessSentMessage(message); truelight@0: belugas@6677: /* autosave game? */ truelight@0: if (_do_autosave) { truelight@0: _do_autosave = false; truelight@0: DoAutosave(); truelight@0: RedrawAutosave(); truelight@0: } truelight@0: belugas@6677: /* handle scrolling of the main window */ tron@4502: HandleKeyScrolling(); dominik@1199: belugas@6677: /* make a screenshot? */ belugas@4184: if (IsScreenshotRequested()) ShowScreenshotResult(MakeScreenshot()); truelight@0: belugas@6677: /* switch game mode? */ tron@3024: if (_switch_mode != SM_NONE) { tron@3024: SwitchMode(_switch_mode); truelight@0: _switch_mode = SM_NONE; truelight@0: } truelight@0: truelight@0: IncreaseSpriteLRU(); truelight@0: InteractiveRandom(); truelight@0: tron@2639: if (_scroller_click_timeout > 3) { truelight@0: _scroller_click_timeout -= 3; tron@2639: } else { truelight@0: _scroller_click_timeout = 0; tron@2639: } truelight@0: truelight@0: _caret_timer += 3; tron@2639: _timer_counter += 8; truelight@0: CursorTick(); truelight@0: truelight@543: #ifdef ENABLE_NETWORK belugas@6677: /* Check for UDP stuff */ Darkvater@4830: if (_network_available) NetworkUDPGameLoop(); truelight@0: truelight@4300: if (_networking && !IsGeneratingWorld()) { belugas@6677: /* Multiplayer */ truelight@543: NetworkGameLoop(); truelight@543: } else { truelight@543: if (_network_reconnect > 0 && --_network_reconnect == 0) { belugas@6677: /* This means that we want to reconnect to the last host belugas@6677: * We do this here, because it means that the network is really closed */ truelight@543: NetworkClientConnectGame(_network_last_host, _network_last_port); signde@206: } belugas@6677: /* Singleplayer */ darkvater@213: StateGameLoop(); truelight@0: } truelight@543: #else truelight@543: StateGameLoop(); truelight@543: #endif /* ENABLE_NETWORK */ truelight@0: truelight@6557: if (!_pause_game && _display_opt & DO_FULL_ANIMATION) DoPaletteAnimations(); truelight@0: truelight@6557: if (!_pause_game || _cheats.build_in_pause.value) MoveAllTextEffects(); truelight@0: pasky@1570: InputLoop(); truelight@0: tron@1608: MusicLoop(); truelight@0: } truelight@0: rubidium@6573: void BeforeSaveGame() truelight@0: { belugas@4171: const Window *w = FindWindowById(WC_MAIN_WINDOW, 0); truelight@0: darkvater@983: if (w != NULL) { tron@2643: _saved_scrollpos_x = WP(w, const vp_d).scrollpos_x; tron@2643: _saved_scrollpos_y = WP(w, const vp_d).scrollpos_y; darkvater@983: _saved_scrollpos_zoom = w->viewport->zoom; darkvater@983: } truelight@0: } truelight@0: rubidium@6573: static void ConvertTownOwner() truelight@0: { tron@1977: TileIndex tile; truelight@0: tron@863: for (tile = 0; tile != MapSize(); tile++) { tron@4000: switch (GetTileType(tile)) { tron@4000: case MP_STREET: tron@4000: if (IsLevelCrossing(tile) && GetCrossingRoadOwner(tile) & 0x80) { tron@4000: SetCrossingRoadOwner(tile, OWNER_TOWN); tron@4000: } tron@4000: /* FALLTHROUGH */ truelight@0: tron@4000: case MP_TUNNELBRIDGE: tron@4000: if (GetTileOwner(tile) & 0x80) SetTileOwner(tile, OWNER_TOWN); tron@4000: break; tron@4000: tron@4000: default: break; truelight@0: } truelight@0: } truelight@0: } truelight@0: belugas@6677: /* before savegame version 4, the name of the company determined if it existed */ rubidium@6573: static void CheckIsPlayerActive() truelight@0: { belugas@4171: Player *p; tron@2639: truelight@0: FOR_ALL_PLAYERS(p) { tron@2639: if (p->name_1 != 0) p->is_active = true; truelight@0: } truelight@0: } truelight@0: belugas@6677: /* since savegame version 4.1, exclusive transport rights are stored at towns */ rubidium@6573: static void UpdateExclusiveRights() dominik@121: { belugas@4171: Town *t; tron@2639: tron@2639: FOR_ALL_TOWNS(t) { rubidium@5838: t->exclusivity = INVALID_PLAYER; dominik@121: } truelight@193: dominik@123: /* FIXME old exclusive rights status is not being imported (stored in s->blocked_months_obsolete) rubidium@4549: * could be implemented this way: rubidium@4549: * 1.) Go through all stations rubidium@4549: * Build an array town_blocked[ town_id ][ player_id ] rubidium@4549: * that stores if at least one station in that town is blocked for a player rubidium@4549: * 2.) Go through that array, if you find a town that is not blocked for rubidium@4549: * one player, but for all others, then give him exclusivity. rubidium@4549: */ dominik@121: } dominik@121: tron@2756: static const byte convert_currency[] = { dominik@762: 0, 1, 12, 8, 3, dominik@762: 10, 14, 19, 4, 5, dominik@762: 9, 11, 13, 6, 17, dominik@762: 16, 22, 21, 7, 15, dominik@762: 18, 2, 20, }; dominik@762: belugas@6677: /* since savegame version 4.2 the currencies are arranged differently */ rubidium@6573: static void UpdateCurrencies() dominik@762: { dominik@768: _opt.currency = convert_currency[_opt.currency]; dominik@762: } dominik@762: tron@1181: /* Up to revision 1413 the invisible tiles at the southern border have not been tron@1181: * MP_VOID, even though they should have. This is fixed by this function tron@1181: */ rubidium@6573: static void UpdateVoidTiles() dominik@929: { tron@959: uint i; tron@1181: tron@3075: for (i = 0; i < MapMaxY(); ++i) MakeVoid(i * MapSizeX() + MapMaxX()); tron@3075: for (i = 0; i < MapSizeX(); ++i) MakeVoid(MapSizeX() * MapMaxY() + i); dominik@929: } dominik@929: belugas@6677: /* since savegame version 6.0 each sign has an "owner", signs without owner (from old games are set to 255) */ rubidium@6573: static void UpdateSignOwner() dominik@1165: { truelight@4349: Sign *si; tron@2639: truelight@4349: FOR_ALL_SIGNS(si) si->owner = OWNER_NONE; dominik@1165: } dominik@1165: rubidium@6573: extern void UpdateOldAircraft(); truelight@0: tron@3656: tron@3656: static inline RailType UpdateRailType(RailType rt, RailType min) tron@3656: { tron@3656: return rt >= min ? (RailType)(rt + 1): rt; tron@3656: } tron@3656: rubidium@6573: bool AfterLoadGame() truelight@0: { tron@5957: TileIndex map_size = MapSize(); truelight@0: Window *w; truelight@0: ViewPort *vp; celestar@2147: Player *p; truelight@988: belugas@6677: /* in version 2.1 of the savegame, town owner was unified. */ truelight@2685: if (CheckSavegameVersionOldStyle(2, 1)) ConvertTownOwner(); truelight@0: belugas@6677: /* from version 4.1 of the savegame, exclusive rights are stored at towns */ truelight@2685: if (CheckSavegameVersionOldStyle(4, 1)) UpdateExclusiveRights(); truelight@193: belugas@6677: /* from version 4.2 of the savegame, currencies are in a different order */ truelight@2685: if (CheckSavegameVersionOldStyle(4, 2)) UpdateCurrencies(); dominik@762: belugas@6677: /* from version 6.1 of the savegame, signs have an "owner" */ truelight@2685: if (CheckSavegameVersionOldStyle(6, 1)) UpdateSignOwner(); dominik@1165: truelight@831: /* In old version there seems to be a problem that water is owned by truelight@831: OWNER_NONE, not OWNER_WATER.. I can't replicate it for the current truelight@2685: (4.3) version, so I just check when versions are older, and then truelight@831: walk through the whole map.. */ truelight@2685: if (CheckSavegameVersionOldStyle(4, 3)) { tron@5957: for (TileIndex t = 0; t < map_size; t++) { tron@5957: if (IsTileType(t, MP_WATER) && GetTileOwner(t) >= MAX_PLAYERS) { tron@5957: SetTileOwner(t, OWNER_WATER); tron@5957: } tron@5957: } truelight@831: } truelight@831: belugas@6677: /* convert road side to my format. */ truelight@0: if (_opt.road_side) _opt.road_side = 1; truelight@0: Darkvater@5898: /* Check if all NewGRFs are present, we are very strict in MP mode */ maedhros@6555: GRFListCompatibility gcf_res = IsGoodGRFConfigList(); maedhros@6555: if (_networking && gcf_res != GLC_ALL_GOOD) return false; Darkvater@5898: Darkvater@5898: switch (gcf_res) { maedhros@6555: case GLC_COMPATIBLE: _switch_mode_errorstr = STR_NEWGRF_COMPATIBLE_LOAD_WARNING; break; maedhros@6555: case GLC_NOT_FOUND: _switch_mode_errorstr = STR_NEWGRF_DISABLED_WARNING; break; glx@5903: default: break; Darkvater@5898: } peter1138@5228: glx@5340: /* Update current year glx@5340: * must be done before loading sprites as some newgrfs check it */ glx@5340: SetDate(_date); glx@5340: belugas@6677: /* Load the sprites */ truelight@0: GfxLoadSprites(); peter1138@5155: LoadStringWidthTable(); truelight@0: peter1138@2814: /* Connect front and rear engines of multiheaded trains and converts peter1138@2814: * subtype to the new format */ peter1138@2814: if (CheckSavegameVersionOldStyle(17, 1)) ConvertOldMultiheadToNew(); peter1138@2814: bjarni@2928: /* Connect front and rear engines of multiheaded trains */ bjarni@2928: ConnectMultiheadedTrains(); bjarni@2855: belugas@6677: /* reinit the landscape variables (landscape might have changed) */ truelight@0: InitializeLandscapeVariables(true); truelight@193: belugas@6677: /* Update all vehicles */ truelight@0: AfterLoadVehicles(); truelight@1542: belugas@6677: /* Update all waypoints */ truelight@2685: if (CheckSavegameVersion(12)) FixOldWaypoints(); truelight@1542: truelight@1542: UpdateAllWaypointSigns(); truelight@1542: belugas@6677: /* in version 2.2 of the savegame, we have new airports */ truelight@2685: if (CheckSavegameVersionOldStyle(2, 2)) UpdateOldAircraft(); truelight@0: truelight@0: UpdateAllStationVirtCoord(); truelight@0: belugas@6677: /* Setup town coords */ truelight@0: AfterLoadTown(); truelight@0: UpdateAllSignVirtCoords(); truelight@0: belugas@6677: /* make sure there is a town in the game */ tron@2639: if (_game_mode == GM_NORMAL && !ClosestTownFromTile(0, (uint)-1)) { truelight@0: _error_message = STR_NO_TOWN_IN_SCENARIO; truelight@0: return false; truelight@0: } truelight@0: belugas@6677: /* Initialize windows */ Darkvater@1474: ResetWindowSystem(); truelight@0: SetupColorsAndInitialWindow(); truelight@0: truelight@0: w = FindWindowById(WC_MAIN_WINDOW, 0); truelight@0: truelight@0: WP(w,vp_d).scrollpos_x = _saved_scrollpos_x; truelight@0: WP(w,vp_d).scrollpos_y = _saved_scrollpos_y; truelight@193: truelight@0: vp = w->viewport; truelight@0: vp->zoom = _saved_scrollpos_zoom; truelight@0: vp->virtual_width = vp->width << vp->zoom; truelight@0: vp->virtual_height = vp->height << vp->zoom; truelight@0: belugas@6677: /* in version 4.1 of the savegame, is_active was introduced to determine belugas@6677: * if a player does exist, rather then checking name_1 */ truelight@2685: if (CheckSavegameVersionOldStyle(4, 1)) CheckIsPlayerActive(); truelight@193: belugas@6677: /* the void tiles on the southern border used to belong to a wrong class (pre 4.3). */ truelight@2685: if (CheckSavegameVersionOldStyle(4, 3)) UpdateVoidTiles(); dominik@929: belugas@6677: /* If Load Scenario / New (Scenario) Game is used, belugas@6677: * a player does not exist yet. So create one here. belugas@6677: * 1 exeption: network-games. Those can have 0 players belugas@6677: * But this exeption is not true for network_servers! */ truelight@543: if (!_players[0].is_active && (!_networking || (_networking && _network_server))) truelight@139: DoStartupNewPlayer(false); truelight@0: darkvater@152: DoZoomInOutWindow(ZOOM_NONE, w); // update button status truelight@0: MarkWholeScreenDirty(); truelight@0: tron@6131: for (TileIndex t = 0; t < map_size; t++) { tron@6131: switch (GetTileType(t)) { tron@6419: case MP_STATION: { tron@6419: Station *st = GetStationByTile(t); tron@6419: tron@6419: st->rect.BeforeAddTile(t, StationRect::ADD_FORCE); tron@6419: tron@6131: switch (GetStationType(t)) { tron@6131: case STATION_TRUCK: tron@6131: case STATION_BUS: tron@6131: if (CheckSavegameVersion(6)) { tron@6419: /* From this version on there can be multiple road stops of the tron@6419: * same type per station. Convert the existing stops to the new tron@6419: * internal data structure. */ tron@6131: RoadStop *rs = new RoadStop(t); tron@6131: if (rs == NULL) error("Too many road stops in savegame"); tron@6130: tron@6131: RoadStop **head = tron@6131: IsTruckStop(t) ? &st->truck_stops : &st->bus_stops; tron@6131: *head = rs; tron@6131: } tron@6131: break; tron@6131: tron@6135: case STATION_OILRIG: { tron@6135: /* Very old savegames sometimes have phantom oil rigs, i.e. tron@6135: * an oil rig which got shut down, but not completly removed from tron@6135: * the map tron@6135: */ tron@6135: TileIndex t1 = TILE_ADDXY(t, 1, 0); tron@6137: if (IsTileType(t1, MP_INDUSTRY) && tron@6137: GetIndustryGfx(t1) == GFX_OILRIG_3) { tron@6137: /* The internal encoding of oil rigs was changed twice. tron@6137: * It was 3 (till 2.2) and later 5 (till 5.1). tron@6137: * Setting it unconditionally does not hurt. tron@6137: */ tron@6137: GetStationByTile(t)->airport_type = AT_OILRIG; tron@6137: } else { tron@6135: DeleteOilRig(t); tron@6135: } tron@6135: break; tron@6135: } tron@6135: tron@6131: default: break; tron@6131: } tron@6131: break; tron@6419: } tron@6131: tron@6131: default: break; tron@6130: } tron@6130: } tron@6130: truelight@2685: /* In version 6.1 we put the town index in the map-array. To do this, we need truelight@2685: * to use m2 (16bit big), so we need to clean m2, and that is where this is truelight@2685: * all about ;) */ truelight@2685: if (CheckSavegameVersionOldStyle(6, 1)) { tron@5957: for (TileIndex t = 0; t < map_size; t++) { tron@5957: switch (GetTileType(t)) { tron@3983: case MP_HOUSE: tron@5957: _m[t].m4 = _m[t].m2; tron@5957: SetTownIndex(t, CalcClosestTownFromTile(t, (uint)-1)->index); tron@3983: break; tron@3983: tron@3983: case MP_STREET: tron@5957: _m[t].m4 |= (_m[t].m2 << 4); tron@5957: if (IsTileOwner(t, OWNER_TOWN)) { tron@5957: SetTownIndex(t, CalcClosestTownFromTile(t, (uint)-1)->index); tron@3983: } else { tron@5957: SetTownIndex(t, 0); tron@3983: } tron@3983: break; tron@3983: tron@3983: default: break; celestar@1264: } tron@5957: } celestar@1264: } celestar@1264: truelight@2685: /* From version 9.0, we update the max passengers of a town (was sometimes negative truelight@2685: * before that. */ truelight@2685: if (CheckSavegameVersion(9)) { celestar@1377: Town *t; tron@2639: FOR_ALL_TOWNS(t) UpdateTownMaxPass(t); celestar@1377: } celestar@1377: truelight@2685: /* From version 16.0, we included autorenew on engines, which are now saved, but truelight@2685: * of course, we do need to initialize them for older savegames. */ truelight@2685: if (CheckSavegameVersion(16)) { bjarni@2293: FOR_ALL_PLAYERS(p) { rubidium@5564: p->engine_renew_list = NULL; rubidium@5564: p->engine_renew = false; bjarni@2293: p->engine_renew_months = -6; rubidium@5564: p->engine_renew_money = 100000; bjarni@2293: } rubidium@5564: rubidium@5564: /* When loading a game, _local_player is not yet set to the correct value. rubidium@5564: * However, in a dedicated server we are a spectator, so nothing needs to rubidium@5564: * happen. In case we are not a dedicated server, the local player always rubidium@5564: * becomes player 0, unless we are in the scenario editor where all the rubidium@5564: * players are 'invalid'. rubidium@5564: */ rubidium@5838: if (!_network_dedicated && IsValidPlayer(PLAYER_FIRST)) { rubidium@5838: p = GetPlayer(PLAYER_FIRST); rubidium@5564: p->engine_renew = _patches.autorenew; bjarni@2322: p->engine_renew_months = _patches.autorenew_months; rubidium@5564: p->engine_renew_money = _patches.autorenew_money; bjarni@2322: } bjarni@2293: } bjarni@2293: celestar@5573: if (CheckSavegameVersion(42)) { celestar@5573: Vehicle* v; celestar@5573: tron@5957: for (TileIndex t = 0; t < map_size; t++) { tron@5957: if (MayHaveBridgeAbove(t)) ClearBridgeMiddle(t); tron@5957: if (IsBridgeTile(t)) { tron@5957: if (HASBIT(_m[t].m5, 6)) { // middle part tron@5957: Axis axis = (Axis)GB(_m[t].m5, 0, 1); celestar@5573: tron@5957: if (HASBIT(_m[t].m5, 5)) { // transport route under bridge? tron@5957: if (GB(_m[t].m5, 3, 2) == TRANSPORT_RAIL) { celestar@5573: MakeRailNormal( tron@5957: t, tron@5957: GetTileOwner(t), celestar@5573: axis == AXIS_X ? TRACK_BIT_Y : TRACK_BIT_X, tron@5957: GetRailType(t) celestar@5573: ); celestar@5573: } else { tron@5957: TownID town = IsTileOwner(t, OWNER_TOWN) ? ClosestTownFromTile(t, (uint)-1)->index : 0; celestar@5573: celestar@5573: MakeRoadNormal( tron@5957: t, tron@5957: GetTileOwner(t), celestar@5573: axis == AXIS_X ? ROAD_Y : ROAD_X, celestar@5573: town celestar@5573: ); celestar@5573: } celestar@5573: } else { tron@5957: if (GB(_m[t].m5, 3, 2) == 0) { tron@5957: MakeClear(t, CLEAR_GRASS, 3); celestar@5573: } else { tron@5957: MakeCanal(t, GetTileOwner(t)); celestar@5573: } celestar@5573: } tron@5957: SetBridgeMiddle(t, axis); celestar@5573: } else { // ramp tron@5957: Axis axis = (Axis)GB(_m[t].m5, 0, 1); tron@5957: uint north_south = GB(_m[t].m5, 5, 1); celestar@5573: DiagDirection dir = ReverseDiagDir(XYNSToDiagDir(axis, north_south)); tron@5957: TransportType type = (TransportType)GB(_m[t].m5, 1, 2); celestar@5573: tron@5957: _m[t].m5 = 1 << 7 | type << 2 | dir; celestar@5573: } celestar@5573: } celestar@5573: } celestar@5573: celestar@5573: FOR_ALL_VEHICLES(v) { rubidium@6585: if (v->type != VEH_TRAIN && v->type != VEH_ROAD) continue; celestar@5573: if (IsBridgeTile(v->tile)) { celestar@5573: DiagDirection dir = GetBridgeRampDirection(v->tile); celestar@5573: celestar@5573: if (dir != DirToDiagDir(v->direction)) continue; celestar@5573: switch (dir) { celestar@5573: default: NOT_REACHED(); celestar@5573: case DIAGDIR_NE: if ((v->x_pos & 0xF) != 0) continue; break; celestar@5573: case DIAGDIR_SE: if ((v->y_pos & 0xF) != TILE_SIZE - 1) continue; break; celestar@5573: case DIAGDIR_SW: if ((v->x_pos & 0xF) != TILE_SIZE - 1) continue; break; celestar@5573: case DIAGDIR_NW: if ((v->y_pos & 0xF) != 0) continue; break; celestar@5573: } celestar@5573: } else if (v->z_pos > GetSlopeZ(v->x_pos, v->y_pos)) { celestar@5573: v->tile = GetNorthernBridgeEnd(v->tile); celestar@5573: } else { celestar@5573: continue; celestar@5573: } rubidium@6585: if (v->type == VEH_TRAIN) { rubidium@5838: v->u.rail.track = TRACK_BIT_WORMHOLE; celestar@5573: } else { rubidium@6326: v->u.road.state = RVSB_WORMHOLE; celestar@5573: } celestar@5573: } celestar@5573: } celestar@5573: rubidium@6498: if (CheckSavegameVersion(48)) { rubidium@6498: for (TileIndex t = 0; t < map_size; t++) { rubidium@6498: switch (GetTileType(t)) { rubidium@6498: case MP_RAILWAY: rubidium@6498: if (IsPlainRailTile(t)) { rubidium@6498: /* Swap ground type and signal type for plain rail tiles, so the rubidium@6498: * ground type uses the same bits as for depots and waypoints. */ rubidium@6498: uint tmp = GB(_m[t].m4, 0, 4); rubidium@6498: SB(_m[t].m4, 0, 4, GB(_m[t].m2, 0, 4)); rubidium@6498: SB(_m[t].m2, 0, 4, tmp); rubidium@6498: } else if (HASBIT(_m[t].m5, 2)) { rubidium@6498: /* Split waypoint and depot rail type and remove the subtype. */ rubidium@6498: CLRBIT(_m[t].m5, 2); rubidium@6498: CLRBIT(_m[t].m5, 6); rubidium@6498: } rubidium@6498: break; rubidium@6498: rubidium@6498: case MP_STREET: rubidium@6498: /* Swap m3 and m4, so the track type for rail crossings is the rubidium@6498: * same as for normal rail. */ rubidium@6498: Swap(_m[t].m3, _m[t].m4); rubidium@6498: break; rubidium@6498: rubidium@6498: default: break; rubidium@6498: } rubidium@6498: } rubidium@6498: } rubidium@6498: celestar@3355: /* Elrails got added in rev 24 */ celestar@3355: if (CheckSavegameVersion(24)) { belugas@4171: Vehicle *v; tron@3656: RailType min_rail = RAILTYPE_ELECTRIC; celestar@3355: celestar@3355: FOR_ALL_VEHICLES(v) { rubidium@6585: if (v->type == VEH_TRAIN) { tron@6074: RailType rt = RailVehInfo(v->engine_type)->railtype; celestar@3355: celestar@3355: v->u.rail.railtype = rt; tron@3656: if (rt == RAILTYPE_ELECTRIC) min_rail = RAILTYPE_RAIL; celestar@3355: } celestar@3355: } celestar@3355: celestar@3355: /* .. so we convert the entire map from normal to elrail (so maintain "fairness") */ tron@5957: for (TileIndex t = 0; t < map_size; t++) { celestar@3355: switch (GetTileType(t)) { celestar@3355: case MP_RAILWAY: tron@3656: SetRailType(t, UpdateRailType(GetRailType(t), min_rail)); celestar@3355: break; celestar@3355: celestar@3355: case MP_STREET: tron@3656: if (IsLevelCrossing(t)) { rubidium@6498: SetRailType(t, UpdateRailType(GetRailType(t), min_rail)); tron@3656: } celestar@3355: break; celestar@3355: celestar@3355: case MP_STATION: tron@4077: if (IsRailwayStation(t)) { tron@3656: SetRailType(t, UpdateRailType(GetRailType(t), min_rail)); tron@3656: } celestar@3355: break; celestar@3355: celestar@3355: case MP_TUNNELBRIDGE: tron@3367: if (IsTunnel(t)) { tron@3367: if (GetTunnelTransportType(t) == TRANSPORT_RAIL) { tron@3656: SetRailType(t, UpdateRailType(GetRailType(t), min_rail)); celestar@3355: } celestar@3355: } else { tron@3367: if (GetBridgeTransportType(t) == TRANSPORT_RAIL) { tron@3656: SetRailType(t, UpdateRailType(GetRailType(t), min_rail)); celestar@3355: } celestar@3355: } celestar@3355: break; celestar@3355: celestar@3355: default: celestar@3355: break; celestar@3355: } celestar@3355: } celestar@3355: celestar@3355: FOR_ALL_VEHICLES(v) { rubidium@6585: if (v->type == VEH_TRAIN && (IsFrontEngine(v) || IsFreeWagon(v))) TrainConsistChanged(v); celestar@3355: } celestar@3355: celestar@3355: } celestar@3355: tron@2805: /* In version 16.1 of the savegame a player can decide if trains, which get tron@2805: * replaced, shall keep their old length. In all prior versions, just default tron@2805: * to false */ truelight@2685: if (CheckSavegameVersionOldStyle(16, 1)) { tron@4077: FOR_ALL_PLAYERS(p) p->renew_keep_length = false; bjarni@2617: } bjarni@2617: peter1138@2670: /* In version 17, ground type is moved from m2 to m4 for depots and peter1138@2670: * waypoints to make way for storing the index in m2. The custom graphics peter1138@2670: * id which was stored in m4 is now saved as a grf/id reference in the peter1138@2670: * waypoint struct. */ truelight@2685: if (CheckSavegameVersion(17)) { peter1138@2670: Waypoint *wp; peter1138@2670: peter1138@2670: FOR_ALL_WAYPOINTS(wp) { truelight@4346: if (wp->deleted == 0) { belugas@3676: const StationSpec *statspec = NULL; peter1138@2670: peter1138@2670: if (HASBIT(_m[wp->xy].m3, 4)) belugas@3676: statspec = GetCustomStationSpec(STAT_CLASS_WAYP, _m[wp->xy].m4 + 1); peter1138@2670: belugas@3676: if (statspec != NULL) { peter1138@2670: wp->stat_id = _m[wp->xy].m4 + 1; peter1138@6947: wp->grfid = statspec->grffile->grfid; belugas@3676: wp->localidx = statspec->localidx; peter1138@2670: } else { belugas@6677: /* No custom graphics set, so set to default. */ peter1138@2670: wp->stat_id = 0; peter1138@2670: wp->grfid = 0; peter1138@2670: wp->localidx = 0; peter1138@2670: } peter1138@2670: belugas@6677: /* Move ground type bits from m2 to m4. */ peter1138@2670: _m[wp->xy].m4 = GB(_m[wp->xy].m2, 0, 4); belugas@6677: /* Store waypoint index in the tile. */ peter1138@2670: _m[wp->xy].m2 = wp->index; peter1138@2670: } peter1138@2670: } peter1138@2670: } else { peter1138@2670: /* As of version 17, we recalculate the custom graphic ID of waypoints peter1138@2670: * from the GRF ID / station index. */ Darkvater@5350: AfterLoadWaypoints(); peter1138@2670: } peter1138@2670: Darkvater@2916: /* From version 15, we moved a semaphore bit from bit 2 to bit 3 in m4, making Darkvater@2916: * room for PBS. Now in version 21 move it back :P. */ Darkvater@2916: if (CheckSavegameVersion(21) && !CheckSavegameVersion(15)) { tron@5957: for (TileIndex t = 0; t < map_size; t++) { tron@5958: switch (GetTileType(t)) { tron@5958: case MP_RAILWAY: tron@5958: if (HasSignals(t)) { belugas@6677: /* convert PBS signals to combo-signals */ rubidium@6498: if (HASBIT(_m[t].m2, 2)) SetSignalType(t, SIGTYPE_COMBO); Darkvater@2916: belugas@6677: /* move the signal variant back */ rubidium@6498: SetSignalVariant(t, HASBIT(_m[t].m2, 3) ? SIG_SEMAPHORE : SIG_ELECTRIC); rubidium@6498: CLRBIT(_m[t].m2, 3); tron@5958: } tron@5958: belugas@6677: /* Clear PBS reservation on track */ tron@5958: if (!IsTileDepotType(t, TRANSPORT_RAIL)) { tron@5958: SB(_m[t].m4, 4, 4, 0); tron@5958: } else { tron@5958: CLRBIT(_m[t].m3, 6); tron@5958: } tron@5958: break; tron@5958: belugas@6677: case MP_STREET: /* Clear PBS reservation on crossing */ tron@5958: if (IsLevelCrossing(t)) CLRBIT(_m[t].m5, 0); tron@5958: break; tron@5958: belugas@6677: case MP_STATION: /* Clear PBS reservation on station */ tron@5957: CLRBIT(_m[t].m3, 6); tron@5958: break; Darkvater@2916: tron@5958: default: break; tron@5958: } tron@5957: } Darkvater@2916: } Darkvater@2916: Darkvater@3121: if (CheckSavegameVersion(22)) UpdatePatches(); Darkvater@3121: celestar@3431: if (CheckSavegameVersion(25)) { celestar@3431: Vehicle *v; celestar@3431: FOR_ALL_VEHICLES(v) { rubidium@6585: if (v->type == VEH_ROAD) { celestar@3431: v->vehstatus &= ~0x40; celestar@3431: v->u.road.slot = NULL; celestar@3431: v->u.road.slot_age = 0; celestar@3431: } celestar@3431: } rubidium@5928: } else { rubidium@5928: Vehicle *v; rubidium@5928: FOR_ALL_VEHICLES(v) { rubidium@6585: if (v->type == VEH_ROAD && v->u.road.slot != NULL) v->u.road.slot->num_vehicles++; rubidium@5928: } celestar@3431: } celestar@3431: celestar@3580: if (CheckSavegameVersion(26)) { celestar@3580: Station *st; celestar@3580: FOR_ALL_STATIONS(st) { rubidium@6585: st->last_vehicle_type = VEH_INVALID; celestar@3580: } celestar@3580: } celestar@3580: KUDr@3900: YapfNotifyTrackLayoutChange(INVALID_TILE, INVALID_TRACK); KUDr@3900: peter1138@4603: if (CheckSavegameVersion(34)) FOR_ALL_PLAYERS(p) ResetPlayerLivery(p); peter1138@4603: tron@2639: FOR_ALL_PLAYERS(p) p->avail_railtypes = GetPlayerRailtypes(p->index); celestar@2147: peter1138@3765: if (!CheckSavegameVersion(27)) AfterLoadStations(); peter1138@3765: bjarni@4574: { bjarni@4574: /* Set up the engine count for all players */ bjarni@4574: Player *players[MAX_PLAYERS]; bjarni@4574: const Vehicle *v; bjarni@4574: rubidium@5838: for (PlayerID i = PLAYER_FIRST; i < MAX_PLAYERS; i++) players[i] = GetPlayer(i); bjarni@4574: bjarni@4574: FOR_ALL_VEHICLES(v) { bjarni@4574: if (!IsEngineCountable(v)) continue; bjarni@4574: players[v->owner]->num_engines[v->engine_type]++; bjarni@4574: } bjarni@4574: } bjarni@4574: rubidium@4326: /* Time starts at 0 instead of 1920. rubidium@4326: * Account for this in older games by adding an offset */ rubidium@4326: if (CheckSavegameVersion(31)) { rubidium@4326: Station *st; rubidium@4326: Waypoint *wp; rubidium@4326: Engine *e; rubidium@4326: Player *player; rubidium@4326: Industry *i; rubidium@4326: Vehicle *v; rubidium@4326: rubidium@4326: _date += DAYS_TILL_ORIGINAL_BASE_YEAR; truelight@4383: _cur_year += ORIGINAL_BASE_YEAR; rubidium@4326: rubidium@4326: FOR_ALL_STATIONS(st) st->build_date += DAYS_TILL_ORIGINAL_BASE_YEAR; rubidium@4326: FOR_ALL_WAYPOINTS(wp) wp->build_date += DAYS_TILL_ORIGINAL_BASE_YEAR; rubidium@4326: FOR_ALL_ENGINES(e) e->intro_date += DAYS_TILL_ORIGINAL_BASE_YEAR; rubidium@4326: FOR_ALL_PLAYERS(player) player->inaugurated_year += ORIGINAL_BASE_YEAR; rubidium@4326: FOR_ALL_INDUSTRIES(i) i->last_prod_year += ORIGINAL_BASE_YEAR; rubidium@4326: rubidium@4326: FOR_ALL_VEHICLES(v) { rubidium@4326: v->date_of_last_service += DAYS_TILL_ORIGINAL_BASE_YEAR; rubidium@4326: v->build_year += ORIGINAL_BASE_YEAR; rubidium@4326: } rubidium@4326: } rubidium@4326: truelight@4328: /* From 32 on we save the industry who made the farmland. truelight@4328: * To give this prettyness to old savegames, we remove all farmfields and truelight@4328: * plant new ones. */ truelight@4328: if (CheckSavegameVersion(32)) { truelight@4328: Industry *i; truelight@4328: tron@5957: for (TileIndex t = 0; t < map_size; t++) { tron@5957: if (IsTileType(t, MP_CLEAR) && IsClearGround(t, CLEAR_FIELDS)) { tron@5957: MakeClear(t, CLEAR_GRASS, 3); truelight@4328: } tron@5957: } truelight@4328: truelight@4328: FOR_ALL_INDUSTRIES(i) { truelight@4328: uint j; truelight@4328: truelight@4328: if (i->type == IT_FARM || i->type == IT_FARM_2) { truelight@4328: for (j = 0; j != 50; j++) PlantRandomFarmField(i); truelight@4328: } truelight@4328: } truelight@4328: } truelight@4328: bjarni@4712: /* Setting no refit flags to all orders in savegames from before refit in orders were added */ bjarni@4712: if (CheckSavegameVersion(36)) { bjarni@4712: Order *order; bjarni@4712: Vehicle *v; bjarni@4712: bjarni@4712: FOR_ALL_ORDERS(order) { bjarni@4712: order->refit_cargo = CT_NO_REFIT; bjarni@4712: order->refit_subtype = CT_NO_REFIT; bjarni@4712: } bjarni@4712: bjarni@4712: FOR_ALL_VEHICLES(v) { bjarni@4712: v->current_order.refit_cargo = CT_NO_REFIT; bjarni@4712: v->current_order.refit_subtype = CT_NO_REFIT; bjarni@4712: } bjarni@4712: } bjarni@4712: peter1138@5108: if (CheckSavegameVersion(37)) { peter1138@5108: ConvertNameArray(); peter1138@5108: } peter1138@5108: Darkvater@5314: /* from version 38 we have optional elrails, since we cannot know the Darkvater@5314: * preference of a user, let elrails enabled; it can be disabled manually */ tron@6099: if (CheckSavegameVersion(38)) _patches.disable_elrails = false; tron@6099: /* do the same as when elrails were enabled/disabled manually just now */ tron@6099: SettingsDisableElrail(_patches.disable_elrails); KUDr@5116: maedhros@6659: /* From version 53, the map array was changed for house tiles to allow maedhros@6658: * space for newhouses grf features. A new byte, m7, was also added. */ maedhros@6659: if (CheckSavegameVersion(53)) { maedhros@6658: for (TileIndex t = 0; t < map_size; t++) { maedhros@6658: _me[t].m7 = 0; maedhros@6658: maedhros@6658: if (IsTileType(t, MP_HOUSE)) { maedhros@6658: if (GB(_m[t].m3, 6, 2) != TOWN_HOUSE_COMPLETED) { maedhros@6658: /* Move the construction stage from m3[7..6] to m5[5..4]. maedhros@6658: * The construction counter does not have to move. */ maedhros@6658: SB(_m[t].m5, 3, 2, GB(_m[t].m3, 6, 2)); maedhros@6658: SB(_m[t].m3, 6, 2, 0); maedhros@6658: maedhros@6658: /* The "house is completed" bit is now in m6[2]. */ maedhros@6658: SetHouseCompleted(t, false); maedhros@6658: } else { maedhros@6658: /* The "lift has destination" bit has been moved from maedhros@6658: * m5[7] to m7[0]. */ maedhros@6658: SB(_me[t].m7, 0, 1, HASBIT(_m[t].m5, 7)); maedhros@6658: CLRBIT(_m[t].m5, 7); maedhros@6658: maedhros@6658: /* The "lift is moving" bit has been removed, as it does maedhros@6658: * the same job as the "lift has destination" bit. */ maedhros@6658: CLRBIT(_m[t].m1, 7); maedhros@6658: maedhros@6658: /* The position of the lift goes from m1[7..0] to m6[7..2], maedhros@6658: * making m1 totally free, now. The lift position does not maedhros@6658: * have to be a full byte since the maximum value is 36. */ maedhros@6658: SetLiftPosition(t, GB(_m[t].m1, 0, 6 )); maedhros@6658: maedhros@6658: _m[t].m1 = 0; maedhros@6658: _m[t].m3 = 0; maedhros@6658: SetHouseCompleted(t, true); maedhros@6658: } maedhros@6658: } maedhros@6658: } maedhros@6658: } maedhros@6658: maedhros@6658: /* Count the buildings after updating the map array. */ maedhros@6658: AfterLoadCountBuildings(); maedhros@6658: rubidium@5687: if (CheckSavegameVersion(43)) { tron@5957: for (TileIndex t = 0; t < map_size; t++) { tron@5957: if (IsTileType(t, MP_INDUSTRY)) { tron@5957: switch (GetIndustryGfx(t)) { rubidium@5687: case GFX_POWERPLANT_SPARKS: tron@5957: SetIndustryAnimationState(t, GB(_m[t].m1, 2, 5)); rubidium@5687: break; rubidium@5687: rubidium@5687: case GFX_OILWELL_ANIMATED_1: rubidium@5687: case GFX_OILWELL_ANIMATED_2: rubidium@5687: case GFX_OILWELL_ANIMATED_3: tron@5957: SetIndustryAnimationState(t, GB(_m[t].m1, 0, 2)); rubidium@5687: break; rubidium@5687: rubidium@5687: case GFX_COAL_MINE_TOWER_ANIMATED: rubidium@5687: case GFX_COPPER_MINE_TOWER_ANIMATED: rubidium@5687: case GFX_GOLD_MINE_TOWER_ANIMATED: tron@5957: SetIndustryAnimationState(t, _m[t].m1); rubidium@5687: break; rubidium@5687: rubidium@5687: default: /* No animation states to change */ rubidium@5687: break; rubidium@5687: } rubidium@5687: } tron@5957: } rubidium@5687: } rubidium@5687: celestar@5934: if (CheckSavegameVersion(44)) { celestar@5934: Vehicle *v; celestar@5934: /* If we remove a station while cargo from it is still enroute, payment calculation will assume celestar@5934: * 0, 0 to be the origin of the cargo, resulting in very high payments usually. v->cargo_source_xy celestar@5934: * stores the coordinates, preserving them even if the station is removed. However, if a game is loaded celestar@5934: * where this situation exists, the cargo-source information is lost. in this case, we set the origin celestar@5934: * to the current tile of the vehicle to prevent excessive profits celestar@5934: */ celestar@5934: FOR_ALL_VEHICLES(v) { celestar@5934: v->cargo_source_xy = IsValidStationID(v->cargo_source) ? GetStation(v->cargo_source)->xy : v->tile; celestar@5934: } peter1138@6630: peter1138@6630: /* Store position of the station where the goods come from, so there peter1138@6630: * are no very high payments when stations get removed. However, if the peter1138@6630: * station where the goods came from is already removed, the source peter1138@6630: * information is lost. In that case we set it to the position of this peter1138@6630: * station */ peter1138@6630: Station *st; peter1138@6630: FOR_ALL_STATIONS(st) { peter1138@6630: for (CargoID c = 0; c < NUM_CARGO; c++) { peter1138@6630: GoodsEntry *ge = &st->goods[c]; peter1138@6630: peter1138@6630: /* In old versions, enroute_from used 0xFF as INVALID_STATION */ peter1138@6630: if (CheckSavegameVersion(7) && ge->enroute_from == 0xFF) { peter1138@6630: ge->enroute_from = INVALID_STATION; peter1138@6630: } peter1138@6630: peter1138@6630: ge->enroute_from_xy = IsValidStationID(ge->enroute_from) ? GetStation(ge->enroute_from)->xy : st->xy; peter1138@6630: } peter1138@6630: } celestar@5934: } celestar@5934: maedhros@6139: if (CheckSavegameVersion(45)) { maedhros@6139: Vehicle *v; maedhros@6139: /* Originally just the fact that some cargo had been paid for was maedhros@6139: * stored to stop people cheating and cashing in several times. This maedhros@6139: * wasn't enough though as it was cleared when the vehicle started maedhros@6139: * loading again, even if it didn't actually load anything, so now the maedhros@6139: * amount of cargo that has been paid for is stored. */ maedhros@6139: FOR_ALL_VEHICLES(v) { maedhros@6501: if (HASBIT(v->vehicle_flags, 2)) { maedhros@6139: v->cargo_paid_for = v->cargo_count; maedhros@6501: CLRBIT(v->vehicle_flags, 2); maedhros@6139: } else { maedhros@6139: v->cargo_paid_for = 0; maedhros@6139: } maedhros@6139: } maedhros@6139: } maedhros@6139: rubidium@6312: /* Buoys do now store the owner of the previous water tile, which can never rubidium@6312: * be OWNER_NONE. So replace OWNER_NONE with OWNER_WATER. */ rubidium@6312: if (CheckSavegameVersion(46)) { rubidium@6312: Station *st; rubidium@6312: FOR_ALL_STATIONS(st) { rubidium@6312: if (st->IsBuoy() && IsTileOwner(st->xy, OWNER_NONE)) SetTileOwner(st->xy, OWNER_WATER); rubidium@6312: } rubidium@6312: } rubidium@6312: celestar@6519: if (CheckSavegameVersion(50)) { celestar@6519: Vehicle *v; celestar@6519: /* Aircraft units changed from 8 mph to 1 km/h */ celestar@6519: FOR_ALL_VEHICLES(v) { rubidium@6585: if (v->type == VEH_AIRCRAFT && v->subtype <= AIR_AIRCRAFT) { celestar@6519: const AircraftVehicleInfo *avi = AircraftVehInfo(v->engine_type); celestar@6519: v->cur_speed *= 129; celestar@6519: v->cur_speed /= 10; celestar@6519: v->max_speed = avi->max_speed; celestar@6519: v->acceleration = avi->acceleration; celestar@6519: } celestar@6519: } celestar@6519: } celestar@6519: rubidium@6516: if (CheckSavegameVersion(49)) FOR_ALL_PLAYERS(p) p->face = ConvertFromOldPlayerFace(p->face); rubidium@6516: truelight@6583: if (CheckSavegameVersion(52)) { truelight@6583: for (TileIndex t = 0; t < map_size; t++) { truelight@6583: if (IsStatueTile(t)) { truelight@6583: _m[t].m2 = CalcClosestTownFromTile(t, (uint)-1)->index; truelight@6583: } truelight@6583: } truelight@6583: } truelight@6583: truelight@0: return true; truelight@0: } Darkvater@5352: Darkvater@5352: /** Reload all NewGRF files during a running game. This is a cut-down Darkvater@5352: * version of AfterLoadGame(). Darkvater@5352: * XXX - We need to reset the vehicle position hash because with a non-empty Darkvater@5352: * hash AfterLoadVehicles() will loop infinitely. We need AfterLoadVehicles() Darkvater@5352: * to recalculate vehicle data as some NewGRF vehicle sets could have been Darkvater@5352: * removed or added and changed statistics */ rubidium@6573: void ReloadNewGRFData() Darkvater@5352: { Darkvater@5352: /* reload grf data */ Darkvater@5352: GfxLoadSprites(); Darkvater@5352: LoadStringWidthTable(); Darkvater@5352: /* reload vehicles */ Darkvater@5352: ResetVehiclePosHash(); Darkvater@5352: AfterLoadVehicles(); Darkvater@6522: StartupEngines(); Darkvater@5352: /* update station and waypoint graphics */ Darkvater@5352: AfterLoadWaypoints(); Darkvater@5352: AfterLoadStations(); maedhros@6658: /* check that house ids are still valid */ maedhros@6658: CheckHouseIDs(); Darkvater@5352: /* redraw the whole screen */ Darkvater@5352: MarkWholeScreenDirty(); Darkvater@5352: } rubidium@5838: rubidium@5838: HalMusicDriver *_music_driver; rubidium@5838: HalSoundDriver *_sound_driver; rubidium@5838: HalVideoDriver *_video_driver; rubidium@5838: