diff --git a/config.lib b/config.lib index ab9bb82b61..a144b9e3ea 100644 --- a/config.lib +++ b/config.lib @@ -1285,7 +1285,7 @@ make_compiler_cflags() { # -W alone doesn't enable all warnings enabled by -Wall; on the other hand, # -Weverything enables too many useless warnings that can't be disabled (as of 3.0) - flags="$flags -Wall -W" + flags="$flags -Wall -W -Wextra" # warning: unused parameter '...' flags="$flags -Wno-unused-parameter" @@ -1326,6 +1326,11 @@ make_compiler_cflags() { flags="$flags -Wno-unused-variable" fi + if [ "$cc_version" -ge "33" ]; then + # clang completed C++11 support in version 3.3 + flags="$flags -std=c++11" + fi + # rdynamic is used to get useful stack traces from crash reports. ldflags="$ldflags -rdynamic" else diff --git a/src/aircraft.h b/src/aircraft.h index 0805ae8ec6..f0ca1c4abb 100644 --- a/src/aircraft.h +++ b/src/aircraft.h @@ -93,7 +93,7 @@ struct Aircraft FINAL : public SpecializedVehicle { void UpdateDeltaXY(Direction direction); ExpensesType GetExpenseType(bool income) const { return income ? EXPENSES_AIRCRAFT_INC : EXPENSES_AIRCRAFT_RUN; } bool IsPrimaryVehicle() const { return this->IsNormalAircraft(); } - SpriteID GetImage(Direction direction, EngineImageType image_type) const; + void GetImage(Direction direction, EngineImageType image_type, VehicleSpriteSeq *result) const; int GetDisplaySpeed() const { return this->cur_speed; } int GetDisplayMaxSpeed() const { return this->vcache.cached_max_speed; } int GetSpeedOldUnits() const { return this->vcache.cached_max_speed * 10 / 128; } @@ -141,7 +141,7 @@ struct Aircraft FINAL : public SpecializedVehicle { */ #define FOR_ALL_AIRCRAFT(var) FOR_ALL_VEHICLES_OF_TYPE(Aircraft, var) -SpriteID GetRotorImage(const Aircraft *v, EngineImageType image_type); +void GetRotorImage(const Aircraft *v, EngineImageType image_type, VehicleSpriteSeq *result); Station *GetTargetAirportIfValid(const Aircraft *v); diff --git a/src/aircraft_cmd.cpp b/src/aircraft_cmd.cpp index edb10af327..c9cddbf97d 100644 --- a/src/aircraft_cmd.cpp +++ b/src/aircraft_cmd.cpp @@ -152,64 +152,69 @@ static StationID FindNearestHangar(const Aircraft *v) return index; } -SpriteID Aircraft::GetImage(Direction direction, EngineImageType image_type) const +void Aircraft::GetImage(Direction direction, EngineImageType image_type, VehicleSpriteSeq *result) const { uint8 spritenum = this->spritenum; if (is_custom_sprite(spritenum)) { - SpriteID sprite = GetCustomVehicleSprite(this, direction, image_type); - if (sprite != 0) return sprite; + GetCustomVehicleSprite(this, direction, image_type, result); + if (result->IsValid()) return; spritenum = this->GetEngine()->original_image_index; } assert(IsValidImageIndex(spritenum)); - return direction + _aircraft_sprite[spritenum]; + result->Set(direction + _aircraft_sprite[spritenum]); } -SpriteID GetRotorImage(const Aircraft *v, EngineImageType image_type) +void GetRotorImage(const Aircraft *v, EngineImageType image_type, VehicleSpriteSeq *result) { assert(v->subtype == AIR_HELICOPTER); const Aircraft *w = v->Next()->Next(); if (is_custom_sprite(v->spritenum)) { - SpriteID sprite = GetCustomRotorSprite(v, false, image_type); - if (sprite != 0) return sprite; + GetCustomRotorSprite(v, false, image_type, result); + if (result->IsValid()) return; } /* Return standard rotor sprites if there are no custom sprites for this helicopter */ - return SPR_ROTOR_STOPPED + w->state; + result->Set(SPR_ROTOR_STOPPED + w->state); } -static SpriteID GetAircraftIcon(EngineID engine, EngineImageType image_type) +static void GetAircraftIcon(EngineID engine, EngineImageType image_type, VehicleSpriteSeq *result) { const Engine *e = Engine::Get(engine); uint8 spritenum = e->u.air.image_index; if (is_custom_sprite(spritenum)) { - SpriteID sprite = GetCustomVehicleIcon(engine, DIR_W, image_type); - if (sprite != 0) return sprite; + GetCustomVehicleIcon(engine, DIR_W, image_type, result); + if (result->IsValid()) return; spritenum = e->original_image_index; } assert(IsValidImageIndex(spritenum)); - return DIR_W + _aircraft_sprite[spritenum]; + result->Set(DIR_W + _aircraft_sprite[spritenum]); } void DrawAircraftEngine(int left, int right, int preferred_x, int y, EngineID engine, PaletteID pal, EngineImageType image_type) { - SpriteID sprite = GetAircraftIcon(engine, image_type); - const Sprite *real_sprite = GetSprite(sprite, ST_NORMAL); + VehicleSpriteSeq seq; + GetAircraftIcon(engine, image_type, &seq); + + Rect rect; + seq.GetBounds(&rect); preferred_x = Clamp(preferred_x, - left - UnScaleGUI(real_sprite->x_offs), - right - UnScaleGUI(real_sprite->width) - UnScaleGUI(real_sprite->x_offs)); - DrawSprite(sprite, pal, preferred_x, y); + left - UnScaleGUI(rect.left), + right - UnScaleGUI(rect.right)); + + seq.Draw(preferred_x, y, pal, pal == PALETTE_CRASH); if (!(AircraftVehInfo(engine)->subtype & AIR_CTOL)) { - SpriteID rotor_sprite = GetCustomRotorIcon(engine, image_type); - if (rotor_sprite == 0) rotor_sprite = SPR_ROTOR_STOPPED; - DrawSprite(rotor_sprite, PAL_NONE, preferred_x, y - ScaleGUITrad(5)); + VehicleSpriteSeq rotor_seq; + GetCustomRotorIcon(engine, image_type, &rotor_seq); + if (!rotor_seq.IsValid()) rotor_seq.Set(SPR_ROTOR_STOPPED); + rotor_seq.Draw(preferred_x, y - ScaleGUITrad(5), PAL_NONE, false); } } @@ -224,12 +229,16 @@ void DrawAircraftEngine(int left, int right, int preferred_x, int y, EngineID en */ void GetAircraftSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type) { - const Sprite *spr = GetSprite(GetAircraftIcon(engine, image_type), ST_NORMAL); + VehicleSpriteSeq seq; + GetAircraftIcon(engine, image_type, &seq); - width = UnScaleGUI(spr->width); - height = UnScaleGUI(spr->height); - xoffs = UnScaleGUI(spr->x_offs); - yoffs = UnScaleGUI(spr->y_offs); + Rect rect; + seq.GetBounds(&rect); + + width = UnScaleGUI(rect.right - rect.left + 1); + height = UnScaleGUI(rect.bottom - rect.top + 1); + xoffs = UnScaleGUI(rect.left); + yoffs = UnScaleGUI(rect.top); } /** @@ -317,7 +326,8 @@ CommandCost CmdBuildAircraft(TileIndex tile, DoCommandFlag flags, const Engine * v->date_of_last_service = _date; v->build_year = u->build_year = _cur_year; - v->cur_image = u->cur_image = SPR_IMG_QUERY; + v->sprite_seq.Set(SPR_IMG_QUERY); + u->sprite_seq.Set(SPR_IMG_QUERY); v->random_bits = VehicleRandomBits(); u->random_bits = VehicleRandomBits(); @@ -349,7 +359,7 @@ CommandCost CmdBuildAircraft(TileIndex tile, DoCommandFlag flags, const Engine * w->vehstatus = VS_HIDDEN | VS_UNCLICKABLE; w->spritenum = 0xFF; w->subtype = AIR_ROTOR; - w->cur_image = SPR_ROTOR_STOPPED; + w->sprite_seq.Set(SPR_ROTOR_STOPPED); w->random_bits = VehicleRandomBits(); /* Use rotor's air.state to store the rotor animation frame */ w->state = HRS_ROTOR_STOPPED; @@ -468,21 +478,21 @@ static void HelicopterTickHandler(Aircraft *v) int tick = ++u->tick_counter; int spd = u->cur_speed >> 4; - SpriteID img; + VehicleSpriteSeq seq; if (spd == 0) { u->state = HRS_ROTOR_STOPPED; - img = GetRotorImage(v, EIT_ON_MAP); - if (u->cur_image == img) return; + GetRotorImage(v, EIT_ON_MAP, &seq); + if (u->sprite_seq == seq) return; } else if (tick >= spd) { u->tick_counter = 0; u->state++; if (u->state > HRS_ROTOR_MOVING_3) u->state = HRS_ROTOR_MOVING_1; - img = GetRotorImage(v, EIT_ON_MAP); + GetRotorImage(v, EIT_ON_MAP, &seq); } else { return; } - u->cur_image = img; + u->sprite_seq = seq; u->UpdatePositionAndViewport(); } @@ -502,7 +512,9 @@ void SetAircraftPosition(Aircraft *v, int x, int y, int z) v->UpdatePosition(); v->UpdateViewport(true, false); - if (v->subtype == AIR_HELICOPTER) v->Next()->Next()->cur_image = GetRotorImage(v, EIT_ON_MAP); + if (v->subtype == AIR_HELICOPTER) { + GetRotorImage(v, EIT_ON_MAP, &v->Next()->Next()->sprite_seq); + } Aircraft *u = v->Next(); @@ -513,7 +525,7 @@ void SetAircraftPosition(Aircraft *v, int x, int y, int z) safe_y = Clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE); u->z_pos = GetSlopePixelZ(safe_x, safe_y); - u->cur_image = v->cur_image; + u->sprite_seq.CopyWithoutPalette(v->sprite_seq); // the shadow is never coloured u->UpdatePositionAndViewport(); @@ -1237,7 +1249,9 @@ void Aircraft::MarkDirty() { this->colourmap = PAL_NONE; this->UpdateViewport(true, false); - if (this->subtype == AIR_HELICOPTER) this->Next()->Next()->cur_image = GetRotorImage(this, EIT_ON_MAP); + if (this->subtype == AIR_HELICOPTER) { + GetRotorImage(this, EIT_ON_MAP, &this->Next()->Next()->sprite_seq); + } } diff --git a/src/aircraft_gui.cpp b/src/aircraft_gui.cpp index c3379d3951..20fca9fc5d 100644 --- a/src/aircraft_gui.cpp +++ b/src/aircraft_gui.cpp @@ -83,11 +83,14 @@ void DrawAircraftImage(const Vehicle *v, int left, int right, int y, VehicleID s { bool rtl = _current_text_dir == TD_RTL; - SpriteID sprite = v->GetImage(rtl ? DIR_E : DIR_W, image_type); - const Sprite *real_sprite = GetSprite(sprite, ST_NORMAL); + VehicleSpriteSeq seq; + v->GetImage(rtl ? DIR_E : DIR_W, image_type, &seq); - int width = UnScaleGUI(real_sprite->width); - int x_offs = UnScaleGUI(real_sprite->x_offs); + Rect rect; + seq.GetBounds(&rect); + + int width = UnScaleGUI(rect.right - rect.left + 1); + int x_offs = UnScaleGUI(rect.left); int x = rtl ? right - width - x_offs : left - x_offs; bool helicopter = v->subtype == AIR_HELICOPTER; @@ -95,17 +98,18 @@ void DrawAircraftImage(const Vehicle *v, int left, int right, int y, VehicleID s int heli_offs = 0; PaletteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v); - DrawSprite(sprite, pal, x, y + y_offs); + seq.Draw(x, y + y_offs, pal, v->vehstatus & VS_CRASHED); if (helicopter) { const Aircraft *a = Aircraft::From(v); - SpriteID rotor_sprite = GetCustomRotorSprite(a, true, image_type); - if (rotor_sprite == 0) rotor_sprite = SPR_ROTOR_STOPPED; + VehicleSpriteSeq rotor_seq; + GetCustomRotorSprite(a, true, image_type, &rotor_seq); + if (!rotor_seq.IsValid()) rotor_seq.Set(SPR_ROTOR_STOPPED); heli_offs = ScaleGUITrad(5); - DrawSprite(rotor_sprite, PAL_NONE, x, y + y_offs - heli_offs); + rotor_seq.Draw(x, y + y_offs - heli_offs, PAL_NONE, false); } if (v->index == selection) { x += x_offs; - y += UnScaleGUI(real_sprite->y_offs) + y_offs - heli_offs; - DrawFrameRect(x - 1, y - 1, x + width + 1, y + UnScaleGUI(real_sprite->height) + heli_offs + 1, COLOUR_WHITE, FR_BORDERONLY); + y += UnScaleGUI(rect.top) + y_offs - heli_offs; + DrawFrameRect(x - 1, y - 1, x + width + 1, y + UnScaleGUI(rect.bottom - rect.top + 1) + heli_offs + 1, COLOUR_WHITE, FR_BORDERONLY); } } diff --git a/src/articulated_vehicles.cpp b/src/articulated_vehicles.cpp index 8c372e7eae..04c9f95a97 100644 --- a/src/articulated_vehicles.cpp +++ b/src/articulated_vehicles.cpp @@ -441,7 +441,7 @@ void AddArticulatedParts(Vehicle *first) v->max_age = 0; v->engine_type = engine_type; v->value = 0; - v->cur_image = SPR_IMG_QUERY; + v->sprite_seq.Set(SPR_IMG_QUERY); v->random_bits = VehicleRandomBits(); if (flip_image) v->spritenum++; diff --git a/src/clear_cmd.cpp b/src/clear_cmd.cpp index 2e9589aee2..f9eb88df54 100644 --- a/src/clear_cmd.cpp +++ b/src/clear_cmd.cpp @@ -56,7 +56,7 @@ void DrawHillyLandTile(const TileInfo *ti) if (ti->tileh != SLOPE_FLAT) { DrawGroundSprite(SPR_FLAT_ROUGH_LAND + SlopeToSpriteOffset(ti->tileh), PAL_NONE); } else { - DrawGroundSprite(_landscape_clear_sprites_rough[GB(ti->x ^ ti->y, 4, 3)], PAL_NONE); + DrawGroundSprite(_landscape_clear_sprites_rough[GB(TileHash(ti->x, ti->y), 0, 3)], PAL_NONE); } } diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 8d9e04113d..9cfc8e8f9d 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -45,6 +45,38 @@ /* scriptfile handling */ static bool _script_running; ///< Script is running (used to abort execution when #ConReturn is encountered). +/** File list storage for the console, for caching the last 'ls' command. */ +class ConsoleFileList : public FileList { +public: + ConsoleFileList() : FileList() + { + this->file_list_valid = false; + } + + /** Declare the file storage cache as being invalid, also clears all stored files. */ + void InvalidateFileList() + { + this->Clear(); + this->file_list_valid = false; + } + + /** + * (Re-)validate the file storage cache. Only makes a change if the storage was invalid, or if \a force_reload. + * @param Always reload the file storage cache. + */ + void ValidateFileList(bool force_reload = false) + { + if (force_reload || !this->file_list_valid) { + this->BuildFileList(FT_SAVEGAME, SLO_LOAD); + this->file_list_valid = true; + } + } + + bool file_list_valid; ///< If set, the file list is valid. +}; + +static ConsoleFileList _console_file_list; ///< File storage cache for the console. + /* console command defines */ #define DEF_CONSOLE_CMD(function) static bool function(byte argc, char *argv[]) #define DEF_CONSOLE_HOOK(function) static ConsoleHookResult function(bool echo) @@ -286,7 +318,7 @@ DEF_CONSOLE_CMD(ConSave) char *filename = str_fmt("%s.sav", argv[1]); IConsolePrint(CC_DEFAULT, "Saving map..."); - if (SaveOrLoad(filename, SL_SAVE, SAVE_DIR) != SL_OK) { + if (SaveOrLoad(filename, SLO_SAVE, DFT_GAME_FILE, SAVE_DIR) != SL_OK) { IConsolePrint(CC_ERROR, "Saving map failed"); } else { IConsolePrintF(CC_DEFAULT, "Map successfully saved to %s", filename); @@ -315,42 +347,6 @@ DEF_CONSOLE_CMD(ConSaveConfig) return true; } -/** - * Get savegame file informations. - * @param file The savegame filename to return information about. Can be the actual name - * or a numbered entry into the filename list. - * @return FiosItem The information on the file. - */ -static const FiosItem *GetFiosItem(const char *file) -{ - _saveload_mode = SLD_LOAD_GAME; - BuildFileList(); - - for (const FiosItem *item = _fios_items.Begin(); item != _fios_items.End(); item++) { - if (strcmp(file, item->name) == 0) return item; - if (strcmp(file, item->title) == 0) return item; - } - - /* If no name matches, try to parse it as number */ - char *endptr; - int i = strtol(file, &endptr, 10); - if (file == endptr || *endptr != '\0') i = -1; - - if (IsInsideMM(i, 0, _fios_items.Length())) return _fios_items.Get(i); - - /* As a last effort assume it is an OpenTTD savegame and - * that the ".sav" part was not given. */ - char long_file[MAX_PATH]; - seprintf(long_file, lastof(long_file), "%s.sav", file); - for (const FiosItem *item = _fios_items.Begin(); item != _fios_items.End(); item++) { - if (strcmp(long_file, item->name) == 0) return item; - if (strcmp(long_file, item->title) == 0) return item; - } - - return NULL; -} - - DEF_CONSOLE_CMD(ConLoad) { if (argc == 0) { @@ -361,24 +357,21 @@ DEF_CONSOLE_CMD(ConLoad) if (argc != 2) return false; const char *file = argv[1]; - const FiosItem *item = GetFiosItem(file); + _console_file_list.ValidateFileList(); + const FiosItem *item = _console_file_list.FindItem(file); if (item != NULL) { - switch (item->type) { - case FIOS_TYPE_FILE: case FIOS_TYPE_OLDFILE: { - _switch_mode = SM_LOAD_GAME; - SetFiosType(item->type); - - strecpy(_file_to_saveload.name, FiosBrowseTo(item), lastof(_file_to_saveload.name)); - strecpy(_file_to_saveload.title, item->title, lastof(_file_to_saveload.title)); - break; - } - default: IConsolePrintF(CC_ERROR, "%s: Not a savegame.", file); + if (GetAbstractFileType(item->type) == FT_SAVEGAME) { + _switch_mode = SM_LOAD_GAME; + _file_to_saveload.SetMode(item->type); + _file_to_saveload.SetName(FiosBrowseTo(item)); + _file_to_saveload.SetTitle(item->title); + } else { + IConsolePrintF(CC_ERROR, "%s: Not a savegame.", file); } } else { IConsolePrintF(CC_ERROR, "%s: No such file or directory.", file); } - FiosFreeSavegameList(); return true; } @@ -393,7 +386,8 @@ DEF_CONSOLE_CMD(ConRemove) if (argc != 2) return false; const char *file = argv[1]; - const FiosItem *item = GetFiosItem(file); + _console_file_list.ValidateFileList(); + const FiosItem *item = _console_file_list.FindItem(file); if (item != NULL) { if (!FiosDelete(item->name)) { IConsolePrintF(CC_ERROR, "%s: Failed to delete file", file); @@ -402,7 +396,7 @@ DEF_CONSOLE_CMD(ConRemove) IConsolePrintF(CC_ERROR, "%s: No such file or directory.", file); } - FiosFreeSavegameList(); + _console_file_list.InvalidateFileList(); return true; } @@ -415,13 +409,11 @@ DEF_CONSOLE_CMD(ConListFiles) return true; } - BuildFileList(); - - for (uint i = 0; i < _fios_items.Length(); i++) { - IConsolePrintF(CC_DEFAULT, "%d) %s", i, _fios_items[i].title); + _console_file_list.ValidateFileList(true); + for (uint i = 0; i < _console_file_list.Length(); i++) { + IConsolePrintF(CC_DEFAULT, "%d) %s", i, _console_file_list[i].title); } - FiosFreeSavegameList(); return true; } @@ -436,7 +428,8 @@ DEF_CONSOLE_CMD(ConChangeDirectory) if (argc != 2) return false; const char *file = argv[1]; - const FiosItem *item = GetFiosItem(file); + _console_file_list.ValidateFileList(true); + const FiosItem *item = _console_file_list.FindItem(file); if (item != NULL) { switch (item->type) { case FIOS_TYPE_DIR: case FIOS_TYPE_DRIVE: case FIOS_TYPE_PARENT: @@ -448,7 +441,7 @@ DEF_CONSOLE_CMD(ConChangeDirectory) IConsolePrintF(CC_ERROR, "%s: No such file or directory.", file); } - FiosFreeSavegameList(); + _console_file_list.InvalidateFileList(); return true; } @@ -462,8 +455,8 @@ DEF_CONSOLE_CMD(ConPrintWorkingDirectory) } /* XXX - Workaround for broken file handling */ - FiosGetSavegameList(SLD_LOAD_GAME); - FiosFreeSavegameList(); + _console_file_list.ValidateFileList(true); + _console_file_list.InvalidateFileList(); FiosGetDescText(&path, NULL); IConsolePrint(CC_DEFAULT, path); diff --git a/src/core/smallvec_type.hpp b/src/core/smallvec_type.hpp index 62de176a54..8676265d26 100644 --- a/src/core/smallvec_type.hpp +++ b/src/core/smallvec_type.hpp @@ -256,6 +256,8 @@ public: /** * Get the number of items in the list. + * + * @return The number of items in the list. */ inline uint Length() const { diff --git a/src/crashlog.cpp b/src/crashlog.cpp index 4000cfb03e..53a85e442c 100644 --- a/src/crashlog.cpp +++ b/src/crashlog.cpp @@ -388,7 +388,7 @@ bool CrashLog::WriteSavegame(char *filename, const char *filename_last) const seprintf(filename, filename_last, "%scrash.sav", _personal_dir); /* Don't do a threaded saveload. */ - return SaveOrLoad(filename, SL_SAVE, NO_DIRECTORY, false) == SL_OK; + return SaveOrLoad(filename, SLO_SAVE, DFT_GAME_FILE, NO_DIRECTORY, false) == SL_OK; } catch (...) { return false; } diff --git a/src/disaster_vehicle.cpp b/src/disaster_vehicle.cpp index d75e2d460e..98978ddfb0 100644 --- a/src/disaster_vehicle.cpp +++ b/src/disaster_vehicle.cpp @@ -112,7 +112,7 @@ void DisasterVehicle::UpdateImage() { SpriteID img = this->image_override; if (img == 0) img = _disaster_images[this->subtype][this->direction]; - this->cur_image = img; + this->sprite_seq.Set(img); } /** @@ -499,7 +499,8 @@ static bool DisasterTick_Helicopter_Rotors(DisasterVehicle *v) v->tick_counter++; if (HasBit(v->tick_counter, 0)) return true; - if (++v->cur_image > SPR_ROTOR_MOVING_3) v->cur_image = SPR_ROTOR_MOVING_1; + SpriteID &cur_image = v->sprite_seq.seq[0].sprite; + if (++cur_image > SPR_ROTOR_MOVING_3) cur_image = SPR_ROTOR_MOVING_1; v->UpdatePositionAndViewport(); diff --git a/src/effectvehicle.cpp b/src/effectvehicle.cpp index 209b78d2e9..5921cd190e 100644 --- a/src/effectvehicle.cpp +++ b/src/effectvehicle.cpp @@ -22,10 +22,26 @@ #include "safeguards.h" +/** + * Increment the sprite unless it has reached the end of the animation. + * @param v Vehicle to increment sprite of. + * @param last Last sprite of animation. + * @return true if the sprite was incremented, false if the end was reached. + */ +static bool IncrementSprite(EffectVehicle *v, SpriteID last) +{ + if (v->sprite_seq.seq[0].sprite != last) { + v->sprite_seq.seq[0].sprite++; + return true; + } else { + return false; + } +} + static void ChimneySmokeInit(EffectVehicle *v) { uint32 r = Random(); - v->cur_image = SPR_CHIMNEY_SMOKE_0 + GB(r, 0, 3); + v->sprite_seq.Set(SPR_CHIMNEY_SMOKE_0 + GB(r, 0, 3)); v->progress = GB(r, 16, 3); } @@ -40,10 +56,8 @@ static bool ChimneySmokeTick(EffectVehicle *v) return false; } - if (v->cur_image != SPR_CHIMNEY_SMOKE_7) { - v->cur_image++; - } else { - v->cur_image = SPR_CHIMNEY_SMOKE_0; + if (!IncrementSprite(v, SPR_CHIMNEY_SMOKE_7)) { + v->sprite_seq.Set(SPR_CHIMNEY_SMOKE_0); } v->progress = 7; v->UpdatePositionAndViewport(); @@ -54,7 +68,7 @@ static bool ChimneySmokeTick(EffectVehicle *v) static void SteamSmokeInit(EffectVehicle *v) { - v->cur_image = SPR_STEAM_SMOKE_0; + v->sprite_seq.Set(SPR_STEAM_SMOKE_0); v->progress = 12; } @@ -70,9 +84,7 @@ static bool SteamSmokeTick(EffectVehicle *v) } if ((v->progress & 0xF) == 4) { - if (v->cur_image != SPR_STEAM_SMOKE_4) { - v->cur_image++; - } else { + if (!IncrementSprite(v, SPR_STEAM_SMOKE_4)) { delete v; return false; } @@ -86,7 +98,7 @@ static bool SteamSmokeTick(EffectVehicle *v) static void DieselSmokeInit(EffectVehicle *v) { - v->cur_image = SPR_DIESEL_SMOKE_0; + v->sprite_seq.Set(SPR_DIESEL_SMOKE_0); v->progress = 0; } @@ -98,13 +110,11 @@ static bool DieselSmokeTick(EffectVehicle *v) v->z_pos++; v->UpdatePositionAndViewport(); } else if ((v->progress & 7) == 1) { - if (v->cur_image != SPR_DIESEL_SMOKE_5) { - v->cur_image++; - v->UpdatePositionAndViewport(); - } else { + if (!IncrementSprite(v, SPR_DIESEL_SMOKE_5)) { delete v; return false; } + v->UpdatePositionAndViewport(); } return true; @@ -112,7 +122,7 @@ static bool DieselSmokeTick(EffectVehicle *v) static void ElectricSparkInit(EffectVehicle *v) { - v->cur_image = SPR_ELECTRIC_SPARK_0; + v->sprite_seq.Set(SPR_ELECTRIC_SPARK_0); v->progress = 1; } @@ -122,13 +132,12 @@ static bool ElectricSparkTick(EffectVehicle *v) v->progress++; } else { v->progress = 0; - if (v->cur_image != SPR_ELECTRIC_SPARK_5) { - v->cur_image++; - v->UpdatePositionAndViewport(); - } else { + + if (!IncrementSprite(v, SPR_ELECTRIC_SPARK_5)) { delete v; return false; } + v->UpdatePositionAndViewport(); } return true; @@ -136,7 +145,7 @@ static bool ElectricSparkTick(EffectVehicle *v) static void SmokeInit(EffectVehicle *v) { - v->cur_image = SPR_SMOKE_0; + v->sprite_seq.Set(SPR_SMOKE_0); v->progress = 12; } @@ -152,9 +161,7 @@ static bool SmokeTick(EffectVehicle *v) } if ((v->progress & 0xF) == 4) { - if (v->cur_image != SPR_SMOKE_4) { - v->cur_image++; - } else { + if (!IncrementSprite(v, SPR_SMOKE_4)) { delete v; return false; } @@ -168,7 +175,7 @@ static bool SmokeTick(EffectVehicle *v) static void ExplosionLargeInit(EffectVehicle *v) { - v->cur_image = SPR_EXPLOSION_LARGE_0; + v->sprite_seq.Set(SPR_EXPLOSION_LARGE_0); v->progress = 0; } @@ -176,13 +183,11 @@ static bool ExplosionLargeTick(EffectVehicle *v) { v->progress++; if ((v->progress & 3) == 0) { - if (v->cur_image != SPR_EXPLOSION_LARGE_F) { - v->cur_image++; - v->UpdatePositionAndViewport(); - } else { + if (!IncrementSprite(v, SPR_EXPLOSION_LARGE_F)) { delete v; return false; } + v->UpdatePositionAndViewport(); } return true; @@ -190,7 +195,7 @@ static bool ExplosionLargeTick(EffectVehicle *v) static void BreakdownSmokeInit(EffectVehicle *v) { - v->cur_image = SPR_BREAKDOWN_SMOKE_0; + v->sprite_seq.Set(SPR_BREAKDOWN_SMOKE_0); v->progress = 0; } @@ -198,10 +203,8 @@ static bool BreakdownSmokeTick(EffectVehicle *v) { v->progress++; if ((v->progress & 7) == 0) { - if (v->cur_image != SPR_BREAKDOWN_SMOKE_3) { - v->cur_image++; - } else { - v->cur_image = SPR_BREAKDOWN_SMOKE_0; + if (!IncrementSprite(v, SPR_BREAKDOWN_SMOKE_3)) { + v->sprite_seq.Set(SPR_BREAKDOWN_SMOKE_0); } v->UpdatePositionAndViewport(); } @@ -217,7 +220,7 @@ static bool BreakdownSmokeTick(EffectVehicle *v) static void ExplosionSmallInit(EffectVehicle *v) { - v->cur_image = SPR_EXPLOSION_SMALL_0; + v->sprite_seq.Set(SPR_EXPLOSION_SMALL_0); v->progress = 0; } @@ -225,13 +228,11 @@ static bool ExplosionSmallTick(EffectVehicle *v) { v->progress++; if ((v->progress & 3) == 0) { - if (v->cur_image != SPR_EXPLOSION_SMALL_B) { - v->cur_image++; - v->UpdatePositionAndViewport(); - } else { + if (!IncrementSprite(v, SPR_EXPLOSION_SMALL_B)) { delete v; return false; } + v->UpdatePositionAndViewport(); } return true; @@ -239,7 +240,7 @@ static bool ExplosionSmallTick(EffectVehicle *v) static void BulldozerInit(EffectVehicle *v) { - v->cur_image = SPR_BULLDOZER_NE; + v->sprite_seq.Set(SPR_BULLDOZER_NE); v->progress = 0; v->animation_state = 0; v->animation_substate = 0; @@ -290,7 +291,7 @@ static bool BulldozerTick(EffectVehicle *v) if ((v->progress & 7) == 0) { const BulldozerMovement *b = &_bulldozer_movement[v->animation_state]; - v->cur_image = SPR_BULLDOZER_NE + b->image; + v->sprite_seq.Set(SPR_BULLDOZER_NE + b->image); v->x_pos += _inc_by_dir[b->direction].x; v->y_pos += _inc_by_dir[b->direction].y; @@ -312,7 +313,7 @@ static bool BulldozerTick(EffectVehicle *v) static void BubbleInit(EffectVehicle *v) { - v->cur_image = SPR_BUBBLE_GENERATE_0; + v->sprite_seq.Set(SPR_BUBBLE_GENERATE_0); v->spritenum = 0; v->progress = 0; } @@ -475,8 +476,8 @@ static bool BubbleTick(EffectVehicle *v) if ((v->progress & 3) != 0) return true; if (v->spritenum == 0) { - v->cur_image++; - if (v->cur_image < SPR_BUBBLE_GENERATE_3) { + v->sprite_seq.seq[0].sprite++; + if (v->sprite_seq.seq[0].sprite < SPR_BUBBLE_GENERATE_3) { v->UpdatePositionAndViewport(); return true; } @@ -521,7 +522,7 @@ static bool BubbleTick(EffectVehicle *v) v->x_pos += b->x; v->y_pos += b->y; v->z_pos += b->z; - v->cur_image = SPR_BUBBLE_0 + b->image; + v->sprite_seq.Set(SPR_BUBBLE_0 + b->image); v->UpdatePositionAndViewport(); diff --git a/src/engine_type.h b/src/engine_type.h index aeebcb3522..8356fda8f8 100644 --- a/src/engine_type.h +++ b/src/engine_type.h @@ -158,6 +158,7 @@ enum EngineMiscFlags { EF_AUTO_REFIT = 4, ///< Automatic refitting is allowed EF_NO_DEFAULT_CARGO_MULTIPLIER = 5, ///< Use the new capacity algorithm. The default cargotype of the vehicle does not affect capacity multipliers. CB 15 is also called in purchase list. EF_NO_BREAKDOWN_SMOKE = 6, ///< Do not show black smoke during a breakdown. + EF_SPRITE_STACK = 7, ///< Draw vehicle by stacking multiple sprites. }; /** diff --git a/src/fileio_type.h b/src/fileio_type.h index 87cb2d1a82..fd3ac84554 100644 --- a/src/fileio_type.h +++ b/src/fileio_type.h @@ -14,6 +14,96 @@ #include "core/enum_type.hpp" +/** The different abstract types of files that the system knows about. */ +enum AbstractFileType { + FT_NONE, ///< nothing to do + FT_SAVEGAME, ///< old or new savegame + FT_SCENARIO, ///< old or new scenario + FT_HEIGHTMAP, ///< heightmap file + + FT_INVALID = 7, ///< Invalid or unknown file type. + FT_NUMBITS = 3, ///< Number of bits required for storing a #AbstractFileType value. + FT_MASK = (1 << FT_NUMBITS) - 1, ///< Bitmask for extracting an abstract file type. +}; + +/** Kinds of files in each #AbstractFileType. */ +enum DetailedFileType { + /* Save game and scenario files. */ + DFT_OLD_GAME_FILE, ///< Old save game or scenario file. + DFT_GAME_FILE, ///< Save game or scenario file. + + /* Heightmap files. */ + DFT_HEIGHTMAP_BMP, ///< BMP file. + DFT_HEIGHTMAP_PNG, ///< PNG file. + + /* fios 'files' */ + DFT_FIOS_DRIVE, ///< A drive (letter) entry. + DFT_FIOS_PARENT, ///< A parent directory entry. + DFT_FIOS_DIR, ///< A directory entry. + DFT_FIOS_DIRECT, ///< Direct filename. + + DFT_INVALID = 255, ///< Unknown or invalid file. +}; + +/** Operation performed on the file. */ +enum SaveLoadOperation { + SLO_CHECK, ///< Load file for checking and/or preview. + SLO_LOAD, ///< File is being loaded. + SLO_SAVE, ///< File is being saved. + + SLO_INVALID, ///< Unknown file operation. +}; + +/** + * Construct an enum value for #FiosType as a combination of an abstract and a detailed file type. + * @param abstract Abstract file type (one of #AbstractFileType). + * @param detailed Detailed file type (one of #DetailedFileType). + */ +#define MAKE_FIOS_TYPE(abstract, detailed) ((abstract) | ((detailed) << FT_NUMBITS)) + +/** + * Elements of a file system that are recognized. + * Values are a combination of #AbstractFileType and #DetailedFileType. + * @see GetAbstractFileType GetDetailedFileType + */ +enum FiosType { + FIOS_TYPE_DRIVE = MAKE_FIOS_TYPE(FT_NONE, DFT_FIOS_DRIVE), + FIOS_TYPE_PARENT = MAKE_FIOS_TYPE(FT_NONE, DFT_FIOS_PARENT), + FIOS_TYPE_DIR = MAKE_FIOS_TYPE(FT_NONE, DFT_FIOS_DIR), + FIOS_TYPE_DIRECT = MAKE_FIOS_TYPE(FT_NONE, DFT_FIOS_DIRECT), + + FIOS_TYPE_FILE = MAKE_FIOS_TYPE(FT_SAVEGAME, DFT_GAME_FILE), + FIOS_TYPE_OLDFILE = MAKE_FIOS_TYPE(FT_SAVEGAME, DFT_OLD_GAME_FILE), + FIOS_TYPE_SCENARIO = MAKE_FIOS_TYPE(FT_SCENARIO, DFT_GAME_FILE), + FIOS_TYPE_OLD_SCENARIO = MAKE_FIOS_TYPE(FT_SCENARIO, DFT_OLD_GAME_FILE), + FIOS_TYPE_PNG = MAKE_FIOS_TYPE(FT_HEIGHTMAP, DFT_HEIGHTMAP_PNG), + FIOS_TYPE_BMP = MAKE_FIOS_TYPE(FT_HEIGHTMAP, DFT_HEIGHTMAP_BMP), + + FIOS_TYPE_INVALID = MAKE_FIOS_TYPE(FT_INVALID, DFT_INVALID), +}; + +#undef MAKE_FIOS_TYPE + +/** + * Extract the abstract file type from a #FiosType. + * @param fios_type Type to query. + * @return The Abstract file type of the \a fios_type. + */ +inline AbstractFileType GetAbstractFileType(FiosType fios_type) +{ + return static_cast(fios_type & FT_MASK); +} + +/** + * Extract the detailed file type from a #FiosType. + * @param fios_type Type to query. + * @return The Detailed file type of the \a fios_type. + */ +inline DetailedFileType GetDetailedFileType(FiosType fios_type) +{ + return static_cast(fios_type >> FT_NUMBITS); +} + /** * The different kinds of subdirectories OpenTTD uses */ diff --git a/src/fios.cpp b/src/fios.cpp index 00ed5a484c..8ed01152f0 100644 --- a/src/fios.cpp +++ b/src/fios.cpp @@ -29,17 +29,15 @@ #include "safeguards.h" /* Variables to display file lists */ -SmallVector _fios_items; static char *_fios_path; static const char *_fios_path_last; -SmallFiosItem _file_to_saveload; SortingBits _savegame_sort_order = SORT_BY_DATE | SORT_DESCENDING; /* OS-specific functions are taken from their respective files (win32/unix/os2 .c) */ extern bool FiosIsRoot(const char *path); extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb); extern bool FiosIsHiddenFile(const struct dirent *ent); -extern void FiosGetDrives(); +extern void FiosGetDrives(FileList &file_list); extern bool FiosGetDiskFreeSpace(const char *path, uint64 *tot); /* get the name of an oldstyle savegame */ @@ -65,11 +63,72 @@ int CDECL CompareFiosItems(const FiosItem *da, const FiosItem *db) return r; } -/** Free the list of savegames. */ -void FiosFreeSavegameList() +FileList::~FileList() { - _fios_items.Clear(); - _fios_items.Compact(); + this->Clear(); +} + +/** + * Construct a file list with the given kind of files, for the stated purpose. + * @param abstract_filetype Kind of files to collect. + * @param fop Purpose of the collection, either #SLO_LOAD or #SLO_SAVE. + */ +void FileList::BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperation fop) +{ + this->Clear(); + + assert(fop == SLO_LOAD || SLO_SAVE); + switch (abstract_filetype) { + case FT_NONE: + break; + + case FT_SAVEGAME: + FiosGetSavegameList(fop, *this); + break; + + case FT_SCENARIO: + FiosGetScenarioList(fop, *this); + break; + + case FT_HEIGHTMAP: + FiosGetHeightmapList(fop, *this); + break; + + default: + NOT_REACHED(); + } +} + +/** + * Find file information of a file by its name from the file list. + * @param file The filename to return information about. Can be the actual name + * or a numbered entry into the filename list. + * @return The information on the file, or \c NULL if the file is not available. + */ +const FiosItem *FileList::FindItem(const char *file) +{ + for (const FiosItem *item = this->Begin(); item != this->End(); item++) { + if (strcmp(file, item->name) == 0) return item; + if (strcmp(file, item->title) == 0) return item; + } + + /* If no name matches, try to parse it as number */ + char *endptr; + int i = strtol(file, &endptr, 10); + if (file == endptr || *endptr != '\0') i = -1; + + if (IsInsideMM(i, 0, this->Length())) return this->Get(i); + + /* As a last effort assume it is an OpenTTD savegame and + * that the ".sav" part was not given. */ + char long_file[MAX_PATH]; + seprintf(long_file, lastof(long_file), "%s.sav", file); + for (const FiosItem *item = this->Begin(); item != this->End(); item++) { + if (strcmp(long_file, item->name) == 0) return item; + if (strcmp(long_file, item->title) == 0) return item; + } + + return NULL; } /** @@ -215,23 +274,24 @@ bool FiosDelete(const char *name) return unlink(filename) == 0; } -typedef FiosType fios_getlist_callback_proc(SaveLoadDialogMode mode, const char *filename, const char *ext, char *title, const char *last); +typedef FiosType fios_getlist_callback_proc(SaveLoadOperation fop, const char *filename, const char *ext, char *title, const char *last); /** * Scanner to scan for a particular type of FIOS file. */ class FiosFileScanner : public FileScanner { - SaveLoadDialogMode mode; ///< The mode we want to search for + SaveLoadOperation fop; ///< The kind of file we are looking for. fios_getlist_callback_proc *callback_proc; ///< Callback to check whether the file may be added + FileList &file_list; ///< Destination of the found files. public: /** * Create the scanner - * @param mode The mode we are in. Some modes don't allow 'parent'. - * @param callback_proc The function that is called where you need to do the filtering. + * @param fop Purpose of collecting the list. + * @param callback_proc The function that is called where you need to do the filtering. + * @param file_list Destination of the found files. */ - FiosFileScanner(SaveLoadDialogMode mode, fios_getlist_callback_proc *callback_proc) : - mode(mode), - callback_proc(callback_proc) + FiosFileScanner(SaveLoadOperation fop, fios_getlist_callback_proc *callback_proc, FileList &file_list) : + fop(fop), callback_proc(callback_proc), file_list(file_list) {} /* virtual */ bool AddFile(const char *filename, size_t basepath_length, const char *tar_filename); @@ -251,14 +311,14 @@ bool FiosFileScanner::AddFile(const char *filename, size_t basepath_length, cons char fios_title[64]; fios_title[0] = '\0'; // reset the title; - FiosType type = this->callback_proc(this->mode, filename, ext, fios_title, lastof(fios_title)); + FiosType type = this->callback_proc(this->fop, filename, ext, fios_title, lastof(fios_title)); if (type == FIOS_TYPE_INVALID) return false; - for (const FiosItem *fios = _fios_items.Begin(); fios != _fios_items.End(); fios++) { + for (const FiosItem *fios = file_list.Begin(); fios != file_list.End(); fios++) { if (strcmp(fios->name, filename) == 0) return false; } - FiosItem *fios = _fios_items.Append(); + FiosItem *fios = file_list.Append(); #ifdef WIN32 struct _stat sb; if (_tstat(OTTD2FS(filename), &sb) == 0) { @@ -289,11 +349,12 @@ bool FiosFileScanner::AddFile(const char *filename, size_t basepath_length, cons /** * Fill the list of the files in a directory, according to some arbitrary rule. - * @param mode The mode we are in. Some modes don't allow 'parent'. - * @param callback_proc The function that is called where you need to do the filtering. - * @param subdir The directory from where to start (global) searching. + * @param fop Purpose of collecting the list. + * @param callback_proc The function that is called where you need to do the filtering. + * @param subdir The directory from where to start (global) searching. + * @param file_list Destination of the found files. */ -static void FiosGetFileList(SaveLoadDialogMode mode, fios_getlist_callback_proc *callback_proc, Subdirectory subdir) +static void FiosGetFileList(SaveLoadOperation fop, fios_getlist_callback_proc *callback_proc, Subdirectory subdir, FileList &file_list) { struct stat sb; struct dirent *dirent; @@ -302,11 +363,11 @@ static void FiosGetFileList(SaveLoadDialogMode mode, fios_getlist_callback_proc int sort_start; char d_name[sizeof(fios->name)]; - _fios_items.Clear(); + file_list.Clear(); /* A parent directory link exists if we are not in the root directory */ if (!FiosIsRoot(_fios_path)) { - fios = _fios_items.Append(); + fios = file_list.Append(); fios->type = FIOS_TYPE_PARENT; fios->mtime = 0; strecpy(fios->name, "..", lastof(fios->name)); @@ -322,7 +383,7 @@ static void FiosGetFileList(SaveLoadDialogMode mode, fios_getlist_callback_proc if (FiosIsValidFile(_fios_path, dirent, &sb) && S_ISDIR(sb.st_mode) && (!FiosIsHiddenFile(dirent) || strncasecmp(d_name, PERSONAL_DIR, strlen(d_name)) == 0) && strcmp(d_name, ".") != 0 && strcmp(d_name, "..") != 0) { - fios = _fios_items.Append(); + fios = file_list.Append(); fios->type = FIOS_TYPE_DIR; fios->mtime = 0; strecpy(fios->name, d_name, lastof(fios->name)); @@ -337,27 +398,27 @@ static void FiosGetFileList(SaveLoadDialogMode mode, fios_getlist_callback_proc { SortingBits order = _savegame_sort_order; _savegame_sort_order = SORT_BY_NAME | SORT_ASCENDING; - QSortT(_fios_items.Begin(), _fios_items.Length(), CompareFiosItems); + QSortT(file_list.files.Begin(), file_list.files.Length(), CompareFiosItems); _savegame_sort_order = order; } /* This is where to start sorting for the filenames */ - sort_start = _fios_items.Length(); + sort_start = file_list.Length(); /* Show files */ - FiosFileScanner scanner(mode, callback_proc); + FiosFileScanner scanner(fop, callback_proc, file_list); if (subdir == NO_DIRECTORY) { scanner.Scan(NULL, _fios_path, false); } else { scanner.Scan(NULL, subdir, true, true); } - QSortT(_fios_items.Get(sort_start), _fios_items.Length() - sort_start, CompareFiosItems); + QSortT(file_list.Get(sort_start), file_list.Length() - sort_start, CompareFiosItems); /* Show drives */ - FiosGetDrives(); + FiosGetDrives(file_list); - _fios_items.Compact(); + file_list.Compact(); } /** @@ -386,7 +447,7 @@ static void GetFileTitle(const char *file, char *title, const char *last, Subdir /** * Callback for FiosGetFileList. It tells if a file is a savegame or not. - * @param mode Save/load mode. + * @param fop Purpose of collecting the list. * @param file Name of the file to check. * @param ext A pointer to the extension identifier inside file * @param title Buffer if a callback wants to lookup the title of the file; NULL to skip the lookup @@ -395,7 +456,7 @@ static void GetFileTitle(const char *file, char *title, const char *last, Subdir * @see FiosGetFileList * @see FiosGetSavegameList */ -FiosType FiosGetSavegameListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last) +FiosType FiosGetSavegameListCallback(SaveLoadOperation fop, const char *file, const char *ext, char *title, const char *last) { /* Show savegame files * .SAV OpenTTD saved game @@ -411,7 +472,7 @@ FiosType FiosGetSavegameListCallback(SaveLoadDialogMode mode, const char *file, return FIOS_TYPE_FILE; } - if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO) { + if (fop == SLO_LOAD) { if (strcasecmp(ext, ".ss1") == 0 || strcasecmp(ext, ".sv1") == 0 || strcasecmp(ext, ".sv2") == 0) { if (title != NULL) GetOldSaveGameName(file, title, last); @@ -424,10 +485,11 @@ FiosType FiosGetSavegameListCallback(SaveLoadDialogMode mode, const char *file, /** * Get a list of savegames. - * @param mode Save/load mode. + * @param fop Purpose of collecting the list. + * @param file_list Destination of the found files. * @see FiosGetFileList */ -void FiosGetSavegameList(SaveLoadDialogMode mode) +void FiosGetSavegameList(SaveLoadOperation fop, FileList &file_list) { static char *fios_save_path = NULL; static char *fios_save_path_last = NULL; @@ -441,12 +503,12 @@ void FiosGetSavegameList(SaveLoadDialogMode mode) _fios_path = fios_save_path; _fios_path_last = fios_save_path_last; - FiosGetFileList(mode, &FiosGetSavegameListCallback, NO_DIRECTORY); + FiosGetFileList(fop, &FiosGetSavegameListCallback, NO_DIRECTORY, file_list); } /** * Callback for FiosGetFileList. It tells if a file is a scenario or not. - * @param mode Save/load mode. + * @param fop Purpose of collecting the list. * @param file Name of the file to check. * @param ext A pointer to the extension identifier inside file * @param title Buffer if a callback wants to lookup the title of the file @@ -455,7 +517,7 @@ void FiosGetSavegameList(SaveLoadDialogMode mode) * @see FiosGetFileList * @see FiosGetScenarioList */ -static FiosType FiosGetScenarioListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last) +static FiosType FiosGetScenarioListCallback(SaveLoadOperation fop, const char *file, const char *ext, char *title, const char *last) { /* Show scenario files * .SCN OpenTTD style scenario file @@ -466,7 +528,7 @@ static FiosType FiosGetScenarioListCallback(SaveLoadDialogMode mode, const char return FIOS_TYPE_SCENARIO; } - if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO) { + if (fop == SLO_LOAD) { if (strcasecmp(ext, ".sv0") == 0 || strcasecmp(ext, ".ss0") == 0 ) { GetOldSaveGameName(file, title, last); return FIOS_TYPE_OLD_SCENARIO; @@ -478,10 +540,11 @@ static FiosType FiosGetScenarioListCallback(SaveLoadDialogMode mode, const char /** * Get a list of scenarios. - * @param mode Save/load mode. + * @param fop Purpose of collecting the list. + * @param file_list Destination of the found files. * @see FiosGetFileList */ -void FiosGetScenarioList(SaveLoadDialogMode mode) +void FiosGetScenarioList(SaveLoadOperation fop, FileList &file_list) { static char *fios_scn_path = NULL; static char *fios_scn_path_last = NULL; @@ -499,10 +562,11 @@ void FiosGetScenarioList(SaveLoadDialogMode mode) char base_path[MAX_PATH]; FioGetDirectory(base_path, lastof(base_path), SCENARIO_DIR); - FiosGetFileList(mode, &FiosGetScenarioListCallback, (mode == SLD_LOAD_SCENARIO && strcmp(base_path, _fios_path) == 0) ? SCENARIO_DIR : NO_DIRECTORY); + Subdirectory subdir = (fop == SLO_LOAD && strcmp(base_path, _fios_path) == 0) ? SCENARIO_DIR : NO_DIRECTORY; + FiosGetFileList(fop, &FiosGetScenarioListCallback, subdir, file_list); } -static FiosType FiosGetHeightmapListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last) +static FiosType FiosGetHeightmapListCallback(SaveLoadOperation fop, const char *file, const char *ext, char *title, const char *last) { /* Show heightmap files * .PNG PNG Based heightmap files @@ -548,9 +612,10 @@ static FiosType FiosGetHeightmapListCallback(SaveLoadDialogMode mode, const char /** * Get a list of heightmaps. - * @param mode Save/load mode. + * @param fop Purpose of collecting the list. + * @param file_list Destination of the found files. */ -void FiosGetHeightmapList(SaveLoadDialogMode mode) +void FiosGetHeightmapList(SaveLoadOperation fop, FileList &file_list) { static char *fios_hmap_path = NULL; static char *fios_hmap_path_last = NULL; @@ -567,7 +632,8 @@ void FiosGetHeightmapList(SaveLoadDialogMode mode) char base_path[MAX_PATH]; FioGetDirectory(base_path, lastof(base_path), HEIGHTMAP_DIR); - FiosGetFileList(mode, &FiosGetHeightmapListCallback, strcmp(base_path, _fios_path) == 0 ? HEIGHTMAP_DIR : NO_DIRECTORY); + Subdirectory subdir = strcmp(base_path, _fios_path) == 0 ? HEIGHTMAP_DIR : NO_DIRECTORY; + FiosGetFileList(fop, &FiosGetHeightmapListCallback, subdir, file_list); } /** diff --git a/src/fios.h b/src/fios.h index b26fe00925..51e0c275a6 100644 --- a/src/fios.h +++ b/src/fios.h @@ -97,38 +97,6 @@ enum FileSlots { MAX_FILE_SLOTS = 64 }; -/** Mode of the file dialogue window. */ -enum SaveLoadDialogMode { - SLD_LOAD_GAME, ///< Load a game. - SLD_LOAD_SCENARIO, ///< Load a scenario. - SLD_SAVE_GAME, ///< Save a game. - SLD_SAVE_SCENARIO, ///< Save a scenario. - SLD_LOAD_HEIGHTMAP, ///< Load a heightmap. - SLD_SAVE_HEIGHTMAP, ///< Save a heightmap. -}; - -/** The different types of files that the system knows about. */ -enum FileType { - FT_NONE, ///< nothing to do - FT_SAVEGAME, ///< old or new savegame - FT_SCENARIO, ///< old or new scenario - FT_HEIGHTMAP, ///< heightmap file -}; - -enum FiosType { - FIOS_TYPE_DRIVE, - FIOS_TYPE_PARENT, - FIOS_TYPE_DIR, - FIOS_TYPE_FILE, - FIOS_TYPE_OLDFILE, - FIOS_TYPE_SCENARIO, - FIOS_TYPE_OLD_SCENARIO, - FIOS_TYPE_DIRECT, - FIOS_TYPE_PNG, - FIOS_TYPE_BMP, - FIOS_TYPE_INVALID = 255, -}; - /** Deals with finding savegames */ struct FiosItem { FiosType type; @@ -137,12 +105,95 @@ struct FiosItem { char name[MAX_PATH]; }; -/** Deals with the type of the savegame, independent of extension */ -struct SmallFiosItem { - int mode; ///< savegame/scenario type (old, new) - FileType filetype; ///< what type of file are we dealing with - char name[MAX_PATH]; ///< name - char title[255]; ///< internal name of the game +/** List of file information. */ +class FileList { +public: + ~FileList(); + + /** + * Construct a new entry in the file list. + * @return Pointer to the new items to be initialized. + */ + inline FiosItem *Append() + { + return this->files.Append(); + } + + /** + * Get the number of files in the list. + * @return The number of files stored in the list. + */ + inline uint Length() const + { + return this->files.Length(); + } + + /** + * Get a pointer to the first file information. + * @return Address of the first file information. + */ + inline const FiosItem *Begin() const + { + return this->files.Begin(); + } + + /** + * Get a pointer behind the last file information. + * @return Address behind the last file information. + */ + inline const FiosItem *End() const + { + return this->files.End(); + } + + /** + * Get a pointer to the indicated file information. File information must exist. + * @return Address of the indicated existing file information. + */ + inline const FiosItem *Get(uint index) const + { + return this->files.Get(index); + } + + /** + * Get a pointer to the indicated file information. File information must exist. + * @return Address of the indicated existing file information. + */ + inline FiosItem *Get(uint index) + { + return this->files.Get(index); + } + + inline const FiosItem &operator[](uint index) const + { + return this->files[index]; + } + + /** + * Get a reference to the indicated file information. File information must exist. + * @return The requested file information. + */ + inline FiosItem &operator[](uint index) + { + return this->files[index]; + } + + /** Remove all items from the list. */ + inline void Clear() + { + this->files.Clear(); + } + + /** Compact the list down to the smallest block size boundary. */ + inline void Compact() + { + this->files.Compact(); + } + + void BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperation fop); + const FiosItem *FindItem(const char *file); + + SmallVector files; ///< The list of files. }; enum SortingBits { @@ -154,18 +205,14 @@ enum SortingBits { DECLARE_ENUM_AS_BIT_SET(SortingBits) /* Variables to display file lists */ -extern SmallVector _fios_items; -extern SmallFiosItem _file_to_saveload; -extern SaveLoadDialogMode _saveload_mode; extern SortingBits _savegame_sort_order; -void ShowSaveLoadDialog(SaveLoadDialogMode mode); +void ShowSaveLoadDialog(AbstractFileType abstract_filetype, SaveLoadOperation fop); -void FiosGetSavegameList(SaveLoadDialogMode mode); -void FiosGetScenarioList(SaveLoadDialogMode mode); -void FiosGetHeightmapList(SaveLoadDialogMode mode); +void FiosGetSavegameList(SaveLoadOperation fop, FileList &file_list); +void FiosGetScenarioList(SaveLoadOperation fop, FileList &file_list); +void FiosGetHeightmapList(SaveLoadOperation fop, FileList &file_list); -void FiosFreeSavegameList(); const char *FiosBrowseTo(const FiosItem *item); StringID FiosGetDescText(const char **path, uint64 *total_free); @@ -173,13 +220,8 @@ bool FiosDelete(const char *name); void FiosMakeHeightmapName(char *buf, const char *name, const char *last); void FiosMakeSavegameName(char *buf, const char *name, const char *last); -FiosType FiosGetSavegameListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last); +FiosType FiosGetSavegameListCallback(SaveLoadOperation fop, const char *file, const char *ext, char *title, const char *last); int CDECL CompareFiosItems(const FiosItem *a, const FiosItem *b); -extern const TextColour _fios_colours[]; - -void BuildFileList(); -void SetFiosType(const byte fiostype); - #endif /* FIOS_H */ diff --git a/src/fios_gui.cpp b/src/fios_gui.cpp index 9316deb658..e6cd9625cc 100644 --- a/src/fios_gui.cpp +++ b/src/fios_gui.cpp @@ -36,7 +36,6 @@ #include "safeguards.h" -SaveLoadDialogMode _saveload_mode; LoadCheckData _load_check_data; ///< Data loaded from save during SL_LOAD_CHECK. static bool _fios_path_changed; @@ -187,33 +186,24 @@ static const NWidgetPart _nested_save_dialog_widgets[] = { EndContainer(), }; -/** Colours for fios types, indexed by #FiosType. */ -const TextColour _fios_colours[] = { - TC_LIGHT_BLUE, TC_DARK_GREEN, TC_DARK_GREEN, TC_ORANGE, TC_LIGHT_BROWN, - TC_ORANGE, TC_LIGHT_BROWN, TC_ORANGE, TC_ORANGE, TC_YELLOW +/** Text colours of #DetailedFileType fios entries in the window. */ +static const TextColour _fios_colours[] = { + TC_LIGHT_BROWN, // DFT_OLD_GAME_FILE + TC_ORANGE, // DFT_GAME_FILE + TC_YELLOW, // DFT_HEIGHTMAP_BMP + TC_ORANGE, // DFT_HEIGHTMAP_PNG + TC_LIGHT_BLUE, // DFT_FIOS_DRIVE + TC_DARK_GREEN, // DFT_FIOS_PARENT + TC_DARK_GREEN, // DFT_FIOS_DIR + TC_ORANGE, // DFT_FIOS_DIRECT }; -void BuildFileList() -{ - _fios_path_changed = true; - FiosFreeSavegameList(); - switch (_saveload_mode) { - case SLD_LOAD_SCENARIO: - case SLD_SAVE_SCENARIO: - FiosGetScenarioList(_saveload_mode); break; - case SLD_SAVE_HEIGHTMAP: - case SLD_LOAD_HEIGHTMAP: - FiosGetHeightmapList(_saveload_mode); break; - - default: FiosGetSavegameList(_saveload_mode); break; - } - - /* Invalidate saveload window */ - InvalidateWindowData(WC_SAVELOAD, 0, 2, true); -} - -static void MakeSortedSaveGameList() +/** + * Sort the collected list save games prior to displaying it in the save/load gui. + * @param [inout] file_list List of save game files found in the directory. + */ +static void SortSaveGameList(FileList &file_list) { uint sort_start = 0; uint sort_end = 0; @@ -222,7 +212,7 @@ static void MakeSortedSaveGameList() * Drives (A:\ (windows only) are always under the files (FIOS_TYPE_DRIVE) * Only sort savegames/scenarios, not directories */ - for (const FiosItem *item = _fios_items.Begin(); item != _fios_items.End(); item++) { + for (const FiosItem *item = file_list.Begin(); item != file_list.End(); item++) { switch (item->type) { case FIOS_TYPE_DIR: sort_start++; break; case FIOS_TYPE_PARENT: sort_start++; break; @@ -231,15 +221,18 @@ static void MakeSortedSaveGameList() } } - uint s_amount = _fios_items.Length() - sort_start - sort_end; - QSortT(_fios_items.Get(sort_start), s_amount, CompareFiosItems); + uint s_amount = file_list.Length() - sort_start - sort_end; + QSortT(file_list.Get(sort_start), s_amount, CompareFiosItems); } struct SaveLoadWindow : public Window { private: QueryString filename_editbox; ///< Filename editbox. + AbstractFileType abstract_filetype; /// Type of file to select. + SaveLoadOperation fop; ///< File operation to perform. + FileList fios_items; ///< Save game list. FiosItem o_dir; - const FiosItem *selected; + const FiosItem *selected; ///< Selected game in #fios_items, or \c NULL. Scrollbar *vscroll; public: @@ -250,35 +243,56 @@ public: this->filename_editbox.text.UpdateSize(); } - SaveLoadWindow(WindowDesc *desc, SaveLoadDialogMode mode) : Window(desc), filename_editbox(64) + SaveLoadWindow(WindowDesc *desc, AbstractFileType abstract_filetype, SaveLoadOperation fop) + : Window(desc), filename_editbox(64), abstract_filetype(abstract_filetype), fop(fop) { - static const StringID saveload_captions[] = { - STR_SAVELOAD_LOAD_CAPTION, - STR_SAVELOAD_LOAD_SCENARIO, - STR_SAVELOAD_SAVE_CAPTION, - STR_SAVELOAD_SAVE_SCENARIO, - STR_SAVELOAD_LOAD_HEIGHTMAP, - STR_SAVELOAD_SAVE_HEIGHTMAP, - }; - assert((uint)mode < lengthof(saveload_captions)); + assert(this->fop == SLO_SAVE || this->fop == SLO_LOAD); - /* Use an array to define what will be the current file type being handled - * by current file mode */ - switch (mode) { - case SLD_SAVE_GAME: this->GenerateFileName(); break; - case SLD_SAVE_HEIGHTMAP: - case SLD_SAVE_SCENARIO: this->filename_editbox.text.Assign("UNNAMED"); break; - default: break; + /* For saving, construct an initial file name. */ + if (this->fop == SLO_SAVE) { + switch (this->abstract_filetype) { + case FT_SAVEGAME: + this->GenerateFileName(); + break; + + case FT_SCENARIO: + case FT_HEIGHTMAP: + this->filename_editbox.text.Assign("UNNAMED"); + break; + + default: + NOT_REACHED(); + } } - this->querystrings[WID_SL_SAVE_OSK_TITLE] = &this->filename_editbox; this->filename_editbox.ok_button = WID_SL_SAVE_GAME; this->CreateNestedTree(true); - if (mode == SLD_LOAD_GAME) this->GetWidget(WID_SL_CONTENT_DOWNLOAD_SEL)->SetDisplayedPlane(SZSP_HORIZONTAL); - this->GetWidget(WID_SL_CAPTION)->widget_data = saveload_captions[mode]; - this->vscroll = this->GetScrollbar(WID_SL_SCROLLBAR); + if (this->fop == SLO_LOAD && this->abstract_filetype == FT_SAVEGAME) { + this->GetWidget(WID_SL_CONTENT_DOWNLOAD_SEL)->SetDisplayedPlane(SZSP_HORIZONTAL); + } + /* Select caption string of the window. */ + StringID caption_string; + switch (this->abstract_filetype) { + case FT_SAVEGAME: + caption_string = (this->fop == SLO_SAVE) ? STR_SAVELOAD_SAVE_CAPTION : STR_SAVELOAD_LOAD_CAPTION; + break; + + case FT_SCENARIO: + caption_string = (this->fop == SLO_SAVE) ? STR_SAVELOAD_SAVE_SCENARIO : STR_SAVELOAD_LOAD_SCENARIO; + break; + + case FT_HEIGHTMAP: + caption_string = (this->fop == SLO_SAVE) ? STR_SAVELOAD_SAVE_HEIGHTMAP : STR_SAVELOAD_LOAD_HEIGHTMAP; + break; + + default: + NOT_REACHED(); + } + this->GetWidget(WID_SL_CAPTION)->widget_data = caption_string; + + this->vscroll = this->GetScrollbar(WID_SL_SCROLLBAR); this->FinishInitNested(0); this->LowerWidget(WID_SL_DRIVES_DIRECTORIES_LIST); @@ -294,20 +308,18 @@ public: ResetObjectToPlace(); + /* Select the initial directory. */ o_dir.type = FIOS_TYPE_DIRECT; - switch (_saveload_mode) { - case SLD_SAVE_GAME: - case SLD_LOAD_GAME: + switch (this->abstract_filetype) { + case FT_SAVEGAME: FioGetDirectory(o_dir.name, lastof(o_dir.name), SAVE_DIR); break; - case SLD_SAVE_SCENARIO: - case SLD_LOAD_SCENARIO: + case FT_SCENARIO: FioGetDirectory(o_dir.name, lastof(o_dir.name), SCENARIO_DIR); break; - case SLD_SAVE_HEIGHTMAP: - case SLD_LOAD_HEIGHTMAP: + case FT_HEIGHTMAP: FioGetDirectory(o_dir.name, lastof(o_dir.name), HEIGHTMAP_DIR); break; @@ -316,9 +328,7 @@ public: } /* Focus the edit box by default in the save windows */ - if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO || _saveload_mode == SLD_SAVE_HEIGHTMAP) { - this->SetFocusedWidget(WID_SL_SAVE_OSK_TITLE); - } + if (this->fop == SLO_SAVE) this->SetFocusedWidget(WID_SL_SAVE_OSK_TITLE); } virtual ~SaveLoadWindow() @@ -327,7 +337,6 @@ public: if (!_networking && _game_mode != GM_EDITOR && _game_mode != GM_MENU) { DoCommandP(0, PM_PAUSED_SAVELOAD, 0, CMD_PAUSE); } - FiosFreeSavegameList(); } virtual void DrawWidget(const Rect &r, int widget) const @@ -360,13 +369,13 @@ public: GfxFillRect(r.left + 1, r.top + 1, r.right, r.bottom, PC_BLACK); uint y = r.top + WD_FRAMERECT_TOP; - for (uint pos = this->vscroll->GetPosition(); pos < _fios_items.Length(); pos++) { - const FiosItem *item = _fios_items.Get(pos); + for (uint pos = this->vscroll->GetPosition(); pos < this->fios_items.Length(); pos++) { + const FiosItem *item = this->fios_items.Get(pos); if (item == this->selected) { GfxFillRect(r.left + 1, y, r.right, y + this->resize.step_height, PC_DARK_BLUE); } - DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, item->title, _fios_colours[item->type]); + DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, item->title, _fios_colours[GetDetailedFileType(item->type)]); y += this->resize.step_height; if (y >= this->vscroll->GetCapacity() * this->resize.step_height + r.top + WD_FRAMERECT_TOP) break; } @@ -421,7 +430,7 @@ public: if (y > y_max) break; /* Hide current date for scenarios */ - if (_saveload_mode != SLD_LOAD_SCENARIO && _saveload_mode != SLD_SAVE_SCENARIO) { + if (this->abstract_filetype != FT_SCENARIO) { /* Current date */ SetDParam(0, _load_check_data.current_date); DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_CURRENT_DATE); @@ -429,7 +438,7 @@ public: } /* Hide the NewGRF stuff when saving. We also hide the button. */ - if (_saveload_mode == SLD_LOAD_GAME || _saveload_mode == SLD_LOAD_SCENARIO) { + if (this->fop == SLO_LOAD && (this->abstract_filetype == FT_SAVEGAME || this->abstract_filetype == FT_SCENARIO)) { y += WD_PAR_VSEP_NORMAL; if (y > y_max) break; @@ -442,7 +451,7 @@ public: if (y > y_max) break; /* Hide the company stuff for scenarios */ - if (_saveload_mode != SLD_LOAD_SCENARIO && _saveload_mode != SLD_SAVE_SCENARIO) { + if (this->abstract_filetype != FT_SCENARIO) { y += FONT_HEIGHT_NORMAL; if (y > y_max) break; @@ -495,10 +504,10 @@ public: { if (_savegame_sort_dirty) { _savegame_sort_dirty = false; - MakeSortedSaveGameList(); + SortSaveGameList(this->fios_items); } - this->vscroll->SetCount(_fios_items.Length()); + this->vscroll->SetCount(this->fios_items.Length()); this->DrawWidgets(); } @@ -527,14 +536,14 @@ public: case WID_SL_LOAD_BUTTON: if (this->selected != NULL && !_load_check_data.HasErrors()) { const char *name = FiosBrowseTo(this->selected); - SetFiosType(this->selected->type); + _file_to_saveload.SetMode(this->selected->type); + _file_to_saveload.SetName(name); + _file_to_saveload.SetTitle(this->selected->title); - strecpy(_file_to_saveload.name, name, lastof(_file_to_saveload.name)); - strecpy(_file_to_saveload.title, this->selected->title, lastof(_file_to_saveload.title)); - - if (_saveload_mode == SLD_LOAD_HEIGHTMAP) { + if (this->abstract_filetype == FT_HEIGHTMAP) { delete this; ShowHeightmapLoad(); + } else if (!_load_check_data.HasNewGrfs() || _load_check_data.grf_compatibility != GLC_NOT_FOUND || _settings_client.gui.UserIsAllowedToChangeNewGRFs()) { _switch_mode = (_game_mode == GM_EDITOR) ? SM_LOAD_SCENARIO : SM_LOAD_GAME; ClearErrorMessages(); @@ -563,7 +572,7 @@ public: int y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SL_DRIVES_DIRECTORIES_LIST, WD_FRAMERECT_TOP); if (y == INT_MAX) return; - const FiosItem *file = _fios_items.Get(y); + const FiosItem *file = this->fios_items.Get(y); const char *name = FiosBrowseTo(file); if (name != NULL) { @@ -572,28 +581,32 @@ public: this->selected = file; _load_check_data.Clear(); - if (file->type == FIOS_TYPE_FILE || file->type == FIOS_TYPE_SCENARIO) { - SaveOrLoad(name, SL_LOAD_CHECK, NO_DIRECTORY, false); + if (GetDetailedFileType(file->type) == DFT_GAME_FILE) { + /* Other detailed file types cannot be checked before. */ + SaveOrLoad(name, SLO_CHECK, DFT_GAME_FILE, NO_DIRECTORY, false); } this->InvalidateData(1); } - if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO || _saveload_mode == SLD_SAVE_HEIGHTMAP) { + if (this->fop == SLO_SAVE) { /* Copy clicked name to editbox */ this->filename_editbox.text.Assign(file->title); this->SetWidgetDirty(WID_SL_SAVE_OSK_TITLE); } } else if (!_load_check_data.HasErrors()) { this->selected = file; - if (_saveload_mode == SLD_LOAD_GAME || _saveload_mode == SLD_LOAD_SCENARIO) { - this->OnClick(pt, WID_SL_LOAD_BUTTON, 1); - } else if (_saveload_mode == SLD_LOAD_HEIGHTMAP) { - SetFiosType(file->type); - strecpy(_file_to_saveload.name, name, lastof(_file_to_saveload.name)); - strecpy(_file_to_saveload.title, file->title, lastof(_file_to_saveload.title)); + if (this->fop == SLO_LOAD) { + if (this->abstract_filetype == FT_SAVEGAME || this->abstract_filetype == FT_SCENARIO) { + this->OnClick(pt, WID_SL_LOAD_BUTTON, 1); + } else { + assert(this->abstract_filetype == FT_HEIGHTMAP); + _file_to_saveload.SetMode(file->type); + _file_to_saveload.SetName(name); + _file_to_saveload.SetTitle(file->title); - delete this; - ShowHeightmapLoad(); + delete this; + ShowHeightmapLoad(); + } } } } else { @@ -608,10 +621,11 @@ public: ShowErrorMessage(STR_NETWORK_ERROR_NOTAVAILABLE, INVALID_STRING_ID, WL_ERROR); } else { #if defined(ENABLE_NETWORK) - switch (_saveload_mode) { + assert(this->fop == SLO_LOAD); + switch (this->abstract_filetype) { default: NOT_REACHED(); - case SLD_LOAD_SCENARIO: ShowNetworkContentListWindow(NULL, CONTENT_TYPE_SCENARIO); break; - case SLD_LOAD_HEIGHTMAP: ShowNetworkContentListWindow(NULL, CONTENT_TYPE_HEIGHTMAP); break; + case FT_SCENARIO: ShowNetworkContentListWindow(NULL, CONTENT_TYPE_SCENARIO); break; + case FT_HEIGHTMAP: ShowNetworkContentListWindow(NULL, CONTENT_TYPE_HEIGHTMAP); break; } #endif } @@ -639,9 +653,8 @@ public: virtual void OnTimeout() { - /* This test protects against using widgets 11 and 12 which are only available - * in those saveload modes. */ - if (!(_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO || _saveload_mode == SLD_SAVE_HEIGHTMAP)) return; + /* Widgets WID_SL_DELETE_SELECTION and WID_SL_SAVE_GAME only exist when saving to a file. */ + if (this->fop != SLO_SAVE) return; if (this->IsWidgetLowered(WID_SL_DELETE_SELECTION)) { // Delete button clicked if (!FiosDelete(this->filename_editbox.text.buf)) { @@ -649,10 +662,10 @@ public: } else { this->InvalidateData(); /* Reset file name to current date on successful delete */ - if (_saveload_mode == SLD_SAVE_GAME) GenerateFileName(); + if (this->abstract_filetype == FT_SAVEGAME) GenerateFileName(); } } else if (this->IsWidgetLowered(WID_SL_SAVE_GAME)) { // Save button clicked - if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) { + if (this->abstract_filetype == FT_SAVEGAME || this->abstract_filetype == FT_SCENARIO) { _switch_mode = SM_SAVE_GAME; FiosMakeSavegameName(_file_to_saveload.name, this->filename_editbox.text.buf, lastof(_file_to_saveload.name)); } else { @@ -683,28 +696,40 @@ public: this->selected = NULL; _load_check_data.Clear(); if (!gui_scope) break; - BuildFileList(); + + _fios_path_changed = true; + this->fios_items.BuildFileList(this->abstract_filetype, this->fop); + this->vscroll->SetCount(this->fios_items.Length()); + this->selected = NULL; + _load_check_data.Clear(); /* FALL THROUGH */ case 1: /* Selection changes */ if (!gui_scope) break; - if (_saveload_mode == SLD_LOAD_HEIGHTMAP) { - this->SetWidgetDisabledState(WID_SL_LOAD_BUTTON, this->selected == NULL || _load_check_data.HasErrors()); + + if (this->fop != SLO_LOAD) break; + + switch (this->abstract_filetype) { + case FT_HEIGHTMAP: + this->SetWidgetDisabledState(WID_SL_LOAD_BUTTON, this->selected == NULL || _load_check_data.HasErrors()); + break; + + case FT_SAVEGAME: + case FT_SCENARIO: { + bool disabled = this->selected == NULL || _load_check_data.HasErrors(); + if (!_settings_client.gui.UserIsAllowedToChangeNewGRFs()) { + disabled |= _load_check_data.HasNewGrfs() && _load_check_data.grf_compatibility == GLC_NOT_FOUND; + } + this->SetWidgetDisabledState(WID_SL_LOAD_BUTTON, disabled); + this->SetWidgetDisabledState(WID_SL_NEWGRF_INFO, !_load_check_data.HasNewGrfs()); + this->SetWidgetDisabledState(WID_SL_MISSING_NEWGRFS, + !_load_check_data.HasNewGrfs() || _load_check_data.grf_compatibility == GLC_ALL_GOOD); + break; + } + + default: + NOT_REACHED(); } - if (_saveload_mode == SLD_LOAD_GAME || _saveload_mode == SLD_LOAD_SCENARIO) { - this->SetWidgetDisabledState(WID_SL_LOAD_BUTTON, - this->selected == NULL || _load_check_data.HasErrors() || !(!_load_check_data.HasNewGrfs() || _load_check_data.grf_compatibility != GLC_NOT_FOUND || _settings_client.gui.UserIsAllowedToChangeNewGRFs())); - this->SetWidgetDisabledState(WID_SL_NEWGRF_INFO, - !_load_check_data.HasNewGrfs()); - this->SetWidgetDisabledState(WID_SL_MISSING_NEWGRFS, - !_load_check_data.HasNewGrfs() || _load_check_data.grf_compatibility == GLC_ALL_GOOD); - } - break; - case 2: - /* _fios_items changed */ - this->vscroll->SetCount(_fios_items.Length()); - this->selected = NULL; - _load_check_data.Clear(); break; } } @@ -734,70 +759,24 @@ static WindowDesc _save_dialog_desc( _nested_save_dialog_widgets, lengthof(_nested_save_dialog_widgets) ); -/** - * These values are used to convert the file/operations mode into a corresponding file type. - * So each entry, as expressed by the related comment, is based on the enum - */ -static const FileType _file_modetotype[] = { - FT_SAVEGAME, // used for SLD_LOAD_GAME - FT_SCENARIO, // used for SLD_LOAD_SCENARIO - FT_SAVEGAME, // used for SLD_SAVE_GAME - FT_SCENARIO, // used for SLD_SAVE_SCENARIO - FT_HEIGHTMAP, // used for SLD_LOAD_HEIGHTMAP - FT_HEIGHTMAP, // used for SLD_SAVE_HEIGHTMAP -}; - /** * Launch save/load dialog in the given mode. - * @param mode Save/load mode. + * @param abstract_filetype Kind of file to handle. + * @param fop File operation to perform (load or save). */ -void ShowSaveLoadDialog(SaveLoadDialogMode mode) +void ShowSaveLoadDialog(AbstractFileType abstract_filetype, SaveLoadOperation fop) { DeleteWindowById(WC_SAVELOAD, 0); WindowDesc *sld; - switch (mode) { - case SLD_SAVE_GAME: - case SLD_SAVE_SCENARIO: - case SLD_SAVE_HEIGHTMAP: - sld = &_save_dialog_desc; break; - case SLD_LOAD_HEIGHTMAP: - sld = &_load_heightmap_dialog_desc; break; - default: - sld = &_load_dialog_desc; break; + if (fop == SLO_SAVE) { + sld = &_save_dialog_desc; + } else { + /* Dialogue for loading a file. */ + sld = (abstract_filetype == FT_HEIGHTMAP) ? &_load_heightmap_dialog_desc : &_load_dialog_desc; } - _saveload_mode = mode; - _file_to_saveload.filetype = _file_modetotype[mode]; + _file_to_saveload.abstract_ftype = abstract_filetype; - new SaveLoadWindow(sld, mode); -} - -void SetFiosType(const byte fiostype) -{ - switch (fiostype) { - case FIOS_TYPE_FILE: - case FIOS_TYPE_SCENARIO: - _file_to_saveload.mode = SL_LOAD; - break; - - case FIOS_TYPE_OLDFILE: - case FIOS_TYPE_OLD_SCENARIO: - _file_to_saveload.mode = SL_OLD_LOAD; - break; - -#ifdef WITH_PNG - case FIOS_TYPE_PNG: - _file_to_saveload.mode = SL_PNG; - break; -#endif /* WITH_PNG */ - - case FIOS_TYPE_BMP: - _file_to_saveload.mode = SL_BMP; - break; - - default: - _file_to_saveload.mode = SL_INVALID; - break; - } + new SaveLoadWindow(sld, abstract_filetype, fop); } diff --git a/src/genworld.cpp b/src/genworld.cpp index 2b2dfb5fd7..5cdb129b9c 100644 --- a/src/genworld.cpp +++ b/src/genworld.cpp @@ -204,7 +204,7 @@ static void _GenerateWorld(void *) if (_debug_desync_level > 0) { char name[MAX_PATH]; seprintf(name, lastof(name), "dmp_cmds_%08x_%08x.sav", _settings_game.game_creation.generation_seed, _date); - SaveOrLoad(name, SL_SAVE, AUTOSAVE_DIR, false); + SaveOrLoad(name, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR, false); } } catch (...) { BasePersistentStorageArray::SwitchMode(PSM_LEAVE_GAMELOOP, true); @@ -331,7 +331,7 @@ void GenerateWorld(GenWorldMode mode, uint size_x, uint size_y, bool reset_setti _gw.thread = NULL; } - if (!VideoDriver::GetInstance()->HasGUI() || !ThreadObject::New(&_GenerateWorld, NULL, &_gw.thread)) { + if (!VideoDriver::GetInstance()->HasGUI() || !ThreadObject::New(&_GenerateWorld, NULL, &_gw.thread, "ottd:genworld")) { DEBUG(misc, 1, "Cannot create genworld thread, reverting to single-threaded mode"); _gw.threaded = false; _modal_progress_work_mutex->EndCritical(); diff --git a/src/genworld_gui.cpp b/src/genworld_gui.cpp index 741fd75d1c..359709e361 100644 --- a/src/genworld_gui.cpp +++ b/src/genworld_gui.cpp @@ -26,6 +26,7 @@ #include "town.h" #include "core/geometry_func.hpp" #include "core/random_func.hpp" +#include "saveload/saveload.h" #include "progress.h" #include "error.h" @@ -832,7 +833,7 @@ static void _ShowGenerateLandscape(GenerateLandscapeWindowMode mode) if (mode == GLWM_HEIGHTMAP) { /* If the function returns negative, it means there was a problem loading the heightmap */ - if (!GetHeightmapDimensions(_file_to_saveload.name, &x, &y)) return; + if (!GetHeightmapDimensions(_file_to_saveload.detail_ftype, _file_to_saveload.name, &x, &y)) return; } WindowDesc *desc = (mode == GLWM_HEIGHTMAP) ? &_heightmap_load_desc : &_generate_landscape_desc; diff --git a/src/heightmap.cpp b/src/heightmap.cpp index ec31257287..630dc69d7e 100644 --- a/src/heightmap.cpp +++ b/src/heightmap.cpp @@ -102,7 +102,7 @@ static void ReadHeightmapPNGImageData(byte *map, png_structp png_ptr, png_infop * If map == NULL only the size of the PNG is read, otherwise a map * with grayscale pixels is allocated and assigned to *map. */ -static bool ReadHeightmapPNG(char *filename, uint *x, uint *y, byte **map) +static bool ReadHeightmapPNG(const char *filename, uint *x, uint *y, byte **map) { FILE *fp; png_structp png_ptr = NULL; @@ -232,7 +232,7 @@ static void ReadHeightmapBMPImageData(byte *map, BmpInfo *info, BmpData *data) * If map == NULL only the size of the BMP is read, otherwise a map * with grayscale pixels is allocated and assigned to *map. */ -static bool ReadHeightmapBMP(char *filename, uint *x, uint *y, byte **map) +static bool ReadHeightmapBMP(const char *filename, uint *x, uint *y, byte **map) { FILE *f; BmpInfo info; @@ -444,45 +444,56 @@ void FixSlopes() } /** - * Reads the heightmap with the correct file reader + * Reads the heightmap with the correct file reader. + * @param dft Type of image file. + * @param filename Name of the file to load. + * @param [out] x Length of the image. + * @param [out] y Height of the image. + * @param [inout] map If not \c NULL, destination to store the loaded block of image data. + * @return Whether loading was successful. */ -static bool ReadHeightMap(char *filename, uint *x, uint *y, byte **map) +static bool ReadHeightMap(DetailedFileType dft, const char *filename, uint *x, uint *y, byte **map) { - switch (_file_to_saveload.mode) { - default: NOT_REACHED(); + switch (dft) { + default: + NOT_REACHED(); + #ifdef WITH_PNG - case SL_PNG: + case DFT_HEIGHTMAP_PNG: return ReadHeightmapPNG(filename, x, y, map); #endif /* WITH_PNG */ - case SL_BMP: + + case DFT_HEIGHTMAP_BMP: return ReadHeightmapBMP(filename, x, y, map); } } /** * Get the dimensions of a heightmap. + * @param dft Type of image file. * @param filename to query * @param x dimension x * @param y dimension y * @return Returns false if loading of the image failed. */ -bool GetHeightmapDimensions(char *filename, uint *x, uint *y) +bool GetHeightmapDimensions(DetailedFileType dft, const char *filename, uint *x, uint *y) { - return ReadHeightMap(filename, x, y, NULL); + return ReadHeightMap(dft, filename, x, y, NULL); } /** * Load a heightmap from file and change the map in his current dimensions * to a landscape representing the heightmap. * It converts pixels to height. The brighter, the higher. + * @param dft Type of image file. * @param filename of the heightmap file to be imported */ -void LoadHeightmap(char *filename) +void LoadHeightmap(DetailedFileType dft, const char *filename) { uint x, y; byte *map = NULL; - if (!ReadHeightMap(filename, &x, &y, &map)) { + if (!ReadHeightMap(dft, filename, &x, &y, &map)) { free(map); return; } diff --git a/src/heightmap.h b/src/heightmap.h index 08ae200ece..67349df483 100644 --- a/src/heightmap.h +++ b/src/heightmap.h @@ -12,6 +12,8 @@ #ifndef HEIGHTMAP_H #define HEIGHTMAP_H +#include "fileio_type.h" + /** * Order of these enums has to be the same as in lang/english.txt * Otherwise you will get inconsistent behaviour. @@ -21,8 +23,8 @@ enum HeightmapRotation { HM_CLOCKWISE, ///< Rotate the map clockwise 45 degrees }; -bool GetHeightmapDimensions(char *filename, uint *x, uint *y); -void LoadHeightmap(char *filename); +bool GetHeightmapDimensions(DetailedFileType dft, const char *filename, uint *x, uint *y); +void LoadHeightmap(DetailedFileType dft, const char *filename); void FlatEmptyWorld(byte tile_height); void FixSlopes(); diff --git a/src/intro_gui.cpp b/src/intro_gui.cpp index de8b379392..a09a59a10d 100644 --- a/src/intro_gui.cpp +++ b/src/intro_gui.cpp @@ -111,9 +111,9 @@ struct SelectGameWindow : public Window { } break; - case WID_SGI_LOAD_GAME: ShowSaveLoadDialog(SLD_LOAD_GAME); break; - case WID_SGI_PLAY_SCENARIO: ShowSaveLoadDialog(SLD_LOAD_SCENARIO); break; - case WID_SGI_PLAY_HEIGHTMAP: ShowSaveLoadDialog(SLD_LOAD_HEIGHTMAP); break; + case WID_SGI_LOAD_GAME: ShowSaveLoadDialog(FT_SAVEGAME, SLO_LOAD); break; + case WID_SGI_PLAY_SCENARIO: ShowSaveLoadDialog(FT_SCENARIO, SLO_LOAD); break; + case WID_SGI_PLAY_HEIGHTMAP: ShowSaveLoadDialog(FT_HEIGHTMAP,SLO_LOAD); break; case WID_SGI_EDIT_SCENARIO: StartScenarioEditor(); break; case WID_SGI_PLAY_NETWORK: diff --git a/src/landscape.cpp b/src/landscape.cpp index d1c73fd42b..185e84a80b 100644 --- a/src/landscape.cpp +++ b/src/landscape.cpp @@ -31,6 +31,7 @@ #include "object_base.h" #include "company_func.h" #include "pathfinder/npf/aystar.h" +#include "saveload/saveload.h" #include #include @@ -1221,7 +1222,7 @@ void GenerateLandscape(byte mode) if (mode == GWM_HEIGHTMAP) { SetGeneratingWorldProgress(GWP_LANDSCAPE, steps + GLS_HEIGHTMAP); - LoadHeightmap(_file_to_saveload.name); + LoadHeightmap(_file_to_saveload.detail_ftype, _file_to_saveload.name); IncreaseGeneratingWorldProgress(GWP_LANDSCAPE); } else if (_settings_game.game_creation.land_generator == LG_TERRAGENESIS) { SetGeneratingWorldProgress(GWP_LANDSCAPE, steps + GLS_TERRAGENESIS); diff --git a/src/lang/danish.txt b/src/lang/danish.txt index 175092f8d2..7bbeab58ab 100644 --- a/src/lang/danish.txt +++ b/src/lang/danish.txt @@ -2874,7 +2874,7 @@ STR_SPRITE_ALIGNER_GOTO_TOOLTIP :{BLACK}Gå til STR_SPRITE_ALIGNER_PREVIOUS_BUTTON :{BLACK}Tidligere grafikelement STR_SPRITE_ALIGNER_PREVIOUS_TOOLTIP :{BLACK}Fortsæt til den forrige normal sprite, springe over nogen pseudo / recolour / FONT sprites og indpakning rundt fra den første sprite til det sidste STR_SPRITE_ALIGNER_SPRITE_TOOLTIP :{BLACK}Repræsentation af den markerede sprite. Justeringen ignoreres når denne sprite tegnes. -STR_SPRITE_ALIGNER_MOVE_TOOLTIP :{BLACK}Flyt spriten rundt for at ændre X- og Y-forskydningen +STR_SPRITE_ALIGNER_MOVE_TOOLTIP :{BLACK}Flyt spriten rundt for at ændre X- og Y-forskydningen. Ctrl+Klik for at flytte spriten otte enheder af gangen STR_SPRITE_ALIGNER_RESET_BUTTON :{BLACK}Nulstil relativ STR_SPRITE_ALIGNER_RESET_TOOLTIP :{BLACK}Nulstil den nuværende relative forskydning STR_SPRITE_ALIGNER_OFFSETS_ABS :{BLACK}X-forskydning: {NUM}, Y-forskydning: {NUM} (Absolut) diff --git a/src/lang/portuguese.txt b/src/lang/portuguese.txt index ec2a83c556..be21b6c4ae 100644 --- a/src/lang/portuguese.txt +++ b/src/lang/portuguese.txt @@ -845,6 +845,7 @@ STR_NEWS_VEHICLE_HAS_TOO_FEW_ORDERS :{WHITE}{VEHICLE STR_NEWS_VEHICLE_HAS_VOID_ORDER :{WHITE}{VEHICLE} tem uma ordem nula STR_NEWS_VEHICLE_HAS_DUPLICATE_ENTRY :{WHITE}{VEHICLE} tem ordens duplicadas STR_NEWS_VEHICLE_HAS_INVALID_ENTRY :{WHITE}{VEHICLE} tem uma ordem inválida no seu horário +STR_NEWS_PLANE_USES_TOO_SHORT_RUNWAY :{WHITE}{VEHICLE} tem um aeroporto nas ordens cuja pista de aterragem é muito curta STR_NEWS_VEHICLE_IS_GETTING_OLD :{WHITE}{VEHICLE} está a ficar velho STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD :{WHITE}{VEHICLE} está a ficar muito velho @@ -1288,17 +1289,21 @@ STR_CONFIG_SETTING_POPULATION_IN_LABEL_HELPTEXT :Mostrar a popul STR_CONFIG_SETTING_GRAPH_LINE_THICKNESS :Grossura das linhas nos gráficos: {STRING} STR_CONFIG_SETTING_GRAPH_LINE_THICKNESS_HELPTEXT :Largura da linha nos gráficos. Uma linha mais estreita é de leitura mais precisa, enquanto uma linha mais espessa é mais fácil de ver e as cores distinguem-se melhor. -STR_CONFIG_SETTING_LAND_GENERATOR :Gerador de terra: {STRING} +STR_CONFIG_SETTING_LANDSCAPE :Cenário: {STRING} +STR_CONFIG_SETTING_LANDSCAPE_HELPTEXT :Os estilos dos cenários definem a jogabilidade base, cada um com cargas e requerimentos diferentes para o desenvolvimento das cidades. NewGRF e Scripts de Jogo permitem um controlo mais refinado +STR_CONFIG_SETTING_LAND_GENERATOR :Gerador de terreno: {STRING} +STR_CONFIG_SETTING_LAND_GENERATOR_HELPTEXT :O gerador original é dependente do conjunto gráfico base, e compõe formas de terreno já afixadas. TerraGenesis é um gerador baseado no algoritmo de ruído de Perlin que permite definições mais refinadas STR_CONFIG_SETTING_LAND_GENERATOR_ORIGINAL :Original STR_CONFIG_SETTING_LAND_GENERATOR_TERRA_GENESIS :TerraGenesis STR_CONFIG_SETTING_TERRAIN_TYPE :Tipo de terreno: {STRING} +STR_CONFIG_SETTING_TERRAIN_TYPE_HELPTEXT :(Apenas TerraGenesis) Relevo do terreno STR_CONFIG_SETTING_INDUSTRY_DENSITY :Densidade industrial: {STRING} STR_CONFIG_SETTING_INDUSTRY_DENSITY_HELPTEXT :Define quantas indústrias devem ser geradas e que nível deve ser mantido durante o jogo STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE :Distância máxima entre o limite do mapa e Refinarias de Petróleo: {STRING} STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE_HELPTEXT :Refinarias de petróleo são construídas apenas próximo da borda do mapa, isto é, na costa para mapas de ilha STR_CONFIG_SETTING_SNOWLINE_HEIGHT :Altura da linha de neve: {STRING} STR_CONFIG_SETTING_SNOWLINE_HEIGHT_HELPTEXT :Controla a que altura a neve começa em paisagens sub-árticas. A neve também afecta a geração de indústrias e os requisitos de crescimento das cidades -STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN :Rudeza do terreno (apenas TerraGenesis) : {STRING} +STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN :Rudeza do terreno: {STRING} STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_HELPTEXT :(Apenas TerraGenesis) Escolhe a frequência de montes: paisagens macias têm menos montes e mais espalhados. Paisagens duras têm muitos montes, que podem parecer repetitivos STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_VERY_SMOOTH :Muito suave STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_SMOOTH :Suave @@ -1597,8 +1602,8 @@ STR_CONFIG_SETTING_LARGER_TOWNS :Proporção de STR_CONFIG_SETTING_LARGER_TOWNS_HELPTEXT :Quantidade de povoações que se tornarão cidades, logo cidades que começarão maiores e crescerão mais depressa STR_CONFIG_SETTING_LARGER_TOWNS_VALUE :1 em {COMMA} STR_CONFIG_SETTING_LARGER_TOWNS_DISABLED :Nenhum -STR_CONFIG_SETTING_CITY_SIZE_MULTIPLIER :Multiplicador inicial para dimensão da metrópole: {STRING} -STR_CONFIG_SETTING_CITY_SIZE_MULTIPLIER_HELPTEXT :Tamanho relativo das cidades em relação ao tamanho normal das mesmas aquando o inicio do jogo +STR_CONFIG_SETTING_CITY_SIZE_MULTIPLIER :Multiplicador inicial para a dimensão das metrópoles: {STRING} +STR_CONFIG_SETTING_CITY_SIZE_MULTIPLIER_HELPTEXT :Tamanho relativo das metrópoles em relação ao tamanho normal das cidades aquando o início do jogo STR_CONFIG_SETTING_LINKGRAPH_INTERVAL :Actualizar gráfico de distribuição a cada {STRING} dia{P 0:2 "" s} STR_CONFIG_SETTING_LINKGRAPH_INTERVAL_HELPTEXT :Tempo entre recalculos subsequentes de cada gráfico. Cada recalculo calcula os planos para cada componente do gráfico. Isto significa que um valor X para essa configuração não indica que o gráfico será todo actualizado a cada X dias. Apenas alguns componentes serão. Quanto mais curto o definir, mais tempo será necessário ao CPU para o calcular. Quanto mais longo, mais tempo levará até que a distribuição da carga inicie em novas rotas. @@ -1661,13 +1666,23 @@ STR_CONFIG_SETTING_LOCALISATION_UNITS_HEIGHT_METRIC :Métrico (m) STR_CONFIG_SETTING_LOCALISATION_UNITS_HEIGHT_SI :SI (m) STR_CONFIG_SETTING_LOCALISATION :{ORANGE}Localização +STR_CONFIG_SETTING_GRAPHICS :{ORANGE}Gráficos STR_CONFIG_SETTING_SOUND :{ORANGE}Efeitos sonoros STR_CONFIG_SETTING_INTERFACE :{ORANGE}Interface +STR_CONFIG_SETTING_INTERFACE_GENERAL :{ORANGE}Geral +STR_CONFIG_SETTING_INTERFACE_VIEWPORTS :{ORANGE}Visualizadores STR_CONFIG_SETTING_INTERFACE_CONSTRUCTION :{ORANGE}Construção +STR_CONFIG_SETTING_ADVISORS :{ORANGE}Notícias / Assessores STR_CONFIG_SETTING_COMPANY :{ORANGE}Empresa +STR_CONFIG_SETTING_ACCOUNTING :{ORANGE}Contabilidade STR_CONFIG_SETTING_VEHICLES :{ORANGE}Veículos +STR_CONFIG_SETTING_VEHICLES_PHYSICS :{ORANGE}Física STR_CONFIG_SETTING_VEHICLES_ROUTING :{ORANGE}Em rota +STR_CONFIG_SETTING_LIMITATIONS :{ORANGE}Limitações STR_CONFIG_SETTING_ACCIDENTS :{ORANGE}Desastres / Acidentes +STR_CONFIG_SETTING_GENWORLD :{ORANGE}Geração do mundo +STR_CONFIG_SETTING_ENVIRONMENT :{ORANGE}Meio Ambiente +STR_CONFIG_SETTING_ENVIRONMENT_AUTHORITIES :{ORANGE}Autoridades STR_CONFIG_SETTING_ENVIRONMENT_TOWNS :{ORANGE}Cidades STR_CONFIG_SETTING_ENVIRONMENT_INDUSTRIES :{ORANGE}Industrias STR_CONFIG_SETTING_ENVIRONMENT_CARGODIST :{ORANGE}Distribuição de Carga @@ -1741,7 +1756,7 @@ STR_INTRO_TOOLTIP_TOYLAND_LANDSCAPE :{BLACK}Seleccio STR_INTRO_TOOLTIP_GAME_OPTIONS :{BLACK}Mostrar opções de jogo STR_INTRO_TOOLTIP_HIGHSCORE :{BLACK}Mostrar tabela de classificações -STR_INTRO_TOOLTIP_CONFIG_SETTINGS_TREE :{BLACK}Definições de Display +STR_INTRO_TOOLTIP_CONFIG_SETTINGS_TREE :{BLACK}Mostrar definições STR_INTRO_TOOLTIP_NEWGRF_SETTINGS :{BLACK}Mostrar definições de NewGRF STR_INTRO_TOOLTIP_ONLINE_CONTENT :{BLACK}Verificar conteúdo novo e actualizado para descarga STR_INTRO_TOOLTIP_SCRIPT_SETTINGS :{BLACK}Mostrar definições de IA e de script de jogo @@ -2860,7 +2875,11 @@ STR_SPRITE_ALIGNER_GOTO_TOOLTIP :{BLACK}Ir para STR_SPRITE_ALIGNER_PREVIOUS_BUTTON :{BLACK}Gráfico anterior STR_SPRITE_ALIGNER_PREVIOUS_TOOLTIP :{BLACK}Proceder ao gráfico normal anterior, ignorando quaisquer gráficos pseudo/recolorir/tipo-de-letra e dando a volta no início STR_SPRITE_ALIGNER_SPRITE_TOOLTIP :{BLACK}Representação do gráfico seleccionado actualmente. O alinhamento é ignorado a desenhar este gráfico -STR_SPRITE_ALIGNER_MOVE_TOOLTIP :{BLACK}Mover o gráfico, mudando os desvios X e Y +STR_SPRITE_ALIGNER_MOVE_TOOLTIP :{BLACK}Desloca o gráfico, alterando os intervalos X e Y. Ctrl+Clique desloca o gráfico 8 unidades de uma só vez +STR_SPRITE_ALIGNER_RESET_BUTTON :{BLACK}Repor relativo +STR_SPRITE_ALIGNER_RESET_TOOLTIP :{BLACK}Repor intervalos relativos atuais +STR_SPRITE_ALIGNER_OFFSETS_ABS :{BLACK}Intervalo X: {NUM}, Intervalo Y: {NUM} (Absoluto) +STR_SPRITE_ALIGNER_OFFSETS_REL :{BLACK}Intervalo X: {NUM}, Intervalo Y: {NUM} (Relativo) STR_SPRITE_ALIGNER_PICKER_BUTTON :{BLACK}Escolher gráfico STR_SPRITE_ALIGNER_PICKER_TOOLTIP :{BLACK}Escolha um gráfico de qualquer lado no ecrã @@ -3338,6 +3357,7 @@ STR_GROUP_RENAME_TOOLTIP :{BLACK}Mudar o STR_GROUP_REPLACE_PROTECTION_TOOLTIP :{BLACK}Clique para proteger este grupo da autosubstituição global STR_QUERY_GROUP_DELETE_CAPTION :{WHITE}Apagar Grupo +STR_GROUP_DELETE_QUERY_TEXT :{WHITE}Tem a certeza de que quer apagar este grupo e quaisquer descendentes? STR_GROUP_ADD_SHARED_VEHICLE :Adic. veíc. partilh. STR_GROUP_REMOVE_ALL_VEHICLES :Remover todos os veículos @@ -3879,6 +3899,10 @@ STR_TIMETABLE_TRAVEL_NOT_TIMETABLED :Viagem (sem pro STR_TIMETABLE_TRAVEL_NOT_TIMETABLED_SPEED :Viajar no máximo a {2:VELOCITY} (sem programação) STR_TIMETABLE_TRAVEL_FOR :Viajar durante {STRING} STR_TIMETABLE_TRAVEL_FOR_SPEED :Viajar por {STRING} no máximo a {VELOCITY} +STR_TIMETABLE_TRAVEL_FOR_ESTIMATED :Viajar (durante {STRING}, sem programação) +STR_TIMETABLE_TRAVEL_FOR_SPEED_ESTIMATED :Viajar (por {STRING}, sem programação) no máximo a {VELOCITY} +STR_TIMETABLE_STAY_FOR_ESTIMATED :(permanecer durante {STRING}, sem programação) +STR_TIMETABLE_AND_TRAVEL_FOR_ESTIMATED :(viajar durante {STRING}, sem programação) STR_TIMETABLE_STAY_FOR :e ficar durante {STRING} STR_TIMETABLE_AND_TRAVEL_FOR :e viaja para {STRING} STR_TIMETABLE_DAYS :{COMMA} dia{P "" s} diff --git a/src/lang/romanian.txt b/src/lang/romanian.txt index 6876ee288c..632787d2af 100644 --- a/src/lang/romanian.txt +++ b/src/lang/romanian.txt @@ -1114,6 +1114,7 @@ STR_CONFIG_SETTING_TYPE_DROPDOWN_GAME_MENU :Setări joc (st STR_CONFIG_SETTING_TYPE_DROPDOWN_GAME_INGAME :Setări joc (stocate în salvări; afectează doar jocul curent) STR_CONFIG_SETTING_TYPE_DROPDOWN_COMPANY_MENU :Setări companie (stocate în salvări; afectează doar jocurile noi) STR_CONFIG_SETTING_TYPE_DROPDOWN_COMPANY_INGAME :Setări compenia (stocate în salvări; afectează doar compania curentă) +STR_CONFIG_SETTINGS_NONE :{WHITE}- Nespecificat - STR_CONFIG_SETTING_OFF :Inactiv STR_CONFIG_SETTING_ON :Activ @@ -1283,6 +1284,7 @@ STR_CONFIG_SETTING_GRAPH_LINE_THICKNESS_HELPTEXT :Grosimea liniil STR_CONFIG_SETTING_LAND_GENERATOR :Generator teren: {STRING} STR_CONFIG_SETTING_LAND_GENERATOR_ORIGINAL :Original STR_CONFIG_SETTING_LAND_GENERATOR_TERRA_GENESIS :TerraGenesis +STR_CONFIG_SETTING_TERRAIN_TYPE :Tip teren: {STRING} STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE :Distanța maximă de la marginea hărții pentru rafinării: {STRING} STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE_HELPTEXT :Rafinăriile de petrol vor fi construite doar la marginea hărţii, sau pe coastă, în cazul harţilor insulare STR_CONFIG_SETTING_SNOWLINE_HEIGHT :Grosimea stratului de zăpadă: {STRING} diff --git a/src/lang/thai.txt b/src/lang/thai.txt index 89f8e69113..86720059ae 100644 --- a/src/lang/thai.txt +++ b/src/lang/thai.txt @@ -250,6 +250,7 @@ STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC :{BLACK}ทำ # Show engines button +STR_SHOW_HIDDEN_ENGINES_VEHICLE_AIRCRAFT_TOOLTIP :ถ้าได้อนุญาตที่ปุ่มนี้แล้ว อากาศยานที่ซ่อนจะถูกแสดงออกมา # Query window STR_BUTTON_DEFAULT :{BLACK}ค่าปกติ @@ -970,6 +971,7 @@ STR_GAME_OPTIONS_RESOLUTION :{BLACK}คว STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}เลือกความละเอียดของหน้าจอที่จะใช้ STR_GAME_OPTIONS_RESOLUTION_OTHER :อื่นๆ +STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}ขนาดของแผงควบคุม STR_GAME_OPTIONS_BASE_GRF :{BLACK}ตั้งค่า Graphic พื้นฐาน @@ -1086,6 +1088,7 @@ STR_CONFIG_SETTING_TYPE_COMPANY_MENU :ตั้งค STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :ตั้งค่าองค์กร (ถูกเก็บไว้ในการบันทึกเกม; มีผลเฉพาะเกมปัจจุบัน) STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}หมวดหมู่: +STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}ชนิด: STR_CONFIG_SETTING_RESTRICT_DROPDOWN_HELPTEXT :{BLACK}จำกัดรายการด้านล่างให้แสดงเฉพาะการตั้งค่าที่มีการเปลี่ยนแปลง STR_CONFIG_SETTING_RESTRICT_BASIC :ตั้งค่าพื้นฐาน STR_CONFIG_SETTING_RESTRICT_ADVANCED :ตั้งค่าขั้นสูง @@ -1270,6 +1273,7 @@ STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE_HELPTEXT :โรงกล STR_CONFIG_SETTING_SNOWLINE_HEIGHT :ระดับความสูงแนวหิมะ: {STRING} STR_CONFIG_SETTING_SNOWLINE_HEIGHT_HELPTEXT :ควบคุมความสูงเริ่มต้นของหิมะบนพื้นภูมิทัศน์อาร์ติคย่อยๆ, หิมะยังมีผลการสร้างอุตสาหกรรม และการเติบโตของเมือง STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN :ความหยาบของพื้นที่ (เฉพาะ TerraGenesis): {STRING} +STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_HELPTEXT :(TerraGenesis only) Choose the frequency of hills: Smooth landscapes have fewer, more wide-spread hills. Rough landscapes have many hills, which may look repetitive STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_VERY_SMOOTH :ราบเรียบมาก STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_SMOOTH :ราบเรียบ STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_ROUGH :หยาบ @@ -1626,6 +1630,7 @@ STR_CONFIG_SETTING_SOUND :{ORANGE}เส STR_CONFIG_SETTING_INTERFACE :{ORANGE}ส่วนเชื่อมต่อผู้ใช้ STR_CONFIG_SETTING_INTERFACE_CONSTRUCTION :{ORANGE}การก่อสร้าง STR_CONFIG_SETTING_VEHICLES :{ORANGE}ยานพาหนะ +STR_CONFIG_SETTING_VEHICLES_PHYSICS :กายภาพ STR_CONFIG_SETTING_VEHICLES_ROUTING :{ORANGE}การค้นหาเส้นทางของพาหนะ STR_CONFIG_SETTING_ENVIRONMENT :{ORANGE}สิ่งแวดล้อม STR_CONFIG_SETTING_ENVIRONMENT_TOWNS :{ORANGE}เมือง @@ -1701,6 +1706,7 @@ STR_INTRO_TOOLTIP_TOYLAND_LANDSCAPE :{BLACK}เล STR_INTRO_TOOLTIP_GAME_OPTIONS :{BLACK}แสดงตัวเลือกเกม STR_INTRO_TOOLTIP_HIGHSCORE :{BLACK}แสดงตารางคะแนนสูงสุด +STR_INTRO_TOOLTIP_CONFIG_SETTINGS_TREE :{BLACK}การตั้งค่า หน้าจอ STR_INTRO_TOOLTIP_NEWGRF_SETTINGS :{BLACK}แสดงการกำหนดค่า NewGRF STR_INTRO_TOOLTIP_ONLINE_CONTENT :{BLACK}ตรวจสอบเนื้อหาใหม่และการปรับปรุงสำหรับดาวโหลด STR_INTRO_TOOLTIP_SCRIPT_SETTINGS :{BLACK}แสดงการตั้งค่า AI/Game script @@ -2663,6 +2669,7 @@ STR_MAPGEN_BY :{BLACK}* STR_MAPGEN_NUMBER_OF_TOWNS :{BLACK}จำนวนเมือง STR_MAPGEN_DATE :{BLACK}วันที่: STR_MAPGEN_NUMBER_OF_INDUSTRIES :{BLACK}จำนวนอุตสาหกรรม: +STR_MAPGEN_MAX_HEIGHTLEVEL :{BLACK}ขนาดความสูงของแผนที่สูงสุด: STR_MAPGEN_SNOW_LINE_HEIGHT :{BLACK}เส้นความสูงเขตหิมะ: STR_MAPGEN_SNOW_LINE_UP :{BLACK}ปรับเปลี่ยนความสูงของหิมะขึ้นไป 1 ระดับ STR_MAPGEN_SNOW_LINE_DOWN :{BLACK}ปรับเปลี่ยนความสูงของหิมะลงมา 1 ระดับ @@ -2773,6 +2780,7 @@ STR_NEWGRF_SETTINGS_DISABLED :{RED}ไม่ STR_NEWGRF_SETTINGS_INCOMPATIBLE :{RED}ไม่รองรับกับ OpenTTD เวอร์ชั่นนี้ # NewGRF save preset window +STR_SAVE_PRESET_SAVE_TOOLTIP :บันทึก ชุดการตั้งค่า ตามที่ได้เลือกไว้ # NewGRF parameters window STR_NEWGRF_PARAMETERS_CAPTION :{WHITE}เปลี่ยนแปลง NewGRF parameters @@ -3342,10 +3350,12 @@ STR_BUY_VEHICLE_ROAD_VEHICLE_RENAME_TOOLTIP :{BLACK}เป STR_BUY_VEHICLE_SHIP_RENAME_TOOLTIP :{BLACK}เปลี่ยนชื่อชนิดของเรือ STR_BUY_VEHICLE_AIRCRAFT_RENAME_TOOLTIP :{BLACK}เปลี่ยนชื่อชนิดของเครื่องบิน +STR_BUY_VEHICLE_AIRCRAFT_HIDE_TOGGLE_BUTTON :ซ่อน STR_BUY_VEHICLE_TRAIN_SHOW_TOGGLE_BUTTON :{BLACK}การแสดงผล STR_BUY_VEHICLE_ROAD_VEHICLE_SHOW_TOGGLE_BUTTON :{BLACK}การแสดงผล +STR_BUY_VEHICLE_AIRCRAFT_HIDE_SHOW_TOGGLE_TOOLTIP :{BLACK}ปรับเปลี่ยน ซ่อน/แสดง ของชนิดเครื่องอากาศยาน STR_QUERY_RENAME_TRAIN_TYPE_CAPTION :{WHITE}เปลี่ยนชื่อชนิดของรถไฟ STR_QUERY_RENAME_ROAD_VEHICLE_TYPE_CAPTION :{WHITE}เปลี่ยนชื่อชนิดของรถ @@ -3451,6 +3461,7 @@ STR_REPLACE_VEHICLE_ROAD_VEHICLE :ยานพา STR_REPLACE_VEHICLE_SHIP :ยานพาหนะทางน้ำ STR_REPLACE_VEHICLE_AIRCRAFT :อากาศยาน +STR_REPLACE_VEHICLE_VEHICLES_IN_USE :{YELLOW}พาหนะถูกใช้งานอยู่ STR_REPLACE_HELP_LEFT_ARRAY :{BLACK}เลือกประเภทของรถจักรที่จะแทนที่ STR_REPLACE_HELP_RIGHT_ARRAY :{BLACK}เลือกประเภทของพาหนะที่จะแทนที่ทางซ้ายมือ diff --git a/src/linkgraph/linkgraphjob.cpp b/src/linkgraph/linkgraphjob.cpp index 20cbf3f64d..537303cf35 100644 --- a/src/linkgraph/linkgraphjob.cpp +++ b/src/linkgraph/linkgraphjob.cpp @@ -61,7 +61,7 @@ void LinkGraphJob::EraseFlows(NodeID from) */ void LinkGraphJob::SpawnThread() { - if (!ThreadObject::New(&(LinkGraphSchedule::Run), this, &this->thread)) { + if (!ThreadObject::New(&(LinkGraphSchedule::Run), this, &this->thread, "ottd:linkgraph")) { this->thread = NULL; /* Of course this will hang a bit. * On the other hand, if you want to play games which make this hang noticably diff --git a/src/music/cocoa_m.cpp b/src/music/cocoa_m.cpp index a201984ae1..925dc21ab5 100644 --- a/src/music/cocoa_m.cpp +++ b/src/music/cocoa_m.cpp @@ -30,6 +30,10 @@ #include "../safeguards.h" +#if !defined(HAVE_OSX_1011_SDK) +#define kMusicSequenceFile_AnyType 0 +#endif + static FMusicDriver_Cocoa iFMusicDriver_Cocoa; @@ -68,7 +72,7 @@ static void DoSetVolume() * risk compilation errors. The header AudioComponent.h * was introduced in 10.6 so use it to decide which * type definition to use. */ -#ifdef __AUDIOCOMPONENT_H__ +#if defined(__AUDIOCOMPONENT_H__) || defined(HAVE_OSX_107_SDK) AudioComponentDescription desc; #else ComponentDescription desc; @@ -159,7 +163,7 @@ void MusicDriver_Cocoa::PlaySong(const char *filename) #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) if (MacOSVersionIsAtLeast(10, 5, 0)) { - if (MusicSequenceFileLoad(_sequence, url, 0, 0) != noErr) { + if (MusicSequenceFileLoad(_sequence, url, kMusicSequenceFile_AnyType, 0) != noErr) { DEBUG(driver, 0, "cocoa_m: Failed to load MIDI file"); CFRelease(url); return; diff --git a/src/music/win32_m.cpp b/src/music/win32_m.cpp index d3a7019a0f..fff0376a09 100644 --- a/src/music/win32_m.cpp +++ b/src/music/win32_m.cpp @@ -14,6 +14,7 @@ #include "win32_m.h" #include #include +#include "../os/windows/win32.h" #include "../safeguards.h" @@ -105,6 +106,8 @@ static bool MidiIntIsSongPlaying() static DWORD WINAPI MidiThread(LPVOID arg) { + SetWin32ThreadName(-1, "ottd:win-midi"); + do { char *s; int vol; diff --git a/src/network/core/tcp_connect.cpp b/src/network/core/tcp_connect.cpp index ccbf93f3c3..2dc7898824 100644 --- a/src/network/core/tcp_connect.cpp +++ b/src/network/core/tcp_connect.cpp @@ -35,7 +35,7 @@ TCPConnecter::TCPConnecter(const NetworkAddress &address) : address(address) { *_tcp_connecters.Append() = this; - if (!ThreadObject::New(TCPConnecter::ThreadEntry, this, &this->thread)) { + if (!ThreadObject::New(TCPConnecter::ThreadEntry, this, &this->thread, "ottd:tcp")) { this->Connect(); } } diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index d4d294146e..edfe4587f3 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -520,7 +520,7 @@ bool ClientNetworkGameSocketHandler::IsConnected() * DEF_CLIENT_RECEIVE_COMMAND has parameter: Packet *p ************/ -extern bool SafeLoad(const char *filename, int mode, GameMode newgm, Subdirectory subdir, struct LoadFilter *lf = NULL); +extern bool SafeLoad(const char *filename, SaveLoadOperation fop, DetailedFileType dft, GameMode newgm, Subdirectory subdir, struct LoadFilter *lf = NULL); NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_FULL(Packet *p) { @@ -836,7 +836,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MAP_DONE(Packet /* The map is done downloading, load it */ ClearErrorMessages(); - bool load_success = SafeLoad(NULL, SL_LOAD, GM_NORMAL, NO_DIRECTORY, lf); + bool load_success = SafeLoad(NULL, SLO_LOAD, DFT_GAME_FILE, GM_NORMAL, NO_DIRECTORY, lf); /* Long savegame loads shouldn't affect the lag calculation! */ this->last_packet = _realtime_tick; diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 7c04ad4e02..520c4f60a8 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -1187,17 +1187,17 @@ struct NetworkStartServerWindow : public Window { case WID_NSS_LOAD_GAME: _is_network_server = true; - ShowSaveLoadDialog(SLD_LOAD_GAME); + ShowSaveLoadDialog(FT_SAVEGAME, SLO_LOAD); break; case WID_NSS_PLAY_SCENARIO: _is_network_server = true; - ShowSaveLoadDialog(SLD_LOAD_SCENARIO); + ShowSaveLoadDialog(FT_SCENARIO, SLO_LOAD); break; case WID_NSS_PLAY_HEIGHTMAP: _is_network_server = true; - ShowSaveLoadDialog(SLD_LOAD_HEIGHTMAP); + ShowSaveLoadDialog(FT_HEIGHTMAP,SLO_LOAD); break; } } diff --git a/src/network/network_udp.cpp b/src/network/network_udp.cpp index 1cccbf6441..1dc6960659 100644 --- a/src/network/network_udp.cpp +++ b/src/network/network_udp.cpp @@ -109,7 +109,7 @@ static void NetworkUDPQueryServerThread(void *pntr) void NetworkUDPQueryServer(NetworkAddress address, bool manually) { NetworkUDPQueryServerInfo *info = new NetworkUDPQueryServerInfo(address, manually); - if (address.IsResolved() || !ThreadObject::New(NetworkUDPQueryServerThread, info)) { + if (address.IsResolved() || !ThreadObject::New(NetworkUDPQueryServerThread, info, NULL, "ottd:udp-query")) { NetworkUDPQueryServerThread(info); } } @@ -565,7 +565,7 @@ void NetworkUDPRemoveAdvertise(bool blocking) /* Check if we are advertising */ if (!_networking || !_network_server || !_network_udp_server) return; - if (blocking || !ThreadObject::New(NetworkUDPRemoveAdvertiseThread, NULL)) { + if (blocking || !ThreadObject::New(NetworkUDPRemoveAdvertiseThread, NULL, NULL, "ottd:udp-advert")) { NetworkUDPRemoveAdvertiseThread(NULL); } } @@ -648,7 +648,7 @@ void NetworkUDPAdvertise() if (_next_advertisement < _last_advertisement) _next_advertisement = UINT32_MAX; if (_next_retry < _last_advertisement) _next_retry = UINT32_MAX; - if (!ThreadObject::New(NetworkUDPAdvertiseThread, NULL)) { + if (!ThreadObject::New(NetworkUDPAdvertiseThread, NULL, NULL, "ottd:udp-advert")) { NetworkUDPAdvertiseThread(NULL); } } diff --git a/src/newgrf_config.cpp b/src/newgrf_config.cpp index bd95373d91..19ac4d4a7e 100644 --- a/src/newgrf_config.cpp +++ b/src/newgrf_config.cpp @@ -786,7 +786,7 @@ void ScanNewGRFFiles(NewGRFScanCallback *callback) /* Only then can we really start, especially by marking the whole screen dirty. Get those other windows hidden!. */ MarkWholeScreenDirty(); - if (!VideoDriver::GetInstance()->HasGUI() || !ThreadObject::New(&DoScanNewGRFFiles, callback, NULL)) { + if (!VideoDriver::GetInstance()->HasGUI() || !ThreadObject::New(&DoScanNewGRFFiles, callback, NULL, "ottd:newgrf-scan")) { _modal_progress_work_mutex->EndCritical(); _modal_progress_paint_mutex->EndCritical(); DoScanNewGRFFiles(callback); diff --git a/src/newgrf_engine.cpp b/src/newgrf_engine.cpp index 99d8c8cc9d..cb6366ce5c 100644 --- a/src/newgrf_engine.cpp +++ b/src/newgrf_engine.cpp @@ -1029,17 +1029,29 @@ VehicleResolverObject::VehicleResolverObject(EngineID engine_type, const Vehicle -SpriteID GetCustomEngineSprite(EngineID engine, const Vehicle *v, Direction direction, EngineImageType image_type) +void GetCustomEngineSprite(EngineID engine, const Vehicle *v, Direction direction, EngineImageType image_type, VehicleSpriteSeq *result) { - VehicleResolverObject object(engine, v, VehicleResolverObject::WO_CACHED, false, CBID_NO_CALLBACK, image_type); - const SpriteGroup *group = object.Resolve(); - if (group == NULL || group->GetNumResults() == 0) return 0; + VehicleResolverObject object(engine, v, VehicleResolverObject::WO_CACHED, false, CBID_NO_CALLBACK); + result->Clear(); - return group->GetResult() + (direction % group->GetNumResults()); + bool sprite_stack = HasBit(EngInfo(engine)->misc_flags, EF_SPRITE_STACK); + uint max_stack = sprite_stack ? lengthof(result->seq) : 1; + for (uint stack = 0; stack < max_stack; ++stack) { + object.ResetState(); + object.callback_param1 = image_type | (stack << 8); + const SpriteGroup *group = object.Resolve(); + uint32 reg100 = sprite_stack ? GetRegister(0x100) : 0; + if (group != NULL && group->GetNumResults() != 0) { + result->seq[result->count].sprite = group->GetResult() + (direction % group->GetNumResults()); + result->seq[result->count].pal = GB(reg100, 0, 16); // zero means default recolouring + result->count++; + } + if (!HasBit(reg100, 31)) break; + } } -SpriteID GetRotorOverrideSprite(EngineID engine, const Aircraft *v, bool info_view, EngineImageType image_type) +void GetRotorOverrideSprite(EngineID engine, const struct Aircraft *v, bool info_view, EngineImageType image_type, VehicleSpriteSeq *result) { const Engine *e = Engine::Get(engine); @@ -1047,14 +1059,24 @@ SpriteID GetRotorOverrideSprite(EngineID engine, const Aircraft *v, bool info_vi assert(e->type == VEH_AIRCRAFT); assert(!(e->u.air.subtype & AIR_CTOL)); - VehicleResolverObject object(engine, v, VehicleResolverObject::WO_SELF, info_view, CBID_NO_CALLBACK, image_type); - const SpriteGroup *group = object.Resolve(); + VehicleResolverObject object(engine, v, VehicleResolverObject::WO_SELF, info_view, CBID_NO_CALLBACK); + result->Clear(); + uint rotor_pos = v == NULL || info_view ? 0 : v->Next()->Next()->state; - if (group == NULL || group->GetNumResults() == 0) return 0; - - if (v == NULL || info_view) return group->GetResult(); - - return group->GetResult() + (v->Next()->Next()->state % group->GetNumResults()); + bool sprite_stack = HasBit(e->info.misc_flags, EF_SPRITE_STACK); + uint max_stack = sprite_stack ? lengthof(result->seq) : 1; + for (uint stack = 0; stack < max_stack; ++stack) { + object.ResetState(); + object.callback_param1 = image_type | (stack << 8); + const SpriteGroup *group = object.Resolve(); + uint32 reg100 = sprite_stack ? GetRegister(0x100) : 0; + if (group != NULL && group->GetNumResults() != 0) { + result->seq[result->count].sprite = group->GetResult() + (rotor_pos % group->GetNumResults()); + result->seq[result->count].pal = GB(reg100, 0, 16); // zero means default recolouring + result->count++; + } + if (!HasBit(reg100, 31)) break; + } } diff --git a/src/newgrf_engine.h b/src/newgrf_engine.h index 4a6f211c49..3c8108737c 100644 --- a/src/newgrf_engine.h +++ b/src/newgrf_engine.h @@ -64,13 +64,19 @@ static const uint TRAININFO_DEFAULT_VEHICLE_WIDTH = 29; static const uint ROADVEHINFO_DEFAULT_VEHICLE_WIDTH = 32; static const uint VEHICLEINFO_FULL_VEHICLE_WIDTH = 32; +struct VehicleSpriteSeq; + void SetWagonOverrideSprites(EngineID engine, CargoID cargo, const struct SpriteGroup *group, EngineID *train_id, uint trains); const SpriteGroup *GetWagonOverrideSpriteSet(EngineID engine, CargoID cargo, EngineID overriding_engine); void SetCustomEngineSprites(EngineID engine, byte cargo, const struct SpriteGroup *group); -SpriteID GetCustomEngineSprite(EngineID engine, const Vehicle *v, Direction direction, EngineImageType image_type); -SpriteID GetRotorOverrideSprite(EngineID engine, const struct Aircraft *v, bool info_view, EngineImageType image_type); -#define GetCustomRotorSprite(v, i, image_type) GetRotorOverrideSprite(v->engine_type, v, i, image_type) -#define GetCustomRotorIcon(et, image_type) GetRotorOverrideSprite(et, NULL, true, image_type) + +void GetCustomEngineSprite(EngineID engine, const Vehicle *v, Direction direction, EngineImageType image_type, VehicleSpriteSeq *result); +#define GetCustomVehicleSprite(v, direction, image_type, result) GetCustomEngineSprite(v->engine_type, v, direction, image_type, result) +#define GetCustomVehicleIcon(et, direction, image_type, result) GetCustomEngineSprite(et, NULL, direction, image_type, result) + +void GetRotorOverrideSprite(EngineID engine, const struct Aircraft *v, bool info_view, EngineImageType image_type, VehicleSpriteSeq *result); +#define GetCustomRotorSprite(v, i, image_type, result) GetRotorOverrideSprite(v->engine_type, v, i, image_type, result) +#define GetCustomRotorIcon(et, image_type, result) GetRotorOverrideSprite(et, NULL, true, image_type, result) /* Forward declaration of GRFFile, to avoid unnecessary inclusion of newgrf.h * elsewhere... */ @@ -81,8 +87,6 @@ void SetEngineGRF(EngineID engine, const struct GRFFile *file); uint16 GetVehicleCallback(CallbackID callback, uint32 param1, uint32 param2, EngineID engine, const Vehicle *v); uint16 GetVehicleCallbackParent(CallbackID callback, uint32 param1, uint32 param2, EngineID engine, const Vehicle *v, const Vehicle *parent); bool UsesWagonOverride(const Vehicle *v); -#define GetCustomVehicleSprite(v, direction, image_type) GetCustomEngineSprite(v->engine_type, v, direction, image_type) -#define GetCustomVehicleIcon(et, direction, image_type) GetCustomEngineSprite(et, NULL, direction, image_type) /* Handler to Evaluate callback 36. If the callback fails (i.e. most of the * time) orig_value is returned */ diff --git a/src/object_cmd.cpp b/src/object_cmd.cpp index b5b9921525..f7ba0d995b 100644 --- a/src/object_cmd.cpp +++ b/src/object_cmd.cpp @@ -770,6 +770,8 @@ static void ChangeTileOwner_Object(TileIndex tile, Owner old_owner, Owner new_ow { if (!IsTileOwner(tile, old_owner)) return; + bool do_clear = false; + if (IsObjectType(tile, OBJECT_OWNED_LAND) && new_owner != INVALID_OWNER) { SetTileOwner(tile, new_owner); } else if (IsObjectType(tile, OBJECT_STATUE)) { @@ -780,12 +782,18 @@ static void ChangeTileOwner_Object(TileIndex tile, Owner old_owner, Owner new_ow SetBit(t->statues, new_owner); SetTileOwner(tile, new_owner); } else { - ReallyClearObjectTile(Object::GetByTile(tile)); + do_clear = true; } SetWindowDirty(WC_TOWN_AUTHORITY, t->index); } else { + do_clear = true; + } + + if (do_clear) { ReallyClearObjectTile(Object::GetByTile(tile)); + /* When clearing objects, they may turn into canal, which may require transfering ownership. */ + ChangeTileOwner(tile, old_owner, new_owner); } } diff --git a/src/openttd.cpp b/src/openttd.cpp index c149ebbd4d..58f011b082 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -328,7 +328,7 @@ static void LoadIntroGame(bool load_newgrfs = true) SetupColoursAndInitialWindow(); /* Load the default opening screen savegame */ - if (SaveOrLoad("opntitle.dat", SL_LOAD, BASESET_DIR) != SL_OK) { + if (SaveOrLoad("opntitle.dat", SLO_LOAD, DFT_GAME_FILE, BASESET_DIR) != SL_OK) { GenerateWorld(GWM_EMPTY, 64, 64); // if failed loading, make empty world. WaitTillGeneratedWorld(); SetLocalCompany(COMPANY_SPECTATOR); @@ -618,15 +618,16 @@ int openttd_main(int argc, char *argv[]) case 'e': _switch_mode = (_switch_mode == SM_LOAD_GAME || _switch_mode == SM_LOAD_SCENARIO ? SM_LOAD_SCENARIO : SM_EDITOR); break; case 'g': if (mgo.opt != NULL) { - strecpy(_file_to_saveload.name, mgo.opt, lastof(_file_to_saveload.name)); - _switch_mode = (_switch_mode == SM_EDITOR || _switch_mode == SM_LOAD_SCENARIO ? SM_LOAD_SCENARIO : SM_LOAD_GAME); - _file_to_saveload.mode = SL_LOAD; + _file_to_saveload.SetName(mgo.opt); + bool is_scenario = _switch_mode == SM_EDITOR || _switch_mode == SM_LOAD_SCENARIO; + _switch_mode = is_scenario ? SM_LOAD_SCENARIO : SM_LOAD_GAME; + _file_to_saveload.SetMode(SLO_LOAD, is_scenario ? FT_SCENARIO : FT_SAVEGAME, DFT_GAME_FILE); /* if the file doesn't exist or it is not a valid savegame, let the saveload code show an error */ const char *t = strrchr(_file_to_saveload.name, '.'); if (t != NULL) { - FiosType ft = FiosGetSavegameListCallback(SLD_LOAD_GAME, _file_to_saveload.name, t, NULL, NULL); - if (ft != FIOS_TYPE_INVALID) SetFiosType(ft); + FiosType ft = FiosGetSavegameListCallback(SLO_LOAD, _file_to_saveload.name, t, NULL, NULL); + if (ft != FIOS_TYPE_INVALID) _file_to_saveload.SetMode(ft); } break; @@ -647,10 +648,10 @@ int openttd_main(int argc, char *argv[]) char title[80]; title[0] = '\0'; - FiosGetSavegameListCallback(SLD_LOAD_GAME, mgo.opt, strrchr(mgo.opt, '.'), title, lastof(title)); + FiosGetSavegameListCallback(SLO_LOAD, mgo.opt, strrchr(mgo.opt, '.'), title, lastof(title)); _load_check_data.Clear(); - SaveOrLoadResult res = SaveOrLoad(mgo.opt, SL_LOAD_CHECK, SAVE_DIR, false); + SaveOrLoadResult res = SaveOrLoad(mgo.opt, SLO_CHECK, DFT_GAME_FILE, SAVE_DIR, false); if (res != SL_OK || _load_check_data.HasErrors()) { fprintf(stderr, "Failed to open savegame\n"); if (_load_check_data.HasErrors()) { @@ -997,14 +998,15 @@ static void MakeNewEditorWorld() * @param subdir default directory to look for filename, set to 0 if not needed * @param lf Load filter to use, if NULL: use filename + subdir. */ -bool SafeLoad(const char *filename, int mode, GameMode newgm, Subdirectory subdir, struct LoadFilter *lf = NULL) +bool SafeLoad(const char *filename, SaveLoadOperation fop, DetailedFileType dft, GameMode newgm, Subdirectory subdir, struct LoadFilter *lf = NULL) { - assert(mode == SL_LOAD || (lf == NULL && mode == SL_OLD_LOAD)); + assert(fop == SLO_LOAD); + assert(dft == DFT_GAME_FILE || (lf == NULL && dft == DFT_OLD_GAME_FILE)); GameMode ogm = _game_mode; _game_mode = newgm; - switch (lf == NULL ? SaveOrLoad(filename, mode, subdir) : LoadWithFilter(lf)) { + switch (lf == NULL ? SaveOrLoad(filename, fop, dft, subdir) : LoadWithFilter(lf)) { case SL_OK: return true; case SL_REINIT: @@ -1093,11 +1095,11 @@ void SwitchToMode(SwitchMode new_mode) ResetGRFConfig(true); ResetWindowSystem(); - if (!SafeLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_NORMAL, NO_DIRECTORY)) { + if (!SafeLoad(_file_to_saveload.name, _file_to_saveload.file_op, _file_to_saveload.detail_ftype, GM_NORMAL, NO_DIRECTORY)) { SetDParamStr(0, GetSaveLoadErrorString()); ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR); } else { - if (_saveload_mode == SLD_LOAD_SCENARIO) { + if (_file_to_saveload.abstract_ftype == FT_SCENARIO) { /* Reset engine pool to simplify changing engine NewGRFs in scenario editor. */ EngineOverrideManager::ResetToCurrentNewGRFConfig(); } @@ -1134,7 +1136,7 @@ void SwitchToMode(SwitchMode new_mode) break; case SM_LOAD_SCENARIO: { // Load scenario from scenario editor - if (SafeLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_EDITOR, NO_DIRECTORY)) { + if (SafeLoad(_file_to_saveload.name, _file_to_saveload.file_op, _file_to_saveload.detail_ftype, GM_EDITOR, NO_DIRECTORY)) { SetLocalCompany(OWNER_NONE); _settings_newgame.game_creation.starting_year = _cur_year; /* Cancel the saveload pausing */ @@ -1156,7 +1158,7 @@ void SwitchToMode(SwitchMode new_mode) case SM_SAVE_GAME: // Save game. /* Make network saved games on pause compatible to singleplayer */ - if (SaveOrLoad(_file_to_saveload.name, SL_SAVE, NO_DIRECTORY) != SL_OK) { + if (SaveOrLoad(_file_to_saveload.name, SLO_SAVE, DFT_GAME_FILE, NO_DIRECTORY) != SL_OK) { SetDParamStr(0, GetSaveLoadErrorString()); ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR); } else { @@ -1367,7 +1369,7 @@ void StateGameLoop() /* Save the desync savegame if needed. */ char name[MAX_PATH]; seprintf(name, lastof(name), "dmp_cmds_%08x_%08x.sav", _settings_game.game_creation.generation_seed, _date); - SaveOrLoad(name, SL_SAVE, AUTOSAVE_DIR, false); + SaveOrLoad(name, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR, false); } CheckCaches(); @@ -1424,7 +1426,7 @@ static void DoAutosave() } DEBUG(sl, 2, "Autosaving to '%s'", buf); - if (SaveOrLoad(buf, SL_SAVE, AUTOSAVE_DIR) != SL_OK) { + if (SaveOrLoad(buf, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR) != SL_OK) { ShowErrorMessage(STR_ERROR_AUTOSAVE_FAILED, INVALID_STRING_ID, WL_ERROR); } } diff --git a/src/os/macosx/osx_stdafx.h b/src/os/macosx/osx_stdafx.h index 35b3f434a1..4e16b5e63b 100644 --- a/src/os/macosx/osx_stdafx.h +++ b/src/os/macosx/osx_stdafx.h @@ -13,6 +13,17 @@ #define MACOS_STDAFX_H +#include + +/* We assume if these macros are defined, the SDK is also at least this version or later. */ +#ifdef MAC_OS_X_VERSION_10_7 +#define HAVE_OSX_107_SDK +#endif + +#ifdef MAC_OS_X_VERSION_10_11 +#define HAVE_OSX_1011_SDK +#endif + /* It would seem that to ensure backward compability we have to ensure that we have defined MAC_OS_X_VERSION_10_x everywhere */ #ifndef MAC_OS_X_VERSION_10_3 #define MAC_OS_X_VERSION_10_3 1030 @@ -57,8 +68,6 @@ # error "Compiling 64 bits without _SQ64 set! (or vice versa)" #endif -#include - /* Name conflict */ #define Rect OTTDRect #define Point OTTDPoint diff --git a/src/os/os2/os2.cpp b/src/os/os2/os2.cpp index 386cc4dbb7..7b34f528a6 100644 --- a/src/os/os2/os2.cpp +++ b/src/os/os2/os2.cpp @@ -45,7 +45,7 @@ bool FiosIsRoot(const char *file) return file[3] == '\0'; } -void FiosGetDrives() +void FiosGetDrives(FileList &file_list) { uint disk, disk2, save, total; @@ -75,7 +75,7 @@ void FiosGetDrives() #endif if (disk == disk2) { - FiosItem *fios = _fios_items.Append(); + FiosItem *fios = file_list.Append(); fios->type = FIOS_TYPE_DRIVE; fios->mtime = 0; #ifndef __INNOTEK_LIBC__ diff --git a/src/os/unix/unix.cpp b/src/os/unix/unix.cpp index 24dedb2ee1..d7c2304ce5 100644 --- a/src/os/unix/unix.cpp +++ b/src/os/unix/unix.cpp @@ -16,6 +16,7 @@ #include "../../core/random_func.hpp" #include "../../debug.h" #include "../../string_func.h" +#include "../../fios.h" #include @@ -77,7 +78,7 @@ bool FiosIsRoot(const char *path) #endif } -void FiosGetDrives() +void FiosGetDrives(FileList &file_list) { return; } diff --git a/src/os/windows/win32.cpp b/src/os/windows/win32.cpp index 041468a139..3fd1777fa8 100644 --- a/src/os/windows/win32.cpp +++ b/src/os/windows/win32.cpp @@ -208,11 +208,11 @@ bool FiosIsRoot(const char *file) return file[3] == '\0'; // C:\... } -void FiosGetDrives() +void FiosGetDrives(FileList &file_list) { #if defined(WINCE) /* WinCE only knows one drive: / */ - FiosItem *fios = _fios_items.Append(); + FiosItem *fios = file_list.Append(); fios->type = FIOS_TYPE_DRIVE; fios->mtime = 0; seprintf(fios->name, lastof(fios->name), PATHSEP ""); @@ -223,7 +223,7 @@ void FiosGetDrives() GetLogicalDriveStrings(lengthof(drives), drives); for (s = drives; *s != '\0';) { - FiosItem *fios = _fios_items.Append(); + FiosItem *fios = file_list.Append(); fios->type = FIOS_TYPE_DRIVE; fios->mtime = 0; seprintf(fios->name, lastof(fios->name), "%c:", s[0] & 0xFF); @@ -785,3 +785,36 @@ uint GetCPUCoreCount() GetSystemInfo(&info); return info.dwNumberOfProcessors; } + +#ifdef _MSC_VER +/* Code from MSDN: https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ +const DWORD MS_VC_EXCEPTION = 0x406D1388; +#pragma pack(push,8) +typedef struct { + DWORD dwType; ///< Must be 0x1000. + LPCSTR szName; ///< Pointer to name (in user addr space). + DWORD dwThreadID; ///< Thread ID (-1=caller thread). + DWORD dwFlags; ///< Reserved for future use, must be zero. +} THREADNAME_INFO; +#pragma pack(pop) + +/** + * Signal thread name to any attached debuggers. + */ +void SetWin32ThreadName(DWORD dwThreadID, const char* threadName) +{ + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = threadName; + info.dwThreadID = dwThreadID; + info.dwFlags = 0; + +#pragma warning(push) +#pragma warning(disable: 6320 6322) + __try { + RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info); + } __except (EXCEPTION_EXECUTE_HANDLER) { + } +#pragma warning(pop) +} +#endif diff --git a/src/os/windows/win32.h b/src/os/windows/win32.h index 4e53879cf0..16632f6e9a 100644 --- a/src/os/windows/win32.h +++ b/src/os/windows/win32.h @@ -39,4 +39,10 @@ HRESULT OTTDSHGetFolderPath(HWND, int, HANDLE, DWORD, LPTSTR); #define SHGFP_TYPE_CURRENT 0 #endif /* __MINGW32__ */ +#ifdef _MSC_VER +void SetWin32ThreadName(DWORD dwThreadID, const char* threadName); +#else +static inline void SetWin32ThreadName(DWORD dwThreadID, const char* threadName) {} +#endif + #endif /* WIN32_H */ diff --git a/src/roadveh.h b/src/roadveh.h index 5b265f0470..b1865fc22e 100644 --- a/src/roadveh.h +++ b/src/roadveh.h @@ -108,7 +108,7 @@ struct RoadVehicle FINAL : public GroundVehicle { void UpdateDeltaXY(Direction direction); ExpensesType GetExpenseType(bool income) const { return income ? EXPENSES_ROADVEH_INC : EXPENSES_ROADVEH_RUN; } bool IsPrimaryVehicle() const { return this->IsFrontEngine(); } - SpriteID GetImage(Direction direction, EngineImageType image_type) const; + void GetImage(Direction direction, EngineImageType image_type, VehicleSpriteSeq *result) const; int GetDisplaySpeed() const { return this->gcache.last_speed / 2; } int GetDisplayMaxSpeed() const { return this->vcache.cached_max_speed / 2; } Money GetRunningCost() const; diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index 35c671d355..9b541a766b 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -113,40 +113,39 @@ int RoadVehicle::GetDisplayImageWidth(Point *offset) const return ScaleGUITrad(this->gcache.cached_veh_length * reference_width / VEHICLE_LENGTH); } -static SpriteID GetRoadVehIcon(EngineID engine, EngineImageType image_type) +static void GetRoadVehIcon(EngineID engine, EngineImageType image_type, VehicleSpriteSeq *result) { const Engine *e = Engine::Get(engine); uint8 spritenum = e->u.road.image_index; if (is_custom_sprite(spritenum)) { - SpriteID sprite = GetCustomVehicleIcon(engine, DIR_W, image_type); - if (sprite != 0) return sprite; + GetCustomVehicleIcon(engine, DIR_W, image_type, result); + if (result->IsValid()) return; spritenum = e->original_image_index; } assert(IsValidImageIndex(spritenum)); - return DIR_W + _roadveh_images[spritenum]; + result->Set(DIR_W + _roadveh_images[spritenum]); } -SpriteID RoadVehicle::GetImage(Direction direction, EngineImageType image_type) const +void RoadVehicle::GetImage(Direction direction, EngineImageType image_type, VehicleSpriteSeq *result) const { uint8 spritenum = this->spritenum; - SpriteID sprite; if (is_custom_sprite(spritenum)) { - sprite = GetCustomVehicleSprite(this, (Direction)(direction + 4 * IS_CUSTOM_SECONDHEAD_SPRITE(spritenum)), image_type); - if (sprite != 0) return sprite; + GetCustomVehicleSprite(this, (Direction)(direction + 4 * IS_CUSTOM_SECONDHEAD_SPRITE(spritenum)), image_type, result); + if (result->IsValid()) return; spritenum = this->GetEngine()->original_image_index; } assert(IsValidImageIndex(spritenum)); - sprite = direction + _roadveh_images[spritenum]; + SpriteID sprite = direction + _roadveh_images[spritenum]; if (this->cargo.StoredCount() >= this->cargo_cap / 2U) sprite += _roadveh_full_adder[spritenum]; - return sprite; + result->Set(sprite); } /** @@ -160,12 +159,16 @@ SpriteID RoadVehicle::GetImage(Direction direction, EngineImageType image_type) */ void DrawRoadVehEngine(int left, int right, int preferred_x, int y, EngineID engine, PaletteID pal, EngineImageType image_type) { - SpriteID sprite = GetRoadVehIcon(engine, image_type); - const Sprite *real_sprite = GetSprite(sprite, ST_NORMAL); + VehicleSpriteSeq seq; + GetRoadVehIcon(engine, image_type, &seq); + + Rect rect; + seq.GetBounds(&rect); preferred_x = Clamp(preferred_x, - left - UnScaleGUI(real_sprite->x_offs), - right - UnScaleGUI(real_sprite->width) - UnScaleGUI(real_sprite->x_offs)); - DrawSprite(sprite, pal, preferred_x, y); + left - UnScaleGUI(rect.left), + right - UnScaleGUI(rect.right)); + + seq.Draw(preferred_x, y, pal, pal == PALETTE_CRASH); } /** @@ -179,12 +182,16 @@ void DrawRoadVehEngine(int left, int right, int preferred_x, int y, EngineID eng */ void GetRoadVehSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type) { - const Sprite *spr = GetSprite(GetRoadVehIcon(engine, image_type), ST_NORMAL); + VehicleSpriteSeq seq; + GetRoadVehIcon(engine, image_type, &seq); - width = UnScaleGUI(spr->width); - height = UnScaleGUI(spr->height); - xoffs = UnScaleGUI(spr->x_offs); - yoffs = UnScaleGUI(spr->y_offs); + Rect rect; + seq.GetBounds(&rect); + + width = UnScaleGUI(rect.right - rect.left + 1); + height = UnScaleGUI(rect.bottom - rect.top + 1); + xoffs = UnScaleGUI(rect.left); + yoffs = UnScaleGUI(rect.top); } /** @@ -306,7 +313,7 @@ CommandCost CmdBuildRoadVehicle(TileIndex tile, DoCommandFlag flags, const Engin v->date_of_last_service = _date; v->build_year = _cur_year; - v->cur_image = SPR_IMG_QUERY; + v->sprite_seq.Set(SPR_IMG_QUERY); v->random_bits = VehicleRandomBits(); v->SetFrontEngine(); diff --git a/src/roadveh_gui.cpp b/src/roadveh_gui.cpp index 46ff6cea78..c446db5109 100644 --- a/src/roadveh_gui.cpp +++ b/src/roadveh_gui.cpp @@ -149,7 +149,9 @@ void DrawRoadVehImage(const Vehicle *v, int left, int right, int y, VehicleID se if (rtl ? px + width > 0 : px - width < max_width) { PaletteID pal = (u->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(u); - DrawSprite(u->GetImage(dir, image_type), pal, px + (rtl ? -offset.x : offset.x), ScaleGUITrad(6) + offset.y); + VehicleSpriteSeq seq; + u->GetImage(dir, image_type, &seq); + seq.Draw(px + (rtl ? -offset.x : offset.x), ScaleGUITrad(6) + offset.y, pal, u->vehstatus & VS_CRASHED); } px += rtl ? -width : width; diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 477786cf2e..47ecab39e4 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -248,7 +248,7 @@ static void InitializeWindowsAndCaches() /* For each company, 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 companies 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 && c->inaugurated_year != MIN_YEAR) { + if (_file_to_saveload.abstract_ftype == FT_SCENARIO && c->inaugurated_year != MIN_YEAR) { c->inaugurated_year = _cur_year; } } diff --git a/src/saveload/oldloader_sl.cpp b/src/saveload/oldloader_sl.cpp index 4afbe6033d..0c57166816 100644 --- a/src/saveload/oldloader_sl.cpp +++ b/src/saveload/oldloader_sl.cpp @@ -1165,7 +1165,7 @@ static const OldChunks vehicle_chunk[] = { OCL_SVAR( OC_UINT8, Vehicle, owner ), OCL_SVAR( OC_TILE, Vehicle, tile ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, cur_image ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, sprite_seq.seq[0].sprite ), OCL_NULL( 8 ), ///< Vehicle sprite box, calculated automatically @@ -1258,7 +1258,7 @@ bool LoadOldVehicle(LoadgameState *ls, int num) if (v == NULL) continue; v->refit_cap = v->cargo_cap; - SpriteID sprite = v->cur_image; + SpriteID sprite = v->sprite_seq.seq[0].sprite; /* no need to override other sprites */ if (IsInsideMM(sprite, 1460, 1465)) { sprite += 580; // aircraft smoke puff @@ -1269,7 +1269,7 @@ bool LoadOldVehicle(LoadgameState *ls, int num) } else if (IsInsideMM(sprite, 2516, 2539)) { sprite += 1385; // rotor or disaster-related vehicles } - v->cur_image = sprite; + v->sprite_seq.seq[0].sprite = sprite; switch (v->type) { case VEH_TRAIN: { diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index 9543d834e6..17760e2332 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -273,6 +273,7 @@ extern const uint16 SAVEGAME_VERSION = 195; ///< Current savegame version of Ope const uint16 SAVEGAME_VERSION_EXT = 0x8000; ///< Savegame extension indicator mask SavegameType _savegame_type; ///< type of savegame we are loading +FileToSaveLoad _file_to_saveload; ///< File to save or load in the openttd loop. uint32 _ttdp_version; ///< version of TTDP savegame (if applicable) uint16 _sl_version; ///< the major savegame version identifier @@ -2674,7 +2675,7 @@ static SaveOrLoadResult DoSave(SaveFilter *writer, bool threaded) SlSaveChunks(); SaveFileStart(); - if (!threaded || !ThreadObject::New(&SaveFileToDiskThread, NULL, &_save_thread)) { + if (!threaded || !ThreadObject::New(&SaveFileToDiskThread, NULL, &_save_thread, "ottd:savegame")) { if (threaded) DEBUG(sl, 1, "Cannot create savegame thread, reverting to single-threaded mode..."); SaveOrLoadResult result = SaveFileToDisk(false); @@ -2879,10 +2880,10 @@ SaveOrLoadResult LoadWithFilter(LoadFilter *reader) * @param threaded True when threaded saving is allowed * @return Return the result of the action. #SL_OK, #SL_ERROR, or #SL_REINIT ("unload" the game) */ -SaveOrLoadResult SaveOrLoad(const char *filename, int mode, Subdirectory sb, bool threaded) +SaveOrLoadResult SaveOrLoad(const char *filename, SaveLoadOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded) { /* An instance of saving is already active, so don't go saving again */ - if (_sl.saveinprogress && mode == SL_SAVE && threaded) { + if (_sl.saveinprogress && fop == SLO_SAVE && dft == DFT_GAME_FILE && threaded) { /* if not an autosave, but a user action, show error message */ if (!_do_autosave) ShowErrorMessage(STR_ERROR_SAVE_STILL_IN_PROGRESS, INVALID_STRING_ID, WL_ERROR); return SL_OK; @@ -2891,7 +2892,7 @@ SaveOrLoadResult SaveOrLoad(const char *filename, int mode, Subdirectory sb, boo try { /* Load a TTDLX or TTDPatch game */ - if (mode == SL_OLD_LOAD) { + if (fop == SLO_LOAD && dft == DFT_OLD_GAME_FILE) { InitializeGame(256, 256, true, true); // set a mapsize of 256x256 for TTDPatch games or it might get confused /* TTD/TTO savegames have no NewGRFs, TTDP savegame have them @@ -2914,25 +2915,35 @@ SaveOrLoadResult SaveOrLoad(const char *filename, int mode, Subdirectory sb, boo return SL_OK; } - switch (mode) { - case SL_LOAD_CHECK: _sl.action = SLA_LOAD_CHECK; break; - case SL_LOAD: _sl.action = SLA_LOAD; break; - case SL_SAVE: _sl.action = SLA_SAVE; break; + assert(dft == DFT_GAME_FILE); + switch (fop) { + case SLO_CHECK: + _sl.action = SLA_LOAD_CHECK; + break; + + case SLO_LOAD: + _sl.action = SLA_LOAD; + break; + + case SLO_SAVE: + _sl.action = SLA_SAVE; + break; + default: NOT_REACHED(); } - FILE *fh = (mode == SL_SAVE) ? FioFOpenFile(filename, "wb", sb) : FioFOpenFile(filename, "rb", sb); + FILE *fh = (fop == SLO_SAVE) ? FioFOpenFile(filename, "wb", sb) : FioFOpenFile(filename, "rb", sb); /* Make it a little easier to load savegames from the console */ - if (fh == NULL && mode != SL_SAVE) fh = FioFOpenFile(filename, "rb", SAVE_DIR); - if (fh == NULL && mode != SL_SAVE) fh = FioFOpenFile(filename, "rb", BASE_DIR); - if (fh == NULL && mode != SL_SAVE) fh = FioFOpenFile(filename, "rb", SCENARIO_DIR); + if (fh == NULL && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", SAVE_DIR); + if (fh == NULL && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", BASE_DIR); + if (fh == NULL && fop != SLO_SAVE) fh = FioFOpenFile(filename, "rb", SCENARIO_DIR); if (fh == NULL) { - SlError(mode == SL_SAVE ? STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE : STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE); + SlError(fop == SLO_SAVE ? STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE : STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE); } - if (mode == SL_SAVE) { // SAVE game + if (fop == SLO_SAVE) { // SAVE game DEBUG(desync, 1, "save: %08x; %02x; %s", _date, _date_fract, filename); if (_network_server || !_settings_client.gui.threaded_saves) threaded = false; @@ -2940,24 +2951,25 @@ SaveOrLoadResult SaveOrLoad(const char *filename, int mode, Subdirectory sb, boo } /* LOAD game */ - assert(mode == SL_LOAD || mode == SL_LOAD_CHECK); + assert(fop == SLO_LOAD || fop == SLO_CHECK); DEBUG(desync, 1, "load: %s", filename); - return DoLoad(new FileReader(fh), mode == SL_LOAD_CHECK); + return DoLoad(new FileReader(fh), fop == SLO_CHECK); } catch (...) { + /* This code may be executed both for old and new save games. */ ClearSaveLoadState(); /* Skip the "colour" character */ - if (mode != SL_LOAD_CHECK) DEBUG(sl, 0, "%s", GetSaveLoadErrorString() + 3); + if (fop != SLO_CHECK) DEBUG(sl, 0, "%s", GetSaveLoadErrorString() + 3); /* A saver/loader exception!! reinitialize all variables to prevent crash! */ - return (mode == SL_LOAD || mode == SL_OLD_LOAD) ? SL_REINIT : SL_ERROR; + return (fop == SLO_LOAD) ? SL_REINIT : SL_ERROR; } } /** Do a save when exiting the game (_settings_client.gui.autosave_on_exit) */ void DoExitSave() { - SaveOrLoad("exit.sav", SL_SAVE, AUTOSAVE_DIR); + SaveOrLoad("exit.sav", SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR); } /** @@ -2995,6 +3007,53 @@ void GenerateDefaultSaveName(char *buf, const char *last) SanitizeFilename(buf); } +/** + * Set the mode and file type of the file to save or load based on the type of file entry at the file system. + * @param ft Type of file entry of the file system. + */ +void FileToSaveLoad::SetMode(FiosType ft) +{ + this->SetMode(SLO_LOAD, GetAbstractFileType(ft), GetDetailedFileType(ft)); +} + +/** + * Set the mode and file type of the file to save or load. + * @param fop File operation being performed. + * @param aft Abstract file type. + * @param dft Detailed file type. + */ +void FileToSaveLoad::SetMode(SaveLoadOperation fop, AbstractFileType aft, DetailedFileType dft) +{ + if (aft == FT_INVALID || aft == FT_NONE) { + this->file_op = SLO_INVALID; + this->detail_ftype = DFT_INVALID; + this->abstract_ftype = FT_INVALID; + return; + } + + this->file_op = fop; + this->detail_ftype = dft; + this->abstract_ftype = aft; +} + +/** + * Set the name of the file. + * @param name Name of the file. + */ +void FileToSaveLoad::SetName(const char *name) +{ + strecpy(this->name, name, lastof(this->name)); +} + +/** + * Set the title of the file. + * @param title Title of the file. + */ +void FileToSaveLoad::SetTitle(const char *title) +{ + strecpy(this->title, title, lastof(this->title)); +} + #if 0 /** * Function to get the type of the savegame by looking at the file header. diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 5bac216bf2..cc3a07de05 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -25,15 +25,18 @@ enum SaveOrLoadResult { SL_REINIT = 2, ///< error that was caught in the middle of updating game state, need to clear it. (can only happen during load) }; -/** Save or load mode. @see SaveOrLoad */ -enum SaveOrLoadMode { - SL_INVALID = -1, ///< Invalid mode. - SL_LOAD = 0, ///< Load game. - SL_SAVE = 1, ///< Save game. - SL_OLD_LOAD = 2, ///< Load old game. - SL_PNG = 3, ///< Load PNG file (height map). - SL_BMP = 4, ///< Load BMP file (height map). - SL_LOAD_CHECK = 5, ///< Load for game preview. +/** Deals with the type of the savegame, independent of extension */ +struct FileToSaveLoad { + SaveLoadOperation file_op; ///< File operation to perform. + DetailedFileType detail_ftype; ///< Concrete file type (PNG, BMP, old save, etc). + AbstractFileType abstract_ftype; ///< Abstract type of file (scenario, heightmap, etc). + char name[MAX_PATH]; ///< Name of the file. + char title[255]; ///< Internal name of the game. + + void SetMode(FiosType ft); + void SetMode(SaveLoadOperation fop, AbstractFileType aft, DetailedFileType dft); + void SetName(const char *name); + void SetTitle(const char *title); }; /** Types of save games. */ @@ -46,10 +49,12 @@ enum SavegameType { SGT_INVALID = 0xFF, ///< broken savegame (used internally) }; +extern FileToSaveLoad _file_to_saveload; + void GenerateDefaultSaveName(char *buf, const char *last); void SetSaveLoadError(uint16 str); const char *GetSaveLoadErrorString(); -SaveOrLoadResult SaveOrLoad(const char *filename, int mode, Subdirectory sb, bool threaded = true); +SaveOrLoadResult SaveOrLoad(const char *filename, SaveLoadOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded = true); void WaitTillSaved(); void ProcessAsyncSaveFinish(); void DoExitSave(); diff --git a/src/saveload/signs_sl.cpp b/src/saveload/signs_sl.cpp index d5bacd509c..132ac181e4 100644 --- a/src/saveload/signs_sl.cpp +++ b/src/saveload/signs_sl.cpp @@ -60,7 +60,7 @@ static void Load_SIGN() } /* Signs placed in scenario editor shall now be OWNER_DEITY */ - if (IsSavegameVersionBefore(171) && si->owner == OWNER_NONE && _saveload_mode == SLD_LOAD_SCENARIO) { + if (IsSavegameVersionBefore(171) && si->owner == OWNER_NONE && _file_to_saveload.abstract_ftype == FT_SCENARIO) { si->owner = OWNER_DEITY; } } diff --git a/src/saveload/vehicle_sl.cpp b/src/saveload/vehicle_sl.cpp index ebc5fc4215..86ca590e4d 100644 --- a/src/saveload/vehicle_sl.cpp +++ b/src/saveload/vehicle_sl.cpp @@ -436,21 +436,21 @@ void AfterLoadVehicles(bool part_of_load) case VEH_TRAIN: case VEH_SHIP: - v->cur_image = v->GetImage(v->direction, EIT_ON_MAP); + v->GetImage(v->direction, EIT_ON_MAP, &v->sprite_seq); break; case VEH_AIRCRAFT: if (Aircraft::From(v)->IsNormalAircraft()) { - v->cur_image = v->GetImage(v->direction, EIT_ON_MAP); + v->GetImage(v->direction, EIT_ON_MAP, &v->sprite_seq); - /* The plane's shadow will have the same image as the plane */ + /* The plane's shadow will have the same image as the plane, but no colour */ Vehicle *shadow = v->Next(); - shadow->cur_image = v->cur_image; + shadow->sprite_seq.CopyWithoutPalette(v->sprite_seq); /* In the case of a helicopter we will update the rotor sprites */ if (v->subtype == AIR_HELICOPTER) { Vehicle *rotor = shadow->Next(); - rotor->cur_image = GetRotorImage(Aircraft::From(v), EIT_ON_MAP); + GetRotorImage(Aircraft::From(v), EIT_ON_MAP, &rotor->sprite_seq); } UpdateAircraftCache(Aircraft::From(v), true); @@ -796,7 +796,7 @@ const SaveLoad *GetVehicleDescription(VehicleType vt) SLE_CONDVAR(Vehicle, z_pos, SLE_FILE_U8 | SLE_VAR_I32, 0, 163), SLE_CONDVAR(Vehicle, z_pos, SLE_INT32, 164, SL_MAX_VERSION), - SLE_VAR(Vehicle, cur_image, SLE_FILE_U16 | SLE_VAR_U32), + SLE_VAR(Vehicle, sprite_seq.seq[0].sprite, SLE_FILE_U16 | SLE_VAR_U32), SLE_CONDNULL(5, 0, 57), SLE_VAR(Vehicle, progress, SLE_UINT8), SLE_VAR(Vehicle, vehstatus, SLE_UINT8), @@ -836,7 +836,7 @@ const SaveLoad *GetVehicleDescription(VehicleType vt) SLE_CONDVAR(Vehicle, current_order.dest, SLE_FILE_U8 | SLE_VAR_U16, 0, 4), SLE_CONDVAR(Vehicle, current_order.dest, SLE_UINT16, 5, SL_MAX_VERSION), - SLE_VAR(Vehicle, cur_image, SLE_FILE_U16 | SLE_VAR_U32), + SLE_VAR(Vehicle, sprite_seq.seq[0].sprite, SLE_FILE_U16 | SLE_VAR_U32), SLE_CONDVAR(Vehicle, age, SLE_FILE_U16 | SLE_VAR_I32, 0, 30), SLE_CONDVAR(Vehicle, age, SLE_INT32, 31, SL_MAX_VERSION), SLE_VAR(Vehicle, tick_counter, SLE_UINT8), diff --git a/src/ship.h b/src/ship.h index 18f04c8faf..c94cbcddb7 100644 --- a/src/ship.h +++ b/src/ship.h @@ -34,7 +34,7 @@ struct Ship FINAL : public SpecializedVehicle { ExpensesType GetExpenseType(bool income) const { return income ? EXPENSES_SHIP_INC : EXPENSES_SHIP_RUN; } void PlayLeaveStationSound() const; bool IsPrimaryVehicle() const { return true; } - SpriteID GetImage(Direction direction, EngineImageType image_type) const; + void GetImage(Direction direction, EngineImageType image_type, VehicleSpriteSeq *result) const; int GetDisplaySpeed() const { return this->cur_speed / 2; } int GetDisplayMaxSpeed() const { return this->vcache.cached_max_speed / 2; } int GetCurrentMaxSpeed() const { return min(this->vcache.cached_max_speed, this->current_order.GetMaxSpeed() * 2); } diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index a24fb35104..2a760e2ca7 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -70,30 +70,34 @@ static inline TrackBits GetTileShipTrackStatus(TileIndex tile) return TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_WATER, 0)); } -static SpriteID GetShipIcon(EngineID engine, EngineImageType image_type) +static void GetShipIcon(EngineID engine, EngineImageType image_type, VehicleSpriteSeq *result) { const Engine *e = Engine::Get(engine); uint8 spritenum = e->u.ship.image_index; if (is_custom_sprite(spritenum)) { - SpriteID sprite = GetCustomVehicleIcon(engine, DIR_W, image_type); - if (sprite != 0) return sprite; + GetCustomVehicleIcon(engine, DIR_W, image_type, result); + if (result->IsValid()) return; spritenum = e->original_image_index; } assert(IsValidImageIndex(spritenum)); - return DIR_W + _ship_sprites[spritenum]; + result->Set(DIR_W + _ship_sprites[spritenum]); } void DrawShipEngine(int left, int right, int preferred_x, int y, EngineID engine, PaletteID pal, EngineImageType image_type) { - SpriteID sprite = GetShipIcon(engine, image_type); - const Sprite *real_sprite = GetSprite(sprite, ST_NORMAL); + VehicleSpriteSeq seq; + GetShipIcon(engine, image_type, &seq); + + Rect rect; + seq.GetBounds(&rect); preferred_x = Clamp(preferred_x, - left - UnScaleGUI(real_sprite->x_offs), - right - UnScaleGUI(real_sprite->width) - UnScaleGUI(real_sprite->x_offs)); - DrawSprite(sprite, pal, preferred_x, y); + left - UnScaleGUI(rect.left), + right - UnScaleGUI(rect.right)); + + seq.Draw(preferred_x, y, pal, pal == PALETTE_CRASH); } /** @@ -107,27 +111,31 @@ void DrawShipEngine(int left, int right, int preferred_x, int y, EngineID engine */ void GetShipSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type) { - const Sprite *spr = GetSprite(GetShipIcon(engine, image_type), ST_NORMAL); + VehicleSpriteSeq seq; + GetShipIcon(engine, image_type, &seq); - width = UnScaleGUI(spr->width); - height = UnScaleGUI(spr->height); - xoffs = UnScaleGUI(spr->x_offs); - yoffs = UnScaleGUI(spr->y_offs); + Rect rect; + seq.GetBounds(&rect); + + width = UnScaleGUI(rect.right - rect.left + 1); + height = UnScaleGUI(rect.bottom - rect.top + 1); + xoffs = UnScaleGUI(rect.left); + yoffs = UnScaleGUI(rect.top); } -SpriteID Ship::GetImage(Direction direction, EngineImageType image_type) const +void Ship::GetImage(Direction direction, EngineImageType image_type, VehicleSpriteSeq *result) const { uint8 spritenum = this->spritenum; if (is_custom_sprite(spritenum)) { - SpriteID sprite = GetCustomVehicleSprite(this, direction, image_type); - if (sprite != 0) return sprite; + GetCustomVehicleSprite(this, direction, image_type, result); + if (result->IsValid()) return; spritenum = this->GetEngine()->original_image_index; } assert(IsValidImageIndex(spritenum)); - return _ship_sprites[spritenum] + direction; + result->Set(_ship_sprites[spritenum] + direction); } static const Depot *FindClosestShipDepot(const Vehicle *v, uint max_distance) @@ -710,7 +718,7 @@ CommandCost CmdBuildShip(TileIndex tile, DoCommandFlag flags, const Engine *e, u v->SetServiceInterval(Company::Get(_current_company)->settings.vehicle.servint_ships); v->date_of_last_service = _date; v->build_year = _cur_year; - v->cur_image = SPR_IMG_QUERY; + v->sprite_seq.Set(SPR_IMG_QUERY); v->random_bits = VehicleRandomBits(); v->UpdateCache(); diff --git a/src/ship_gui.cpp b/src/ship_gui.cpp index 7ad2e8473a..c6bf342d97 100644 --- a/src/ship_gui.cpp +++ b/src/ship_gui.cpp @@ -35,20 +35,23 @@ void DrawShipImage(const Vehicle *v, int left, int right, int y, VehicleID selec { bool rtl = _current_text_dir == TD_RTL; - SpriteID sprite = v->GetImage(rtl ? DIR_E : DIR_W, image_type); - const Sprite *real_sprite = GetSprite(sprite, ST_NORMAL); + VehicleSpriteSeq seq; + v->GetImage(rtl ? DIR_E : DIR_W, image_type, &seq); - int width = UnScaleGUI(real_sprite->width); - int x_offs = UnScaleGUI(real_sprite->x_offs); + Rect rect; + seq.GetBounds(&rect); + + int width = UnScaleGUI(rect.right - rect.left + 1); + int x_offs = UnScaleGUI(rect.left); int x = rtl ? right - width - x_offs : left - x_offs; y += ScaleGUITrad(10); - DrawSprite(sprite, GetVehiclePalette(v), x, y); + seq.Draw(x, y, GetVehiclePalette(v), false); if (v->index == selection) { x += x_offs; - y += UnScaleGUI(real_sprite->y_offs); - DrawFrameRect(x - 1, y - 1, x + width + 1, y + UnScaleGUI(real_sprite->height) + 1, COLOUR_WHITE, FR_BORDERONLY); + y += UnScaleGUI(rect.top); + DrawFrameRect(x - 1, y - 1, x + width + 1, y + UnScaleGUI(rect.bottom - rect.top + 1) + 1, COLOUR_WHITE, FR_BORDERONLY); } } diff --git a/src/sound/win32_s.cpp b/src/sound/win32_s.cpp index 45f88172a9..c9c1a8afdc 100644 --- a/src/sound/win32_s.cpp +++ b/src/sound/win32_s.cpp @@ -19,6 +19,7 @@ #include "win32_s.h" #include #include +#include "../os/windows/win32.h" #include "../safeguards.h" @@ -41,6 +42,8 @@ static void PrepareHeader(WAVEHDR *hdr) static DWORD WINAPI SoundThread(LPVOID arg) { + SetWin32ThreadName(-1, "ottd:win-sound"); + do { for (WAVEHDR *hdr = _wave_hdr; hdr != endof(_wave_hdr); hdr++) { if ((hdr->dwFlags & WHDR_INQUEUE) != 0) continue; diff --git a/src/table/newgrf_debug_data.h b/src/table/newgrf_debug_data.h index b7d0c92024..afe19bbedb 100644 --- a/src/table/newgrf_debug_data.h +++ b/src/table/newgrf_debug_data.h @@ -17,7 +17,7 @@ #define NIP_END() { NULL, 0, 0, 0, 0 } /* Helper for filling callback tables */ -#define NIC(cb_id, base, variable, bit) { #cb_id, cpp_offsetof(base, variable), cpp_sizeof(base, variable), bit, cb_id } +#define NIC(cb_id, base, variable, bit) { #cb_id, (ptrdiff_t)cpp_offsetof(base, variable), cpp_sizeof(base, variable), bit, cb_id } #define NIC_END() { NULL, 0, 0, 0, 0 } /* Helper for filling variable tables */ diff --git a/src/thread/thread.h b/src/thread/thread.h index b944a53b96..07831bb4ba 100644 --- a/src/thread/thread.h +++ b/src/thread/thread.h @@ -44,9 +44,10 @@ public: * @param proc The procedure to call inside the thread. * @param param The params to give with 'proc'. * @param thread Place to store a pointer to the thread in. May be NULL. + * @param name A name for the thread. May be NULL. * @return True if the thread was started correctly. */ - static bool New(OTTDThreadFunc proc, void *param, ThreadObject **thread = NULL); + static bool New(OTTDThreadFunc proc, void *param, ThreadObject **thread = NULL, const char *name = NULL); }; /** diff --git a/src/thread/thread_morphos.cpp b/src/thread/thread_morphos.cpp index cc6b2f9d3d..6d00d0579c 100644 --- a/src/thread/thread_morphos.cpp +++ b/src/thread/thread_morphos.cpp @@ -193,7 +193,7 @@ private: } }; -/* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread) +/* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread, const char *name) { ThreadObject *to = new ThreadObject_MorphOS(proc, param, thread == NULL); if (thread != NULL) *thread = to; diff --git a/src/thread/thread_none.cpp b/src/thread/thread_none.cpp index afa799b239..91eb50b113 100644 --- a/src/thread/thread_none.cpp +++ b/src/thread/thread_none.cpp @@ -14,7 +14,7 @@ #include "../safeguards.h" -/* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread) +/* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread, const char *name) { if (thread != NULL) *thread = NULL; return false; diff --git a/src/thread/thread_os2.cpp b/src/thread/thread_os2.cpp index 2dcb8288f4..c66e2ad643 100644 --- a/src/thread/thread_os2.cpp +++ b/src/thread/thread_os2.cpp @@ -83,7 +83,7 @@ private: } }; -/* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread) +/* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread, const char *name) { ThreadObject *to = new ThreadObject_OS2(proc, param, thread == NULL); if (thread != NULL) *thread = to; diff --git a/src/thread/thread_pthread.cpp b/src/thread/thread_pthread.cpp index ce407fd941..747b8943d6 100644 --- a/src/thread/thread_pthread.cpp +++ b/src/thread/thread_pthread.cpp @@ -25,16 +25,18 @@ private: OTTDThreadFunc proc; ///< External thread procedure. void *param; ///< Parameter for the external thread procedure. bool self_destruct; ///< Free ourselves when done? + const char *name; ///< Name for the thread public: /** * Create a pthread and start it, calling proc(param). */ - ThreadObject_pthread(OTTDThreadFunc proc, void *param, bool self_destruct) : + ThreadObject_pthread(OTTDThreadFunc proc, void *param, bool self_destruct, const char *name) : thread(0), proc(proc), param(param), - self_destruct(self_destruct) + self_destruct(self_destruct), + name(name) { pthread_create(&this->thread, NULL, &stThreadProc, this); } @@ -60,7 +62,15 @@ private: */ static void *stThreadProc(void *thr) { - ((ThreadObject_pthread *)thr)->ThreadProc(); + ThreadObject_pthread *self = (ThreadObject_pthread *) thr; +#if defined(__GLIBC__) +#if __GLIBC_PREREQ(2, 12) + if (self->name) { + pthread_setname_np(pthread_self(), self->name); + } +#endif +#endif + self->ThreadProc(); pthread_exit(NULL); } @@ -85,9 +95,9 @@ private: } }; -/* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread) +/* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread, const char *name) { - ThreadObject *to = new ThreadObject_pthread(proc, param, thread == NULL); + ThreadObject *to = new ThreadObject_pthread(proc, param, thread == NULL, name); if (thread != NULL) *thread = to; return true; } diff --git a/src/thread/thread_win32.cpp b/src/thread/thread_win32.cpp index c37baf7c36..a01ea8e108 100644 --- a/src/thread/thread_win32.cpp +++ b/src/thread/thread_win32.cpp @@ -16,6 +16,7 @@ #include #include #include +#include "../os/windows/win32.h" #include "../safeguards.h" @@ -29,17 +30,19 @@ private: OTTDThreadFunc proc; ///< External thread procedure. void *param; ///< Parameter for the external thread procedure. bool self_destruct; ///< Free ourselves when done? + const char *name; ///< Thread name. public: /** * Create a win32 thread and start it, calling proc(param). */ - ThreadObject_Win32(OTTDThreadFunc proc, void *param, bool self_destruct) : + ThreadObject_Win32(OTTDThreadFunc proc, void *param, bool self_destruct, const char *name) : thread(NULL), id(0), proc(proc), param(param), - self_destruct(self_destruct) + self_destruct(self_destruct), + name(name) { this->thread = (HANDLE)_beginthreadex(NULL, 0, &stThreadProc, this, CREATE_SUSPENDED, &this->id); if (this->thread == NULL) return; @@ -85,6 +88,10 @@ private: */ void ThreadProc() { +#ifdef _MSC_VER + /* Set thread name for debuggers. Has to be done from the thread due to a race condition in older MS debuggers. */ + SetWin32ThreadName(-1, this->name); +#endif try { this->proc(this->param); } catch (OTTDThreadExitSignal) { @@ -96,9 +103,9 @@ private: } }; -/* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread) +/* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread, const char *name) { - ThreadObject *to = new ThreadObject_Win32(proc, param, thread == NULL); + ThreadObject *to = new ThreadObject_Win32(proc, param, thread == NULL, name); if (thread != NULL) *thread = to; return true; } diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index 40c75ad710..f253be8b3a 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -427,17 +427,17 @@ static CallBackFunction MenuClickSaveLoad(int index = 0) { if (_game_mode == GM_EDITOR) { switch (index) { - case SLEME_SAVE_SCENARIO: ShowSaveLoadDialog(SLD_SAVE_SCENARIO); break; - case SLEME_LOAD_SCENARIO: ShowSaveLoadDialog(SLD_LOAD_SCENARIO); break; - case SLEME_SAVE_HEIGHTMAP: ShowSaveLoadDialog(SLD_SAVE_HEIGHTMAP); break; - case SLEME_LOAD_HEIGHTMAP: ShowSaveLoadDialog(SLD_LOAD_HEIGHTMAP); break; + case SLEME_SAVE_SCENARIO: ShowSaveLoadDialog(FT_SCENARIO, SLO_SAVE); break; + case SLEME_LOAD_SCENARIO: ShowSaveLoadDialog(FT_SCENARIO, SLO_LOAD); break; + case SLEME_SAVE_HEIGHTMAP: ShowSaveLoadDialog(FT_HEIGHTMAP,SLO_SAVE); break; + case SLEME_LOAD_HEIGHTMAP: ShowSaveLoadDialog(FT_HEIGHTMAP,SLO_LOAD); break; case SLEME_EXIT_TOINTRO: AskExitToGameMenu(); break; case SLEME_EXIT_GAME: HandleExitGameRequest(); break; } } else { switch (index) { - case SLNME_SAVE_GAME: ShowSaveLoadDialog(SLD_SAVE_GAME); break; - case SLNME_LOAD_GAME: ShowSaveLoadDialog(SLD_LOAD_GAME); break; + case SLNME_SAVE_GAME: ShowSaveLoadDialog(FT_SAVEGAME, SLO_SAVE); break; + case SLNME_LOAD_GAME: ShowSaveLoadDialog(FT_SAVEGAME, SLO_LOAD); break; case SLNME_EXIT_TOINTRO: AskExitToGameMenu(); break; case SLNME_EXIT_GAME: HandleExitGameRequest(); break; } @@ -1469,36 +1469,302 @@ class NWidgetMainToolbarContainer : public NWidgetToolbarContainer { { static const uint SMALLEST_ARRANGEMENT = 14; static const uint BIGGEST_ARRANGEMENT = 20; + + /* The number of buttons of each row of the toolbar should match the number of items which we want to be visible. + * The total number of buttons should be equal to arrangable_count * 2. + * No bad things happen, but we could see strange behaviours if we have buttons < (arrangable_count * 2) like a + * pause button appearing on the right of the lower toolbar and weird resizing of the widgets even if there is + * enough space. + */ static const byte arrange14[] = { - 0, 1, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 29, - 2, 3, 4, 5, 6, 7, 8, 9, 12, 14, 26, 27, 28, 29, + WID_TN_PAUSE, + WID_TN_FAST_FORWARD, + WID_TN_TRAINS, + WID_TN_ROADVEHS, + WID_TN_SHIPS, + WID_TN_AIRCRAFTS, + WID_TN_ZOOM_IN, + WID_TN_ZOOM_OUT, + WID_TN_RAILS, + WID_TN_ROADS, + WID_TN_WATER, + WID_TN_AIR, + WID_TN_LANDSCAPE, + WID_TN_SWITCH_BAR, + // lower toolbar + WID_TN_SETTINGS, + WID_TN_SAVE, + WID_TN_SMALL_MAP, + WID_TN_TOWNS, + WID_TN_SUBSIDIES, + WID_TN_STATIONS, + WID_TN_FINANCES, + WID_TN_COMPANIES, + WID_TN_GRAPHS, + WID_TN_INDUSTRIES, + WID_TN_MUSIC_SOUND, + WID_TN_MESSAGES, + WID_TN_HELP, + WID_TN_SWITCH_BAR, }; static const byte arrange15[] = { - 0, 1, 4, 15, 16, 17, 18, 21, 22, 23, 24, 25, 19, 20, 29, - 0, 2, 4, 3, 5, 6, 7, 8, 9, 12, 14, 26, 27, 28, 29, + WID_TN_PAUSE, + WID_TN_FAST_FORWARD, + WID_TN_SMALL_MAP, + WID_TN_TRAINS, + WID_TN_ROADVEHS, + WID_TN_SHIPS, + WID_TN_AIRCRAFTS, + WID_TN_RAILS, + WID_TN_ROADS, + WID_TN_WATER, + WID_TN_AIR, + WID_TN_LANDSCAPE, + WID_TN_ZOOM_IN, + WID_TN_ZOOM_OUT, + WID_TN_SWITCH_BAR, + // lower toolbar + WID_TN_PAUSE, + WID_TN_SETTINGS, + WID_TN_SMALL_MAP, + WID_TN_SAVE, + WID_TN_TOWNS, + WID_TN_SUBSIDIES, + WID_TN_STATIONS, + WID_TN_FINANCES, + WID_TN_COMPANIES, + WID_TN_GRAPHS, + WID_TN_INDUSTRIES, + WID_TN_MUSIC_SOUND, + WID_TN_MESSAGES, + WID_TN_HELP, + WID_TN_SWITCH_BAR, }; static const byte arrange16[] = { - 0, 1, 2, 4, 15, 16, 17, 18, 21, 22, 23, 24, 25, 19, 20, 29, - 0, 1, 3, 5, 6, 7, 8, 9, 12, 14, 26, 27, 28, 19, 20, 29, + WID_TN_PAUSE, + WID_TN_FAST_FORWARD, + WID_TN_SETTINGS, + WID_TN_SMALL_MAP, + WID_TN_TRAINS, + WID_TN_ROADVEHS, + WID_TN_SHIPS, + WID_TN_AIRCRAFTS, + WID_TN_RAILS, + WID_TN_ROADS, + WID_TN_WATER, + WID_TN_AIR, + WID_TN_LANDSCAPE, + WID_TN_ZOOM_IN, + WID_TN_ZOOM_OUT, + WID_TN_SWITCH_BAR, + // lower toolbar + WID_TN_PAUSE, + WID_TN_FAST_FORWARD, + WID_TN_SAVE, + WID_TN_TOWNS, + WID_TN_SUBSIDIES, + WID_TN_STATIONS, + WID_TN_FINANCES, + WID_TN_COMPANIES, + WID_TN_GRAPHS, + WID_TN_INDUSTRIES, + WID_TN_MUSIC_SOUND, + WID_TN_MESSAGES, + WID_TN_HELP, + WID_TN_ZOOM_IN, + WID_TN_ZOOM_OUT, + WID_TN_SWITCH_BAR, }; static const byte arrange17[] = { - 0, 1, 2, 4, 6, 15, 16, 17, 18, 21, 22, 23, 24, 25, 19, 20, 29, - 0, 1, 3, 4, 6, 5, 7, 8, 9, 12, 14, 26, 27, 28, 19, 20, 29, + WID_TN_PAUSE, + WID_TN_FAST_FORWARD, + WID_TN_SETTINGS, + WID_TN_SMALL_MAP, + WID_TN_SUBSIDIES, + WID_TN_TRAINS, + WID_TN_ROADVEHS, + WID_TN_SHIPS, + WID_TN_AIRCRAFTS, + WID_TN_RAILS, + WID_TN_ROADS, + WID_TN_WATER, + WID_TN_AIR, + WID_TN_LANDSCAPE, + WID_TN_ZOOM_IN, + WID_TN_ZOOM_OUT, + WID_TN_SWITCH_BAR, + // lower toolbar + WID_TN_PAUSE, + WID_TN_FAST_FORWARD, + WID_TN_SAVE, + WID_TN_SMALL_MAP, + WID_TN_SUBSIDIES, + WID_TN_TOWNS, + WID_TN_STATIONS, + WID_TN_FINANCES, + WID_TN_COMPANIES, + WID_TN_GRAPHS, + WID_TN_INDUSTRIES, + WID_TN_MUSIC_SOUND, + WID_TN_MESSAGES, + WID_TN_HELP, + WID_TN_ZOOM_IN, + WID_TN_ZOOM_OUT, + WID_TN_SWITCH_BAR, }; static const byte arrange18[] = { - 0, 1, 2, 4, 5, 6, 7, 8, 9, 14, 21, 22, 23, 24, 25, 19, 20, 29, - 0, 1, 3, 4, 5, 6, 7, 12, 15, 16, 17, 18, 26, 27, 28, 19, 20, 29, + WID_TN_PAUSE, + WID_TN_FAST_FORWARD, + WID_TN_SETTINGS, + WID_TN_SMALL_MAP, + WID_TN_TOWNS, + WID_TN_SUBSIDIES, + WID_TN_STATIONS, + WID_TN_FINANCES, + WID_TN_COMPANIES, + WID_TN_INDUSTRIES, + WID_TN_RAILS, + WID_TN_ROADS, + WID_TN_WATER, + WID_TN_AIR, + WID_TN_LANDSCAPE, + WID_TN_ZOOM_IN, + WID_TN_ZOOM_OUT, + WID_TN_SWITCH_BAR, + // lower toolbar + WID_TN_PAUSE, + WID_TN_FAST_FORWARD, + WID_TN_SAVE, + WID_TN_SMALL_MAP, + WID_TN_TOWNS, + WID_TN_SUBSIDIES, + WID_TN_STATIONS, + WID_TN_GRAPHS, + WID_TN_TRAINS, + WID_TN_ROADVEHS, + WID_TN_SHIPS, + WID_TN_AIRCRAFTS, + WID_TN_MUSIC_SOUND, + WID_TN_MESSAGES, + WID_TN_HELP, + WID_TN_ZOOM_IN, + WID_TN_ZOOM_OUT, + WID_TN_SWITCH_BAR, }; static const byte arrange19[] = { - 0, 1, 2, 4, 5, 6, 15, 16, 17, 18, 21, 22, 23, 24, 25, 26, 19, 20, 29, - 0, 1, 3, 4, 7, 8, 9, 12, 14, 27, 21, 22, 23, 24, 25, 28, 19, 20, 29, + WID_TN_PAUSE, + WID_TN_FAST_FORWARD, + WID_TN_SETTINGS, + WID_TN_SMALL_MAP, + WID_TN_TOWNS, + WID_TN_SUBSIDIES, + WID_TN_TRAINS, + WID_TN_ROADVEHS, + WID_TN_SHIPS, + WID_TN_AIRCRAFTS, + WID_TN_RAILS, + WID_TN_ROADS, + WID_TN_WATER, + WID_TN_AIR, + WID_TN_LANDSCAPE, + WID_TN_MUSIC_SOUND, + WID_TN_ZOOM_IN, + WID_TN_ZOOM_OUT, + WID_TN_SWITCH_BAR, + // lower toolbar + WID_TN_PAUSE, + WID_TN_FAST_FORWARD, + WID_TN_SAVE, + WID_TN_SMALL_MAP, + WID_TN_STATIONS, + WID_TN_FINANCES, + WID_TN_COMPANIES, + WID_TN_GRAPHS, + WID_TN_INDUSTRIES, + WID_TN_MESSAGES, + WID_TN_RAILS, + WID_TN_ROADS, + WID_TN_WATER, + WID_TN_AIR, + WID_TN_LANDSCAPE, + WID_TN_HELP, + WID_TN_ZOOM_IN, + WID_TN_ZOOM_OUT, + WID_TN_SWITCH_BAR, }; static const byte arrange20[] = { - 0, 1, 2, 4, 5, 6, 15, 16, 17, 18, 21, 22, 23, 24, 25, 26, 11, 19, 20, 29, - 0, 1, 3, 4, 7, 8, 9, 12, 14, 27, 21, 22, 23, 24, 25, 10, 28, 19, 20, 29, + WID_TN_PAUSE, + WID_TN_FAST_FORWARD, + WID_TN_SETTINGS, + WID_TN_SMALL_MAP, + WID_TN_TOWNS, + WID_TN_SUBSIDIES, + WID_TN_TRAINS, + WID_TN_ROADVEHS, + WID_TN_SHIPS, + WID_TN_AIRCRAFTS, + WID_TN_RAILS, + WID_TN_ROADS, + WID_TN_WATER, + WID_TN_AIR, + WID_TN_LANDSCAPE, + WID_TN_MUSIC_SOUND, + WID_TN_GOAL, + WID_TN_ZOOM_IN, + WID_TN_ZOOM_OUT, + WID_TN_SWITCH_BAR, + // lower toolbar + WID_TN_PAUSE, + WID_TN_FAST_FORWARD, + WID_TN_SAVE, + WID_TN_SMALL_MAP, + WID_TN_STATIONS, + WID_TN_FINANCES, + WID_TN_COMPANIES, + WID_TN_GRAPHS, + WID_TN_INDUSTRIES, + WID_TN_MESSAGES, + WID_TN_RAILS, + WID_TN_ROADS, + WID_TN_WATER, + WID_TN_AIR, + WID_TN_LANDSCAPE, + WID_TN_STORY, + WID_TN_HELP, + WID_TN_ZOOM_IN, + WID_TN_ZOOM_OUT, + WID_TN_SWITCH_BAR, }; static const byte arrange_all[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28 + WID_TN_PAUSE, + WID_TN_FAST_FORWARD, + WID_TN_SETTINGS, + WID_TN_SAVE, + WID_TN_SMALL_MAP, + WID_TN_TOWNS, + WID_TN_SUBSIDIES, + WID_TN_STATIONS, + WID_TN_FINANCES, + WID_TN_COMPANIES, + WID_TN_STORY, + WID_TN_GOAL, + WID_TN_GRAPHS, + WID_TN_LEAGUE, + WID_TN_INDUSTRIES, + WID_TN_TRAINS, + WID_TN_ROADVEHS, + WID_TN_SHIPS, + WID_TN_AIRCRAFTS, + WID_TN_ZOOM_IN, + WID_TN_ZOOM_OUT, + WID_TN_RAILS, + WID_TN_ROADS, + WID_TN_WATER, + WID_TN_AIR, + WID_TN_LANDSCAPE, + WID_TN_MUSIC_SOUND, + WID_TN_MESSAGES, + WID_TN_HELP }; /* If at least BIGGEST_ARRANGEMENT fit, just spread all the buttons nicely */ @@ -1540,14 +1806,65 @@ class NWidgetScenarioToolbarContainer : public NWidgetToolbarContainer { /* virtual */ const byte *GetButtonArrangement(uint &width, uint &arrangable_count, uint &button_count, uint &spacer_count) const { static const byte arrange_all[] = { - 0, 1, 2, 3, 4, 18, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 26, 28, + WID_TE_PAUSE, + WID_TE_FAST_FORWARD, + WID_TE_SETTINGS, + WID_TE_SAVE, + WID_TE_SPACER, + WID_TE_DATE_PANEL, + WID_TE_SMALL_MAP, + WID_TE_ZOOM_IN, + WID_TE_ZOOM_OUT, + WID_TE_LAND_GENERATE, + WID_TE_TOWN_GENERATE, + WID_TE_INDUSTRY, + WID_TE_ROADS, + WID_TE_WATER, + WID_TE_TREES, + WID_TE_SIGNS, + WID_TE_MUSIC_SOUND, + WID_TE_HELP, }; static const byte arrange_nopanel[] = { - 0, 1, 2, 3, 18, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 26, 28, + WID_TE_PAUSE, + WID_TE_FAST_FORWARD, + WID_TE_SETTINGS, + WID_TE_SAVE, + WID_TE_DATE_PANEL, + WID_TE_SMALL_MAP, + WID_TE_ZOOM_IN, + WID_TE_ZOOM_OUT, + WID_TE_LAND_GENERATE, + WID_TE_TOWN_GENERATE, + WID_TE_INDUSTRY, + WID_TE_ROADS, + WID_TE_WATER, + WID_TE_TREES, + WID_TE_SIGNS, + WID_TE_MUSIC_SOUND, + WID_TE_HELP, }; static const byte arrange_switch[] = { - 18, 8, 11, 12, 13, 14, 15, 16, 17, 29, - 0, 1, 2, 3, 18, 9, 10, 26, 28, 29, + WID_TE_DATE_PANEL, + WID_TE_SMALL_MAP, + WID_TE_LAND_GENERATE, + WID_TE_TOWN_GENERATE, + WID_TE_INDUSTRY, + WID_TE_ROADS, + WID_TE_WATER, + WID_TE_TREES, + WID_TE_SIGNS, + WID_TE_SWITCH_BAR, + // lower toolbar + WID_TE_PAUSE, + WID_TE_FAST_FORWARD, + WID_TE_SETTINGS, + WID_TE_SAVE, + WID_TE_DATE_PANEL, + WID_TE_ZOOM_IN, + WID_TE_ZOOM_OUT, + WID_TE_MUSIC_SOUND, + WID_TE_HELP, WID_TE_SWITCH_BAR, }; /* If we can place all buttons *and* the panels, show them. */ @@ -1712,7 +2029,7 @@ struct MainToolbarWindow : Window { case MTHK_FASTFORWARD: ToolbarFastForwardClick(this); break; case MTHK_SETTINGS: ShowGameOptions(); break; case MTHK_SAVEGAME: MenuClickSaveLoad(); break; - case MTHK_LOADGAME: ShowSaveLoadDialog(SLD_LOAD_GAME); break; + case MTHK_LOADGAME: ShowSaveLoadDialog(FT_SAVEGAME, SLO_LOAD); break; case MTHK_SMALLMAP: ShowSmallMap(); break; case MTHK_TOWNDIRECTORY: ShowTownDirectory(); break; case MTHK_SUBSIDIES: ShowSubsidiesList(); break; @@ -1901,7 +2218,14 @@ static NWidgetBase *MakeMainToolbar(int *biggest_index) NWidgetMainToolbarContainer *hor = new NWidgetMainToolbarContainer(); for (uint i = 0; i < WID_TN_END; i++) { switch (i) { - case 4: case 8: case 15: case 19: case 21: case 26: hor->Add(new NWidgetSpacer(0, 0)); break; + case WID_TN_SMALL_MAP: + case WID_TN_FINANCES: + case WID_TN_VEHICLE_START: + case WID_TN_ZOOM_IN: + case WID_TN_BUILDING_TOOLS_START: + case WID_TN_MUSIC_SOUND: + hor->Add(new NWidgetSpacer(0, 0)); + break; } hor->Add(new NWidgetLeaf(i == WID_TN_SAVE ? WWT_IMGBTN_2 : WWT_IMGBTN, COLOUR_GREY, i, toolbar_button_sprites[i], STR_TOOLBAR_TOOLTIP_PAUSE_GAME + i)); } diff --git a/src/train.h b/src/train.h index 562b704801..7aa39034a5 100644 --- a/src/train.h +++ b/src/train.h @@ -114,7 +114,7 @@ struct Train FINAL : public GroundVehicle { ExpensesType GetExpenseType(bool income) const { return income ? EXPENSES_TRAIN_INC : EXPENSES_TRAIN_RUN; } void PlayLeaveStationSound() const; bool IsPrimaryVehicle() const { return this->IsFrontEngine(); } - SpriteID GetImage(Direction direction, EngineImageType image_type) const; + void GetImage(Direction direction, EngineImageType image_type, VehicleSpriteSeq *result) const; int GetDisplaySpeed() const { return this->gcache.last_speed; } int GetDisplayMaxSpeed() const { return this->vcache.cached_max_speed; } Money GetRunningCost() const; diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index b0c693cd31..4832083e7c 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -485,41 +485,40 @@ static SpriteID GetDefaultTrainSprite(uint8 spritenum, Direction direction) * @param image_type Visualisation context. * @return Sprite to display. */ -SpriteID Train::GetImage(Direction direction, EngineImageType image_type) const +void Train::GetImage(Direction direction, EngineImageType image_type, VehicleSpriteSeq *result) const { uint8 spritenum = this->spritenum; - SpriteID sprite; if (HasBit(this->flags, VRF_REVERSE_DIRECTION)) direction = ReverseDir(direction); if (is_custom_sprite(spritenum)) { - sprite = GetCustomVehicleSprite(this, (Direction)(direction + 4 * IS_CUSTOM_SECONDHEAD_SPRITE(spritenum)), image_type); - if (sprite != 0) return sprite; + GetCustomVehicleSprite(this, (Direction)(direction + 4 * IS_CUSTOM_SECONDHEAD_SPRITE(spritenum)), image_type, result); + if (result->IsValid()) return; spritenum = this->GetEngine()->original_image_index; } assert(IsValidImageIndex(spritenum)); - sprite = GetDefaultTrainSprite(spritenum, direction); + SpriteID sprite = GetDefaultTrainSprite(spritenum, direction); if (this->cargo.StoredCount() >= this->cargo_cap / 2U) sprite += _wagon_full_adder[spritenum]; - return sprite; + result->Set(sprite); } -static SpriteID GetRailIcon(EngineID engine, bool rear_head, int &y, EngineImageType image_type) +static void GetRailIcon(EngineID engine, bool rear_head, int &y, EngineImageType image_type, VehicleSpriteSeq *result) { const Engine *e = Engine::Get(engine); Direction dir = rear_head ? DIR_E : DIR_W; uint8 spritenum = e->u.rail.image_index; if (is_custom_sprite(spritenum)) { - SpriteID sprite = GetCustomVehicleIcon(engine, dir, image_type); - if (sprite != 0) { + GetCustomVehicleIcon(engine, dir, image_type, result); + if (result->IsValid()) { if (e->GetGRF() != NULL) { y += ScaleGUITrad(e->GetGRF()->traininfo_vehicle_pitch); } - return sprite; + return; } spritenum = Engine::Get(engine)->original_image_index; @@ -527,7 +526,7 @@ static SpriteID GetRailIcon(EngineID engine, bool rear_head, int &y, EngineImage if (rear_head) spritenum++; - return GetDefaultTrainSprite(spritenum, DIR_W); + result->Set(GetDefaultTrainSprite(spritenum, DIR_W)); } void DrawTrainEngine(int left, int right, int preferred_x, int y, EngineID engine, PaletteID pal, EngineImageType image_type) @@ -536,24 +535,31 @@ void DrawTrainEngine(int left, int right, int preferred_x, int y, EngineID engin int yf = y; int yr = y; - SpriteID spritef = GetRailIcon(engine, false, yf, image_type); - SpriteID spriter = GetRailIcon(engine, true, yr, image_type); - const Sprite *real_spritef = GetSprite(spritef, ST_NORMAL); - const Sprite *real_spriter = GetSprite(spriter, ST_NORMAL); + VehicleSpriteSeq seqf, seqr; + GetRailIcon(engine, false, yf, image_type, &seqf); + GetRailIcon(engine, true, yr, image_type, &seqr); + + Rect rectf, rectr; + seqf.GetBounds(&rectf); + seqr.GetBounds(&rectr); preferred_x = Clamp(preferred_x, - left - UnScaleGUI(real_spritef->x_offs) + ScaleGUITrad(14), - right - UnScaleGUI(real_spriter->width) - UnScaleGUI(real_spriter->x_offs) - ScaleGUITrad(15)); + left - UnScaleGUI(rectf.left) + ScaleGUITrad(14), + right - UnScaleGUI(rectr.right) - ScaleGUITrad(15)); - DrawSprite(spritef, pal, preferred_x - ScaleGUITrad(14), yf); - DrawSprite(spriter, pal, preferred_x + ScaleGUITrad(15), yr); + seqf.Draw(preferred_x - ScaleGUITrad(14), yf, pal, pal == PALETTE_CRASH); + seqr.Draw(preferred_x + ScaleGUITrad(15), yr, pal, pal == PALETTE_CRASH); } else { - SpriteID sprite = GetRailIcon(engine, false, y, image_type); - const Sprite *real_sprite = GetSprite(sprite, ST_NORMAL); + VehicleSpriteSeq seq; + GetRailIcon(engine, false, y, image_type, &seq); + + Rect rect; + seq.GetBounds(&rect); preferred_x = Clamp(preferred_x, - left - UnScaleGUI(real_sprite->x_offs), - right - UnScaleGUI(real_sprite->width) - UnScaleGUI(real_sprite->x_offs)); - DrawSprite(sprite, pal, preferred_x, y); + left - UnScaleGUI(rect.left), + right - UnScaleGUI(rect.right)); + + seq.Draw(preferred_x, y, pal, pal == PALETTE_CRASH); } } @@ -570,23 +576,26 @@ void GetTrainSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, { int y = 0; - SpriteID sprite = GetRailIcon(engine, false, y, image_type); - const Sprite *real_sprite = GetSprite(sprite, ST_NORMAL); + VehicleSpriteSeq seq; + GetRailIcon(engine, false, y, image_type, &seq); - width = UnScaleGUI(real_sprite->width); - height = UnScaleGUI(real_sprite->height); - xoffs = UnScaleGUI(real_sprite->x_offs); - yoffs = UnScaleGUI(real_sprite->y_offs); + Rect rect; + seq.GetBounds(&rect); + + width = UnScaleGUI(rect.right - rect.left + 1); + height = UnScaleGUI(rect.bottom - rect.top + 1); + xoffs = UnScaleGUI(rect.left); + yoffs = UnScaleGUI(rect.top); if (RailVehInfo(engine)->railveh_type == RAILVEH_MULTIHEAD) { - sprite = GetRailIcon(engine, true, y, image_type); - real_sprite = GetSprite(sprite, ST_NORMAL); + GetRailIcon(engine, true, y, image_type, &seq); + seq.GetBounds(&rect); /* Calculate values relative to an imaginary center between the two sprites. */ - width = ScaleGUITrad(TRAININFO_DEFAULT_VEHICLE_WIDTH) + UnScaleGUI(real_sprite->width) + UnScaleGUI(real_sprite->x_offs) - xoffs; - height = max(height, UnScaleGUI(real_sprite->height)); + width = ScaleGUITrad(TRAININFO_DEFAULT_VEHICLE_WIDTH) + UnScaleGUI(rect.right) - xoffs; + height = max(height, UnScaleGUI(rect.bottom - rect.top + 1)); xoffs = xoffs - ScaleGUITrad(TRAININFO_DEFAULT_VEHICLE_WIDTH) / 2; - yoffs = min(yoffs, UnScaleGUI(real_sprite->y_offs)); + yoffs = min(yoffs, UnScaleGUI(rect.top)); } } @@ -641,7 +650,7 @@ static CommandCost CmdBuildRailWagon(TileIndex tile, DoCommandFlag flags, const v->date_of_last_service = _date; v->build_year = _cur_year; - v->cur_image = SPR_IMG_QUERY; + v->sprite_seq.Set(SPR_IMG_QUERY); v->random_bits = VehicleRandomBits(); v->group_id = DEFAULT_GROUP; @@ -709,7 +718,7 @@ static void AddRearEngineToMultiheadedTrain(Train *v) u->engine_type = v->engine_type; u->date_of_last_service = v->date_of_last_service; u->build_year = v->build_year; - u->cur_image = SPR_IMG_QUERY; + u->sprite_seq.Set(SPR_IMG_QUERY); u->random_bits = VehicleRandomBits(); v->SetMultiheaded(); u->SetMultiheaded(); @@ -775,7 +784,7 @@ CommandCost CmdBuildRailVehicle(TileIndex tile, DoCommandFlag flags, const Engin v->SetServiceInterval(Company::Get(_current_company)->settings.vehicle.servint_trains); v->date_of_last_service = _date; v->build_year = _cur_year; - v->cur_image = SPR_IMG_QUERY; + v->sprite_seq.Set(SPR_IMG_QUERY); v->random_bits = VehicleRandomBits(); if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE); @@ -3537,14 +3546,14 @@ static void ChangeTrainDirRandomly(Train *v) /* We don't need to twist around vehicles if they're not visible */ if (!(v->vehstatus & VS_HIDDEN)) { v->direction = ChangeDir(v->direction, delta[GB(Random(), 0, 2)]); - v->UpdateDeltaXY(v->direction); - v->cur_image = v->GetImage(v->direction, EIT_ON_MAP); /* Refrain from updating the z position of the vehicle when on * a bridge, because UpdateInclination() will put the vehicle under * the bridge in that case */ if (v->track != TRACK_BIT_WORMHOLE) { v->UpdatePosition(); - v->UpdateInclination(false, false); + v->UpdateInclination(false, true); + } else { + v->UpdateViewport(false, true); } } } while ((v = v->Next()) != NULL); diff --git a/src/train_gui.cpp b/src/train_gui.cpp index 219d016ec0..513f57fbba 100644 --- a/src/train_gui.cpp +++ b/src/train_gui.cpp @@ -124,7 +124,9 @@ void DrawTrainImage(const Train *v, int left, int right, int y, VehicleID select if (rtl ? px + width > 0 : px - width < max_width) { PaletteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v); - DrawSprite(v->GetImage(dir, image_type), pal, px + (rtl ? -offset.x : offset.x), height / 2 + offset.y); + VehicleSpriteSeq seq; + v->GetImage(dir, image_type, &seq); + seq.Draw(px + (rtl ? -offset.x : offset.x), height / 2 + offset.y, pal, v->vehstatus & VS_CRASHED); } if (!v->IsArticulatedPart()) sel_articulated = false; @@ -383,7 +385,9 @@ void DrawTrainDetails(const Train *v, int left, int right, int y, int vscroll_po pitch = ScaleGUITrad(e->GetGRF()->traininfo_vehicle_pitch); } PaletteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v); - DrawSprite(u->GetImage(dir, EIT_IN_DETAILS), pal, px + (rtl ? -offset.x : offset.x), y - line_height * vscroll_pos + sprite_y_offset + pitch); + VehicleSpriteSeq seq; + u->GetImage(dir, EIT_IN_DETAILS, &seq); + seq.Draw(px + (rtl ? -offset.x : offset.x), y - line_height * vscroll_pos + sprite_y_offset + pitch, pal, v->vehstatus & VS_CRASHED); } px += rtl ? -width : width; dx += width; diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 75b725423c..5774aa7c0f 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -69,6 +69,47 @@ uint16 _returned_mail_refit_capacity; ///< Stores the mail capacity after a refi VehiclePool _vehicle_pool("Vehicle"); INSTANTIATE_POOL_METHODS(Vehicle) + +/** + * Determine shared bounds of all sprites. + * @param [out] bounds Shared bounds. + */ +void VehicleSpriteSeq::GetBounds(Rect *bounds) const +{ + bounds->left = bounds->top = bounds->right = bounds->bottom = 0; + for (uint i = 0; i < this->count; ++i) { + const Sprite *spr = GetSprite(this->seq[i].sprite, ST_NORMAL); + if (i == 0) { + bounds->left = spr->x_offs; + bounds->top = spr->y_offs; + bounds->right = spr->width + spr->x_offs - 1; + bounds->bottom = spr->height + spr->y_offs - 1; + } else { + if (spr->x_offs < bounds->left) bounds->left = spr->x_offs; + if (spr->y_offs < bounds->top) bounds->top = spr->y_offs; + int right = spr->width + spr->x_offs - 1; + int bottom = spr->height + spr->y_offs - 1; + if (right > bounds->right) bounds->right = right; + if (bottom > bounds->bottom) bounds->bottom = bottom; + } + } +} + +/** + * Draw the sprite sequence. + * @param x X position + * @param y Y position + * @param default_pal Vehicle palette + * @param force_pal Whether to ignore individual palettes, and draw everything with \a default_pal. + */ +void VehicleSpriteSeq::Draw(int x, int y, PaletteID default_pal, bool force_pal) const +{ + for (uint i = 0; i < this->count; ++i) { + PaletteID pal = force_pal || !this->seq[i].pal ? default_pal : this->seq[i].pal; + DrawSprite(this->seq[i].sprite, pal, x, y); + } +} + /** * Function to tell if a vehicle needs to be autorenewed * @param *c The vehicle owner @@ -1063,7 +1104,6 @@ void CallVehicleTicks() */ static void DoDrawVehicle(const Vehicle *v) { - SpriteID image = v->cur_image; PaletteID pal = PAL_NONE; if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v); @@ -1078,8 +1118,14 @@ static void DoDrawVehicle(const Vehicle *v) if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return; } - AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs, - v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs); + StartSpriteCombine(); + for (uint i = 0; i < v->sprite_seq.count; ++i) { + PaletteID pal2 = v->sprite_seq.seq[i].pal; + if (!pal2 || (v->vehstatus & VS_CRASHED)) pal2 = pal; + AddSortableSpriteToDraw(v->sprite_seq.seq[i].sprite, pal2, v->x_pos + v->x_offs, v->y_pos + v->y_offs, + v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs); + } + EndSpriteCombine(); } /** @@ -1542,20 +1588,19 @@ void Vehicle::UpdatePosition() */ void Vehicle::UpdateViewport(bool dirty) { - int img = this->cur_image; + Rect new_coord; + this->sprite_seq.GetBounds(&new_coord); + Point pt = RemapCoords(this->x_pos + this->x_offs, this->y_pos + this->y_offs, this->z_pos); - const Sprite *spr = GetSprite(img, ST_NORMAL); + new_coord.left += pt.x; + new_coord.top += pt.y; + new_coord.right += pt.x + 2 * ZOOM_LVL_BASE; + new_coord.bottom += pt.y + 2 * ZOOM_LVL_BASE; - pt.x += spr->x_offs; - pt.y += spr->y_offs; - - UpdateVehicleViewportHash(this, pt.x, pt.y); + UpdateVehicleViewportHash(this, new_coord.left, new_coord.top); Rect old_coord = this->coord; - this->coord.left = pt.x; - this->coord.top = pt.y; - this->coord.right = pt.x + spr->width + 2 * ZOOM_LVL_BASE; - this->coord.bottom = pt.y + spr->height + 2 * ZOOM_LVL_BASE; + this->coord = new_coord; if (dirty) { if (old_coord.left == INVALID_COORD) { diff --git a/src/vehicle_base.h b/src/vehicle_base.h index 3889a33bac..71a0cd1201 100644 --- a/src/vehicle_base.h +++ b/src/vehicle_base.h @@ -129,6 +129,63 @@ struct VehicleCache { byte cached_vis_effect; ///< Visual effect to show (see #VisualEffect) }; +/** Sprite sequence for a vehicle part. */ +struct VehicleSpriteSeq { + PalSpriteID seq[4]; + uint count; + + bool operator==(const VehicleSpriteSeq &other) const + { + return this->count == other.count && MemCmpT(this->seq, other.seq, this->count) == 0; + } + + bool operator!=(const VehicleSpriteSeq &other) const + { + return !this->operator==(other); + } + + /** + * Check whether the sequence contains any sprites. + */ + bool IsValid() const + { + return this->count != 0; + } + + /** + * Clear all information. + */ + void Clear() + { + this->count = 0; + } + + /** + * Assign a single sprite to the sequence. + */ + void Set(SpriteID sprite) + { + this->count = 1; + this->seq[0].sprite = sprite; + this->seq[0].pal = 0; + } + + /** + * Copy data from another sprite sequence, while dropping all recolouring information. + */ + void CopyWithoutPalette(const VehicleSpriteSeq &src) + { + this->count = src.count; + for (uint i = 0; i < src.count; ++i) { + this->seq[i].sprite = src.seq[i].sprite; + this->seq[i].pal = 0; + } + } + + void GetBounds(Rect *bounds) const; + void Draw(int x, int y, PaletteID default_pal, bool force_pal) const; +}; + /** A vehicle pool for a little over 1 million vehicles. */ typedef Pool VehiclePool; extern VehiclePool _vehicle_pool; @@ -223,7 +280,7 @@ public: * 0xff == reserved for another custom sprite */ byte spritenum; - SpriteID cur_image; ///< sprite number for this vehicle + VehicleSpriteSeq sprite_seq; ///< Vehicle appearance. byte x_extent; ///< x-extent of vehicle bounding box byte y_extent; ///< y-extent of vehicle bounding box byte z_extent; ///< z-extent of vehicle bounding box @@ -383,9 +440,9 @@ public: /** * Gets the sprite to show for the given direction * @param direction the direction the vehicle is facing - * @return the sprite for the given vehicle in the given direction + * @param [out] result Vehicle sprite sequence. */ - virtual SpriteID GetImage(Direction direction, EngineImageType image_type) const { return 0; } + virtual void GetImage(Direction direction, EngineImageType image_type, VehicleSpriteSeq *result) const { result->Clear(); } const GRFFile *GetGRF() const; uint32 GetGRFID() const; @@ -944,7 +1001,10 @@ struct SpecializedVehicle : public Vehicle { /** * Set vehicle type correctly */ - inline SpecializedVehicle() : Vehicle(Type) { } + inline SpecializedVehicle() : Vehicle(Type) + { + this->sprite_seq.count = 1; + } /** * Get the first vehicle in the chain @@ -1083,9 +1143,12 @@ struct SpecializedVehicle : public Vehicle { /* Explicitly choose method to call to prevent vtable dereference - * it gives ~3% runtime improvements in games with many vehicles */ if (update_delta) ((T *)this)->T::UpdateDeltaXY(this->direction); - SpriteID old_image = this->cur_image; - this->cur_image = ((T *)this)->T::GetImage(this->direction, EIT_ON_MAP); - if (force_update || this->cur_image != old_image) this->Vehicle::UpdateViewport(true); + VehicleSpriteSeq seq; + ((T *)this)->T::GetImage(this->direction, EIT_ON_MAP, &seq); + if (force_update || this->sprite_seq != seq) { + this->sprite_seq = seq; + this->Vehicle::UpdateViewport(true); + } } }; diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index e6c3a63f65..24c4d7e52c 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -2118,9 +2118,7 @@ struct VehicleDetailsWindow : Window { case WID_VD_MIDDLE_DETAILS: { /* For other vehicles, at the place of the matrix. */ bool rtl = _current_text_dir == TD_RTL; - uint sprite_width = UnScaleGUI( - max(GetSprite(v->GetImage(rtl ? DIR_E : DIR_W, EIT_IN_DETAILS), ST_NORMAL)->width, 70U)) + - WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; + uint sprite_width = GetSingleVehicleWidth(v, EIT_IN_DETAILS) + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; uint text_left = r.left + (rtl ? 0 : sprite_width); uint text_right = r.right - (rtl ? sprite_width : 0); @@ -2390,8 +2388,8 @@ static const uint32 _vehicle_command_translation_table[][4] = { }; /** - * This is the Callback method after the cloning attempt of a vehicle - * @param result the result of the cloning command + * This is the Callback method after attempting to start/stop a vehicle + * @param result the result of the start/stop command * @param tile unused * @param p1 vehicle ID * @param p2 unused @@ -2862,9 +2860,11 @@ int GetSingleVehicleWidth(const Vehicle *v, EngineImageType image_type) default: bool rtl = _current_text_dir == TD_RTL; - SpriteID sprite = v->GetImage(rtl ? DIR_E : DIR_W, image_type); - const Sprite *real_sprite = GetSprite(sprite, ST_NORMAL); - return UnScaleGUI(real_sprite->width); + VehicleSpriteSeq seq; + v->GetImage(rtl ? DIR_E : DIR_W, image_type, &seq); + Rect rec; + seq.GetBounds(&rec); + return UnScaleGUI(rec.right - rec.left + 1); } } @@ -2898,16 +2898,24 @@ void SetMouseCursorVehicle(const Vehicle *v, EngineImageType image_type) _cursor.sprite_count = 0; int total_width = 0; for (; v != NULL; v = v->HasArticulatedPart() ? v->GetNextArticulatedPart() : NULL) { - if (_cursor.sprite_count == lengthof(_cursor.sprite_seq)) break; if (total_width >= 2 * (int)VEHICLEINFO_FULL_VEHICLE_WIDTH) break; - _cursor.sprite_seq[_cursor.sprite_count].sprite = v->GetImage(rtl ? DIR_E : DIR_W, image_type); - _cursor.sprite_seq[_cursor.sprite_count].pal = GetVehiclePalette(v); - _cursor.sprite_pos[_cursor.sprite_count].x = rtl ? -total_width : total_width; - _cursor.sprite_pos[_cursor.sprite_count].y = 0; + PaletteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v); + VehicleSpriteSeq seq; + v->GetImage(rtl ? DIR_E : DIR_W, image_type, &seq); + + if (_cursor.sprite_count + seq.count > lengthof(_cursor.sprite_seq)) break; + + for (uint i = 0; i < seq.count; ++i) { + PaletteID pal2 = (v->vehstatus & VS_CRASHED) || !seq.seq[i].pal ? pal : seq.seq[i].pal; + _cursor.sprite_seq[_cursor.sprite_count].sprite = seq.seq[i].sprite; + _cursor.sprite_seq[_cursor.sprite_count].pal = pal2; + _cursor.sprite_pos[_cursor.sprite_count].x = rtl ? -total_width : total_width; + _cursor.sprite_pos[_cursor.sprite_count].y = 0; + _cursor.sprite_count++; + } total_width += GetSingleVehicleWidth(v, image_type); - _cursor.sprite_count++; } int offs = ((int)VEHICLEINFO_FULL_VEHICLE_WIDTH - total_width) / 2; diff --git a/src/video/cocoa/wnd_quartz.mm b/src/video/cocoa/wnd_quartz.mm index c93a1f6fb3..795a311bc6 100644 --- a/src/video/cocoa/wnd_quartz.mm +++ b/src/video/cocoa/wnd_quartz.mm @@ -110,15 +110,23 @@ static CGColorSpaceRef QZ_GetCorrectColorSpace() static CGColorSpaceRef colorSpace = NULL; if (colorSpace == NULL) { - CMProfileRef sysProfile; - - if (CMGetSystemProfile(&sysProfile) == noErr) { - colorSpace = CGColorSpaceCreateWithPlatformColorSpace(sysProfile); - CMCloseProfile(sysProfile); - } else { - colorSpace = CGColorSpaceCreateDeviceRGB(); +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) + if (MacOSVersionIsAtLeast(10, 5, 0)) { + colorSpace = CGDisplayCopyColorSpace(CGMainDisplayID()); + } else +#endif + { +#if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) && !defined(HAVE_OSX_1011_SDK) + CMProfileRef sysProfile; + if (CMGetSystemProfile(&sysProfile) == noErr) { + colorSpace = CGColorSpaceCreateWithPlatformColorSpace(sysProfile); + CMCloseProfile(sysProfile); + } +#endif } + if (colorSpace == NULL) colorSpace = CGColorSpaceCreateDeviceRGB(); + if (colorSpace == NULL) error("Could not get system colour space. You might need to recalibrate your monitor."); } diff --git a/src/video/dedicated_v.cpp b/src/video/dedicated_v.cpp index bfe853e9a0..5e2be481c9 100644 --- a/src/video/dedicated_v.cpp +++ b/src/video/dedicated_v.cpp @@ -84,6 +84,7 @@ static void DedicatedSignalHandler(int sig) # endif # include # include +# include "../os/windows/win32.h" static HANDLE _hInputReady, _hWaitForInputHandling; static HANDLE _hThread; // Thread to close static char _win_console_thread_buffer[200]; @@ -95,6 +96,8 @@ static void WINAPI CheckForConsoleInput() /* WinCE doesn't support console stuff */ return; #else + SetWin32ThreadName(-1, "ottd:win-console"); + DWORD nb; HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); for (;;) { @@ -142,7 +145,7 @@ static void *_dedicated_video_mem; /* Whether a fork has been done. */ bool _dedicated_forks; -extern bool SafeLoad(const char *filename, int mode, GameMode newgm, Subdirectory subdir, struct LoadFilter *lf = NULL); +extern bool SafeLoad(const char *filename, SaveLoadOperation fop, DetailedFileType dft, GameMode newgm, Subdirectory subdir, struct LoadFilter *lf = NULL); static FVideoDriver_Dedicated iFVideoDriver_Dedicated; @@ -286,7 +289,7 @@ void VideoDriver_Dedicated::MainLoop() _switch_mode = SM_NONE; /* First we need to test if the savegame can be loaded, else we will end up playing the * intro game... */ - if (!SafeLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_NORMAL, BASE_DIR)) { + if (!SafeLoad(_file_to_saveload.name, _file_to_saveload.file_op, _file_to_saveload.detail_ftype, GM_NORMAL, BASE_DIR)) { /* Loading failed, pop out.. */ DEBUG(net, 0, "Loading requested map failed, aborting"); _networking = false; diff --git a/src/video/sdl_v.cpp b/src/video/sdl_v.cpp index ea68c21656..4ee96db778 100644 --- a/src/video/sdl_v.cpp +++ b/src/video/sdl_v.cpp @@ -687,7 +687,7 @@ void VideoDriver_SDL::MainLoop() _draw_mutex->BeginCritical(); _draw_continue = true; - _draw_threaded = ThreadObject::New(&DrawSurfaceToScreenThread, NULL, &_draw_thread); + _draw_threaded = ThreadObject::New(&DrawSurfaceToScreenThread, NULL, &_draw_thread, "ottd:draw-sdl"); /* Free the mutex if we won't be able to use it. */ if (!_draw_threaded) { diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp index 4dcc9eb703..c37ebd7dd6 100644 --- a/src/video/win32_v.cpp +++ b/src/video/win32_v.cpp @@ -1199,7 +1199,7 @@ void VideoDriver_Win32::MainLoop() _draw_threaded = false; } else { _draw_continue = true; - _draw_threaded = ThreadObject::New(&PaintWindowThread, NULL, &_draw_thread); + _draw_threaded = ThreadObject::New(&PaintWindowThread, NULL, &_draw_thread, "ottd:draw-win32"); /* Free the mutex if we won't be able to use it. */ if (!_draw_threaded) { diff --git a/src/widgets/toolbar_widget.h b/src/widgets/toolbar_widget.h index 6ede3ad634..434a238acc 100644 --- a/src/widgets/toolbar_widget.h +++ b/src/widgets/toolbar_widget.h @@ -36,7 +36,8 @@ enum ToolbarNormalWidgets { WID_TN_AIRCRAFTS, ///< Aircraft menu. WID_TN_ZOOM_IN, ///< Zoom in the main viewport. WID_TN_ZOOM_OUT, ///< Zoom out the main viewport. - WID_TN_RAILS, ///< Rail building menu. + WID_TN_BUILDING_TOOLS_START, ///< Helper for the offset of the building tools + WID_TN_RAILS = WID_TN_BUILDING_TOOLS_START, ///< Rail building menu. WID_TN_ROADS, ///< Road building menu. WID_TN_WATER, ///< Water building toolbar. WID_TN_AIR, ///< Airport building toolbar.