OpenTTD-patches/ttd.c

1200 lines
26 KiB
C
Raw Normal View History

#include "stdafx.h"
#define VARDEF
#include "ttd.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 "saveload.h"
2004-08-25 10:17:39 +00:00
#include "ai.h"
#include "console.h"
#include "screenshot.h"
#include <stdarg.h>
void GameLoop();
void IncreaseSpriteLRU();
void InitializeGame();
void GenerateWorld(int mode);
void CallLandscapeTick();
void IncreaseDate();
void RunOtherPlayersLoop();
void DoPaletteAnimations();
void MusicLoop();
void ResetMusic();
void InitializeStations();
void DeleteAllPlayerStations();
extern void SetDifficultyLevel(int mode, GameOptions *gm_opt);
extern void DoStartupNewPlayer(bool is_ai);
extern void UpdateAllSignVirtCoords();
extern void ShowOSErrorBox(const char *buf);
void redsq_debug(int tile);
bool LoadSavegame(const char *filename);
extern void HalGameLoop();
uint32 _pixels_redrawn;
bool _dbg_screen_rect;
bool disable_computer;
static byte _os_version = 0;
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 debug(const char *s, ...)
{
va_list va;
char buf[1024];
va_start(va, s);
vsprintf(buf, s, va);
va_end(va);
2004-08-25 10:17:39 +00:00
fprintf(stderr, "dbg: %s\n", buf);
IConsoleDebug((byte *) &buf);
}
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 char *NullMidiStart(char **parm) { return NULL; }
static void NullMidiStop() {}
static void NullMidiPlaySong(const char *filename) {}
static void NullMidiStopSong() {}
static bool NullMidiIsSongPlaying() { 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(char **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() { free(_null_video_mem); }
static void NullVideoMakeDirty(int left, int top, int width, int height) {}
static int NullVideoMainLoop() {
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; }
const HalVideoDriver _null_video_driver = {
NullVideoStart,
NullVideoStop,
NullVideoMakeDirty,
NullVideoMainLoop,
NullVideoChangeRes,
};
// NULL sound driver
static char *NullSoundStart(char **parm) { return NULL; }
static void NullSoundStop() {}
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 ttd_strlcpy(char *dst, const char *src, size_t len)
{
assert(len > 0);
while (--len && *src)
*dst++=*src++;
*dst = 0;
}
char *strecpy(char *dst, const char *src)
{
while ( (*dst++ = *src++) != 0) {}
return dst - 1;
}
byte *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize)
{
FILE *in;
void *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=(byte*)malloc(len + 1)) == NULL) {
fclose(in);
return NULL;
}
((byte*)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];
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) {
int 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()
{
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 dbg = Debug mode\n"
" -l lng = Select Language\n"
" -e = Start Editor\n"
" -g = Start new game immediately (can optionally take a game to load)\n"
" -G seed= Set random seed\n"
);
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);
}
char *GetDriverParam(char **parm, const char *name)
{
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(char **parm, const char *name)
{
char *p = GetDriverParam(parm, name);
return p != NULL;
}
int GetDriverParamInt(char **parm, const char *name, int def)
{
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;
}
}
}
void SetDebugString(const char *s)
{
int v;
char *end;
const char *t;
int *p;
// global debugging level?
if (*s >= '0' && *s <= '9') {
v = strtoul(s, &end, 0);
s = end;
_debug_spritecache_level = v;
_debug_misc_level = v;
_debug_grf_level = v;
_debug_ai_level = v;
_debug_net_level = v;
}
// individual levels
for(;;) {
// skip delimiters
while (*s == ' ' || *s == ',' || *s == '\t') s++;
if (*s == 0) break;
t = s;
while (*s >= 'a' && *s <= 'z') s++;
#define IS_LVL(x) (s - t == sizeof(x)-1 && !memcmp(t, x, sizeof(x)-1))
// check debugging levels
if IS_LVL("misc") p = &_debug_misc_level;
else if IS_LVL("spritecache") p = &_debug_spritecache_level;
else if IS_LVL("grf") p = &_debug_grf_level;
else if IS_LVL("ai") p = &_debug_ai_level;
else if IS_LVL("net") p = &_debug_net_level;
else {
ShowInfoF("Unknown debug level '%.*s'", s-t, t);
return;
}
#undef IS_LVL
if (*s == '=') s++;
v = strtoul(s, &end, 0);
s = end;
if (p) *p = v;
}
}
void ParseResolution(int res[2], char *s)
{
char *t = strchr(s, 'x');
if (t == NULL) {
ShowInfoF("Invalid resolution '%s'", s);
return;
}
2004-09-23 21:44:36 +00:00
res[0] = strtoul(s, NULL, 0);
res[1] = strtoul(t + 1, NULL, 0);
}
int ttd_main(int argc, char* argv[])
{
MyGetOptData mgo;
int i;
int network = 0;
char *network_conn = NULL;
char *language = NULL;
char musicdriver[32], sounddriver[32], videodriver[32];
int resolution[2] = {0,0};
uint startdate = -1;
_ignore_wrong_grf = false;
musicdriver[0] = sounddriver[0] = videodriver[0] = 0;
_networking_override=false;
_game_mode = GM_MENU;
_switch_mode = SM_MENU;
_switch_mode_errorstr = INVALID_STRING_ID;
MyGetOptInit(&mgo, argc-1, argv+1, "m:s:v:hn::l:eit:d::r:g::G:cp:");
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 'n': {
network = 1;
_networking_override=true;
if (mgo.opt) {
network_conn = mgo.opt;
network++;
}
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': _ignore_wrong_grf = 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);
if (IS_INT_INSIDE(i, 0, MAX_PLAYERS)) _network_playas = i + 1;
break;
}
case -2:
case 'h':
showhelp();
return 0;
}
}
DeterminePaths();
LoadFromConfig();
// override config?
if (musicdriver[0]) strcpy(_ini_musicdriver, musicdriver);
if (sounddriver[0]) strcpy(_ini_sounddriver, sounddriver);
if (videodriver[0]) strcpy(_ini_videodriver, videodriver);
if (resolution[0]) { _cur_resolution[0] = resolution[0]; _cur_resolution[1] = resolution[1]; }
if (startdate != -1) _patches.starting_date = startdate;
// initialize network-core
NetworkCoreInit();
// enumerate language files
InitializeLanguagePacks();
// initialize screenshot formats
InitializeScreenshotFormats();
// initialize airport state machines
InitializeAirports();
// Sample catalogue
DEBUG(misc, 1) ("Loading sound effects...");
_os_version = GetOSVersion();
MxInitialize(11025, "sample.cat");
// This must be done early, since functions use the InvalidateWindow* calls
2004-08-25 10:17:39 +00:00
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
MusicLoop();
_savegame_sort_order = 1; // default sorting of savegames is by date, newest first
// Default difficulty level
_opt_mod_ptr = &_new_opt;
// ugly hack, if diff_level is 9, it means we got no setting from the config file, so we load the default settings.
if (_opt_mod_ptr->diff_level == 9)
SetDifficultyLevel(0, _opt_mod_ptr);
if ((network) && (_network_available)) {
NetworkLobbyInit();
if (network_conn!=NULL) {
NetworkCoreConnectGame(network_conn,_network_server_port);
} else {
NetworkCoreConnectGame("auto",_network_server_port);
}
}
2004-08-25 10:17:39 +00:00
// initialize the ingame console
IConsoleInit();
InitPlayerRandoms();
while (_video_driver->main_loop() == ML_SWITCHDRIVER) {}
2004-08-25 10:17:39 +00:00
IConsoleFree();
if (_network_available) {
// shutdown network-core
NetworkCoreShutdown();
}
_video_driver->stop();
_music_driver->stop();
_sound_driver->stop();
SaveToConfig();
// uninitialize airport state machines
UnInitializeAirports();
return 0;
}
static void ShowScreenshotResult(bool b)
{
if (b) {
SET_DPARAM16(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 LoadIntroGame()
{
char filename[256];
_game_mode = GM_MENU;
_display_opt |= DO_TRANS_BUILDINGS; // don't make buildings transparent in intro
_opt_mod_ptr = &_new_opt;
GfxLoadSprites();
LoadStringWidthTable();
// Setup main window
InitWindowSystem();
SetupColorsAndInitialWindow();
// Generate a world.
sprintf(filename, "%sopntitle.dat", _path.data_dir);
if (SaveOrLoad(filename, SL_LOAD) != SL_OK)
GenerateWorld(1); // if failed loading, make empty world.
_opt.currency = _new_opt.currency;
_pause = 0;
_local_player = 0;
MarkWholeScreenDirty();
// Play main theme
if (_music_driver->is_song_playing()) ResetMusic();
}
void MakeNewGame()
{
_game_mode = GM_NORMAL;
// Copy in game options
_opt_mod_ptr = &_opt;
memcpy(&_opt, &_new_opt, sizeof(_opt));
GfxLoadSprites();
// Reinitialize windows
InitWindowSystem();
LoadStringWidthTable();
SetupColorsAndInitialWindow();
// Randomize world
GenerateWorld(0);
// Create a single player
DoStartupNewPlayer(false);
_local_player = 0;
MarkWholeScreenDirty();
}
static void MakeNewEditorWorld()
{
_game_mode = GM_EDITOR;
// Copy in game options
_opt_mod_ptr = &_opt;
memcpy(&_opt, &_new_opt, sizeof(_opt));
GfxLoadSprites();
// Re-init the windowing system
InitWindowSystem();
// Create toolbars
SetupColorsAndInitialWindow();
// Startup the game system
GenerateWorld(1);
_local_player = OWNER_NONE;
MarkWholeScreenDirty();
}
void StartupPlayers();
void StartupDisasters();
void StartScenario()
{
_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;
}
// Copy in game options
// Removed copying of game options when using "new game". --dominik
// _opt_mod_ptr = &_opt;
// memcpy(&_opt, &_new_opt, sizeof(_opt));
GfxLoadSprites();
// Reinitialize windows
InitWindowSystem();
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);
}
// Inititalize data
StartupPlayers();
StartupEngines();
StartupDisasters();
2004-08-25 10:17:39 +00:00
// When starting a scenario, is it really a load..
// and in AfterLoad a player is started when it is
// a scenario.. so we do not need it here.
// DoStartupNewPlayer(false);
_local_player = 0;
MarkWholeScreenDirty();
}
static 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;
}
static void SwitchMode(int new_mode)
{
2004-08-25 10:17:39 +00:00
_in_state_game_loop = true;
switch(new_mode) {
case SM_EDITOR: // Switch to scenario editor
MakeNewEditorWorld();
break;
case SM_NEWGAME:
if (_networking) { NetworkStartSync(true); } // UGLY HACK HACK HACK
MakeNewGame();
break;
normal_load:
case SM_LOAD: { // Load game
if (_networking) { NetworkStartSync(true); } // UGLY HACK HACK HACK
_error_message = INVALID_STRING_ID;
if (!SafeSaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_NORMAL)) {
ShowErrorMessage(_error_message, STR_4009_GAME_LOAD_FAILED, 0, 0);
} else {
_opt_mod_ptr = &_opt;
_local_player = 0;
DoCommandP(0, 0, 0, NULL, CMD_PAUSE); // decrease pause counter (was increased from opening load dialog)
}
break;
}
case SM_LOAD_SCENARIO: {
int i;
if (_game_mode == GM_MENU) goto normal_load;
if (SafeSaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_EDITOR)) {
_opt_mod_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();
} else
ShowErrorMessage(INVALID_STRING_ID, STR_4009_GAME_LOAD_FAILED, 0, 0);
break;
}
case SM_MENU: // Switch to game menu
if ((_networking) && (!_networking_override)) NetworkCoreDisconnect();
_networking_override=false;
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:
GenerateWorld(2);
// 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.
static void StateGameLoop()
{
// dont execute the state loop during pause
if (_pause) return;
_in_state_game_loop = true;
_frame_counter++;
// store the random seed to be able to detect out of sync errors
_sync_seed_1 = _random_seeds[0][0];
_sync_seed_2 = _random_seeds[0][1];
if (_networking) disable_computer=true;
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();
if (!disable_computer)
RunOtherPlayersLoop();
CallWindowTickEvent();
NewsLoop();
_current_player = p;
}
_in_state_game_loop = false;
}
static void DoAutosave()
{
char buf[200];
if (_patches.keep_all_autosave && _local_player != OWNER_SPECTATOR) {
Player *p;
char *s;
sprintf(buf, "%s%s", _path.autosave_dir, PATHSEP);
p = DEREF_PLAYER(_local_player);
SET_DPARAM16(0, p->name_1);
SET_DPARAM32(1, p->name_2);
SET_DPARAM16(2, _date);
s= (char*)GetString(buf + strlen(_path.autosave_dir) + strlen(PATHSEP) - 1, STR_4004);
strcpy(s, ".sav");
} else {
int n = _autosave_ctr;
_autosave_ctr = (_autosave_ctr + 1) & 15;
sprintf(buf, "%s%sautosave%d.sav", _path.autosave_dir, PATHSEP, n);
}
if (SaveOrLoad(buf, SL_SAVE) != SL_OK)
ShowErrorMessage(INVALID_STRING_ID, STR_AUTOSAVE_FAILED, 0, 0);
}
void GameLoop()
{
int m;
// autosave game?
if (_do_autosave) {
_do_autosave = false;
DoAutosave();
RedrawAutosave();
}
// 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(-(TILE_X_MAX)*32, 0, TILE_X_MAX*32 + (TILE_X_MAX)*32, TILES_Y * 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();
// incomming packets
NetworkCoreLoop(true);
if (_networking_sync) {
// client: make sure client's time is synched to the server by running frames quickly up to where the server is.
if (!_networking_server) {
while (_frame_counter < _frame_counter_srv) {
NetworkCoreLoop(true);
StateGameLoop();
NetworkProcessCommands(); // need to process queue to make sure that packets get executed.
NetworkCoreLoop(false);
}
// client: don't exceed the max count told by the server
if (_frame_counter < _frame_counter_max) {
StateGameLoop();
NetworkProcessCommands();
}
// client: send the ready packet
NetworkSendReadyPacket();
} else {
// server: work on to the frame max
if (_frame_counter < _frame_counter_max) {
StateGameLoop();
NetworkProcessCommands(); // to check if we got any new commands belonging to the current frame before we increase it.
NetworkSendFrameSyncPackets();
}
// server: wait until all clients were ready for going on
if (_frame_counter == _frame_counter_max) {
if (NetworkCheckClientReady()) NetworkSendSyncPackets();
}
}
} else {
// server/client/standalone: not synced --> state game loop
StateGameLoop();
// server/client: process queued network commands
if (_networking) NetworkProcessCommands();
}
if (!_pause && _display_opt&DO_FULL_ANIMATION)
DoPaletteAnimations();
if (!_pause || _cheats.build_in_pause.value)
MoveAllTextEffects();
MouseLoop();
// send outgoing packets.
NetworkCoreLoop(false);
if (_game_mode != GM_MENU)
MusicLoop();
}
void BeforeSaveGame()
{
Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
_saved_scrollpos_x = WP(w,vp_d).scrollpos_x;
_saved_scrollpos_y = WP(w,vp_d).scrollpos_y;
_saved_scrollpos_zoom = w->viewport->zoom;
}
void ConvertTownOwner()
{
uint tile;
for(tile=0; tile!=TILES_X * TILES_Y; tile++) {
if (IS_TILETYPE(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 (IS_TILETYPE(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
void CheckIsPlayerActive()
{
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
void UpdateExclusiveRights()
{
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.
*/
}
extern void UpdateOldAircraft();
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();
}
// 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();
// 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
InitWindowSystem();
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();
}
// If Load Scenario / New (Scenario) Game is used,
2004-08-25 10:17:39 +00:00
// a player does not exist yet. So create one here.
if (!_players[0].is_active)
DoStartupNewPlayer(false);
DoZoomInOutWindow(ZOOM_NONE, w); // update button status
MarkWholeScreenDirty();
return true;
}
void DebugProc(int i)
{
switch(i) {
case 0:
*(byte*)0 = 0;
break;
case 1:
DoCommandP(0, -10000000, 0, NULL, CMD_MONEY_CHEAT);
break;
case 2:
UpdateAllStationVirtCoord();
break;
}
}