diff --git a/bin/data/openttdd.grf b/bin/data/openttdd.grf index eaf075845d..2d4de97e3f 100644 Binary files a/bin/data/openttdd.grf and b/bin/data/openttdd.grf differ diff --git a/bin/data/openttdw.grf b/bin/data/openttdw.grf index c52eb3c278..c8852b4d93 100644 Binary files a/bin/data/openttdw.grf and b/bin/data/openttdw.grf differ diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj index 0877410820..b56616fd26 100644 --- a/projects/openttd_vs80.vcproj +++ b/projects/openttd_vs80.vcproj @@ -987,6 +987,10 @@ RelativePath=".\..\src\core\endian_func.hpp" > + + diff --git a/projects/openttd_vs90.vcproj b/projects/openttd_vs90.vcproj index 1031c2a1ee..20f9d23ccc 100644 --- a/projects/openttd_vs90.vcproj +++ b/projects/openttd_vs90.vcproj @@ -984,6 +984,10 @@ RelativePath=".\..\src\core\endian_func.hpp" > + + diff --git a/source.list b/source.list index 4170e39f2c..bcf382c7b1 100644 --- a/source.list +++ b/source.list @@ -168,6 +168,7 @@ economy_type.h effectvehicle_func.h effectvehicle_base.h core/endian_func.hpp +engine_base.h engine_func.h engine_type.h core/enum_type.hpp diff --git a/src/ai/default/default.cpp b/src/ai/default/default.cpp index 27a86cd65d..3a2d90c943 100644 --- a/src/ai/default/default.cpp +++ b/src/ai/default/default.cpp @@ -137,11 +137,11 @@ static EngineID AiChooseTrainToBuild(RailType railtype, Money money, byte flag, { EngineID best_veh_index = INVALID_ENGINE; byte best_veh_score = 0; - EngineID i; + const Engine *e; - FOR_ALL_ENGINEIDS_OF_TYPE(i, VEH_TRAIN) { - const RailVehicleInfo *rvi = RailVehInfo(i); - const Engine* e = GetEngine(i); + FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) { + EngineID i = e->index; + const RailVehicleInfo *rvi = &e->u.rail; if (!IsCompatibleRail(rvi->railtype, railtype) || rvi->railveh_type == RAILVEH_WAGON || @@ -168,11 +168,11 @@ static EngineID AiChooseRoadVehToBuild(CargoID cargo, Money money, TileIndex til { EngineID best_veh_index = INVALID_ENGINE; int32 best_veh_rating = 0; - EngineID i; + const Engine *e; - FOR_ALL_ENGINEIDS_OF_TYPE(i, VEH_ROAD) { - const RoadVehicleInfo *rvi = RoadVehInfo(i); - const Engine* e = GetEngine(i); + FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) { + EngineID i = e->index; + const RoadVehicleInfo *rvi = &e->u.road; if (!HasBit(e->player_avail, _current_player) || e->reliability < 0x8A3D) { continue; @@ -209,16 +209,17 @@ static EngineID AiChooseAircraftToBuild(Money money, byte forbidden) { EngineID best_veh_index = INVALID_ENGINE; Money best_veh_cost = 0; - EngineID i; + const Engine *e; - FOR_ALL_ENGINEIDS_OF_TYPE(i, VEH_AIRCRAFT) { - const Engine* e = GetEngine(i); + FOR_ALL_ENGINES_OF_TYPE(e, VEH_AIRCRAFT) { + EngineID i = e->index; + const AircraftVehicleInfo *avi = &e->u.air; if (!HasBit(e->player_avail, _current_player) || e->reliability < 0x8A3D) { continue; } - if ((AircraftVehInfo(i)->subtype & forbidden) != 0) continue; + if ((avi->subtype & forbidden) != 0) continue; CommandCost ret = DoCommand(0, i, 0, DC_QUERY_COST, CMD_BUILD_AIRCRAFT); if (CmdSucceeded(ret) && ret.GetCost() <= money && ret.GetCost() >= best_veh_cost) { @@ -2445,14 +2446,14 @@ static StationID AiGetStationIdByDef(TileIndex tile, int id) static EngineID AiFindBestWagon(CargoID cargo, RailType railtype) { EngineID best_veh_index = INVALID_ENGINE; - EngineID i; uint16 best_capacity = 0; uint16 best_speed = 0; uint speed; + const Engine *e; - FOR_ALL_ENGINEIDS_OF_TYPE(i, VEH_TRAIN) { - const RailVehicleInfo *rvi = RailVehInfo(i); - const Engine* e = GetEngine(i); + FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) { + EngineID i = e->index; + const RailVehicleInfo *rvi = &e->u.rail; if (!IsCompatibleRail(rvi->railtype, railtype) || rvi->railveh_type != RAILVEH_WAGON || diff --git a/src/ai/trolly/build.cpp b/src/ai/trolly/build.cpp index b46984d0a7..eb71540929 100644 --- a/src/ai/trolly/build.cpp +++ b/src/ai/trolly/build.cpp @@ -7,6 +7,7 @@ #include "../../command_func.h" #include "trolly.h" #include "../../engine_func.h" +#include "../../engine_base.h" #include "../../variables.h" #include "../../bridge.h" #include "../../vehicle_func.h" @@ -235,12 +236,12 @@ EngineID AiNew_PickVehicle(Player *p) } else { EngineID best_veh_index = INVALID_ENGINE; int32 best_veh_rating = 0; - EngineID i; + const Engine *e; /* Loop through all road vehicles */ - FOR_ALL_ENGINEIDS_OF_TYPE(i, VEH_ROAD) { - const RoadVehicleInfo *rvi = RoadVehInfo(i); - const Engine* e = GetEngine(i); + FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) { + EngineID i = e->index; + const RoadVehicleInfo *rvi = &e->u.road; /* Skip vehicles which can't take our cargo type */ if (rvi->cargo_type != _players_ainew[p->index].cargo && !CanRefitTo(i, _players_ainew[p->index].cargo)) continue; diff --git a/src/ai/trolly/trolly.cpp b/src/ai/trolly/trolly.cpp index 49133386df..2a369cd03c 100644 --- a/src/ai/trolly/trolly.cpp +++ b/src/ai/trolly/trolly.cpp @@ -29,6 +29,7 @@ #include "../../industry.h" #include "../../station_base.h" #include "../../engine_func.h" +#include "../../engine_base.h" #include "../../gui.h" #include "../../depot_base.h" #include "../../vehicle_base.h" diff --git a/src/aircraft.h b/src/aircraft.h index bfb8c3b93e..cef1debd1e 100644 --- a/src/aircraft.h +++ b/src/aircraft.h @@ -9,6 +9,7 @@ #include "station_base.h" #include "vehicle_base.h" #include "engine_func.h" +#include "engine_base.h" /** An aircraft can be one ot those types */ enum AircraftSubType { diff --git a/src/aircraft_cmd.cpp b/src/aircraft_cmd.cpp index c0795b6fbe..197bb24b09 100644 --- a/src/aircraft_cmd.cpp +++ b/src/aircraft_cmd.cpp @@ -168,7 +168,7 @@ SpriteID Aircraft::GetImage(Direction direction) const SpriteID sprite = GetCustomVehicleSprite(this, direction); if (sprite != 0) return sprite; - spritenum = _orig_aircraft_vehicle_info[this->engine_type - AIRCRAFT_ENGINES_INDEX].image_index; + spritenum = GetEngine(this->engine_type)->image_index; } return direction + _aircraft_sprite[spritenum]; @@ -196,7 +196,7 @@ static SpriteID GetAircraftIcon(EngineID engine) SpriteID sprite = GetCustomVehicleIcon(engine, DIR_W); if (sprite != 0) return sprite; - spritenum = _orig_aircraft_vehicle_info[engine - AIRCRAFT_ENGINES_INDEX].image_index; + spritenum = GetEngine(engine)->image_index; } return 6 + _aircraft_sprite[spritenum]; diff --git a/src/articulated_vehicles.cpp b/src/articulated_vehicles.cpp index ef63e71b54..d6e1a97d28 100644 --- a/src/articulated_vehicles.cpp +++ b/src/articulated_vehicles.cpp @@ -11,6 +11,19 @@ #include "newgrf_engine.h" #include "vehicle_func.h" +static EngineID GetNewEngineID(const GRFFile *file, VehicleType type, uint16 internal_id) +{ + const Engine *e = NULL; + FOR_ALL_ENGINES(e) { + if (e->grffile != file) continue; + if (e->type != type) continue; + if (e->internal_id != internal_id) continue; + + return e->index; + } + + return INVALID_ENGINE; +} uint CountArticulatedParts(EngineID engine_type, bool purchase_window) { @@ -54,7 +67,7 @@ uint16 *GetCapacityOfArticulatedParts(EngineID engine, VehicleType type) uint16 callback = GetVehicleCallback(CBID_VEHICLE_ARTIC_ENGINE, i, 0, engine, NULL); if (callback == CALLBACK_FAILED || GB(callback, 0, 8) == 0xFF) break; - EngineID artic_engine = GetFirstEngineOfType(type) + GB(callback, 0, 7); + EngineID artic_engine = GetNewEngineID(GetEngineGRF(engine), type, GB(callback, 0, 7)); if (type == VEH_TRAIN) { const RailVehicleInfo *rvi = RailVehInfo(artic_engine); @@ -88,7 +101,7 @@ void AddArticulatedParts(Vehicle **vl, VehicleType type) Vehicle *previous = u; u = u->Next(); - EngineID engine_type = GetFirstEngineOfType(type) + GB(callback, 0, 7); + EngineID engine_type = GetNewEngineID(GetEngineGRF(v->engine_type), type, GB(callback, 0, 7)); bool flip_image = HasBit(callback, 7); /* get common values from first engine */ diff --git a/src/autoreplace_gui.cpp b/src/autoreplace_gui.cpp index 7dc37ab3b6..a980d59c0c 100644 --- a/src/autoreplace_gui.cpp +++ b/src/autoreplace_gui.cpp @@ -20,6 +20,7 @@ #include "player_func.h" #include "widgets/dropdown_func.h" #include "engine_func.h" +#include "engine_base.h" #include "table/sprites.h" #include "table/strings.h" @@ -198,8 +199,9 @@ static void GenerateReplaceVehList(Window *w, bool draw_left) EngineList *list = &WP(w, replaceveh_d).list[i]; EngList_RemoveAll(list); - EngineID eid; - FOR_ALL_ENGINEIDS_OF_TYPE(eid, type) { + const Engine *e; + FOR_ALL_ENGINES_OF_TYPE(e, type) { + EngineID eid = e->index; if (type == VEH_TRAIN && !GenerateReplaceRailList(eid, draw_left, WP(w, replaceveh_d).wagon_btnstate)) continue; // special rules for trains if (draw_left) { @@ -214,7 +216,7 @@ static void GenerateReplaceVehList(Window *w, bool draw_left) if (!EnginesGotCargoInCommon(eid, WP(w, replaceveh_d).sel_engine[0])) continue; // the engines needs to be able to carry the same cargo /* Road vehicles can't be replaced by trams and vice-versa */ - if (type == VEH_ROAD && HasBit(EngInfo(WP(w, replaceveh_d).sel_engine[0])->misc_flags, EF_ROAD_TRAM) != HasBit(EngInfo(eid)->misc_flags, EF_ROAD_TRAM)) continue; + if (type == VEH_ROAD && HasBit(EngInfo(WP(w, replaceveh_d).sel_engine[0])->misc_flags, EF_ROAD_TRAM) != HasBit(e->info.misc_flags, EF_ROAD_TRAM)) continue; if (eid == WP(w, replaceveh_d).sel_engine[0]) continue; // we can't replace an engine into itself (that would be autorenew) } diff --git a/src/build_vehicle_gui.cpp b/src/build_vehicle_gui.cpp index 5174a949e1..3170377236 100644 --- a/src/build_vehicle_gui.cpp +++ b/src/build_vehicle_gui.cpp @@ -800,9 +800,10 @@ static void GenerateBuildTrainList(Window *w) * Also check to see if the previously selected engine is still available, * and if not, reset selection to INVALID_ENGINE. This could be the case * when engines become obsolete and are removed */ - EngineID eid; - FOR_ALL_ENGINEIDS_OF_TYPE(eid, VEH_TRAIN) { - const RailVehicleInfo *rvi = RailVehInfo(eid); + const Engine *e; + FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) { + EngineID eid = e->index; + const RailVehicleInfo *rvi = &e->u.rail; if (bv->filter.railtype != RAILTYPE_END && !HasPowerOnRail(rvi->railtype, bv->filter.railtype)) continue; if (!IsEngineBuildable(eid, VEH_TRAIN, _local_player)) continue; @@ -839,8 +840,9 @@ static void GenerateBuildRoadVehList(Window *w) EngList_RemoveAll(&bv->eng_list); - EngineID eid; - FOR_ALL_ENGINEIDS_OF_TYPE(eid, VEH_ROAD) { + const Engine *e; + FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) { + EngineID eid = e->index; if (!IsEngineBuildable(eid, VEH_ROAD, _local_player)) continue; if (!HasBit(bv->filter.roadtypes, HasBit(EngInfo(eid)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD)) continue; EngList_Add(&bv->eng_list, eid); @@ -858,8 +860,9 @@ static void GenerateBuildShipList(Window *w) EngList_RemoveAll(&bv->eng_list); - EngineID eid; - FOR_ALL_ENGINEIDS_OF_TYPE(eid, VEH_SHIP) { + const Engine *e; + FOR_ALL_ENGINES_OF_TYPE(e, VEH_SHIP) { + EngineID eid = e->index; if (!IsEngineBuildable(eid, VEH_SHIP, _local_player)) continue; EngList_Add(&bv->eng_list, eid); @@ -880,8 +883,9 @@ static void GenerateBuildAircraftList(Window *w) * Also check to see if the previously selected plane is still available, * and if not, reset selection to INVALID_ENGINE. This could be the case * when planes become obsolete and are removed */ - EngineID eid; - FOR_ALL_ENGINEIDS_OF_TYPE(eid, VEH_AIRCRAFT) { + const Engine *e; + FOR_ALL_ENGINES_OF_TYPE(e, VEH_AIRCRAFT) { + EngineID eid = e->index; if (!IsEngineBuildable(eid, VEH_AIRCRAFT, _local_player)) continue; /* First VEH_END window_numbers are fake to allow a window open for all different types at once */ if (w->window_number > VEH_END && !CanAircraftUseStation(eid, w->window_number)) continue; diff --git a/src/depot_gui.cpp b/src/depot_gui.cpp index e88fb6de8c..7a859b1bc3 100644 --- a/src/depot_gui.cpp +++ b/src/depot_gui.cpp @@ -656,8 +656,9 @@ static void ResizeDefaultWindowSize(VehicleType type) uint max_width = 0; uint max_height = 0; - EngineID eid; - FOR_ALL_ENGINEIDS_OF_TYPE(eid, type) { + const Engine *e; + FOR_ALL_ENGINES_OF_TYPE(e, type) { + EngineID eid = e->index; uint x, y; switch (type) { diff --git a/src/elrail.cpp b/src/elrail.cpp index c999c4375c..6e776ea0ac 100644 --- a/src/elrail.cpp +++ b/src/elrail.cpp @@ -66,6 +66,7 @@ #include "player_base.h" #include "tunnelbridge.h" #include "engine_func.h" +#include "engine_base.h" #include "table/sprites.h" #include "table/elrail_data.h" @@ -480,9 +481,9 @@ int32 SettingsDisableElrail(int32 p1) const RailType new_railtype = disable ? RAILTYPE_RAIL : RAILTYPE_ELECTRIC; /* walk through all train engines */ - EngineID eid; - FOR_ALL_ENGINEIDS_OF_TYPE(eid, VEH_TRAIN) { - RailVehicleInfo *rv_info = &_rail_vehicle_info[eid]; + Engine *e; + FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) { + RailVehicleInfo *rv_info = &e->u.rail; /* if it is an electric rail engine and its railtype is the wrong one */ if (rv_info->engclass == 2 && rv_info->railtype == old_railtype) { /* change it to the proper one */ diff --git a/src/engine.cpp b/src/engine.cpp index 3bc0657c29..c721e31a1d 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -14,6 +14,7 @@ #include "train.h" #include "aircraft.h" #include "newgrf_cargo.h" +#include "newgrf_engine.h" #include "group.h" #include "strings_func.h" #include "gfx_func.h" @@ -25,43 +26,103 @@ #include "string_func.h" #include "settings_type.h" #include "oldpool_func.h" +#include "core/alloc_func.hpp" +#include "map" #include "table/strings.h" #include "table/engines.h" -Engine _engines[TOTAL_NUM_ENGINES]; -EngineInfo _engine_info[TOTAL_NUM_ENGINES]; -RailVehicleInfo _rail_vehicle_info[NUM_TRAIN_ENGINES]; -ShipVehicleInfo _ship_vehicle_info[NUM_SHIP_ENGINES]; -AircraftVehicleInfo _aircraft_vehicle_info[NUM_AIRCRAFT_ENGINES]; -RoadVehicleInfo _road_vehicle_info[NUM_ROAD_ENGINES]; +DEFINE_OLD_POOL_GENERIC(Engine, Engine) enum { YEAR_ENGINE_AGING_STOPS = 2050, }; +/** Number of engines of each vehicle type in original engine data */ +const uint8 _engine_counts[4] = { + lengthof(_orig_rail_vehicle_info), + lengthof(_orig_road_vehicle_info), + lengthof(_orig_ship_vehicle_info), + lengthof(_orig_aircraft_vehicle_info), +}; + +/** Offset of the first engine of each vehicle type in original engine data */ +const uint8 _engine_offsets[4] = { + 0, + lengthof(_orig_rail_vehicle_info), + lengthof(_orig_rail_vehicle_info) + lengthof(_orig_road_vehicle_info), + lengthof(_orig_rail_vehicle_info) + lengthof(_orig_road_vehicle_info) + lengthof(_orig_ship_vehicle_info), +}; + +Engine::Engine() : + name(NULL), + overrides_count(0), + overrides(NULL) +{ +} + +Engine::Engine(VehicleType type, EngineID base) +{ + this->type = type; + this->internal_id = base; + this->list_position = base; + + /* Check if this base engine is within the original engine data range */ + if (base >= _engine_counts[type]) { + /* Mark engine as valid anyway */ + this->info.climates = 0x80; + return; + } + + /* Copy the original engine info for this slot */ + this->info = _orig_engine_info[_engine_offsets[type] + base]; + + /* Copy the original engine data for this slot */ + switch (type) { + default: NOT_REACHED(); + + case VEH_TRAIN: + this->u.rail = _orig_rail_vehicle_info[base]; + this->image_index = this->u.rail.image_index; + this->info.string_id = STR_8000_KIRBY_PAUL_TANK_STEAM + base; + break; + + case VEH_ROAD: + this->u.road = _orig_road_vehicle_info[base]; + this->image_index = this->u.road.image_index; + this->info.string_id = STR_8074_MPS_REGAL_BUS + base; + break; + + case VEH_SHIP: + this->u.ship = _orig_ship_vehicle_info[base]; + this->image_index = this->u.ship.image_index; + this->info.string_id = STR_80CC_MPS_OIL_TANKER + base; + break; + + case VEH_AIRCRAFT: + this->u.air = _orig_aircraft_vehicle_info[base]; + this->image_index = this->u.air.image_index; + this->info.string_id = STR_80D7_SAMPSON_U52 + base; + break; + } +} + +Engine::~Engine() +{ + UnloadWagonOverrides(this); + free(this->name); +} + void SetupEngines() { - /* Copy original static engine data */ - memcpy(&_engine_info, &_orig_engine_info, sizeof(_orig_engine_info)); - memcpy(&_rail_vehicle_info, &_orig_rail_vehicle_info, sizeof(_orig_rail_vehicle_info)); - memcpy(&_ship_vehicle_info, &_orig_ship_vehicle_info, sizeof(_orig_ship_vehicle_info)); - memcpy(&_aircraft_vehicle_info, &_orig_aircraft_vehicle_info, sizeof(_orig_aircraft_vehicle_info)); - memcpy(&_road_vehicle_info, &_orig_road_vehicle_info, sizeof(_orig_road_vehicle_info)); + _Engine_pool.CleanPool(); + _Engine_pool.AddBlockToPool(); - /* Add type to engines */ - Engine* e = _engines; - do e->type = VEH_TRAIN; while (++e < &_engines[ROAD_ENGINES_INDEX]); - do e->type = VEH_ROAD; while (++e < &_engines[SHIP_ENGINES_INDEX]); - do e->type = VEH_SHIP; while (++e < &_engines[AIRCRAFT_ENGINES_INDEX]); - do e->type = VEH_AIRCRAFT; while (++e < &_engines[TOTAL_NUM_ENGINES]); - - /* Set up default engine names */ - for (EngineID engine = 0; engine < TOTAL_NUM_ENGINES; engine++) { - EngineInfo *ei = &_engine_info[engine]; - ei->string_id = STR_8000_KIRBY_PAUL_TANK_STEAM + engine; - } + for (uint i = 0; i < lengthof(_orig_rail_vehicle_info); i++) new Engine(VEH_TRAIN, i); + for (uint i = 0; i < lengthof(_orig_road_vehicle_info); i++) new Engine(VEH_ROAD, i); + for (uint i = 0; i < lengthof(_orig_ship_vehicle_info); i++) new Engine(VEH_SHIP, i); + for (uint i = 0; i < lengthof(_orig_aircraft_vehicle_info); i++) new Engine(VEH_AIRCRAFT, i); } @@ -90,7 +151,7 @@ static void CalcEngineReliability(Engine *e) /* Check for early retirement */ if (e->player_avail != 0 && !_patches.never_expire_vehicles) { - int retire_early = EngInfo(e - _engines)->retire_early; + int retire_early = e->info.retire_early; uint retire_early_max_age = max(0, e->duration_phase_1 + e->duration_phase_2 - retire_early * 12); if (retire_early != 0 && age >= retire_early_max_age) { /* Early retirement is enabled and we're past the date... */ @@ -124,11 +185,11 @@ static void CalcEngineReliability(Engine *e) void StartupEngines() { Engine *e; - const EngineInfo *ei; /* Aging of vehicles stops, so account for that when starting late */ const Date aging_date = min(_date, ConvertYMDToDate(YEAR_ENGINE_AGING_STOPS, 0, 1)); - for (e = _engines, ei = _engine_info; e != endof(_engines); e++, ei++) { + FOR_ALL_ENGINES(e) { + const EngineInfo *ei = &e->info; uint32 r; e->age = 0; @@ -224,13 +285,11 @@ static PlayerID GetBestPlayer(uint8 pp) void EnginesDailyLoop() { - EngineID i; - if (_cur_year >= YEAR_ENGINE_AGING_STOPS) return; - for (i = 0; i != lengthof(_engines); i++) { - Engine *e = &_engines[i]; - + Engine *e; + FOR_ALL_ENGINES(e) { + EngineID i = e->index; if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) { if (e->flags & ENGINE_OFFER_WINDOW_OPEN) { if (e->preview_player_rank != 0xFF && !--e->preview_wait) { @@ -282,14 +341,15 @@ CommandCost CmdWantEnginePreview(TileIndex tile, uint32 flags, uint32 p1, uint32 /* Determine if an engine type is a wagon (and not a loco) */ static bool IsWagon(EngineID index) { - return index < NUM_TRAIN_ENGINES && RailVehInfo(index)->railveh_type == RAILVEH_WAGON; + const Engine *e = GetEngine(index); + return e->type == VEH_TRAIN && e->u.rail.railveh_type == RAILVEH_WAGON; } static void NewVehicleAvailable(Engine *e) { Vehicle *v; Player *p; - EngineID index = e - _engines; + EngineID index = e->index; /* In case the player didn't build the vehicle during the intro period, * prevent that player from getting future intro periods for a while. */ @@ -326,7 +386,7 @@ static void NewVehicleAvailable(Engine *e) if (e->type == VEH_TRAIN) { /* maybe make another rail type available */ - RailType railtype = RailVehInfo(index)->railtype; + RailType railtype = e->u.rail.railtype; assert(railtype < RAILTYPE_END); FOR_ALL_PLAYERS(p) { if (p->is_active) SetBit(p->avail_railtypes, railtype); @@ -334,7 +394,7 @@ static void NewVehicleAvailable(Engine *e) } else if (e->type == VEH_ROAD) { /* maybe make another road type available */ FOR_ALL_PLAYERS(p) { - if (p->is_active) SetBit(p->avail_roadtypes, HasBit(EngInfo(index)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD); + if (p->is_active) SetBit(p->avail_roadtypes, HasBit(e->info.misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD); } } AddNewsItem(index, NM_CALLBACK, NF_NONE, NT_NEW_VEHICLES, DNC_VEHICLEAVAIL, 0, 0); @@ -359,7 +419,7 @@ void EnginesMonthlyLoop() e->flags |= ENGINE_EXCLUSIVE_PREVIEW; /* Do not introduce new rail wagons */ - if (!IsWagon(e - _engines)) + if (!IsWagon(e->index)) e->preview_player_rank = 1; // Give to the player with the highest rating. } } @@ -370,8 +430,9 @@ static bool IsUniqueEngineName(const char *name) { char buf[512]; - for (EngineID i = 0; i < TOTAL_NUM_ENGINES; i++) { - SetDParam(0, i); + const Engine *e; + FOR_ALL_ENGINES(e) { + SetDParam(0, e->index); GetString(buf, STR_ENGINE_NAME, lastof(buf)); if (strcmp(buf, name) == 0) return false; } @@ -620,32 +681,77 @@ static const SaveLoad _engine_desc[] = { SLE_END() }; +static std::map _temp_engine; + +Engine *GetTempDataEngine(EngineID index) +{ + return &_temp_engine[index]; +} + static void Save_ENGN() { - uint i; - - for (i = 0; i != lengthof(_engines); i++) { - SlSetArrayIndex(i); - SlObject(&_engines[i], _engine_desc); + Engine *e; + FOR_ALL_ENGINES(e) { + SlSetArrayIndex(e->index); + SlObject(e, _engine_desc); } } static void Load_ENGN() { + /* As engine data is loaded before engines are initialized we need to load + * this information into a temporary array. This is then copied into the + * engine pool after processing NewGRFs by CopyTempEngineData(). */ int index; while ((index = SlIterateArray()) != -1) { - SlObject(GetEngine(index), _engine_desc); + Engine *e = GetTempDataEngine(index); + SlObject(e, _engine_desc); } } +/** + * Copy data from temporary engine array into the real engine pool. + */ +void CopyTempEngineData() +{ + Engine *e; + FOR_ALL_ENGINES(e) { + if (e->index >= _temp_engine.size()) break; + + const Engine *se = GetTempDataEngine(e->index); + e->intro_date = se->intro_date; + e->age = se->age; + e->reliability = se->reliability; + e->reliability_spd_dec = se->reliability_spd_dec; + e->reliability_start = se->reliability_start; + e->reliability_max = se->reliability_max; + e->reliability_final = se->reliability_final; + e->duration_phase_1 = se->duration_phase_1; + e->duration_phase_2 = se->duration_phase_2; + e->duration_phase_3 = se->duration_phase_3; + e->lifelength = se->lifelength; + e->flags = se->flags; + e->preview_player_rank = se->preview_player_rank; + e->preview_wait = se->preview_wait; + e->player_avail = se->player_avail; + if (se->name != NULL) e->name = strdup(se->name); + } + + /* Get rid of temporary data */ + _temp_engine.clear(); +} + static void Load_ENGS() { - StringID names[TOTAL_NUM_ENGINES]; + /* Load old separate String ID list into a temporary array. This + * was always 256 entries. */ + StringID names[256]; SlArray(names, lengthof(names), SLE_STRINGID); + /* Copy each string into the temporary engine array. */ for (EngineID engine = 0; engine < lengthof(names); engine++) { - Engine *e = GetEngine(engine); + Engine *e = GetTempDataEngine(engine); e->name = CopyFromOldName(names[engine]); } } @@ -661,10 +767,4 @@ void InitializeEngines() /* Clean the engine renew pool and create 1 block in it */ _EngineRenew_pool.CleanPool(); _EngineRenew_pool.AddBlockToPool(); - - Engine *e; - FOR_ALL_ENGINES(e) { - free(e->name); - e->name = NULL; - } } diff --git a/src/engine_base.h b/src/engine_base.h new file mode 100644 index 0000000000..cff59fcb20 --- /dev/null +++ b/src/engine_base.h @@ -0,0 +1,88 @@ +/* $Id$ */ + +/** @file engine_base.h Base class for engines. */ + +#ifndef ENGINE_BASE_H +#define ENGINE_BASE_H + +#include "engine_type.h" +#include "oldpool.h" + +DECLARE_OLD_POOL(Engine, Engine, 6, 10000) + +struct Engine : PoolItem { + char *name; ///< Custom name of engine + Date intro_date; + Date age; + uint16 reliability; + uint16 reliability_spd_dec; + uint16 reliability_start, reliability_max, reliability_final; + uint16 duration_phase_1, duration_phase_2, duration_phase_3; + byte lifelength; + byte flags; + uint8 preview_player_rank; + byte preview_wait; + byte player_avail; + uint8 image_index; ///< Original vehicle image index + VehicleType type; ///< type, ie VEH_ROAD, VEH_TRAIN, etc. + + EngineInfo info; + + union { + RailVehicleInfo rail; + RoadVehicleInfo road; + ShipVehicleInfo ship; + AircraftVehicleInfo air; + } u; + + /* NewGRF related data */ + const struct GRFFile *grffile; + const struct SpriteGroup *group[NUM_CARGO + 2]; + uint16 internal_id; ///< ID within the GRF file + uint16 overrides_count; + struct WagonOverride *overrides; + uint16 list_position; + + Engine(); + Engine(VehicleType type, EngineID base); + ~Engine(); + + inline bool IsValid() const { return this->info.climates != 0; } +}; + +static inline bool IsEngineIndex(uint index) +{ + return index < GetEnginePoolSize(); +} + +#define FOR_ALL_ENGINES_FROM(e, start) for (e = GetEngine(start); e != NULL; e = (e->index + 1U < GetEnginePoolSize()) ? GetEngine(e->index + 1U) : NULL) if (e->IsValid()) +#define FOR_ALL_ENGINES(e) FOR_ALL_ENGINES_FROM(e, 0) + +#define FOR_ALL_ENGINES_OF_TYPE(e, engine_type) FOR_ALL_ENGINES(e) if (e->type == engine_type) + +static inline const EngineInfo *EngInfo(EngineID e) +{ + return &GetEngine(e)->info; +} + +static inline const RailVehicleInfo *RailVehInfo(EngineID e) +{ + return &GetEngine(e)->u.rail; +} + +static inline const RoadVehicleInfo *RoadVehInfo(EngineID e) +{ + return &GetEngine(e)->u.road; +} + +static inline const ShipVehicleInfo *ShipVehInfo(EngineID e) +{ + return &GetEngine(e)->u.ship; +} + +static inline const AircraftVehicleInfo *AircraftVehInfo(EngineID e) +{ + return &GetEngine(e)->u.air; +} + +#endif /* ENGINE_TYPE_H */ diff --git a/src/engine_func.h b/src/engine_func.h index 387cf55523..a5cc164539 100644 --- a/src/engine_func.h +++ b/src/engine_func.h @@ -10,6 +10,12 @@ void SetupEngines(); void StartupEngines(); +Engine *GetTempDataEngine(EngineID index); +void CopyTempEngineData(); + +/* Original engine data counts and offsets */ +extern const uint8 _engine_counts[4]; +extern const uint8 _engine_offsets[4]; void DrawTrainEngine(int x, int y, EngineID engine, SpriteID pal); void DrawRoadVehEngine(int x, int y, EngineID engine, SpriteID pal); @@ -22,83 +28,6 @@ void DeleteCustomEngineNames(); bool IsEngineBuildable(EngineID engine, VehicleType type, PlayerID player); CargoID GetEngineCargoType(EngineID engine); -static inline EngineID GetFirstEngineOfType(VehicleType type) -{ - const EngineID start[] = {0, ROAD_ENGINES_INDEX, SHIP_ENGINES_INDEX, AIRCRAFT_ENGINES_INDEX}; - - return start[type]; -} - -static inline EngineID GetLastEngineOfType(VehicleType type) -{ - const EngineID end[] = { - NUM_TRAIN_ENGINES, - ROAD_ENGINES_INDEX + NUM_ROAD_ENGINES, - SHIP_ENGINES_INDEX + NUM_SHIP_ENGINES, - AIRCRAFT_ENGINES_INDEX + NUM_AIRCRAFT_ENGINES}; - - return end[type]; -} - -extern Engine _engines[TOTAL_NUM_ENGINES]; -#define FOR_ALL_ENGINES(e) for (e = _engines; e != endof(_engines); e++) -#define FOR_ALL_ENGINEIDS_OF_TYPE(e, type) for (e = GetFirstEngineOfType(type); e != GetLastEngineOfType(type); e++) - - -static inline Engine* GetEngine(EngineID i) -{ - assert(i < lengthof(_engines)); - return &_engines[i]; -} - -static inline bool IsEngineIndex(uint index) -{ - return index < TOTAL_NUM_ENGINES; -} - -/* Access Vehicle Data */ -extern const EngineInfo _orig_engine_info[TOTAL_NUM_ENGINES]; -extern const RailVehicleInfo _orig_rail_vehicle_info[NUM_TRAIN_ENGINES]; -extern const ShipVehicleInfo _orig_ship_vehicle_info[NUM_SHIP_ENGINES]; -extern const AircraftVehicleInfo _orig_aircraft_vehicle_info[NUM_AIRCRAFT_ENGINES]; -extern const RoadVehicleInfo _orig_road_vehicle_info[NUM_ROAD_ENGINES]; - -extern EngineInfo _engine_info[TOTAL_NUM_ENGINES]; -extern RailVehicleInfo _rail_vehicle_info[NUM_TRAIN_ENGINES]; -extern ShipVehicleInfo _ship_vehicle_info[NUM_SHIP_ENGINES]; -extern AircraftVehicleInfo _aircraft_vehicle_info[NUM_AIRCRAFT_ENGINES]; -extern RoadVehicleInfo _road_vehicle_info[NUM_ROAD_ENGINES]; - -static inline const EngineInfo *EngInfo(EngineID e) -{ - assert(e < lengthof(_engine_info)); - return &_engine_info[e]; -} - -static inline const RailVehicleInfo* RailVehInfo(EngineID e) -{ - assert(e < lengthof(_rail_vehicle_info)); - return &_rail_vehicle_info[e]; -} - -static inline const ShipVehicleInfo* ShipVehInfo(EngineID e) -{ - assert(e >= SHIP_ENGINES_INDEX && e < SHIP_ENGINES_INDEX + lengthof(_ship_vehicle_info)); - return &_ship_vehicle_info[e - SHIP_ENGINES_INDEX]; -} - -static inline const AircraftVehicleInfo* AircraftVehInfo(EngineID e) -{ - assert(e >= AIRCRAFT_ENGINES_INDEX && e < AIRCRAFT_ENGINES_INDEX + lengthof(_aircraft_vehicle_info)); - return &_aircraft_vehicle_info[e - AIRCRAFT_ENGINES_INDEX]; -} - -static inline const RoadVehicleInfo* RoadVehInfo(EngineID e) -{ - assert(e >= ROAD_ENGINES_INDEX && e < ROAD_ENGINES_INDEX + lengthof(_road_vehicle_info)); - return &_road_vehicle_info[e - ROAD_ENGINES_INDEX]; -} - /* Engine list manipulators - current implementation is only C wrapper of CBlobT class (helpers.cpp) */ void EngList_Create(EngineList *el); ///< Creates engine list void EngList_Destroy(EngineList *el); ///< Deallocate and destroy engine list diff --git a/src/engine_gui.cpp b/src/engine_gui.cpp index cf0b83f7f1..9e1d3c80a9 100644 --- a/src/engine_gui.cpp +++ b/src/engine_gui.cpp @@ -8,6 +8,7 @@ #include "window_gui.h" #include "gfx_func.h" #include "engine_func.h" +#include "engine_base.h" #include "command_func.h" #include "economy_func.h" #include "news_func.h" diff --git a/src/engine_type.h b/src/engine_type.h index 6079cdc6af..03bee6fe4f 100644 --- a/src/engine_type.h +++ b/src/engine_type.h @@ -18,6 +18,8 @@ typedef uint16 EngineID; typedef uint16 EngineRenewID; typedef EngineID *EngineList; ///< engine list type placeholder acceptable for C code (see helpers.cpp) +struct Engine; + enum RailVehicleTypes { RAILVEH_SINGLEHEAD, ///< indicates a "standalone" locomotive RAILVEH_MULTIHEAD, ///< indicates a combination of two locomotives @@ -119,22 +121,6 @@ struct EngineInfo { StringID string_id; ///< Default name of engine }; -struct Engine { - char *name; ///< Custom name of engine - Date intro_date; - Date age; - uint16 reliability; - uint16 reliability_spd_dec; - uint16 reliability_start, reliability_max, reliability_final; - uint16 duration_phase_1, duration_phase_2, duration_phase_3; - byte lifelength; - byte flags; - uint8 preview_player_rank; - byte preview_wait; - byte player_avail; - VehicleType type; ///< type, ie VEH_ROAD, VEH_TRAIN, etc. -}; - /** * EngineInfo.misc_flags is a bitmask, with the following values */ @@ -160,18 +146,4 @@ enum { static const EngineID INVALID_ENGINE = 0xFFFF; -enum { - NUM_NORMAL_RAIL_ENGINES = 54, - NUM_MONORAIL_ENGINES = 30, - NUM_MAGLEV_ENGINES = 32, - NUM_TRAIN_ENGINES = NUM_NORMAL_RAIL_ENGINES + NUM_MONORAIL_ENGINES + NUM_MAGLEV_ENGINES, - NUM_ROAD_ENGINES = 88, - NUM_SHIP_ENGINES = 11, - NUM_AIRCRAFT_ENGINES = 41, - TOTAL_NUM_ENGINES = NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES + NUM_SHIP_ENGINES + NUM_AIRCRAFT_ENGINES, - AIRCRAFT_ENGINES_INDEX = NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES + NUM_SHIP_ENGINES, - SHIP_ENGINES_INDEX = NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES, - ROAD_ENGINES_INDEX = NUM_TRAIN_ENGINES, -}; - #endif /* ENGINE_TYPE_H */ diff --git a/src/group.h b/src/group.h index 7bac2b48fa..850a3c5deb 100644 --- a/src/group.h +++ b/src/group.h @@ -21,7 +21,7 @@ struct Group : PoolItem { VehicleTypeByte vehicle_type; ///< Vehicle type of the group bool replace_protection; ///< If set to true, the global autoreplace have no effect on the group - uint16 num_engines[TOTAL_NUM_ENGINES]; ///< Caches the number of engines of each type the player owns (no need to save this) + uint16 *num_engines; ///< Caches the number of engines of each type the player owns (no need to save this) Group(PlayerID owner = INVALID_PLAYER); virtual ~Group(); diff --git a/src/group_cmd.cpp b/src/group_cmd.cpp index 8696b017e7..43ccbe29f5 100644 --- a/src/group_cmd.cpp +++ b/src/group_cmd.cpp @@ -22,6 +22,7 @@ #include "player_func.h" #include "order_func.h" #include "oldpool_func.h" +#include "core/alloc_func.hpp" #include "table/strings.h" @@ -50,12 +51,14 @@ DEFINE_OLD_POOL_GENERIC(Group, Group) Group::Group(PlayerID owner) { this->owner = owner; + this->num_engines = CallocT(GetEnginePoolSize()); } Group::~Group() { free(this->name); this->owner = INVALID_PLAYER; + free(this->num_engines); } bool Group::IsValid() const diff --git a/src/lang/english.txt b/src/lang/english.txt index 972666cc36..8d11fe3e03 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1054,6 +1054,7 @@ STR_CONFIG_PATCHES_FREIGHT_TRAINS :{LTBLUE}Weight STR_CONFIG_PATCHES_PLANE_SPEED :{LTBLUE}Plane speed factor: {ORANGE}1 / {STRING1} STR_CONFIG_PATCHES_STOP_ON_TOWN_ROAD :{LTBLUE}Allow drive-through road stops on town owned roads: {ORANGE}{STRING} STR_CONFIG_PATCHES_ADJACENT_STATIONS :{LTBLUE}Allow building adjacent stations: {ORANGE}{STRING} +STR_CONFIG_PATCHES_DYNAMIC_ENGINES :{LTBLUE}Enable multiple NewGRF engine sets: {ORANGE}{STRING} STR_CONFIG_PATCHES_SMALL_AIRPORTS :{LTBLUE}Always allow small airports: {ORANGE}{STRING1} diff --git a/src/newgrf.cpp b/src/newgrf.cpp index dee8bae67f..0ef3e81d46 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -10,6 +10,7 @@ #include "debug.h" #include "fileio.h" #include "engine_func.h" +#include "engine_base.h" #include "spritecache.h" #include "sprite.h" #include "newgrf.h" @@ -45,6 +46,7 @@ #include "player_base.h" #include "settings_type.h" #include "map_func.h" +#include #include @@ -97,20 +99,6 @@ static GrfDataType _grf_data_type; typedef void (*SpecialSpriteHandler)(byte *buf, int len); -static const uint _vehcounts[4] = { - /* GSF_TRAIN */ NUM_TRAIN_ENGINES, - /* GSF_ROAD */ NUM_ROAD_ENGINES, - /* GSF_SHIP */ NUM_SHIP_ENGINES, - /* GSF_AIRCRAFT */ NUM_AIRCRAFT_ENGINES -}; - -static const uint _vehshifts[4] = { - /* GSF_TRAIN */ 0, - /* GSF_ROAD */ ROAD_ENGINES_INDEX, - /* GSF_SHIP */ SHIP_ENGINES_INDEX, - /* GSF_AIRCRAFT */ AIRCRAFT_ENGINES_INDEX, -}; - enum { MAX_STATIONS = 256, }; @@ -123,8 +111,10 @@ struct GRFTempEngineData { static GRFTempEngineData *_gted; -/* Contains the GRF ID of the owner of a vehicle if it has been reserved */ -static uint32 _grm_engines[TOTAL_NUM_ENGINES]; +/* Contains the GRF ID of the owner of a vehicle if it has been reserved. + * GRM for vehicles is only used if dynamic engine allocation is disabled, + * so 256 is the number of original engines. */ +static uint32 _grm_engines[256]; /* Contains the GRF ID of the owner of a cargo if it has been reserved */ static uint32 _grm_cargos[NUM_CARGO * 2]; @@ -310,6 +300,65 @@ static uint8 MapDOSColour(uint8 colour) return colour; } +static std::map _grf_id_overrides; + +static void SetNewGRFOverride(uint32 source_grfid, uint32 target_grfid) +{ + _grf_id_overrides[source_grfid] = target_grfid; + grfmsg(5, "SetNewGRFOverride: Added override of 0x%X to 0x%X", BSWAP32(source_grfid), BSWAP32(target_grfid)); +} + +static Engine *GetNewEngine(const GRFFile *file, VehicleType type, uint16 internal_id) +{ + /* Hack for add-on GRFs that need to modify another GRF's engines. This lets + * them use the same engine slots. */ + const GRFFile *grf_match = NULL; + if (_patches.dynamic_engines) { + uint32 override = _grf_id_overrides[file->grfid]; + if (override != 0) { + grf_match = GetFileByGRFID(override); + if (grf_match == NULL) { + grfmsg(5, "Tried mapping from GRFID %x to %x but target is not loaded", BSWAP32(file->grfid), BSWAP32(override)); + } else { + grfmsg(5, "Mapping from GRFID %x to %x", BSWAP32(file->grfid), BSWAP32(override)); + } + } + } + + /* Check if this vehicle is already defined... */ + Engine *e = NULL; + FOR_ALL_ENGINES(e) { + if (_patches.dynamic_engines && e->grffile != NULL && e->grffile != file && e->grffile != grf_match) continue; + if (e->type != type) continue; + if (e->internal_id != internal_id) continue; + + if (e->grffile == NULL) { + e->grffile = file; + grfmsg(5, "Replaced engine at index %d for GRFID %x, type %d, index %d", e->index, BSWAP32(file->grfid), type, internal_id); + } + return e; + } + + uint engine_pool_size = GetEnginePoolSize(); + + /* ... it's not, so create a new one based off an existing engine */ + e = new Engine(type, internal_id); + e->grffile = file; + + if (engine_pool_size != GetEnginePoolSize()) { + /* Resize temporary engine data ... */ + _gted = ReallocT(_gted, GetEnginePoolSize()); + + /* and blank the new block. */ + size_t len = (GetEnginePoolSize() - engine_pool_size) * sizeof(*_gted); + memset(_gted + engine_pool_size, 0, len); + } + + grfmsg(5, "Created new engine at index %d for GRFID %x, type %d, index %d", e->index, BSWAP32(file->grfid), type, internal_id); + + return e; +} + /** Map the colour modifiers of TTDPatch to those that Open is using. * @param grf_sprite pointer to the structure been modified */ @@ -333,18 +382,15 @@ static void MapSpriteMappingRecolour(PalSpriteID *grf_sprite) typedef bool (*VCI_Handler)(uint engine, int numinfo, int prop, byte **buf, int len); -static void dewagonize(int condition, int engine) +static void dewagonize(int condition, Engine *e) { - EngineInfo *ei = &_engine_info[engine]; - RailVehicleInfo *rvi = &_rail_vehicle_info[engine]; - if (condition != 0) { - ei->unk2 &= ~0x80; - if (rvi->railveh_type == RAILVEH_WAGON) - rvi->railveh_type = RAILVEH_SINGLEHEAD; + e->info.unk2 &= ~0x80; + if (e->u.rail.railveh_type == RAILVEH_WAGON) + e->u.rail.railveh_type = RAILVEH_SINGLEHEAD; } else { - ei->unk2 |= 0x80; - rvi->railveh_type = RAILVEH_WAGON; + e->info.unk2 |= 0x80; + e->u.rail.railveh_type = RAILVEH_WAGON; } } @@ -354,8 +400,9 @@ static bool RailVehicleChangeInfo(uint engine, int numinfo, int prop, byte **buf bool ret = false; for (int i = 0; i < numinfo; i++) { - EngineInfo *ei = &_engine_info[engine + i]; - RailVehicleInfo *rvi = &_rail_vehicle_info[engine + i]; + Engine *e = GetNewEngine(_cur_grffile, VEH_TRAIN, engine + i); + EngineInfo *ei = &e->info; + RailVehicleInfo *rvi = &e->u.rail; switch (prop) { case 0x05: { // Track type @@ -390,7 +437,7 @@ static bool RailVehicleChangeInfo(uint engine, int numinfo, int prop, byte **buf if (rvi->railveh_type == RAILVEH_MULTIHEAD) power /= 2; rvi->power = power; - dewagonize(power, engine + i); + dewagonize(power, e); } break; case 0x0D: { // Running cost factor @@ -505,15 +552,9 @@ static bool RailVehicleChangeInfo(uint engine, int numinfo, int prop, byte **buf rvi->engclass = engclass; } break; - case 0x1A: { // Alter purchase list sort order - EngineID pos = grf_load_byte(&buf); - - if (pos < NUM_TRAIN_ENGINES) { - AlterRailVehListOrder(engine + i, pos); - } else { - grfmsg(2, "RailVehicleChangeInfo: Invalid train engine ID %d, ignoring", pos); - } - } break; + case 0x1A: // Alter purchase list sort order + AlterRailVehListOrder(e->index, grf_load_byte(&buf)); + break; case 0x1B: // Powered wagons power bonus rvi->pow_wag_power = grf_load_word(&buf); @@ -578,11 +619,11 @@ static bool RailVehicleChangeInfo(uint engine, int numinfo, int prop, byte **buf break; case 0x28: // Cargo classes allowed - _gted[engine + i].cargo_allowed = grf_load_word(&buf); + _gted[e->index].cargo_allowed = grf_load_word(&buf); break; case 0x29: // Cargo classes disallowed - _gted[engine + i].cargo_disallowed = grf_load_word(&buf); + _gted[e->index].cargo_disallowed = grf_load_word(&buf); break; case 0x2A: // Long format introduction date (days since year 0) @@ -605,8 +646,9 @@ static bool RoadVehicleChangeInfo(uint engine, int numinfo, int prop, byte **buf bool ret = false; for (int i = 0; i < numinfo; i++) { - EngineInfo *ei = &_engine_info[ROAD_ENGINES_INDEX + engine + i]; - RoadVehicleInfo *rvi = &_road_vehicle_info[engine + i]; + Engine *e = GetNewEngine(_cur_grffile, VEH_ROAD, engine + i); + EngineInfo *ei = &e->info; + RoadVehicleInfo *rvi = &e->u.road; switch (prop) { case 0x08: // Speed (1 unit is 0.5 kmh) @@ -708,11 +750,11 @@ static bool RoadVehicleChangeInfo(uint engine, int numinfo, int prop, byte **buf break; case 0x1D: // Cargo classes allowed - _gted[ROAD_ENGINES_INDEX + engine + i].cargo_allowed = grf_load_word(&buf); + _gted[e->index].cargo_allowed = grf_load_word(&buf); break; case 0x1E: // Cargo classes disallowed - _gted[ROAD_ENGINES_INDEX + engine + i].cargo_disallowed = grf_load_word(&buf); + _gted[e->index].cargo_disallowed = grf_load_word(&buf); break; case 0x1F: // Long format introduction date (days since year 0) @@ -735,8 +777,9 @@ static bool ShipVehicleChangeInfo(uint engine, int numinfo, int prop, byte **buf bool ret = false; for (int i = 0; i < numinfo; i++) { - EngineInfo *ei = &_engine_info[SHIP_ENGINES_INDEX + engine + i]; - ShipVehicleInfo *svi = &_ship_vehicle_info[engine + i]; + Engine *e = GetNewEngine(_cur_grffile, VEH_SHIP, engine + i); + EngineInfo *ei = &e->info; + ShipVehicleInfo *svi = &e->u.ship; switch (prop) { case 0x08: { // Sprite ID @@ -814,11 +857,11 @@ static bool ShipVehicleChangeInfo(uint engine, int numinfo, int prop, byte **buf break; case 0x18: // Cargo classes allowed - _gted[SHIP_ENGINES_INDEX + engine + i].cargo_allowed = grf_load_word(&buf); + _gted[e->index].cargo_allowed = grf_load_word(&buf); break; case 0x19: // Cargo classes disallowed - _gted[SHIP_ENGINES_INDEX + engine + i].cargo_disallowed = grf_load_word(&buf); + _gted[e->index].cargo_disallowed = grf_load_word(&buf); break; case 0x1A: // Long format introduction date (days since year 0) @@ -841,8 +884,9 @@ static bool AircraftVehicleChangeInfo(uint engine, int numinfo, int prop, byte * bool ret = false; for (int i = 0; i < numinfo; i++) { - EngineInfo *ei = &_engine_info[AIRCRAFT_ENGINES_INDEX + engine + i]; - AircraftVehicleInfo *avi = &_aircraft_vehicle_info[engine + i]; + Engine *e = GetNewEngine(_cur_grffile, VEH_AIRCRAFT, engine + i); + EngineInfo *ei = &e->info; + AircraftVehicleInfo *avi = &e->u.air; switch (prop) { case 0x08: { // Sprite ID @@ -918,11 +962,11 @@ static bool AircraftVehicleChangeInfo(uint engine, int numinfo, int prop, byte * break; case 0x18: // Cargo classes allowed - _gted[AIRCRAFT_ENGINES_INDEX + engine + i].cargo_allowed = grf_load_word(&buf); + _gted[e->index].cargo_allowed = grf_load_word(&buf); break; case 0x19: // Cargo classes disallowed - _gted[AIRCRAFT_ENGINES_INDEX + engine + i].cargo_disallowed = grf_load_word(&buf); + _gted[e->index].cargo_disallowed = grf_load_word(&buf); break; case 0x1A: // Long format introduction date (days since year 0) @@ -1604,6 +1648,12 @@ static bool GlobalVarChangeInfo(uint gvid, int numinfo, int prop, byte **bufp, i } break; + case 0x11: // GRF match for engine allocation + /* This is loaded during the reservation stage, so just skip it here. */ + /* Each entry is 8 bytes. */ + buf += 8; + break; + default: ret = true; break; @@ -2211,13 +2261,6 @@ static void FeatureChangeInfo(byte *buf, int len) return; } - if (feature <= GSF_AIRCRAFT) { - if (engine + numinfo > _vehcounts[feature]) { - grfmsg(0, "FeatureChangeInfo: Last engine ID %d out of bounds (max %d), skipping", engine + numinfo, _vehcounts[feature]); - return; - } - } - while (numprops-- && buf < bufend) { uint8 prop = grf_load_byte(&buf); bool ignoring = false; @@ -2230,7 +2273,8 @@ static void FeatureChangeInfo(byte *buf, int len) bool handled = true; for (uint i = 0; i < numinfo; i++) { - EngineInfo *ei = &_engine_info[engine + _vehshifts[feature] + i]; + Engine *e = GetNewEngine(_cur_grffile, (VehicleType)feature, engine + i); + EngineInfo *ei = &e->info; /* Common properties for vehicles */ switch (prop) { @@ -2252,6 +2296,8 @@ static void FeatureChangeInfo(byte *buf, int len) case 0x06: // Climates available ei->climates = grf_load_byte(&buf); + // XXX sometimes a grf wants hidden vehicles :o + if (ei->climates == 0) ei->climates = 0x80; break; case 0x07: // Loading speed @@ -2345,6 +2391,14 @@ static void ReserveChangeInfo(byte *buf, int len) _cur_grffile->cargo_list[i] = BSWAP32(cl); } break; + + case 0x11: // GRF match for engine allocation + for (uint i = 0; i < numinfo; i++) { + uint32 s = grf_load_dword(&buf); + uint32 t = grf_load_dword(&buf); + SetNewGRFOverride(s, t); + } + break; } break; } @@ -2857,14 +2911,9 @@ static void VehicleMapSpriteGroup(byte *buf, byte feature, uint8 idcount, uint8 for (uint i = 0; i < idcount; i++) { uint8 engine_id = buf[3 + i]; - uint8 engine = engine_id + _vehshifts[feature]; + EngineID engine = GetNewEngine(_cur_grffile, (VehicleType)feature, engine_id)->index; byte *bp = &buf[4 + idcount]; - if (engine_id > _vehcounts[feature]) { - grfmsg(0, "Id %u for feature 0x%02X is out of bounds", engine_id, feature); - return; - } - grfmsg(7, "VehicleMapSpriteGroup: [%d] Engine %d...", i, engine); for (uint c = 0; c < cidcount; c++) { @@ -2897,7 +2946,7 @@ static void VehicleMapSpriteGroup(byte *buf, byte feature, uint8 idcount, uint8 grfmsg(8, "-- Default group id 0x%04X", groupid); for (uint i = 0; i < idcount; i++) { - uint8 engine = buf[3 + i] + _vehshifts[feature]; + EngineID engine = GetNewEngine(_cur_grffile, (VehicleType)feature, buf[3 + i])->index; /* Don't tell me you don't love duplicated code! */ if (groupid >= _cur_grffile->spritegroups_count || _cur_grffile->spritegroups[groupid] == NULL) { @@ -3213,9 +3262,6 @@ static void FeatureNewName(byte *buf, int len) ClrBit(lang, 7); - if (feature <= GSF_AIRCRAFT && id < _vehcounts[feature]) { - id += _vehshifts[feature]; - } uint16 endid = id + num; grfmsg(6, "FeatureNewName: About to rename engines %d..%d (feature %d) in language 0x%02X", @@ -3236,10 +3282,10 @@ static void FeatureNewName(byte *buf, int len) case GSF_ROAD: case GSF_SHIP: case GSF_AIRCRAFT: - if (id < TOTAL_NUM_ENGINES) { - StringID string = AddGRFString(_cur_grffile->grfid, id, lang, new_scheme, name, STR_8000_KIRBY_PAUL_TANK_STEAM + id); - EngineInfo *ei = &_engine_info[id]; - ei->string_id = string; + if (id < GetEnginePoolSize()) { + Engine *e = GetNewEngine(_cur_grffile, (VehicleType)feature, id); + StringID string = AddGRFString(_cur_grffile->grfid, e->index, lang, new_scheme, name, e->info.string_id); + e->info.string_id = string; } else { AddGRFString(_cur_grffile->grfid, id, lang, new_scheme, name, id); } @@ -4303,8 +4349,22 @@ static void ParamSet(byte *buf, int len) case 0x01: // Road Vehicles case 0x02: // Ships case 0x03: // Aircraft - src1 = PerformGRM(&_grm_engines[_vehshifts[feature]], _vehcounts[feature], count, op, target, "vehicles"); - if (_skip_sprites == -1) return; + if (!_patches.dynamic_engines) { + src1 = PerformGRM(&_grm_engines[_engine_offsets[feature]], _engine_counts[feature], count, op, target, "vehicles"); + if (_skip_sprites == -1) return; + } else { + // GRM does not apply for dynamic engine allocation. + switch (op) { + case 2: + case 3: + src1 = _cur_grffile->param[target]; + break; + + default: + src1 = 0; + break; + } + } break; case 0x08: // General sprites @@ -5068,6 +5128,7 @@ static void InitializeGRFSpecial() | (0 << 0x15) // enhancetunnels | (1 << 0x16) // shortrvs | (1 << 0x17) // articulatedrvs + | ((_patches.dynamic_engines ? 1 : 0) << 0x18) // dynamic engines | (1 << 0x1E); // variablerunningcosts } @@ -5224,17 +5285,12 @@ static void ResetNewGRFData() ResetBridges(); /* Allocate temporary refit/cargo class data */ - _gted = CallocT(TOTAL_NUM_ENGINES); + _gted = CallocT(GetEnginePoolSize()); /* Reset GRM reservations */ memset(&_grm_engines, 0, sizeof(_grm_engines)); memset(&_grm_cargos, 0, sizeof(_grm_cargos)); - /* Unload sprite group data */ - UnloadWagonOverrides(); - UnloadCustomEngineSprites(); - ResetEngineListOrder(); - /* Reset generic feature callback lists */ ResetGenericCallbacks(); @@ -5284,6 +5340,9 @@ static void ResetNewGRFData() _loaded_newgrf_features.has_newindustries = false; _loaded_newgrf_features.shore = SHORE_REPLACE_NONE; + /* Clear all GRF overrides */ + _grf_id_overrides.clear(); + InitializeSoundPool(); InitializeSpriteGroupPool(); } @@ -5397,14 +5456,17 @@ static const CargoLabel *_default_refitmasks[] = { */ static void CalculateRefitMasks() { - for (EngineID engine = 0; engine < TOTAL_NUM_ENGINES; engine++) { - EngineInfo *ei = &_engine_info[engine]; + Engine *e; + + FOR_ALL_ENGINES(e) { + EngineID engine = e->index; + EngineInfo *ei = &e->info; uint32 mask = 0; uint32 not_mask = 0; uint32 xor_mask = 0; if (ei->refit_mask != 0) { - const GRFFile *file = GetEngineGRF(engine); + const GRFFile *file = e->grffile; if (file != NULL && file->cargo_max != 0) { /* Apply cargo translation table to the refit mask */ uint num_cargo = min(32, file->cargo_max); @@ -5434,15 +5496,10 @@ static void CalculateRefitMasks() if (_gted[engine].cargo_allowed & cs->classes) SetBit(mask, i); if (_gted[engine].cargo_disallowed & cs->classes) SetBit(not_mask, i); } - } else { + } else if (xor_mask == 0) { /* Don't apply default refit mask to wagons or engines with no capacity */ - if (xor_mask == 0 && ( - GetEngine(engine)->type != VEH_TRAIN || ( - RailVehInfo(engine)->capacity != 0 && - RailVehInfo(engine)->railveh_type != RAILVEH_WAGON - ) - )) { - const CargoLabel *cl = _default_refitmasks[GetEngine(engine)->type]; + if (e->type != VEH_TRAIN || (e->u.rail.capacity != 0 && e->u.rail.railveh_type != RAILVEH_WAGON)) { + const CargoLabel *cl = _default_refitmasks[e->type]; for (uint i = 0;; i++) { if (cl[i] == 0) break; @@ -5458,25 +5515,25 @@ static void CalculateRefitMasks() /* Check if this engine's cargo type is valid. If not, set to the first refittable * cargo type. Apparently cargo_type isn't a common property... */ - switch (GetEngine(engine)->type) { + switch (e->type) { default: NOT_REACHED(); case VEH_AIRCRAFT: break; case VEH_TRAIN: { - RailVehicleInfo *rvi = &_rail_vehicle_info[engine]; + RailVehicleInfo *rvi = &e->u.rail; if (rvi->cargo_type == CT_INVALID) rvi->cargo_type = FindFirstRefittableCargo(engine); - if (rvi->cargo_type == CT_INVALID) ei->climates = 0; + if (rvi->cargo_type == CT_INVALID) ei->climates = 0x80; break; } case VEH_ROAD: { - RoadVehicleInfo *rvi = &_road_vehicle_info[engine - ROAD_ENGINES_INDEX]; + RoadVehicleInfo *rvi = &e->u.road; if (rvi->cargo_type == CT_INVALID) rvi->cargo_type = FindFirstRefittableCargo(engine); - if (rvi->cargo_type == CT_INVALID) ei->climates = 0; + if (rvi->cargo_type == CT_INVALID) ei->climates = 0x80; break; } case VEH_SHIP: { - ShipVehicleInfo *svi = &_ship_vehicle_info[engine - SHIP_ENGINES_INDEX]; + ShipVehicleInfo *svi = &e->u.ship; if (svi->cargo_type == CT_INVALID) svi->cargo_type = FindFirstRefittableCargo(engine); - if (svi->cargo_type == CT_INVALID) ei->climates = 0; + if (svi->cargo_type == CT_INVALID) ei->climates = 0x80; break; } } @@ -5851,6 +5908,9 @@ static void AfterLoadGRFs() /* Update the townname generators list */ InitGRFTownGeneratorNames(); + /* Run all queued vehicle list order changes */ + CommitRailVehListOrderChanges(); + /* Load old shore sprites in new position, if they were replaced by ActionA */ ActivateOldShore(); diff --git a/src/newgrf_engine.cpp b/src/newgrf_engine.cpp index 7309e7cfc7..454ee0a25d 100644 --- a/src/newgrf_engine.cpp +++ b/src/newgrf_engine.cpp @@ -7,6 +7,7 @@ #include "variables.h" #include "debug.h" #include "engine_func.h" +#include "engine_base.h" #include "train.h" #include "player_func.h" #include "player_base.h" @@ -25,6 +26,8 @@ #include "direction_func.h" #include "rail_map.h" #include "rail.h" +#include "settings_type.h" +#include int _traininfo_vehicle_pitch = 0; @@ -37,29 +40,17 @@ struct WagonOverride { const SpriteGroup *group; }; -struct WagonOverrides { - uint overrides_count; - WagonOverride *overrides; -}; - -static WagonOverrides _engine_wagon_overrides[TOTAL_NUM_ENGINES]; - void SetWagonOverrideSprites(EngineID engine, CargoID cargo, const SpriteGroup *group, EngineID *train_id, uint trains) { - WagonOverrides *wos; + Engine *e = GetEngine(engine); WagonOverride *wo; - assert(engine < TOTAL_NUM_ENGINES); assert(cargo < NUM_CARGO + 2); // Include CT_DEFAULT and CT_PURCHASE pseudo cargos. - wos = &_engine_wagon_overrides[engine]; - wos->overrides_count++; - wos->overrides = ReallocT(wos->overrides, wos->overrides_count); + e->overrides_count++; + e->overrides = ReallocT(e->overrides, e->overrides_count); - wo = &wos->overrides[wos->overrides_count - 1]; - /* FIXME: If we are replacing an override, release original SpriteGroup - * to prevent leaks. But first we need to refcount the SpriteGroup. - * --pasky */ + wo = &e->overrides[e->overrides_count - 1]; wo->group = group; wo->cargo = cargo; wo->trains = trains; @@ -69,15 +60,15 @@ void SetWagonOverrideSprites(EngineID engine, CargoID cargo, const SpriteGroup * const SpriteGroup *GetWagonOverrideSpriteSet(EngineID engine, CargoID cargo, EngineID overriding_engine) { - const WagonOverrides *wos = &_engine_wagon_overrides[engine]; + const Engine *e = GetEngine(engine); /* XXX: This could turn out to be a timesink on profiles. We could * always just dedicate 65535 bytes for an [engine][train] trampoline * for O(1). Or O(logMlogN) and searching binary tree or smt. like * that. --pasky */ - for (uint i = 0; i < wos->overrides_count; i++) { - const WagonOverride *wo = &wos->overrides[i]; + for (uint i = 0; i < e->overrides_count; i++) { + const WagonOverride *wo = &e->overrides[i]; if (wo->cargo != cargo && wo->cargo != CT_DEFAULT) continue; @@ -91,43 +82,29 @@ const SpriteGroup *GetWagonOverrideSpriteSet(EngineID engine, CargoID cargo, Eng /** * Unload all wagon override sprite groups. */ -void UnloadWagonOverrides() +void UnloadWagonOverrides(Engine *e) { - for (EngineID engine = 0; engine < TOTAL_NUM_ENGINES; engine++) { - WagonOverrides *wos = &_engine_wagon_overrides[engine]; - for (uint i = 0; i < wos->overrides_count; i++) { - WagonOverride *wo = &wos->overrides[i]; - free(wo->train_id); - } - free(wos->overrides); - wos->overrides_count = 0; - wos->overrides = NULL; + for (uint i = 0; i < e->overrides_count; i++) { + WagonOverride *wo = &e->overrides[i]; + free(wo->train_id); } + free(e->overrides); + e->overrides_count = 0; + e->overrides = NULL; } -/* Space for NUM_CARGO real cargos and 2 pseudo cargos, CT_DEFAULT and CT_PURCHASE */ -static const SpriteGroup *_engine_custom_sprites[TOTAL_NUM_ENGINES][NUM_CARGO + 2]; -static const GRFFile *_engine_grf[TOTAL_NUM_ENGINES]; void SetCustomEngineSprites(EngineID engine, byte cargo, const SpriteGroup *group) { - assert(engine < lengthof(_engine_custom_sprites)); - assert(cargo < lengthof(*_engine_custom_sprites)); + Engine *e = GetEngine(engine); + assert(cargo < lengthof(e->group)); - if (_engine_custom_sprites[engine][cargo] != NULL) { + if (e->group[cargo] != NULL) { grfmsg(6, "SetCustomEngineSprites: engine %d cargo %d already has group -- replacing", engine, cargo); } - _engine_custom_sprites[engine][cargo] = group; + e->group[cargo] = group; } -/** - * Unload all engine sprite groups. - */ -void UnloadCustomEngineSprites() -{ - memset(_engine_custom_sprites, 0, sizeof(_engine_custom_sprites)); - memset(_engine_grf, 0, sizeof(_engine_grf)); -} /** * Tie a GRFFile entry to an engine, to allow us to retrieve GRF parameters @@ -137,8 +114,8 @@ void UnloadCustomEngineSprites() */ void SetEngineGRF(EngineID engine, const GRFFile *file) { - assert(engine < TOTAL_NUM_ENGINES); - _engine_grf[engine] = file; + Engine *e = GetEngine(engine); + e->grffile = file; } @@ -149,8 +126,7 @@ void SetEngineGRF(EngineID engine, const GRFFile *file) */ const GRFFile *GetEngineGRF(EngineID engine) { - assert(engine < TOTAL_NUM_ENGINES); - return _engine_grf[engine]; + return GetEngine(engine)->grffile; } @@ -161,8 +137,7 @@ const GRFFile *GetEngineGRF(EngineID engine) */ uint32 GetEngineGRFID(EngineID engine) { - assert(engine < TOTAL_NUM_ENGINES); - return _engine_grf[engine]->grfid; + return GetEngineGRF(engine)->grfid; } @@ -468,6 +443,7 @@ static uint32 GetGRFParameter(EngineID engine_type, byte parameter) { const GRFFile *file = GetEngineGRF(engine_type); + if (file == NULL) return 0; if (parameter >= file->param_end) return 0; return file->param[parameter]; } @@ -643,12 +619,13 @@ static uint32 VehicleGetVariable(const ResolverObject *object, byte variable, by /* Variables which use the parameter */ case 0x60: // Count consist's engine ID occurance - if (v->type != VEH_TRAIN) return v->engine_type == parameter; + //EngineID engine = GetNewEngineID(GetEngineGRF(v->engine_type), v->type, parameter); + if (v->type != VEH_TRAIN) return GetEngine(v->engine_type)->internal_id == parameter; { uint count = 0; for (; v != NULL; v = v->Next()) { - if (v->engine_type == parameter) count++; + if (GetEngine(v->engine_type)->internal_id == parameter) count++; } return count; } @@ -723,8 +700,8 @@ static uint32 VehicleGetVariable(const ResolverObject *object, byte variable, by case 0x43: return GB(v->max_age, 8, 8); case 0x44: return Clamp(v->build_year, ORIGINAL_BASE_YEAR, ORIGINAL_MAX_YEAR) - ORIGINAL_BASE_YEAR; case 0x45: return v->unitnumber; - case 0x46: return v->engine_type; - case 0x47: return GB(v->engine_type, 8, 8); + case 0x46: return GetEngine(v->engine_type)->internal_id; + case 0x47: return GB(GetEngine(v->engine_type)->internal_id, 8, 8); case 0x48: if (v->type != VEH_TRAIN || v->spritenum != 0xFD) return v->spritenum; return HasBit(v->u.rail.flags, VRF_REVERSE_DIRECTION) ? 0xFE : 0xFD; @@ -883,11 +860,13 @@ static const SpriteGroup *GetVehicleSpriteGroup(EngineID engine, const Vehicle * } } - group = _engine_custom_sprites[engine][cargo]; + const Engine *e = GetEngine(engine); + + group = e->group[cargo]; if (group != NULL) return group; /* Fall back to the default set if the selected cargo type is not defined */ - return _engine_custom_sprites[engine][CT_DEFAULT]; + return e->group[CT_DEFAULT]; } @@ -907,14 +886,14 @@ SpriteID GetCustomEngineSprite(EngineID engine, const Vehicle *v, Direction dire SpriteID GetRotorOverrideSprite(EngineID engine, const Vehicle *v, bool info_view) { + const Engine *e = GetEngine(engine); const SpriteGroup *group; ResolverObject object; - assert(engine >= AIRCRAFT_ENGINES_INDEX); - assert(engine < AIRCRAFT_ENGINES_INDEX + NUM_AIRCRAFT_ENGINES); + assert(e->type == VEH_AIRCRAFT); /* Only valid for helicopters */ - assert(!(AircraftVehInfo(engine)->subtype & AIR_CTOL)); + assert(!(e->u.air.subtype & AIR_CTOL)); NewVehicleResolver(&object, engine, v); @@ -1090,18 +1069,6 @@ void TriggerVehicle(Vehicle *v, VehicleTrigger trigger) /* Functions for changing the order of vehicle purchase lists * This is currently only implemented for rail vehicles. */ -static EngineID _engine_list_order[NUM_TRAIN_ENGINES]; -static byte _engine_list_position[NUM_TRAIN_ENGINES]; - -void ResetEngineListOrder() -{ - EngineID i; - - for (i = 0; i < NUM_TRAIN_ENGINES; i++) { - _engine_list_order[i] = i; - _engine_list_position[i] = i; - } -} /** * Get the list position of an engine. @@ -1109,36 +1076,72 @@ void ResetEngineListOrder() * @param engine ID of the engine. * @return The list position of the engine. */ -uint16 ListPositionOfEngine(EngineID engine) +uint ListPositionOfEngine(EngineID engine) { - if (engine < NUM_TRAIN_ENGINES) return _engine_list_position[engine]; - return engine; + const Engine *e = GetEngine(engine); + if (e->grffile == NULL) return e->list_position; + + /* Crude sorting to group by GRF ID */ + return (e->grffile->grfid * 256) + e->list_position; } +struct ListOrderChange { + EngineID engine; + EngineID target; +}; + +static std::list _list_order_changes; + void AlterRailVehListOrder(EngineID engine, EngineID target) { - EngineID i; - bool moving = false; - - if (engine == target) return; - - /* First, remove our ID from the list. */ - for (i = 0; i < NUM_TRAIN_ENGINES - 1; i++) { - if (_engine_list_order[i] == engine) moving = true; - if (moving) _engine_list_order[i] = _engine_list_order[i + 1]; - } - - /* Now, insert it again, before the target engine. */ - for (i = NUM_TRAIN_ENGINES - 1; i > 0; i--) { - _engine_list_order[i] = _engine_list_order[i - 1]; - if (_engine_list_order[i] == target) { - _engine_list_order[i - 1] = engine; - break; - } - } - - /* Update the engine list position (a reverse of engine list order) */ - for (i = 0; i < NUM_TRAIN_ENGINES; i++) { - _engine_list_position[_engine_list_order[i]] = i; - } + /* Add the list order change to a queue */ + ListOrderChange loc; + loc.engine = engine; + loc.target = target; + _list_order_changes.push_back(loc); +} + +void CommitRailVehListOrderChanges() +{ + /* List position to Engine map */ + typedef std::map ListPositionMap; + ListPositionMap lptr_map; + + std::list::iterator it; + for (it = _list_order_changes.begin(); it != _list_order_changes.end(); ++it) { + EngineID engine = it->engine; + EngineID target = it->target; + + if (engine == target) continue; + + Engine *source_e = GetEngine(engine); + Engine *target_e = NULL; + + /* Populate map with current list positions */ + Engine *e; + FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) { + if (!_patches.dynamic_engines || e->grffile == source_e->grffile) { + if (e->internal_id == target) target_e = e; + lptr_map[e->list_position] = e; + } + } + + /* Get the target position, if it exists */ + if (target_e != NULL) { + uint16 target_position = target_e->list_position; + + bool moving = false; + for (ListPositionMap::iterator it = lptr_map.begin(); it != lptr_map.end(); ++it) { + if (it->first == target_position) moving = true; + if (moving) it->second->list_position++; + } + + source_e->list_position = target_position; + } + + lptr_map.clear(); + } + + /* Clear out the queue */ + _list_order_changes.clear(); } diff --git a/src/newgrf_engine.h b/src/newgrf_engine.h index 9b7e64cdbf..48dde76085 100644 --- a/src/newgrf_engine.h +++ b/src/newgrf_engine.h @@ -53,11 +53,10 @@ enum VehicleTrigger { }; void TriggerVehicle(Vehicle *veh, VehicleTrigger trigger); -void UnloadWagonOverrides(); -void UnloadCustomEngineSprites(); +void UnloadWagonOverrides(Engine *e); -void ResetEngineListOrder(); -uint16 ListPositionOfEngine(EngineID engine); +uint ListPositionOfEngine(EngineID engine); void AlterRailVehListOrder(EngineID engine, EngineID target); +void CommitRailVehListOrderChanges(); #endif /* NEWGRF_ENGINE_H */ diff --git a/src/newgrf_sound.cpp b/src/newgrf_sound.cpp index adafd8d906..078e77b261 100644 --- a/src/newgrf_sound.cpp +++ b/src/newgrf_sound.cpp @@ -6,6 +6,7 @@ #include "openttd.h" #include "oldpool.h" #include "engine_func.h" +#include "engine_base.h" #include "newgrf_callbacks.h" #include "newgrf_engine.h" #include "newgrf_sound.h" diff --git a/src/oldloader.cpp b/src/oldloader.cpp index ef367731c8..dd4924a8fb 100644 --- a/src/oldloader.cpp +++ b/src/oldloader.cpp @@ -1330,18 +1330,19 @@ static const OldChunks engine_chunk[] = { static bool LoadOldEngine(LoadgameState *ls, int num) { - if (!LoadChunk(ls, GetEngine(num), engine_chunk)) return false; + Engine *e = GetTempDataEngine(num); + if (!LoadChunk(ls, e, engine_chunk)) return false; /* Make sure wagons are marked as do-not-age */ if ((num >= 27 && num < 54) || (num >= 57 && num < 84) || (num >= 89 && num < 116)) - GetEngine(num)->age = 0xFFFF; + e->age = 0xFFFF; return true; } static bool LoadOldEngineName(LoadgameState *ls, int num) { - Engine *e = GetEngine(num); + Engine *e = GetTempDataEngine(num); e->name = CopyFromOldName(RemapOldStringID(ReadUint16(ls))); return true; } diff --git a/src/openttd.cpp b/src/openttd.cpp index 402d6276b6..be999af168 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -305,6 +305,7 @@ static void UnInitializeGame() _Order_pool.CleanPool(); _Group_pool.CleanPool(); _CargoPacket_pool.CleanPool(); + _Engine_pool.CleanPool(); free((void*)_town_sort); free((void*)_industry_sort); @@ -1332,6 +1333,8 @@ static bool InitializeWindowsAndCaches() /* Recalculate */ Group *g; FOR_ALL_GROUPS(g) { + g->num_engines = CallocT(GetEnginePoolSize()); + const Vehicle *v; FOR_ALL_VEHICLES(v) { if (!IsEngineCountable(v)) continue; @@ -1354,6 +1357,9 @@ static bool InitializeWindowsAndCaches() * thus the MIN_YEAR (which is really nothing more than Zero, initialized value) test */ if (_file_to_saveload.filetype == FT_SCENARIO && players[i]->inaugurated_year != MIN_YEAR) players[i]->inaugurated_year = _cur_year; + + free(players[i]->num_engines); + players[i]->num_engines = CallocT(GetEnginePoolSize()); } FOR_ALL_VEHICLES(v) { @@ -1444,10 +1450,16 @@ bool AfterLoadGame() * must be done before loading sprites as some newgrfs check it */ SetDate(_date); + /* Force dynamic engines off when loading older savegames */ + if (CheckSavegameVersion(95)) _patches.dynamic_engines = 0; + /* Load the sprites */ GfxLoadSprites(); LoadStringWidthTable(); + /* Copy temporary data to Engine pool */ + CopyTempEngineData(); + /* Connect front and rear engines of multiheaded trains and converts * subtype to the new format */ if (CheckSavegameVersionOldStyle(17, 1)) ConvertOldMultiheadToNew(); diff --git a/src/player_base.h b/src/player_base.h index cce41eb600..a5009b490f 100644 --- a/src/player_base.h +++ b/src/player_base.h @@ -70,7 +70,7 @@ struct Player { bool renew_keep_length; int16 engine_renew_months; uint32 engine_renew_money; - uint16 num_engines[TOTAL_NUM_ENGINES]; ///< caches the number of engines of each type the player owns (no need to save this) + uint16 *num_engines; ///< caches the number of engines of each type the player owns (no need to save this) }; struct PlayerMoneyBackup { diff --git a/src/players.cpp b/src/players.cpp index f4cd472f8a..188a6b9e91 100644 --- a/src/players.cpp +++ b/src/players.cpp @@ -5,6 +5,7 @@ #include "stdafx.h" #include "openttd.h" #include "engine_func.h" +#include "engine_base.h" #include "player_func.h" #include "player_gui.h" #include "town.h" @@ -26,6 +27,7 @@ #include "date_func.h" #include "vehicle_func.h" #include "sound_func.h" +#include "core/alloc_func.hpp" #include "autoreplace_func.h" #include "autoreplace_gui.h" #include "string_func.h" @@ -548,7 +550,8 @@ Player *DoStartupNewPlayer(bool is_ai) if (is_ai && (!_networking || _network_server) && _ai.enabled) AI_StartNewAI(p->index); - memset(p->num_engines, 0, sizeof(p->num_engines)); + free(p->num_engines); + p->num_engines = CallocT(GetEnginePoolSize()); return p; } diff --git a/src/rail.cpp b/src/rail.cpp index 6ab511a2bb..bae773a91d 100644 --- a/src/rail.cpp +++ b/src/rail.cpp @@ -14,6 +14,7 @@ #include "player_func.h" #include "player_base.h" #include "engine_func.h" +#include "engine_base.h" /* XXX: Below 3 tables store duplicate data. Maybe remove some? */ @@ -202,14 +203,13 @@ RailTypes GetPlayerRailtypes(PlayerID p) { RailTypes rt = RAILTYPES_NONE; - EngineID eid; - FOR_ALL_ENGINEIDS_OF_TYPE(eid, VEH_TRAIN) { - const Engine* e = GetEngine(eid); - const EngineInfo *ei = EngInfo(eid); + Engine *e; + FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) { + const EngineInfo *ei = &e->info; if (HasBit(ei->climates, _opt.landscape) && (HasBit(e->player_avail, p) || _date >= e->intro_date + 365)) { - const RailVehicleInfo *rvi = RailVehInfo(eid); + const RailVehicleInfo *rvi = &e->u.rail; if (rvi->railveh_type != RAILVEH_WAGON) { assert(rvi->railtype < RAILTYPE_END); diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index 4bdc8dd09c..692412630c 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -18,6 +18,7 @@ #include "command_func.h" #include "pathfind.h" #include "engine_func.h" +#include "engine_base.h" #include "town.h" #include "sprite.h" #include "depot_base.h" diff --git a/src/road.cpp b/src/road.cpp index 095f82f9c7..c0ebde9ed5 100644 --- a/src/road.cpp +++ b/src/road.cpp @@ -10,6 +10,7 @@ #include "player_func.h" #include "player_base.h" #include "engine_func.h" +#include "engine_base.h" #include "settings_type.h" #include "date_func.h" @@ -96,10 +97,9 @@ RoadTypes GetPlayerRoadtypes(PlayerID p) { RoadTypes rt = ROADTYPES_NONE; - EngineID eid; - FOR_ALL_ENGINEIDS_OF_TYPE(eid, VEH_ROAD) { - const Engine* e = GetEngine(eid); - const EngineInfo *ei = EngInfo(eid); + Engine *e; + FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) { + const EngineInfo *ei = &e->info; if (HasBit(ei->climates, _opt.landscape) && (HasBit(e->player_avail, p) || _date >= e->intro_date + 365)) { diff --git a/src/roadveh.h b/src/roadveh.h index 3dd20c7c30..984f0f2498 100644 --- a/src/roadveh.h +++ b/src/roadveh.h @@ -7,6 +7,7 @@ #include "vehicle_base.h" #include "engine_func.h" +#include "engine_base.h" #include "economy_func.h" enum RoadVehicleSubType { diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index 28d515330d..6b112101a3 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -102,7 +102,7 @@ static SpriteID GetRoadVehIcon(EngineID engine) SpriteID sprite = GetCustomVehicleIcon(engine, DIR_W); if (sprite != 0) return sprite; - spritenum = _orig_road_vehicle_info[engine - ROAD_ENGINES_INDEX].image_index; + spritenum = GetEngine(engine)->image_index; } return 6 + _roadveh_images[spritenum]; @@ -117,7 +117,7 @@ SpriteID RoadVehicle::GetImage(Direction direction) const sprite = GetCustomVehicleSprite(this, (Direction)(direction + 4 * IS_CUSTOM_SECONDHEAD_SPRITE(spritenum))); if (sprite != 0) return sprite; - spritenum = _orig_road_vehicle_info[this->engine_type - ROAD_ENGINES_INDEX].image_index; + spritenum = GetEngine(this->engine_type)->image_index; } sprite = direction + _roadveh_images[spritenum]; diff --git a/src/saveload.cpp b/src/saveload.cpp index 02df1aa798..33b5c7eaf0 100644 --- a/src/saveload.cpp +++ b/src/saveload.cpp @@ -34,7 +34,7 @@ #include "table/strings.h" -extern const uint16 SAVEGAME_VERSION = 94; +extern const uint16 SAVEGAME_VERSION = 95; uint16 _sl_version; ///< the major savegame version identifier byte _sl_minor_version; ///< the minor savegame version, DO NOT USE! diff --git a/src/settings.cpp b/src/settings.cpp index bd75029af6..eabd3c1b75 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1459,6 +1459,7 @@ const SettingDesc _patch_settings[] = { SDT_CONDVAR(Patches, freight_trains, SLE_UINT8, 39, SL_MAX_VERSION, 0,NN, 1, 1, 255, 1, STR_CONFIG_PATCHES_FREIGHT_TRAINS, NULL), SDT_CONDBOOL(Patches, timetabling, 67, SL_MAX_VERSION, 0, 0, true, STR_CONFIG_PATCHES_TIMETABLE_ALLOW, NULL), SDT_CONDVAR(Patches, plane_speed, SLE_UINT8, 90, SL_MAX_VERSION, 0, 0, 4, 1, 4, 0, STR_CONFIG_PATCHES_PLANE_SPEED, NULL), + SDT_CONDBOOL(Patches, dynamic_engines, 95, SL_MAX_VERSION, 0,NN, false, STR_CONFIG_PATCHES_DYNAMIC_ENGINES, NULL), /***************************************************************************/ /* Station section of the GUI-configure patches window */ diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index c699443b61..e94f8b87b0 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -819,6 +819,7 @@ static const char *_patches_vehicles[] = { "freight_trains", "plane_speed", "timetabling", + "dynamic_engines", }; struct PatchEntry { diff --git a/src/settings_type.h b/src/settings_type.h index 54903d46cd..46e6f6b39c 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -232,6 +232,8 @@ struct Patches { bool give_money; ///< allow giving other players money bool enable_signal_gui; ///< Show the signal GUI when the signal button is pressed + + bool dynamic_engines; ///< Enable dynamic allocation of engine data }; extern Patches _patches; diff --git a/src/ship.h b/src/ship.h index f433c065d1..35b2d7a931 100644 --- a/src/ship.h +++ b/src/ship.h @@ -7,6 +7,7 @@ #include "vehicle_base.h" #include "engine_func.h" +#include "engine_base.h" #include "economy_func.h" void CcBuildShip(bool success, TileIndex tile, uint32 p1, uint32 p2); diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index 12881d1e0d..7fe3418a29 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -14,6 +14,7 @@ #include "station_base.h" #include "news_func.h" #include "engine_func.h" +#include "engine_base.h" #include "player_func.h" #include "player_base.h" #include "npf.h" @@ -65,7 +66,7 @@ static SpriteID GetShipIcon(EngineID engine) SpriteID sprite = GetCustomVehicleIcon(engine, DIR_W); if (sprite != 0) return sprite; - spritenum = _orig_ship_vehicle_info[engine - SHIP_ENGINES_INDEX].image_index; + spritenum = GetEngine(engine)->image_index; } return 6 + _ship_sprites[spritenum]; @@ -97,7 +98,7 @@ SpriteID Ship::GetImage(Direction direction) const SpriteID sprite = GetCustomVehicleSprite(this, direction); if (sprite != 0) return sprite; - spritenum = _orig_ship_vehicle_info[this->engine_type - SHIP_ENGINES_INDEX].image_index; + spritenum = GetEngine(this->engine_type)->image_index; } return _ship_sprites[spritenum] + direction; diff --git a/src/strings.cpp b/src/strings.cpp index 7ed45a7452..5cebdf7da7 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -37,6 +37,7 @@ #include "settings_type.h" #include "video/video_driver.hpp" #include "engine_func.h" +#include "engine_base.h" #include "saveload.h" #include "table/strings.h" @@ -934,7 +935,7 @@ static char* FormatString(char* buff, const char* str, const int64* argv, uint c if (e->name != NULL) { buff = strecpy(buff, e->name, last); } else { - buff = GetStringWithArgs(buff, EngInfo(engine)->string_id, NULL, last); + buff = GetStringWithArgs(buff, e->info.string_id, NULL, last); } break; } diff --git a/src/table/engines.h b/src/table/engines.h index 59303e7c30..8d892972aa 100644 --- a/src/table/engines.h +++ b/src/table/engines.h @@ -69,7 +69,7 @@ enum { #define A 2 #define S 4 #define Y 8 -const EngineInfo _orig_engine_info[] = { +static const EngineInfo _orig_engine_info[] = { MK( 1827, 20, 15, 30, T ), /* 0 Kirby Paul Tank (Steam) */ MK( 12784, 20, 22, 30, A|S ), /* 1 MJS 250 (Diesel) */ MK( 9497, 20, 20, 50, Y), /* 2 Ploddyphut Choo-Choo */ @@ -370,7 +370,7 @@ const EngineInfo _orig_engine_info[] = { #define O RAILTYPE_MONO #define L RAILTYPE_MAGLEV -const RailVehicleInfo _orig_rail_vehicle_info[NUM_TRAIN_ENGINES] = { +static const RailVehicleInfo _orig_rail_vehicle_info[] = { // image_index max_speed (kph) running_cost ai_rank // | flags | power (hp) | running_cost_class | railtype // | | base_cost | weight | | capacity | | @@ -519,7 +519,7 @@ const RailVehicleInfo _orig_rail_vehicle_info[NUM_TRAIN_ENGINES] = { * @param h refittable */ #define SVI(a, b, c, d, e, f, g, h) { a, b, c, d, e, f, {g}, h } -const ShipVehicleInfo _orig_ship_vehicle_info[NUM_SHIP_ENGINES] = { +static const ShipVehicleInfo _orig_ship_vehicle_info[] = { // image_index cargo_type cargo_amount refittable // | base_cost | | running_cost | // | | max_speed | | sfx | @@ -554,7 +554,7 @@ const ShipVehicleInfo _orig_ship_vehicle_info[NUM_SHIP_ENGINES] = { #define H AIR_HELI #define P AIR_CTOL #define J AIR_CTOL | AIR_FAST -const AircraftVehicleInfo _orig_aircraft_vehicle_info[NUM_AIRCRAFT_ENGINES] = { +static const AircraftVehicleInfo _orig_aircraft_vehicle_info[] = { // image_index sfx acceleration // | base_cost | | max_speed // | | running_cost | | mail_capacity @@ -618,7 +618,7 @@ const AircraftVehicleInfo _orig_aircraft_vehicle_info[NUM_AIRCRAFT_ENGINES] = { * @param g cargo_type */ #define ROV(a, b, c, d, e, f, g) { a, b, c, RC_R, {d}, e, f, g } -const RoadVehicleInfo _orig_road_vehicle_info[NUM_ROAD_ENGINES] = { +static const RoadVehicleInfo _orig_road_vehicle_info[] = { // image_index sfx max_speed // | base_cost | | capacity // | | running_cost | | cargo_type diff --git a/src/table/files.h b/src/table/files.h index 8bafb868ba..eeaab5c448 100644 --- a/src/table/files.h +++ b/src/table/files.h @@ -33,7 +33,7 @@ static FileList files_dos = { { "TRGT.GRF", {0xfc, 0xde, 0x1d, 0x7e, 0x8a, 0x74, 0x19, 0x7d, 0x72, 0xa6, 0x26, 0x95, 0x88, 0x4b, 0x90, 0x9e} } }, { "SAMPLE.CAT", {0x42, 0x2e, 0xa3, 0xdd, 0x07, 0x4d, 0x28, 0x59, 0xbb, 0x51, 0x63, 0x9a, 0x6e, 0x0e, 0x85, 0xda} }, - { "OPENTTDD.GRF", {0x32, 0xb4, 0xec, 0x0c, 0xc9, 0x5d, 0xa0, 0x14, 0x3a, 0x2f, 0xe1, 0xd4, 0x20, 0x63, 0x49, 0x74} } + { "OPENTTDD.GRF", {0xb2, 0xbd, 0xd2, 0xa4, 0x1b, 0xfa, 0x2c, 0x60, 0x4f, 0xd5, 0x5e, 0x4c, 0xb5, 0xba, 0x37, 0x73} } }; @@ -47,5 +47,5 @@ static FileList files_win = { { "TRGTR.GRF", {0xde, 0x53, 0x65, 0x05, 0x17, 0xfe, 0x66, 0x1c, 0xea, 0xa3, 0x13, 0x8c, 0x6e, 0xdb, 0x0e, 0xb8} } }, { "SAMPLE.CAT", {0x92, 0x12, 0xe8, 0x1e, 0x72, 0xba, 0xdd, 0x4b, 0xbe, 0x1e, 0xae, 0xae, 0x66, 0x45, 0x8e, 0x10} }, - { "OPENTTDW.GRF", {0xc6, 0x1f, 0xcc, 0x4e, 0x83, 0x98, 0x5b, 0x67, 0xb7, 0x03, 0xa0, 0x31, 0x39, 0x2e, 0x75, 0xfc} } + { "OPENTTDW.GRF", {0x3b, 0x1a, 0x0d, 0x8c, 0x2d, 0x01, 0x0e, 0xee, 0x47, 0x7f, 0x5d, 0x70, 0x8f, 0xb2, 0xe4, 0xfb} } }; diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 26a5134aa8..b3391af373 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -18,6 +18,7 @@ #include "station_base.h" #include "news_func.h" #include "engine_func.h" +#include "engine_base.h" #include "player_func.h" #include "player_base.h" #include "depot_base.h" @@ -487,7 +488,7 @@ SpriteID Train::GetImage(Direction direction) const sprite = GetCustomVehicleSprite(this, (Direction)(direction + 4 * IS_CUSTOM_SECONDHEAD_SPRITE(spritenum))); if (sprite != 0) return sprite; - spritenum = _orig_rail_vehicle_info[this->engine_type].image_index; + spritenum = GetEngine(this->engine_type)->image_index; } sprite = _engine_sprite_base[spritenum] + ((direction + _engine_sprite_add[spritenum]) & _engine_sprite_and[spritenum]); @@ -509,7 +510,7 @@ static SpriteID GetRailIcon(EngineID engine, bool rear_head, int &y) return sprite; } - spritenum = _orig_rail_vehicle_info[engine].image_index; + spritenum = GetEngine(engine)->image_index; } if (rear_head) spritenum++; diff --git a/src/train_gui.cpp b/src/train_gui.cpp index 49ac235a53..c78ca9475b 100644 --- a/src/train_gui.cpp +++ b/src/train_gui.cpp @@ -17,6 +17,7 @@ #include "settings_type.h" #include "order_func.h" #include "engine_func.h" +#include "engine_base.h" #include "table/sprites.h" #include "table/strings.h" diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index d95aa9f1c2..e6499e9bce 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -35,6 +35,7 @@ #include "tunnelbridge.h" #include "player_base.h" #include "engine_func.h" +#include "engine_base.h" #include "economy_func.h" #include "rail.h" #include "cheat_func.h" diff --git a/src/vehicle.cpp b/src/vehicle.cpp index fc92bc7fce..e601a3f53a 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -1985,9 +1985,9 @@ bool CanBuildVehicleInfrastructure(VehicleType type) /* We can build vehicle infrastructure when we may build the vehicle type */ if (max > 0) { /* Can we actually build the vehicle type? */ - EngineID eid; - FOR_ALL_ENGINEIDS_OF_TYPE(eid, type) { - if (HasBit(GetEngine(eid)->player_avail, _local_player)) return true; + const Engine *e; + FOR_ALL_ENGINES_OF_TYPE(e, type) { + if (HasBit(e->player_avail, _local_player)) return true; } return false; }