|
@@ -1047,98 +1047,98 @@ void StateGameLoop()
|
|
|
RunTileLoop();
|
|
|
CallVehicleTicks();
|
|
|
CallLandscapeTick();
|
|
|
ClearStorageChanges(true);
|
|
|
|
|
|
AI_RunGameLoop();
|
|
|
|
|
|
CallWindowTickEvent();
|
|
|
NewsLoop();
|
|
|
_current_player = p;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/** Create an autosave. The default name is "autosave#.sav". However with
|
|
|
* the patch setting 'keep_all_autosave' the name defaults to company-name + date */
|
|
|
static void DoAutosave()
|
|
|
{
|
|
|
char buf[MAX_PATH];
|
|
|
|
|
|
#if defined(PSP)
|
|
|
/* Autosaving in networking is too time expensive for the PSP */
|
|
|
if (_networking) return;
|
|
|
#endif /* PSP */
|
|
|
|
|
|
if (_patches.keep_all_autosave && _local_player != PLAYER_SPECTATOR) {
|
|
|
SetDParam(0, _local_player);
|
|
|
SetDParam(1, _date);
|
|
|
GetString(buf, STR_4004, lastof(buf));
|
|
|
ttd_strlcat(buf, ".sav", lengthof(buf));
|
|
|
} else {
|
|
|
/* generate a savegame name and number according to _patches.max_num_autosaves */
|
|
|
snprintf(buf, sizeof(buf), "autosave%d.sav", _autosave_ctr);
|
|
|
|
|
|
if (++_autosave_ctr >= _patches.max_num_autosaves) _autosave_ctr = 0;
|
|
|
}
|
|
|
|
|
|
DEBUG(sl, 2, "Autosaving to '%s'", buf);
|
|
|
if (SaveOrLoad(buf, SL_SAVE, AUTOSAVE_DIR) != SL_OK) {
|
|
|
ShowErrorMessage(INVALID_STRING_ID, STR_AUTOSAVE_FAILED, 0, 0);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
static void ScrollMainViewport(int x, int y)
|
|
|
{
|
|
|
if (_game_mode != GM_MENU) {
|
|
|
Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
|
|
|
assert(w);
|
|
|
|
|
|
WP(w, vp_d).dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
|
|
|
WP(w, vp_d).dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
|
|
|
w->viewport->dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
|
|
|
w->viewport->dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Describes all the different arrow key combinations the game allows
|
|
|
* when it is in scrolling mode.
|
|
|
* The real arrow keys are bitwise numbered as
|
|
|
* 1 = left
|
|
|
* 2 = up
|
|
|
* 4 = right
|
|
|
* 8 = down
|
|
|
*/
|
|
|
static const int8 scrollamt[16][2] = {
|
|
|
{ 0, 0}, ///< no key specified
|
|
|
{-2, 0}, ///< 1 : left
|
|
|
{ 0, -2}, ///< 2 : up
|
|
|
{-2, -1}, ///< 3 : left + up
|
|
|
{ 2, 0}, ///< 4 : right
|
|
|
{ 0, 0}, ///< 5 : left + right = nothing
|
|
|
{ 2, -1}, ///< 6 : right + up
|
|
|
{ 0, -2}, ///< 7 : right + left + up = up
|
|
|
{ 0 ,2}, ///< 8 : down
|
|
|
{-2 ,1}, ///< 9 : down + left
|
|
|
{ 0, 0}, ///< 10 : down + up = nothing
|
|
|
{-2, 0}, ///< 11 : left + up + down = left
|
|
|
{ 2, 1}, ///< 12 : down + right
|
|
|
{ 0, 2}, ///< 13 : left + right + down = down
|
|
|
{ 2, 0}, ///< 14 : right + up + down = right
|
|
|
{ 0, 0}, ///< 15 : left + up + right + down = nothing
|
|
|
};
|
|
|
|
|
|
static void HandleKeyScrolling()
|
|
|
{
|
|
|
if (_dirkeys && !_no_scroll) {
|
|
|
int factor = _shift_pressed ? 50 : 10;
|
|
|
ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void GameLoop()
|
|
|
{
|
|
|
ProcessAsyncSaveFinish();
|
|
|
|
|
|
/* autosave game? */
|
|
|
if (_do_autosave) {
|
|
|
_do_autosave = false;
|
|
|
DoAutosave();
|
|
|
RedrawAutosave();
|
|
@@ -1158,98 +1158,98 @@ void GameLoop()
|
|
|
|
|
|
IncreaseSpriteLRU();
|
|
|
InteractiveRandom();
|
|
|
|
|
|
if (_scroller_click_timeout > 3) {
|
|
|
_scroller_click_timeout -= 3;
|
|
|
} else {
|
|
|
_scroller_click_timeout = 0;
|
|
|
}
|
|
|
|
|
|
_caret_timer += 3;
|
|
|
_palette_animation_counter += 8;
|
|
|
CursorTick();
|
|
|
|
|
|
#ifdef ENABLE_NETWORK
|
|
|
/* Check for UDP stuff */
|
|
|
if (_network_available) NetworkUDPGameLoop();
|
|
|
|
|
|
if (_networking && !IsGeneratingWorld()) {
|
|
|
/* Multiplayer */
|
|
|
NetworkGameLoop();
|
|
|
} else {
|
|
|
if (_network_reconnect > 0 && --_network_reconnect == 0) {
|
|
|
/* This means that we want to reconnect to the last host
|
|
|
* We do this here, because it means that the network is really closed */
|
|
|
NetworkClientConnectGame(_network_last_host, _network_last_port);
|
|
|
}
|
|
|
/* Singleplayer */
|
|
|
StateGameLoop();
|
|
|
}
|
|
|
#else
|
|
|
StateGameLoop();
|
|
|
#endif /* ENABLE_NETWORK */
|
|
|
|
|
|
if (!_pause_game && HasBit(_display_opt, DO_FULL_ANIMATION)) DoPaletteAnimations();
|
|
|
|
|
|
if (!_pause_game || _cheats.build_in_pause.value) MoveAllTextEffects();
|
|
|
|
|
|
InputLoop();
|
|
|
|
|
|
MusicLoop();
|
|
|
}
|
|
|
|
|
|
void BeforeSaveGame()
|
|
|
{
|
|
|
const Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
|
|
|
|
|
|
if (w != NULL) {
|
|
|
_saved_scrollpos_x = WP(w, const vp_d).scrollpos_x;
|
|
|
_saved_scrollpos_y = WP(w, const vp_d).scrollpos_y;
|
|
|
_saved_scrollpos_x = w->viewport->scrollpos_x;
|
|
|
_saved_scrollpos_y = w->viewport->scrollpos_y;
|
|
|
_saved_scrollpos_zoom = w->viewport->zoom;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
static void ConvertTownOwner()
|
|
|
{
|
|
|
for (TileIndex tile = 0; tile != MapSize(); tile++) {
|
|
|
switch (GetTileType(tile)) {
|
|
|
case MP_ROAD:
|
|
|
if (GB(_m[tile].m5, 4, 2) == ROAD_TILE_CROSSING && HasBit(_m[tile].m4, 7)) {
|
|
|
_m[tile].m4 = OWNER_TOWN;
|
|
|
}
|
|
|
/* FALLTHROUGH */
|
|
|
|
|
|
case MP_TUNNELBRIDGE:
|
|
|
if (GetTileOwner(tile) & 0x80) SetTileOwner(tile, OWNER_TOWN);
|
|
|
break;
|
|
|
|
|
|
default: break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* before savegame version 4, the name of the company determined if it existed */
|
|
|
static 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 */
|
|
|
static void UpdateExclusiveRights()
|
|
|
{
|
|
|
Town *t;
|
|
|
|
|
|
FOR_ALL_TOWNS(t) {
|
|
|
t->exclusivity = INVALID_PLAYER;
|
|
|
}
|
|
|
|
|
|
/* 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
|
|
@@ -1271,100 +1271,100 @@ static void UpdateCurrencies()
|
|
|
}
|
|
|
|
|
|
/* Up to revision 1413 the invisible tiles at the southern border have not been
|
|
|
* MP_VOID, even though they should have. This is fixed by this function
|
|
|
*/
|
|
|
static void UpdateVoidTiles()
|
|
|
{
|
|
|
uint i;
|
|
|
|
|
|
for (i = 0; i < MapMaxY(); ++i) MakeVoid(i * MapSizeX() + MapMaxX());
|
|
|
for (i = 0; i < MapSizeX(); ++i) MakeVoid(MapSizeX() * MapMaxY() + i);
|
|
|
}
|
|
|
|
|
|
/* since savegame version 6.0 each sign has an "owner", signs without owner (from old games are set to 255) */
|
|
|
static void UpdateSignOwner()
|
|
|
{
|
|
|
Sign *si;
|
|
|
|
|
|
FOR_ALL_SIGNS(si) si->owner = OWNER_NONE;
|
|
|
}
|
|
|
|
|
|
extern void UpdateOldAircraft();
|
|
|
|
|
|
|
|
|
static inline RailType UpdateRailType(RailType rt, RailType min)
|
|
|
{
|
|
|
return rt >= min ? (RailType)(rt + 1): rt;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Initialization of the windows and several kinds of caches.
|
|
|
* This is not done directly in AfterLoadGame because these
|
|
|
* functions require that all saveload conversions have been
|
|
|
* done. As people tend to add savegame conversion stuff after
|
|
|
* the intialization of the windows and caches quite some bugs
|
|
|
* had been made.
|
|
|
* Moving this out of there is both cleaner and less bug-prone.
|
|
|
*
|
|
|
* @return true if everything went according to plan, otherwise false.
|
|
|
*/
|
|
|
static bool InitializeWindowsAndCaches()
|
|
|
{
|
|
|
/* Initialize windows */
|
|
|
ResetWindowSystem();
|
|
|
SetupColorsAndInitialWindow();
|
|
|
|
|
|
Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
|
|
|
|
|
|
WP(w, vp_d).scrollpos_x = _saved_scrollpos_x;
|
|
|
WP(w, vp_d).scrollpos_y = _saved_scrollpos_y;
|
|
|
WP(w, vp_d).dest_scrollpos_x = _saved_scrollpos_x;
|
|
|
WP(w, vp_d).dest_scrollpos_y = _saved_scrollpos_y;
|
|
|
w->viewport->scrollpos_x = _saved_scrollpos_x;
|
|
|
w->viewport->scrollpos_y = _saved_scrollpos_y;
|
|
|
w->viewport->dest_scrollpos_x = _saved_scrollpos_x;
|
|
|
w->viewport->dest_scrollpos_y = _saved_scrollpos_y;
|
|
|
|
|
|
ViewPort *vp = w->viewport;
|
|
|
vp->zoom = min(_saved_scrollpos_zoom, ZOOM_LVL_MAX);
|
|
|
vp->virtual_width = ScaleByZoom(vp->width, vp->zoom);
|
|
|
vp->virtual_height = ScaleByZoom(vp->height, vp->zoom);
|
|
|
|
|
|
DoZoomInOutWindow(ZOOM_NONE, w); // update button status
|
|
|
MarkWholeScreenDirty();
|
|
|
|
|
|
/* Update coordinates of the signs. */
|
|
|
UpdateAllStationVirtCoord();
|
|
|
UpdateAllSignVirtCoords();
|
|
|
UpdateAllTownVirtCoords();
|
|
|
UpdateAllWaypointSigns();
|
|
|
|
|
|
/* Recalculate */
|
|
|
Group *g;
|
|
|
FOR_ALL_GROUPS(g) {
|
|
|
g->num_engines = CallocT<uint16>(GetEnginePoolSize());
|
|
|
|
|
|
const Vehicle *v;
|
|
|
FOR_ALL_VEHICLES(v) {
|
|
|
if (!IsEngineCountable(v)) continue;
|
|
|
|
|
|
if (v->group_id != g->index || v->type != g->vehicle_type || v->owner != g->owner) continue;
|
|
|
|
|
|
g->num_engines[v->engine_type]++;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* Set up the engine count for all players */
|
|
|
Player *players[MAX_PLAYERS];
|
|
|
const Vehicle *v;
|
|
|
|
|
|
for (PlayerID i = PLAYER_FIRST; i < MAX_PLAYERS; i++) {
|
|
|
players[i] = GetPlayer(i);
|
|
|
|
|
|
/* For each player, verify (while loading a scenario) that the inauguration date is the current year and set it
|
|
|
* accordingly if it is not the case. No need to set it on players that are not been used already,
|
|
|
* thus the MIN_YEAR (which is really nothing more than Zero, initialized value) test */
|
|
|
if (_file_to_saveload.filetype == FT_SCENARIO && players[i]->inaugurated_year != MIN_YEAR)
|
|
|
players[i]->inaugurated_year = _cur_year;
|
|
|
|
|
|
free(players[i]->num_engines);
|
|
|
players[i]->num_engines = CallocT<uint16>(GetEnginePoolSize());
|
|
|
}
|
|
|
|
|
|
FOR_ALL_VEHICLES(v) {
|