diff options
-rw-r--r-- | projects/openttd.vcproj | 6 | ||||
-rw-r--r-- | projects/openttd_vs80.vcproj | 8 | ||||
-rw-r--r-- | source.list | 2 | ||||
-rw-r--r-- | src/ai/trolly/trolly.cpp | 4 | ||||
-rw-r--r-- | src/aircraft_cmd.cpp | 19 | ||||
-rw-r--r-- | src/aircraft_gui.cpp | 9 | ||||
-rw-r--r-- | src/autoreplace_cmd.cpp | 12 | ||||
-rw-r--r-- | src/cargopacket.cpp | 351 | ||||
-rw-r--r-- | src/cargopacket.h | 221 | ||||
-rw-r--r-- | src/economy.cpp | 181 | ||||
-rw-r--r-- | src/misc.cpp | 2 | ||||
-rw-r--r-- | src/newgrf_engine.cpp | 14 | ||||
-rw-r--r-- | src/newgrf_station.cpp | 22 | ||||
-rw-r--r-- | src/oldloader.cpp | 39 | ||||
-rw-r--r-- | src/openttd.cpp | 60 | ||||
-rw-r--r-- | src/openttd.h | 1 | ||||
-rw-r--r-- | src/roadveh_cmd.cpp | 8 | ||||
-rw-r--r-- | src/roadveh_gui.cpp | 14 | ||||
-rw-r--r-- | src/saveload.cpp | 21 | ||||
-rw-r--r-- | src/saveload.h | 16 | ||||
-rw-r--r-- | src/ship_cmd.cpp | 4 | ||||
-rw-r--r-- | src/ship_gui.cpp | 8 | ||||
-rw-r--r-- | src/station.cpp | 4 | ||||
-rw-r--r-- | src/station.h | 18 | ||||
-rw-r--r-- | src/station_cmd.cpp | 88 | ||||
-rw-r--r-- | src/station_gui.cpp | 31 | ||||
-rw-r--r-- | src/train_cmd.cpp | 11 | ||||
-rw-r--r-- | src/train_gui.cpp | 11 | ||||
-rw-r--r-- | src/vehicle.cpp | 73 | ||||
-rw-r--r-- | src/vehicle.h | 10 | ||||
-rw-r--r-- | src/water_cmd.cpp | 2 |
31 files changed, 949 insertions, 321 deletions
diff --git a/projects/openttd.vcproj b/projects/openttd.vcproj index ee294dc98..651884773 100644 --- a/projects/openttd.vcproj +++ b/projects/openttd.vcproj @@ -182,6 +182,9 @@ RelativePath=".\..\src\cargotype.cpp"> </File> <File + RelativePath=".\..\src\cargopacket.cpp"> + </File> + <File RelativePath=".\..\src\command.cpp"> </File> <File @@ -402,6 +405,9 @@ RelativePath=".\..\src\cargotype.h"> </File> <File + RelativePath=".\..\src\cargopacket.h"> + </File> + <File RelativePath=".\..\src\command.h"> </File> <File diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj index 28f1d7a01..47a302fb0 100644 --- a/projects/openttd_vs80.vcproj +++ b/projects/openttd_vs80.vcproj @@ -472,6 +472,10 @@ > </File> <File + RelativePath=".\..\src\cargopacket.cpp" + > + </File> + <File RelativePath=".\..\src\command.cpp" > </File> @@ -764,6 +768,10 @@ > </File> <File + RelativePath=".\..\src\cargopacket.h" + > + </File> + <File RelativePath=".\..\src\command.h" > </File> diff --git a/source.list b/source.list index 61a31086f..28ffb40d3 100644 --- a/source.list +++ b/source.list @@ -6,6 +6,7 @@ aystar.cpp bmp.cpp callback_table.cpp cargotype.cpp +cargopacket.cpp command.cpp console.cpp console_cmds.cpp @@ -101,6 +102,7 @@ articulated_vehicles.h aystar.h bmp.h cargotype.h +cargopacket.h command.h console.h currency.h diff --git a/src/ai/trolly/trolly.cpp b/src/ai/trolly/trolly.cpp index 1f540ceed..0b2beddac 100644 --- a/src/ai/trolly/trolly.cpp +++ b/src/ai/trolly/trolly.cpp @@ -617,7 +617,7 @@ static void AiNew_State_FindStation(Player *p) if (p->ainew.tbt == AI_BUS && (FACIL_BUS_STOP & st->facilities) == FACIL_BUS_STOP) { if (st->town == town) { // Check how much cargo there is left in the station - if ((st->goods[p->ainew.cargo].waiting_acceptance & 0xFFF) > RoadVehInfo(i)->capacity * AI_STATION_REUSE_MULTIPLER) { + if ((int)st->goods[p->ainew.cargo].cargo.Count() > RoadVehInfo(i)->capacity * AI_STATION_REUSE_MULTIPLER) { if (AiNew_CheckVehicleStation(p, st)) { // We did found a station that was good enough! new_tile = st->xy; @@ -1258,7 +1258,7 @@ static void AiNew_CheckVehicle(Player *p, Vehicle *v) if (v->profit_last_year + v->profit_this_year < AI_MINIMUM_ROUTE_PROFIT || (v->reliability * 100 >> 16) < 40) { // There is a possibility that the route is fucked up... - if (v->cargo_days > AI_VEHICLE_LOST_DAYS) { + if (v->cargo.DaysInTransit() > AI_VEHICLE_LOST_DAYS) { // The vehicle is lost.. check the route, or else, get the vehicle // back to a depot // TODO: make this piece of code diff --git a/src/aircraft_cmd.cpp b/src/aircraft_cmd.cpp index 2f4f90daa..92720a335 100644 --- a/src/aircraft_cmd.cpp +++ b/src/aircraft_cmd.cpp @@ -680,13 +680,8 @@ CommandCost CmdRefitAircraft(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) Vehicle *u = v->next; uint mail = IsCargoInClass(new_cid, CC_PASSENGERS) ? avi->mail_capacity : 0; u->cargo_cap = mail; - if (v->cargo_type == new_cid) { - v->cargo_count = min(pass, v->cargo_count); - u->cargo_count = min(mail, u->cargo_count); - } else { - v->cargo_count = 0; - u->cargo_count = 0; - } + v->cargo.Truncate(v->cargo_type == new_cid ? pass : 0); + u->cargo.Truncate(v->cargo_type == new_cid ? mail : 0); v->cargo_type = new_cid; v->cargo_subtype = new_subtype; InvalidateWindow(WC_VEHICLE_DETAILS, v->index); @@ -774,7 +769,7 @@ static void AgeAircraftCargo(Vehicle *v) if (_age_cargo_skip_counter != 0) return; do { - if (v->cargo_days != 0xFF) v->cargo_days++; + v->cargo.AgeCargo(); v = v->next; } while (v != NULL); } @@ -1429,11 +1424,11 @@ static void CrashAirplane(Vehicle *v) InvalidateWindow(WC_VEHICLE_VIEW, v->index); uint amt = 2; - if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) amt += v->cargo_count; + if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) amt += v->cargo.Count(); SetDParam(0, amt); - v->cargo_count = 0; - v->next->cargo_count = 0; + v->cargo.Truncate(0); + v->next->cargo.Truncate(0); const Station *st = GetStation(v->u.air.targetairport); StringID newsitem; if (st->airport_tile == 0) { @@ -1469,7 +1464,7 @@ static void MaybeCrashAirplane(Vehicle *v) /* Crash the airplane. Remove all goods stored at the station. */ for (CargoID i = 0; i < NUM_CARGO; i++) { st->goods[i].rating = 1; - SB(st->goods[i].waiting_acceptance, 0, 12, 0); + st->goods[i].cargo.Truncate(0); } CrashAirplane(v); diff --git a/src/aircraft_gui.cpp b/src/aircraft_gui.cpp index b63365361..5ddea35fd 100644 --- a/src/aircraft_gui.cpp +++ b/src/aircraft_gui.cpp @@ -125,7 +125,7 @@ static void AircraftDetailsWndProc(Window *w, WindowEvent *e) /* Draw Transfer credits text */ { - SetDParam(0, v->cargo_feeder_share); + SetDParam(0, v->cargo.FeederShare()); DrawString(60, 101, STR_FEEDER_CARGO_VALUE, 0); } @@ -152,12 +152,13 @@ static void AircraftDetailsWndProc(Window *w, WindowEvent *e) y += 14; } - if (v->cargo_count != 0) { + uint cargo_count = v->cargo.Count(); + if (cargo_count != 0) { /* Cargo names (fix pluralness) */ SetDParam(0, v->cargo_type); - SetDParam(1, v->cargo_count); - SetDParam(2, v->cargo_source); + SetDParam(1, cargo_count); + SetDParam(2, v->cargo.Source()); DrawString(60, y, STR_8813_FROM, 0); y += 10; diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp index 14c5e3310..88df05c5c 100644 --- a/src/autoreplace_cmd.cpp +++ b/src/autoreplace_cmd.cpp @@ -25,27 +25,23 @@ static void MoveVehicleCargo(Vehicle *dest, Vehicle *source) { Vehicle *v = dest; - int units_moved; do { do { if (source->cargo_type != dest->cargo_type) continue; // cargo not compatible - if (dest->cargo_count == dest->cargo_cap) + if (dest->cargo.Count() == dest->cargo_cap) continue; // the destination vehicle is already full - units_moved = min(source->cargo_count, dest->cargo_cap - dest->cargo_count); - source->cargo_count -= units_moved; - dest->cargo_count += units_moved; - dest->cargo_source = source->cargo_source; + uint units_moved = min(source->cargo.Count(), dest->cargo_cap - dest->cargo.Count()); + source->cargo.MoveTo(&dest->cargo, units_moved); // copy the age of the cargo - dest->cargo_days = source->cargo_days; dest->day_counter = source->day_counter; dest->tick_counter = source->tick_counter; - } while (source->cargo_count > 0 && (dest = dest->next) != NULL); + } while (source->cargo.Count() > 0 && (dest = dest->next) != NULL); dest = v; } while ((source = source->next) != NULL); diff --git a/src/cargopacket.cpp b/src/cargopacket.cpp new file mode 100644 index 000000000..897812b11 --- /dev/null +++ b/src/cargopacket.cpp @@ -0,0 +1,351 @@ +/* $Id$ */ + +/** @file cargopacket.cpp Implementation of the cargo packets */ + +#include "stdafx.h" +#include "openttd.h" +#include "station.h" +#include "cargopacket.h" +#include "saveload.h" + +/** + * Called if a new block is added to the station-pool + */ +static void CargoPacketPoolNewBlock(uint cpart_item) +{ + /* We don't use FOR_ALL here, because FOR_ALL skips invalid items. + * TODO - This is just a temporary stage, this will be removed. */ + for (CargoPacket *cp = GetCargoPacket(cpart_item); cp != NULL; cp = (cp->index + 1U < GetCargoPacketPoolSize()) ? GetCargoPacket(cp->index + 1U) : NULL) cp->index = cpart_item++; +} + +static void CargoPacketPoolCleanBlock(uint cpart_item, uint end_item) +{ + for (uint i = cpart_item; i <= end_item; i++) { + CargoPacket *cp = GetCargoPacket(i); + if (cp->IsValid()) cp->~CargoPacket(); + } +} + +/* Initialize the cargopacket-pool */ +DEFINE_OLD_POOL(CargoPacket, CargoPacket, CargoPacketPoolNewBlock, CargoPacketPoolCleanBlock) + +void InitializeCargoPackets() +{ + /* Clean the cargo packet pool and create 1 block in it */ + CleanPool(&_CargoPacket_pool); + AddBlockToPool(&_CargoPacket_pool); + + /* Check whether our &cargolist == &cargolist.packets "hack" works */ + CargoList::AssertOnWrongPacketOffset(); +} + +CargoPacket::CargoPacket(StationID source, uint16 count) +{ + if (source != INVALID_STATION) assert(count != 0); + + this->source = source; + this->source_xy = (source != INVALID_STATION) ? GetStation(source)->xy : 0; + this->loaded_at_xy = this->source_xy; + + this->count = count; + this->days_in_transit = 0; + this->feeder_share = 0; + this->paid_for = false; +} + +CargoPacket::~CargoPacket() +{ + this->count = 0; +} + +bool CargoPacket::SameSource(CargoPacket *cp) +{ + return this->source_xy == cp->source_xy && this->days_in_transit == cp->days_in_transit && this->paid_for == cp->paid_for; +} + +void *CargoPacket::operator new(size_t size) +{ + CargoPacket *cp = AllocateRaw(); + return cp; +} + +void *CargoPacket::operator new(size_t size, CargoPacket::ID cp_idx) +{ + if (!AddBlockIfNeeded(&_CargoPacket_pool, cp_idx)) + error("CargoPackets: failed loading savegame: too many cargo packets"); + + CargoPacket *cp = GetCargoPacket(cp_idx); + return cp; +} + +void CargoPacket::operator delete(void *p) +{ +} + +void CargoPacket::operator delete(void *p, CargoPacket::ID cp_idx) +{ +} + +/*static*/ CargoPacket *CargoPacket::AllocateRaw() +{ + CargoPacket *cp = NULL; + + /* We don't use FOR_ALL here, because FOR_ALL skips invalid items. + * TODO - This is just a temporary stage, this will be removed. */ + for (cp = GetCargoPacket(0); cp != NULL; cp = (cp->index + 1U < GetCargoPacketPoolSize()) ? GetCargoPacket(cp->index + 1U) : NULL) { + if (!cp->IsValid()) { + CargoPacket::ID index = cp->index; + + memset(cp, 0, sizeof(CargoPacket)); + cp->index = index; + return cp; + } + } + + /* Check if we can add a block to the pool */ + if (AddBlockToPool(&_CargoPacket_pool)) return AllocateRaw(); + + error("CargoPackets: too many cargo packets"); +} + +static const SaveLoad _cargopacket_desc[] = { + SLE_VAR(CargoPacket, source, SLE_UINT16), + SLE_VAR(CargoPacket, source_xy, SLE_UINT32), + SLE_VAR(CargoPacket, loaded_at_xy, SLE_UINT32), + SLE_VAR(CargoPacket, count, SLE_UINT16), + SLE_VAR(CargoPacket, days_in_transit, SLE_UINT8), + SLE_VAR(CargoPacket, feeder_share, SLE_INT64), + SLE_VAR(CargoPacket, paid_for, SLE_BOOL), + + SLE_END() +}; + +static void Save_CAPA() +{ + CargoPacket *cp; + + FOR_ALL_CARGOPACKETS(cp) { + SlSetArrayIndex(cp->index); + SlObject(cp, _cargopacket_desc); + } +} + +static void Load_CAPA() +{ + int index; + + while ((index = SlIterateArray()) != -1) { + if (!AddBlockIfNeeded(&_CargoPacket_pool, index)) { + error("CargoPackets: failed loading savegame: too many cargo packets"); + } + + CargoPacket *cp = GetCargoPacket(index); + SlObject(cp, _cargopacket_desc); + } +} + +extern const ChunkHandler _cargopacket_chunk_handlers[] = { + { 'CAPA', Save_CAPA, Load_CAPA, CH_ARRAY | CH_LAST}, +}; + +/* + * + * Cargo list implementation + * + */ + +/* static */ void CargoList::AssertOnWrongPacketOffset() +{ + CargoList cl; + if ((void*)&cl != (void*)cl.Packets()) NOT_REACHED(); +} + + +CargoList::~CargoList() +{ + while (!packets.empty()) { + delete packets.front(); + packets.pop_front(); + } +} + +const CargoList::List *CargoList::Packets() const +{ + return &packets; +} + +void CargoList::AgeCargo() +{ + if (empty) return; + + uint dit = 0; + for (List::const_iterator it = packets.begin(); it != packets.end(); it++) { + if ((*it)->days_in_transit != 0xFF) (*it)->days_in_transit++; + dit += (*it)->days_in_transit * (*it)->count; + } + days_in_transit = dit / count; +} + +bool CargoList::Empty() const +{ + return empty; +} + +uint CargoList::Count() const +{ + return count; +} + +bool CargoList::UnpaidCargo() const +{ + return unpaid_cargo; +} + +Money CargoList::FeederShare() const +{ + return feeder_share; +} + +StationID CargoList::Source() const +{ + return source; +} + +uint CargoList::DaysInTransit() const +{ + return days_in_transit; +} + +void CargoList::Append(CargoPacket *cp) +{ + assert(cp != NULL); + assert(cp->IsValid()); + + for (List::iterator it = packets.begin(); it != packets.end(); it++) { + if ((*it)->SameSource(cp)) { + (*it)->count += cp->count; + (*it)->feeder_share += cp->feeder_share; + delete cp; + + InvalidateCache(); + return; + } + } + + /* The packet could not be merged with another one */ + packets.push_back(cp); + InvalidateCache(); +} + + +void CargoList::Truncate(uint count) +{ + for (List::iterator it = packets.begin(); it != packets.end(); it++) { + uint local_count = (*it)->count; + if (local_count <= count) { + count -= local_count; + continue; + } + + (*it)->count = count; + count = 0; + } + + while (!packets.empty()) { + CargoPacket *cp = packets.back(); + if (cp->count != 0) break; + delete cp; + packets.pop_back(); + } + + InvalidateCache(); +} + +bool CargoList::MoveTo(CargoList *dest, uint count, CargoList::MoveToAction mta, uint data) +{ + assert(mta == MTA_FINAL_DELIVERY || dest != NULL); + CargoList tmp; + + while (!packets.empty() && count > 0) { + CargoPacket *cp = *packets.begin(); + if (cp->count <= count) { + /* Can move the complete packet */ + packets.remove(cp); + switch (mta) { + case MTA_FINAL_DELIVERY: + if (cp->source == data) { + tmp.Append(cp); + } else { + count -= cp->count; + delete cp; + } + break; + case MTA_CARGO_LOAD: + cp->loaded_at_xy = data; + /* When cargo is moved into another vehicle you have *always* paid for it */ + cp->paid_for = false; + /* FALL THROUGH */ + case MTA_OTHER: + count -= cp->count; + dest->packets.push_back(cp); + break; + } + } else { + /* Can move only part of the packet, so split it into two pieces */ + if (mta != MTA_FINAL_DELIVERY) { + CargoPacket *cp_new = new CargoPacket(); + cp_new->source = cp->source; + cp_new->source_xy = cp->source_xy; + cp_new->loaded_at_xy = (mta == MTA_CARGO_LOAD) ? data : cp->loaded_at_xy; + + cp_new->days_in_transit = cp->days_in_transit; + cp_new->feeder_share = cp->feeder_share / count; + /* When cargo is moved into another vehicle you have *always* paid for it */ + cp_new->paid_for = (mta == MTA_CARGO_LOAD) ? false : cp->paid_for; + + cp_new->count = count; + dest->packets.push_back(cp_new); + + cp->feeder_share /= cp->count - count; + } + cp->count -= count; + + count = 0; + } + } + + bool remaining = !packets.empty(); + + if (mta == MTA_FINAL_DELIVERY && !tmp.Empty()) { + /* There are some packets that could not be delivered at the station, put them back */ + tmp.MoveTo(this, MAX_UVALUE(uint)); + tmp.packets.clear(); + } + + if (dest != NULL) dest->InvalidateCache(); + InvalidateCache(); + + return remaining; +} + +void CargoList::InvalidateCache() +{ + empty = packets.empty(); + count = 0; + unpaid_cargo = false; + feeder_share = 0; + source = INVALID_STATION; + days_in_transit = 0; + + if (empty) return; + + uint dit = 0; + for (List::const_iterator it = packets.begin(); it != packets.end(); it++) { + count += (*it)->count; + unpaid_cargo |= !(*it)->paid_for; + dit += (*it)->days_in_transit * (*it)->count; + feeder_share += (*it)->feeder_share; + } + days_in_transit = dit / count; + source = (*packets.begin())->source; +} diff --git a/src/cargopacket.h b/src/cargopacket.h new file mode 100644 index 000000000..fab8418d5 --- /dev/null +++ b/src/cargopacket.h @@ -0,0 +1,221 @@ +/* $Id$ */ + +/** @file cargotype.h */ + +#ifndef CARGOPACKET_H +#define CARGOPACKET_H + +#include <list> + +/** + * Container for cargo from the same location and time + */ +struct CargoPacket { + bool touched; + + typedef uint32 ID; ///< Type for cargopacket identifiers + + ID index; ///< The unique index of this packet + + StationID source; ///< The station where the cargo came from first + TileIndex source_xy; ///< The origin of the cargo (first station in feeder chain) + TileIndex loaded_at_xy; ///< Location where this cargo has been loaded into the vehicle + + uint16 count; ///< The amount of cargo in this packet + byte days_in_transit; ///< Amount of days this packet has been in transit + Money feeder_share; ///< Value of feeder pickup to be paid for on delivery of cargo + bool paid_for; ///< Have we been paid for this cargo packet? + + /** + * Creates a new cargo packet + * @param source the source of the packet + * @param count the number of cargo entities to put in this packet + * @pre count != 0 || source == INVALID_STATION + */ + CargoPacket(StationID source = INVALID_STATION, uint16 count = 0); + + /** Destroy the packet */ + ~CargoPacket(); + + + /** + * Is this a valid cargo packet ? + * @return true if and only it is valid + */ + bool IsValid() const { return this->count != 0; } + + /** + * Checks whether the cargo packet is from (exactly) the same source + * in time and location. + * @param cp the cargo packet to compare to + * @return true if and only if days_in_transit and source_xy are equal + */ + bool SameSource(CargoPacket *cp); + + + /* normal new/delete operators. Used when building/removing station */ + void* operator new (size_t size); + void operator delete(void *p); + + /* new/delete operators accepting station index. Used when loading station from savegame. */ + void* operator new (size_t size, CargoPacket::ID cp_idx); + void operator delete(void *p, CargoPacket::ID cp_idx); + +private: + /** + * Allocate the raw memory for this cargo packet + * @return the allocated memory + */ + static CargoPacket *AllocateRaw(); +}; + +/** We want to use a pool */ +DECLARE_OLD_POOL(CargoPacket, CargoPacket, 10, 1000) + +/** + * Iterate over all _valid_ cargo packets from the given start + * @param cp the variable used as "iterator" + * @param start the cargo packet ID of the first packet to iterate over + */ +#define FOR_ALL_CARGOPACKETS_FROM(cp, start) for (cp = GetCargoPacket(start); cp != NULL; cp = (cp->index + 1U < GetCargoPacketPoolSize()) ? GetCargoPacket(cp->index + 1U) : NULL) if (cp->IsValid()) + +/** + * Iterate over all _valid_ cargo packets from the begin of the pool + * @param cp the variable used as "iterator" + */ +#define FOR_ALL_CARGOPACKETS(cp) FOR_ALL_CARGOPACKETS_FROM(cp, 0) + +/** + * Simple collection class for a list of cargo packets + */ +class CargoList { +public: + /** List of cargo packets */ + typedef std::list<CargoPacket *> List; + + /** Kind of actions that could be done with packets on move */ + enum MoveToAction { + MTA_FINAL_DELIVERY, ///< "Deliver" the packet to the final destination, i.e. destroy the packet + MTA_CARGO_LOAD, ///< Load the packet onto a vehicle, i.e. set the last loaded station ID + MTA_OTHER ///< "Just" move the packet to another cargo list + }; + +private: + List packets; ///< The cargo packets in this list + + bool empty; ///< Cache for whether this list is empty or not + uint count; ///< Cache for the number of cargo entities + bool unpaid_cargo; ///< Cache for the unpaid cargo + Money feeder_share; ///< Cache for the feeder share + StationID source; ///< Cache for the source of the packet + uint days_in_transit; ///< Cache for the number of days in transit + +public: + /** + * Needed for an ugly hack: + * - vehicles and stations need to store cargo lists, so they use CargoList as container + * - this internals of the container should be protected, e.g. private (or protected) by C++ + * - for saving/loading we need to pass pointer to objects + * -> so *if* the pointer to the cargo list is the same as the pointer to the packet list + * encapsulated in the CargoList, we can just pass the CargoList as "offset". + * Normally we would then just add the offset of the packets variable within the cargo list + * but that is not possible because the variable is private. Furthermore we are not sure + * that this works on all platforms, we need to check whether the offset is actually 0. + * This cannot be done compile time, because the variable is private. So we need to write + * a function that does actually check the offset runtime and call it somewhere where it + * is always called but it should not be called often. + */ + static void AssertOnWrongPacketOffset(); + + /** Create the cargo list */ + CargoList() { this->InvalidateCache(); } + /** And destroy it ("frees" all cargo packets) */ + ~CargoList(); + + /** + * Returns a pointer to the cargo packet list (so you can iterate over it etc). + * @return pointer to the packet list + */ + const CargoList::List *Packets() const; + + /** + * Ages the all cargo in this list + */ + void AgeCargo(); + + /** + * Checks whether this list is empty + * @return true if and only if the list is empty + */ + bool Empty() const; + + /** + * Returns the number of cargo entities in this list + * @return the before mentioned number + */ + uint Count() const; + + /** + * Is there some cargo that has not been paid for? + * @return true if and only if there is such a cargo + */ + bool UnpaidCargo() const; + + /** + * Returns total sum of the feeder share for all packets + * @return the before mentioned number + */ + Money FeederShare() const; + + /** + * Returns source of the first cargo packet in this list + * @return the before mentioned source + */ + StationID Source() const; + + /** + * Returns average number of days in transit for a cargo entity + * @return the before mentioned number + */ + uint DaysInTransit() const; + + + /** + * Appends the given cargo packet + * @warning After appending this packet may not exist anymore! + * @note Do not use the cargo packet anymore after it has been appended to this CargoList! + * @param cp the cargo packet to add + * @pre cp != NULL + */ + void Append(CargoPacket *cp); + + /** + * Truncates the cargo in this list to the given amount. It leaves the + * first count cargo entities and removes the rest. + * @param count the maximum amount of entities to be in the list after the command + */ + void Truncate(uint count); + + /** + * Moves the given amount of cargo to another list. + * Depending on the value of mta the side effects of this function differ: + * - MTA_FINAL_DELIVERY: destroys the packets that do not originate from a specific station + * - MTA_CARGO_LOAD: sets the loaded_at_xy value of the moved packets + * - MTA_OTHER: just move without side effects + * @param dest the destination to move the cargo to + * @param count the amount of cargo entities to move + * @param mta how to handle the moving (side effects) + * @param data Depending on mta the data of this variable differs: + * - MTA_FINAL_DELIVERY - station ID of packet's origin not to remove + * - MTA_CARGO_LOAD - station's tile index of load + * - MTA_OTHER - unused + * @param mta == MTA_FINAL_DELIVERY || dest != NULL + * @return true if there are still packets that might be moved from this cargo list + */ + bool MoveTo(CargoList *dest, uint count, CargoList::MoveToAction mta = MTA_OTHER, uint data = 0); + + /** Invalidates the cached data and rebuild it */ + void InvalidateCache(); +}; + +#endif /* CARGOPACKET_H */ diff --git a/src/economy.cpp b/src/economy.cpp index 62240444d..b2091057d 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -1371,14 +1371,9 @@ void VehiclePayment(Vehicle *front_v) { int result = 0; - Money profit = 0; - Money total_veh_profit = 0; // accumulates the profit across the vehicle chain (used by trains) - Money route_profit = 0; // the grand total amount for the route. A-D of transfer chain A-B-C-D - Money virtual_profit = 0; // virtual profit of one vehicle element for feeder systems - Money virtual_profit_total = 0; // virtual profit for entire vehicle chain - Money total_cargo_feeder_share = 0; // the feeder cash amount for the goods being loaded/unloaded in this load step - - Money all_vehicles_cargo_feeder_share = front_v->cargo_feeder_share; // used to hold transfer value of complete vehicle chain - used by trains + Money vehicle_profit = 0; // Money paid to the train + Money route_profit = 0; // The grand total amount for the route. A-D of transfer chain A-B-C-D + Money virtual_profit = 0; // The virtual profit for entire vehicle chain StationID last_visited = front_v->last_station_visited; Station *st = GetStation(last_visited); @@ -1395,75 +1390,70 @@ void VehiclePayment(Vehicle *front_v) for (Vehicle *v = front_v; v != NULL; v = v->next) { /* No cargo to unload */ - if (v->cargo_cap == 0 || v->cargo_count == 0) continue; + if (v->cargo_cap == 0 || v->cargo.Empty()) continue; /* All cargo has already been paid for, no need to pay again */ - if (v->cargo_count == v->cargo_paid_for) { + if (!v->cargo.UnpaidCargo()) { SETBIT(v->vehicle_flags, VF_CARGO_UNLOADING); continue; } GoodsEntry *ge = &st->goods[v->cargo_type]; + const CargoList::List *cargos = v->cargo.Packets(); + + for (CargoList::List::const_iterator it = cargos->begin(); it != cargos->end(); it++) { + CargoPacket *cp = *it; + if (!cp->paid_for && + cp->source != last_visited && + ge->acceptance && + (front_v->current_order.flags & OF_TRANSFER) == 0) { + /* Deliver goods to the station */ + st->time_since_unload = 0; + + /* handle end of route payment */ + Money profit = DeliverGoods(cp->count, v->cargo_type, cp->source, last_visited, cp->source_xy, cp->days_in_transit); + cp->paid_for = true; + route_profit += profit - cp->feeder_share; // display amount paid for final route delivery, A-D of a chain A-B-C-D + vehicle_profit += profit; // whole vehicle is not payed for transfers picked up earlier - if (v->cargo_source != last_visited && - HASBIT(ge->waiting_acceptance, 15) && - (front_v->current_order.flags & OF_TRANSFER) == 0) { - /* Deliver goods to the station */ - st->time_since_unload = 0; - - /* handle end of route payment */ - profit += DeliverGoods(v->cargo_count - v->cargo_paid_for, v->cargo_type, v->cargo_source, last_visited, v->cargo_source_xy, v->cargo_days); - v->cargo_paid_for = v->cargo_count; - route_profit = profit; // display amount paid for final route delivery, A-D of a chain A-B-C-D - total_veh_profit = profit - all_vehicles_cargo_feeder_share; // whole vehicle is not payed for transfers picked up earlier - total_cargo_feeder_share = -all_vehicles_cargo_feeder_share; // total of transfer fees in vehicle chain needs to be zero at end of unload + result |= 1; - v->cargo_feeder_share = 0; // clear transfer cost per vehicle - result |= 1; + SETBIT(v->vehicle_flags, VF_CARGO_UNLOADING); + } else if (front_v->current_order.flags & (OF_UNLOAD | OF_TRANSFER)) { + if (!cp->paid_for && (front_v->current_order.flags & OF_TRANSFER) != 0) { + Money profit = GetTransportedGoodsIncome( + cp->count, + /* pay transfer vehicle for only the part of transfer it has done: ie. cargo_loaded_at_xy to here */ + DistanceManhattan(cp->loaded_at_xy, GetStation(last_visited)->xy), + cp->days_in_transit, + v->cargo_type); + + front_v->profit_this_year += profit; + virtual_profit += profit; // accumulate transfer profits for whole vehicle + cp->feeder_share += profit; // account for the (virtual) profit already made for the cargo packet + cp->paid_for = true; // record that the cargo has been paid for to eliminate double counting + } + result |= 2; - SETBIT(v->vehicle_flags, VF_CARGO_UNLOADING); - } else if (front_v->current_order.flags & (OF_UNLOAD | OF_TRANSFER)) { - if ((front_v->current_order.flags & OF_TRANSFER) != 0) { - virtual_profit = GetTransportedGoodsIncome( - v->cargo_count - v->cargo_paid_for, - /* pay transfer vehicle for only the part of transfer it has done: ie. cargo_loaded_at_xy to here */ - DistanceManhattan(v->cargo_loaded_at_xy, GetStation(last_visited)->xy), - v->cargo_days, - v->cargo_type); - - front_v->profit_this_year += virtual_profit; - ge->feeder_profit += v->cargo_feeder_share + virtual_profit; // transfer cargo transfer fees to station - total_cargo_feeder_share -= v->cargo_feeder_share; // accumulate deduction of feeder shares - v->cargo_feeder_share = 0; // clear transfer cost - - /* keep total of cargo unloaded (pending) for accurate cargoshare calculation on load */ - SB(ge->unload_pending, 0, 12, GB(ge->unload_pending, 0, 12) + v->cargo_count); - - virtual_profit_total += virtual_profit; // accumulate transfer profits for whole vehicle - v->cargo_paid_for = v->cargo_count; // record how much of the cargo has been paid for to eliminate double counting + SETBIT(v->vehicle_flags, VF_CARGO_UNLOADING); } - result |= 2; - - SETBIT(v->vehicle_flags, VF_CARGO_UNLOADING); } + v->cargo.InvalidateCache(); } - /* Ensure a negative total is only applied to the vehicle if there is value to reduce. */ - front_v->cargo_feeder_share = max(front_v->cargo_feeder_share + total_cargo_feeder_share, 0LL); - - if (virtual_profit_total > 0) { - ShowFeederIncomeAnimation(front_v->x_pos, front_v->y_pos, front_v->z_pos, virtual_profit_total); + if (virtual_profit > 0) { + ShowFeederIncomeAnimation(front_v->x_pos, front_v->y_pos, front_v->z_pos, virtual_profit); } if (route_profit != 0) { - front_v->profit_this_year += total_veh_profit; + front_v->profit_this_year += vehicle_profit; SubtractMoneyFromPlayer(-route_profit); if (IsLocalPlayer() && !PlayVehicleSound(front_v, VSE_LOAD_UNLOAD)) { SndPlayVehicleFx(SND_14_CASHTILL, front_v); } - ShowCostOrIncomeAnimation(front_v->x_pos, front_v->y_pos, front_v->z_pos, -total_veh_profit); + ShowCostOrIncomeAnimation(front_v->x_pos, front_v->y_pos, front_v->z_pos, -vehicle_profit); } _current_player = old_player; @@ -1486,7 +1476,7 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left) if (_patches.improved_load && HASBIT(v->current_order.flags, OFB_FULL_LOAD)) { /* 'Reserve' this cargo for this vehicle, because we were first. */ for (; v != NULL; v = v->next) { - if (v->cargo_cap != 0) cargo_left[v->cargo_type] -= v->cargo_cap - v->cargo_count; + if (v->cargo_cap != 0) cargo_left[v->cargo_type] -= v->cargo_cap - v->cargo.Count(); } } return; @@ -1502,14 +1492,13 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left) int unloading_time = 0; Vehicle *u = v; int result = 0; - int cap; + uint cap; bool completely_empty = true; bool anything_unloaded = false; bool anything_loaded = false; uint32 cargo_not_full = 0; uint32 cargo_full = 0; - Money total_cargo_feeder_share = 0; // the feeder cash amount for the goods being loaded/unloaded in this load step v->cur_speed = 0; @@ -1526,36 +1515,19 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left) } GoodsEntry *ge = &st->goods[v->cargo_type]; - int count = GB(ge->waiting_acceptance, 0, 12); if (HASBIT(v->vehicle_flags, VF_CARGO_UNLOADING)) { - uint16 amount_unloaded = _patches.gradual_loading ? min(v->cargo_count, load_amount) : v->cargo_count; + uint cargo_count = v->cargo.Count(); + uint amount_unloaded = _patches.gradual_loading ? min(cargo_count, load_amount) : cargo_count; + bool remaining; // Are there cargo entities in this vehicle that can still be unloaded here? + + if (ge->acceptance && !(u->current_order.flags & OF_TRANSFER)) { + /* The cargo has reached it's final destination, the packets may now be destroyed */ + remaining = v->cargo.MoveTo(NULL, amount_unloaded, CargoList::MTA_FINAL_DELIVERY, last_visited); - if (v->cargo_source != last_visited && ge->waiting_acceptance & 0x8000 && !(u->current_order.flags & OF_TRANSFER)) { result |= 1; } else if (u->current_order.flags & (OF_UNLOAD | OF_TRANSFER)) { - if (count == 0) { - /* No goods waiting at station */ - ge->enroute_time = v->cargo_days; - ge->enroute_from = v->cargo_source; - ge->enroute_from_xy = v->cargo_source_xy; - } else { - /* Goods already waiting at station. Set counters to the worst value. */ - if (v->cargo_days >= ge->enroute_time) ge->enroute_time = v->cargo_days; - - if (last_visited != ge->enroute_from) { - ge->enroute_from = v->cargo_source; - ge->enroute_from_xy = v->cargo_source_xy; - } - } - /* Update amount of waiting cargo. There is, however, no sense in - * updating the count variable because this vehicle will not be - * able to take the cargo. */ - SB(ge->waiting_acceptance, 0, 12, min(amount_unloaded + count, 0xFFF)); - - /* if there is not enough to unload from pending, ensure it does not go -ve - * else deduct amount actually unloaded from unload_pending */ - SB(ge->unload_pending, 0, 12, max(GB(ge->unload_pending, 0, 12) - amount_unloaded, 0U)); + remaining = v->cargo.MoveTo(&ge->cargo, amount_unloaded); result |= 2; } else { @@ -1570,11 +1542,8 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left) unloading_time += amount_unloaded; - v->cargo_count -= amount_unloaded; - v->cargo_paid_for -= min(amount_unloaded, v->cargo_paid_for); - anything_unloaded = true; - if (_patches.gradual_loading && v->cargo_count != 0) { + if (_patches.gradual_loading && remaining) { completely_empty = false; } else { /* We have finished unloading (cargo count == 0) */ @@ -1584,9 +1553,6 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left) continue; } - /* We cannot have paid for more cargo than there is on board. */ - assert(v->cargo_paid_for <= v->cargo_count); - /* Do not pick up goods that we unloaded */ if (u->current_order.flags & OF_UNLOAD) continue; @@ -1605,8 +1571,9 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left) /* If there's goods waiting at the station, and the vehicle * has capacity for it, load it on the vehicle. */ - if (count != 0 && - (cap = v->cargo_cap - v->cargo_count) != 0) { + if (!ge->cargo.Empty() && + (cap = v->cargo_cap - v->cargo.Count()) != 0) { + uint count = ge->cargo.Count(); /* Skip loading this vehicle if another train/vehicle is already handling * the same cargo type at this station */ @@ -1623,7 +1590,7 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left) cargo_left[v->cargo_type] -= cap; } - if (v->cargo_count == 0) TriggerVehicle(v, VEHICLE_TRIGGER_NEW_CARGO); + if (v->cargo.Empty()) TriggerVehicle(v, VEHICLE_TRIGGER_NEW_CARGO); /* TODO: Regarding this, when we do gradual loading, we * should first unload all vehicles and then start @@ -1635,32 +1602,16 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left) completely_empty = false; anything_loaded = true; - /* cargoshare is proportioned by the amount due to unload - * Otherwise, with gradual loading, 100% of credits would be taken immediately, - * even if the cargo volume represents a tiny percent of the whole. - * ge->unload_pending holds the amount that has been credited, but has not yet been unloaded. - */ - int cargoshare = cap * 10000 / (ge->waiting_acceptance + ge->unload_pending); - Money feeder_profit_share = ge->feeder_profit * cargoshare / 10000; - v->cargo_count += cap; - ge->waiting_acceptance -= cap; - - total_cargo_feeder_share += feeder_profit_share; // store cost for later payment when cargo unloaded - v->cargo_loaded_at_xy = st->xy; // retains location of where the cargo was picked up for intermediate payments + ge->cargo.MoveTo(&v->cargo, cap, CargoList::MTA_CARGO_LOAD, st->xy); - ge->feeder_profit -= feeder_profit_share; unloading_time += cap; st->time_since_load = 0; + st->last_vehicle_type = v->type; - /* And record the source of the cargo, and the days in travel. */ - v->cargo_source = ge->enroute_from; - v->cargo_source_xy = ge->enroute_from_xy; - v->cargo_days = ge->enroute_time; result |= 2; - st->last_vehicle_type = v->type; } - if (v->cargo_count == v->cargo_cap) { + if (v->cargo.Count() == v->cargo_cap) { SETBIT(cargo_full, v->cargo_type); } else { SETBIT(cargo_not_full, v->cargo_type); @@ -1674,14 +1625,12 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left) if (_patches.improved_load && HASBIT(u->current_order.flags, OFB_FULL_LOAD)) { /* Update left cargo */ for (v = u; v != NULL; v = v->next) { - if (v->cargo_cap != 0) cargo_left[v->cargo_type] -= v->cargo_cap - v->cargo_count; + if (v->cargo_cap != 0) cargo_left[v->cargo_type] -= v->cargo_cap - v->cargo.Count(); } } v = u; - v->cargo_feeder_share += total_cargo_feeder_share; - if (anything_loaded || anything_unloaded) { if (_patches.gradual_loading) { /* The time it takes to load one 'slice' of cargo or passengers depends @@ -1696,7 +1645,7 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left) if (_patches.full_load_any) { /* if the aircraft carries passengers and is NOT full, then * continue loading, no matter how much mail is in */ - if ((v->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS) && v->cargo_cap != v->cargo_count) || + if ((v->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS) && v->cargo_cap != v->cargo.Count()) || (cargo_not_full && (cargo_full & ~cargo_not_full) == 0)) { // There are stull non-full cargos finished_loading = false; } @@ -1754,7 +1703,7 @@ void LoadUnloadStation(Station *st) { int cargo_left[NUM_CARGO]; - for (uint i = 0; i < NUM_CARGO; i++) cargo_left[i] = GB(st->goods[i].waiting_acceptance, 0, 12); + for (uint i = 0; i < NUM_CARGO; i++) cargo_left[i] = st->goods[i].cargo.Count(); std::list<Vehicle *>::iterator iter; for (iter = st->loading_vehicles.begin(); iter != st->loading_vehicles.end(); ++iter) { diff --git a/src/misc.cpp b/src/misc.cpp index d143980c4..8c80e6035 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -93,6 +93,7 @@ void InitializeTowns(); void InitializeTrees(); void InitializeSigns(); void InitializeStations(); +void InitializeCargoPackets(); static void InitializeNameMgr(); void InitializePlayers(); static void InitializeCheats(); @@ -134,6 +135,7 @@ void InitializeGame(int mode, uint size_x, uint size_y) InitializeTrees(); InitializeSigns(); InitializeStations(); + InitializeCargoPackets(); InitializeIndustries(); InitializeBuildingCounts(); InitializeMainGui(); diff --git a/src/newgrf_engine.cpp b/src/newgrf_engine.cpp index fc2dacdcf..48d06b329 100644 --- a/src/newgrf_engine.cpp +++ b/src/newgrf_engine.cpp @@ -705,10 +705,10 @@ static uint32 VehicleGetVariable(const ResolverObject *object, byte variable, by case 0x39: return v->cargo_type; case 0x3A: return v->cargo_cap; case 0x3B: return GB(v->cargo_cap, 8, 8); - case 0x3C: return v->cargo_count; - case 0x3D: return GB(v->cargo_count, 8, 8); - case 0x3E: return v->cargo_source; - case 0x3F: return v->cargo_days; + case 0x3C: return v->cargo.Count(); + case 0x3D: return GB(v->cargo.Count(), 8, 8); + case 0x3E: return v->cargo.Source(); + case 0x3F: return v->cargo.DaysInTransit(); case 0x40: return v->age; case 0x41: return GB(v->age, 8, 8); case 0x42: return v->max_age; @@ -811,12 +811,12 @@ static const SpriteGroup *VehicleResolveReal(const ResolverObject *object, const totalsets = in_motion ? group->g.real.num_loaded : group->g.real.num_loading; - if (v->cargo_count == v->cargo_cap || totalsets == 1) { + if (v->cargo.Count() == v->cargo_cap || totalsets == 1) { set = totalsets - 1; - } else if (v->cargo_count == 0 || totalsets == 2) { + } else if (v->cargo.Empty() || totalsets == 2) { set = 0; } else { - set = v->cargo_count * (totalsets - 2) / max((uint16)1, v->cargo_cap) + 1; + set = v->cargo.Count() * (totalsets - 2) / max((uint16)1, v->cargo_cap) + 1; } return in_motion ? group->g.real.loaded[set] : group->g.real.loading[set]; diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp index e53e4d912..bbaaf8246 100644 --- a/src/newgrf_station.cpp +++ b/src/newgrf_station.cpp @@ -400,7 +400,7 @@ static uint32 StationGetVariable(const ResolverObject *object, byte variable, by uint32 value = 0; for (cargo_type = 0; cargo_type < NUM_CARGO; cargo_type++) { - if (HASBIT(st->goods[cargo_type].waiting_acceptance, 15)) SETBIT(value, cargo_type); + if (st->goods[cargo_type].acceptance) SETBIT(value, cargo_type); } return value; } @@ -431,12 +431,12 @@ static uint32 StationGetVariable(const ResolverObject *object, byte variable, by const GoodsEntry *ge = &st->goods[c]; switch (variable) { - case 0x60: return GB(ge->waiting_acceptance, 0, 12); + case 0x60: return min(ge->cargo.Count(), 4095); case 0x61: return ge->days_since_pickup; case 0x62: return ge->rating; - case 0x63: return ge->enroute_time; + case 0x63: return ge->cargo.DaysInTransit(); case 0x64: return ge->last_speed | (ge->last_age << 8); - case 0x65: return GB(ge->waiting_acceptance, 12, 4); + case 0x65: return ge->acceptance << 3; } } @@ -444,12 +444,12 @@ static uint32 StationGetVariable(const ResolverObject *object, byte variable, by if (variable >= 0x8C && variable <= 0xEC) { const GoodsEntry *g = &st->goods[GB(variable - 0x8C, 3, 4)]; switch (GB(variable - 0x8C, 0, 3)) { - case 0: return g->waiting_acceptance; - case 1: return GB(g->waiting_acceptance, 8, 8); + case 0: return g->cargo.Count(); + case 1: return GB(min(g->cargo.Count(), 4095), 0, 4) | (g->acceptance << 7); case 2: return g->days_since_pickup; case 3: return g->rating; - case 4: return g->enroute_from; - case 5: return g->enroute_time; + case 4: return g->cargo.Source(); + case 5: return g->cargo.DaysInTransit(); case 6: return g->last_speed; case 7: return g->last_age; } @@ -484,12 +484,12 @@ static const SpriteGroup *StationResolveReal(const ResolverObject *object, const case CT_DEFAULT: for (cargo_type = 0; cargo_type < NUM_CARGO; cargo_type++) { - cargo += GB(st->goods[cargo_type].waiting_acceptance, 0, 12); + cargo += st->goods[cargo_type].cargo.Count(); } break; default: - cargo = GB(st->goods[cargo_type].waiting_acceptance, 0, 12); + cargo = st->goods[cargo_type].cargo.Count(); break; } @@ -545,7 +545,7 @@ static const SpriteGroup *ResolveStation(ResolverObject *object) for (CargoID cargo = 0; cargo < NUM_CARGO; cargo++) { const CargoSpec *cs = GetCargo(cargo); if (cs->IsValid() && object->u.station.statspec->spritegroup[cargo] != NULL && - GB(object->u.station.st->goods[cargo].waiting_acceptance, 0, 12) != 0) { + !object->u.station.st->goods[cargo].cargo.Empty()) { ctype = cargo; break; } diff --git a/src/oldloader.cpp b/src/oldloader.cpp index 28496cc7c..f361ea7e3 100644 --- a/src/oldloader.cpp +++ b/src/oldloader.cpp @@ -565,15 +565,18 @@ static bool LoadOldCargoPaymentRate(LoadgameState *ls, int num) return true; } -static uint8 _old_platforms; -static uint _current_station_id; +static uint8 _old_platforms; +static uint _current_station_id; +static uint16 _waiting_acceptance; +static uint8 _cargo_source; +static uint8 _cargo_days; static const OldChunks goods_chunk[] = { - OCL_SVAR( OC_UINT16, GoodsEntry, waiting_acceptance ), + OCL_VAR ( OC_UINT16, 1, &_waiting_acceptance ), OCL_SVAR( OC_UINT8, GoodsEntry, days_since_pickup ), OCL_SVAR( OC_UINT8, GoodsEntry, rating ), - OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, GoodsEntry, enroute_from ), - OCL_SVAR( OC_UINT8, GoodsEntry, enroute_time ), + OCL_VAR ( OC_UINT8, 1, &_cargo_source ), + OCL_VAR ( OC_UINT8, 1, &_cargo_days ), OCL_SVAR( OC_UINT8, GoodsEntry, last_speed ), OCL_SVAR( OC_UINT8, GoodsEntry, last_age ), @@ -583,7 +586,17 @@ static const OldChunks goods_chunk[] = { static bool LoadOldGood(LoadgameState *ls, int num) { Station *st = GetStation(_current_station_id); - return LoadChunk(ls, &st->goods[num], goods_chunk); + GoodsEntry *ge = &st->goods[num]; + bool ret = LoadChunk(ls, ge, goods_chunk); + if (ret && GB(_waiting_acceptance, 0, 12) != 0) { + CargoPacket *cp = new CargoPacket(); + cp->source = (_cargo_source == 0xFF) ? INVALID_STATION : _cargo_source; + cp->count = GB(_waiting_acceptance, 0, 12); + cp->days_in_transit = _cargo_days; + ge->acceptance = HASBIT(_waiting_acceptance, 15); + ge->cargo.Append(cp); + } + return ret; } static const OldChunks station_chunk[] = { @@ -1091,6 +1104,8 @@ static bool LoadOldVehicleUnion(LoadgameState *ls, int num) return res; } +static uint16 _cargo_count; + static const OldChunks vehicle_chunk[] = { OCL_SVAR( OC_UINT8, Vehicle, type ), OCL_SVAR( OC_UINT8, Vehicle, subtype ), @@ -1133,9 +1148,9 @@ static const OldChunks vehicle_chunk[] = { OCL_SVAR( OC_UINT8, Vehicle, cargo_type ), OCL_SVAR( OC_UINT16, Vehicle, cargo_cap ), - OCL_SVAR( OC_UINT16, Vehicle, cargo_count ), - OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Vehicle, cargo_source ), - OCL_SVAR( OC_UINT8, Vehicle, cargo_days ), + OCL_VAR ( OC_UINT16, 1, &_cargo_count ), + OCL_VAR ( OC_UINT8, 1, &_cargo_source ), + OCL_VAR ( OC_UINT8, 1, &_cargo_days ), OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, age ), OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, max_age ), @@ -1213,6 +1228,12 @@ static bool LoadOldVehicle(LoadgameState *ls, int num) /* Vehicle-subtype is different in TTD(Patch) */ if (v->type == VEH_SPECIAL) v->subtype = v->subtype >> 1; + + if (_cargo_count != 0) { + CargoPacket *cp = new CargoPacket((_cargo_source == 0xFF) ? INVALID_STATION : _cargo_source, _cargo_count); + cp->days_in_transit = _cargo_days; + v->cargo.Append(cp); + } } return true; diff --git a/src/openttd.cpp b/src/openttd.cpp index 4a26c3199..515b6e5a3 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -303,6 +303,7 @@ static void UnInitializeGame() CleanPool(&_Sign_pool); CleanPool(&_Order_pool); CleanPool(&_Group_pool); + CleanPool(&_CargoPacket_pool); free((void*)_town_sort); free((void*)_industry_sort); @@ -930,6 +931,31 @@ void SwitchMode(int new_mode) } } +#include "cargopacket.h" +void CheckCargoPacketLeaks() +{ + CargoPacket *cp; + FOR_ALL_CARGOPACKETS(cp) cp->touched = false; + + Vehicle *v; + FOR_ALL_VEHICLES(v) { + const CargoList::List *packets = v->cargo.Packets(); + for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) (*it)->touched = true; + } + + Station *st; + FOR_ALL_STATIONS(st) { + for (CargoID c = 0; c < NUM_CARGO; c++) { + GoodsEntry *ge = &st->goods[c]; + + const CargoList::List *packets = ge->cargo.Packets(); + for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) (*it)->touched = true; + } + } + + FOR_ALL_CARGOPACKETS(cp) assert(cp->touched); +} + /* State controlling game loop. * The state must not be changed from anywhere @@ -964,6 +990,7 @@ void StateGameLoop() CallWindowTickEvent(); NewsLoop(); _current_player = p; + CheckCargoPacketLeaks(); } } @@ -1896,13 +1923,19 @@ bool AfterLoadGame() if (CheckSavegameVersion(44)) { Vehicle *v; /* If we remove a station while cargo from it is still enroute, payment calculation will assume - * 0, 0 to be the origin of the cargo, resulting in very high payments usually. v->cargo_source_xy + * 0, 0 to be the source of the cargo, resulting in very high payments usually. v->source_xy * stores the coordinates, preserving them even if the station is removed. However, if a game is loaded - * where this situation exists, the cargo-source information is lost. in this case, we set the origin + * where this situation exists, the cargo-source information is lost. in this case, we set the source * to the current tile of the vehicle to prevent excessive profits */ FOR_ALL_VEHICLES(v) { - v->cargo_source_xy = IsValidStationID(v->cargo_source) ? GetStation(v->cargo_source)->xy : v->tile; + const CargoList::List *packets = v->cargo.Packets(); + for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) { + CargoPacket *cp = *it; + cp->source_xy = IsValidStationID(cp->source) ? GetStation(cp->source)->xy : v->tile; + cp->loaded_at_xy = cp->source_xy; + } + v->cargo.InvalidateCache(); } /* Store position of the station where the goods come from, so there @@ -1915,12 +1948,12 @@ bool AfterLoadGame() for (CargoID c = 0; c < NUM_CARGO; c++) { GoodsEntry *ge = &st->goods[c]; - /* In old versions, enroute_from used 0xFF as INVALID_STATION */ - if (CheckSavegameVersion(7) && ge->enroute_from == 0xFF) { - ge->enroute_from = INVALID_STATION; + const CargoList::List *packets = ge->cargo.Packets(); + for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) { + CargoPacket *cp = *it; + cp->source_xy = IsValidStationID(cp->source) ? GetStation(cp->source)->xy : st->xy; + cp->loaded_at_xy = cp->source_xy; } - - ge->enroute_from_xy = IsValidStationID(ge->enroute_from) ? GetStation(ge->enroute_from)->xy : st->xy; } } } @@ -1933,12 +1966,13 @@ bool AfterLoadGame() * loading again, even if it didn't actually load anything, so now the * amount of cargo that has been paid for is stored. */ FOR_ALL_VEHICLES(v) { - if (HASBIT(v->vehicle_flags, 2)) { - v->cargo_paid_for = v->cargo_count; - CLRBIT(v->vehicle_flags, 2); - } else { - v->cargo_paid_for = 0; + const CargoList::List *packets = v->cargo.Packets(); + for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) { + CargoPacket *cp = *it; + cp->paid_for = HASBIT(v->vehicle_flags, 2); } + CLRBIT(v->vehicle_flags, 2); + v->cargo.InvalidateCache(); } } diff --git a/src/openttd.h b/src/openttd.h index fda9893d6..1041166bc 100644 --- a/src/openttd.h +++ b/src/openttd.h @@ -59,6 +59,7 @@ typedef EngineID *EngineList; ///< engine list type placeholder acceptable for C /* IDs used in Pools */ typedef uint16 VehicleID; typedef uint16 StationID; +static const StationID INVALID_STATION = 0xFFFF; typedef uint16 RoadStopID; typedef uint16 TownID; typedef uint16 IndustryID; diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index e0757429e..0ed011ce7 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -97,7 +97,7 @@ int GetRoadVehImage(const Vehicle* v, Direction direction) } image = direction + _roadveh_images[img]; - if (v->cargo_count >= v->cargo_cap / 2) image += _roadveh_full_adder[img]; + if (v->cargo.Count() >= v->cargo_cap / 2) image += _roadveh_full_adder[img]; return image; } @@ -677,7 +677,7 @@ static void RoadVehCrash(Vehicle *v) v->u.road.crashed_ctr++; for (Vehicle *u = v; u != NULL; u = u->next) { - if (IsCargoInClass(u->cargo_type, CC_PASSENGERS)) pass += u->cargo_count; + if (IsCargoInClass(u->cargo_type, CC_PASSENGERS)) pass += u->cargo.Count(); u->vehstatus |= VS_CRASHED; @@ -1826,7 +1826,7 @@ static void RoadVehController(Vehicle *v) static void AgeRoadVehCargo(Vehicle *v) { if (_age_cargo_skip_counter != 0) return; - if (v->cargo_days != 255) v->cargo_days++; + v->cargo.AgeCargo(); } void RoadVeh_Tick(Vehicle *v) @@ -2069,7 +2069,7 @@ CommandCost CmdRefitRoadVeh(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) if (flags & DC_EXEC) { v->cargo_cap = capacity; - v->cargo_count = (v->cargo_type == new_cid) ? min(capacity, v->cargo_count) : 0; + v->cargo.Truncate((v->cargo_type == new_cid) ? capacity : 0); v->cargo_type = new_cid; v->cargo_subtype = new_subtype; InvalidateWindow(WC_VEHICLE_DETAILS, v->index); diff --git a/src/roadveh_gui.cpp b/src/roadveh_gui.cpp index a07203f0b..d97b20dbb 100644 --- a/src/roadveh_gui.cpp +++ b/src/roadveh_gui.cpp @@ -154,10 +154,10 @@ static void RoadVehDetailsWndProc(Window *w, WindowEvent *e) for (const Vehicle *u = v; u != NULL; u = u->next) { str = STR_8812_EMPTY; - if (u->cargo_count != 0) { + if (!u->cargo.Empty()) { SetDParam(0, u->cargo_type); - SetDParam(1, u->cargo_count); - SetDParam(2, u->cargo_source); + SetDParam(1, u->cargo.Count()); + SetDParam(2, u->cargo.Source()); str = STR_8813_FROM; } DrawString(34, 78 + y_offset, str, 0); @@ -172,17 +172,17 @@ static void RoadVehDetailsWndProc(Window *w, WindowEvent *e) DrawString(34, 67 + y_offset, STR_9012_CAPACITY, 0); str = STR_8812_EMPTY; - if (v->cargo_count != 0) { + if (!v->cargo.Empty()) { SetDParam(0, v->cargo_type); - SetDParam(1, v->cargo_count); - SetDParam(2, v->cargo_source); + SetDParam(1, v->cargo.Count()); + SetDParam(2, v->cargo.Source()); str = STR_8813_FROM; } DrawString(34, 78 + y_offset, str, 0); } /* Draw Transfer credits text */ - SetDParam(0, v->cargo_feeder_share); + SetDParam(0, v->cargo.FeederShare()); DrawString(34, 90 + y_offset, STR_FEEDER_CARGO_VALUE, 0); /* Draw service interval text */ diff --git a/src/saveload.cpp b/src/saveload.cpp index 36be798ee..f8af5dc9b 100644 --- a/src/saveload.cpp +++ b/src/saveload.cpp @@ -29,7 +29,7 @@ #include <setjmp.h> #include <list> -extern const uint16 SAVEGAME_VERSION = 67; +extern const uint16 SAVEGAME_VERSION = 68; uint16 _sl_version; ///< the major savegame version identifier byte _sl_minor_version; ///< the minor savegame version, DO NOT USE! @@ -820,7 +820,7 @@ void SlObject(void *object, const SaveLoad *sld) } for (; sld->cmd != SL_END; sld++) { - void *ptr = GetVariableAddress(object, sld); + void *ptr = sld->global ? sld->address : GetVariableAddress(object, sld); SlObjectMember(ptr, sld); } } @@ -831,14 +831,7 @@ void SlObject(void *object, const SaveLoad *sld) */ void SlGlobList(const SaveLoadGlobVarList *sldg) { - if (_sl.need_length != NL_NONE) { - SlSetLength(SlCalcObjLength(NULL, (const SaveLoad*)sldg)); - if (_sl.need_length == NL_CALCLENGTH) return; - } - - for (; sldg->cmd != SL_END; sldg++) { - SlObjectMember(sldg->address, (const SaveLoad*)sldg); - } + SlObject(NULL, (const SaveLoad*)sldg); } /** @@ -1258,6 +1251,7 @@ extern const ChunkHandler _economy_chunk_handlers[]; extern const ChunkHandler _animated_tile_chunk_handlers[]; extern const ChunkHandler _newgrf_chunk_handlers[]; extern const ChunkHandler _group_chunk_handlers[]; +extern const ChunkHandler _cargopacket_chunk_handlers[]; static const ChunkHandler * const _chunk_handlers[] = { _misc_chunk_handlers, @@ -1276,6 +1270,7 @@ static const ChunkHandler * const _chunk_handlers[] = { _animated_tile_chunk_handlers, _newgrf_chunk_handlers, _group_chunk_handlers, + _cargopacket_chunk_handlers, NULL, }; @@ -1307,6 +1302,7 @@ static uint ReferenceToInt(const void *obj, SLRefType rt) case REF_ORDER: return ((const Order*)obj)->index + 1; case REF_ROADSTOPS: return ((const RoadStop*)obj)->index + 1; case REF_ENGINE_RENEWS: return ((const EngineRenew*)obj)->index + 1; + case REF_CARGO_PACKET: return ((const CargoPacket*)obj)->index + 1; default: NOT_REACHED(); } @@ -1367,6 +1363,11 @@ static void *IntToReference(uint index, SLRefType rt) error("EngineRenews: failed loading savegame: too many EngineRenews"); return GetEngineRenew(index); } + case REF_CARGO_PACKET: { + if (!AddBlockIfNeeded(&_CargoPacket_pool, index)) + error("CargoPackets: failed loading savegame: too many Cargo packets"); + return GetCargoPacket(index); + } case REF_VEHICLE_OLD: { /* Old vehicles were saved differently: diff --git a/src/saveload.h b/src/saveload.h index 9a212a872..009e73267 100644 --- a/src/saveload.h +++ b/src/saveload.h @@ -55,6 +55,7 @@ enum SLRefType { REF_VEHICLE_OLD = 4, REF_ROADSTOPS = 5, REF_ENGINE_RENEWS = 6, + REF_CARGO_PACKET = 7, }; #define SL_MAX_VERSION 255 @@ -172,6 +173,7 @@ typedef byte SaveLoadType; /** SaveLoad type struct. Do NOT use this directly but use the SLE_ macros defined just below! */ struct SaveLoad { + bool global; ///< should we load a global variable or a non-global one SaveLoadType cmd; ///< the action to take with the saved/loaded type, All types need different action VarType conv; ///< type of the variable to be saved, int uint16 length; ///< (conditional) length of the variable (eg. arrays) (max array size is 65536 elements) @@ -180,7 +182,7 @@ struct SaveLoad { /* NOTE: This element either denotes the address of the variable for a global * variable, or the offset within a struct which is then bound to a variable * during runtime. Decision on which one to use is controlled by the function - * that is called to save it. address: SlGlobList, offset: SlObject */ + * that is called to save it. address: global=true, offset: global=false */ void *address; ///< address of variable OR offset of variable in the struct (max offset is 65536) }; @@ -188,7 +190,7 @@ struct SaveLoad { typedef SaveLoad SaveLoadGlobVarList; /* Simple variables, references (pointers) and arrays */ -#define SLE_GENERAL(cmd, base, variable, type, length, from, to) {cmd, type, length, from, to, (void*)cpp_offsetof(base, variable)} +#define SLE_GENERAL(cmd, base, variable, type, length, from, to) {false, cmd, type, length, from, to, (void*)cpp_offsetof(base, variable)} #define SLE_CONDVAR(base, variable, type, from, to) SLE_GENERAL(SL_VAR, base, variable, type, 0, from, to) #define SLE_CONDREF(base, variable, type, from, to) SLE_GENERAL(SL_REF, base, variable, type, 0, from, to) #define SLE_CONDARR(base, variable, type, length, from, to) SLE_GENERAL(SL_ARR, base, variable, type, length, from, to) @@ -209,7 +211,7 @@ typedef SaveLoad SaveLoadGlobVarList; #define SLE_INCLUDE(base, variable, include_index) SLE_GENERAL(SL_INCLUDE, base, variable, 0, 0, include_index, 0) /* The same as the ones at the top, only the offset is given directly; used for unions */ -#define SLE_GENERALX(cmd, offset, type, param1, param2) {cmd, type, 0, param1, param2, (void*)(offset)} +#define SLE_GENERALX(cmd, offset, type, param1, param2) {false, cmd, type, 0, param1, param2, (void*)(offset)} #define SLE_CONDVARX(offset, type, from, to) SLE_GENERALX(SL_VAR, offset, type, from, to) #define SLE_CONDREFX(offset, type, from, to) SLE_GENERALX(SL_REF, offset, type, from, to) @@ -220,10 +222,10 @@ typedef SaveLoad SaveLoadGlobVarList; #define SLE_INCLUDEX(offset, type) SLE_GENERALX(SL_INCLUDE, offset, type, 0, SL_MAX_VERSION) /* End marker */ -#define SLE_END() {SL_END, 0, 0, 0, 0, NULL} +#define SLE_END() {false, SL_END, 0, 0, 0, 0, NULL} /* Simple variables, references (pointers) and arrays, but for global variables */ -#define SLEG_GENERAL(cmd, variable, type, length, from, to) {cmd, type, length, from, to, (void*)&variable} +#define SLEG_GENERAL(cmd, variable, type, length, from, to) {true, cmd, type, length, from, to, (void*)&variable} #define SLEG_CONDVAR(variable, type, from, to) SLEG_GENERAL(SL_VAR, variable, type, 0, from, to) #define SLEG_CONDREF(variable, type, from, to) SLEG_GENERAL(SL_REF, variable, type, 0, from, to) @@ -237,9 +239,9 @@ typedef SaveLoad SaveLoadGlobVarList; #define SLEG_STR(variable, type) SLEG_CONDSTR(variable, type, lengthof(variable), 0, SL_MAX_VERSION) #define SLEG_LST(variable, type) SLEG_CONDLST(variable, type, 0, SL_MAX_VERSION) -#define SLEG_CONDNULL(length, from, to) {SL_ARR, SLE_FILE_U8 | SLE_VAR_NULL | SLF_CONFIG_NO, length, from, to, (void*)NULL} +#define SLEG_CONDNULL(length, from, to) {true, SL_ARR, SLE_FILE_U8 | SLE_VAR_NULL | SLF_CONFIG_NO, length, from, to, (void*)NULL} -#define SLEG_END() {SL_END, 0, 0, 0, 0, NULL} +#define SLEG_END() {true, SL_END, 0, 0, 0, 0, NULL} /** Checks if the savegame is below major.minor. */ diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index 23ca3b337..92caf6e5b 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -784,7 +784,7 @@ reverse_direction: static void AgeShipCargo(Vehicle *v) { if (_age_cargo_skip_counter != 0) return; - if (v->cargo_days != 255) v->cargo_days++; + v->cargo.AgeCargo(); } void Ship_Tick(Vehicle *v) @@ -1110,7 +1110,7 @@ CommandCost CmdRefitShip(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) if (flags & DC_EXEC) { v->cargo_cap = capacity; - v->cargo_count = (v->cargo_type == new_cid) ? min(v->cargo_cap, v->cargo_count) : 0; + v->cargo.Truncate((v->cargo_type == new_cid) ? capacity : 0); v->cargo_type = new_cid; v->cargo_subtype = new_subtype; InvalidateWindow(WC_VEHICLE_DETAILS, v->index); diff --git a/src/ship_gui.cpp b/src/ship_gui.cpp index 4cca7e17d..53eee6784 100644 --- a/src/ship_gui.cpp +++ b/src/ship_gui.cpp @@ -93,16 +93,16 @@ static void ShipDetailsWndProc(Window *w, WindowEvent *e) DrawString(74, 67, STR_9817_CAPACITY, 0); str = STR_8812_EMPTY; - if (v->cargo_count != 0) { + if (!v->cargo.Empty()) { SetDParam(0, v->cargo_type); - SetDParam(1, v->cargo_count); - SetDParam(2, v->cargo_source); + SetDParam(1, v->cargo.Count()); + SetDParam(2, v->cargo.Source()); str = STR_8813_FROM; } DrawString(74, 78, str, 0); /* Draw Transfer credits text */ - SetDParam(0, v->cargo_feeder_share); + SetDParam(0, v->cargo.FeederShare()); DrawString(74, 89, STR_FEEDER_CARGO_VALUE, 0); } break; diff --git a/src/station.cpp b/src/station.cpp index 9ddebb10e..02f165b6f 100644 --- a/src/station.cpp +++ b/src/station.cpp @@ -79,6 +79,10 @@ Station::~Station() free(speclist); xy = 0; + + for (CargoID c = 0; c < NUM_CARGO; c++) { + goods[c].cargo.Truncate(0); + } } void* Station::operator new(size_t size) diff --git a/src/station.h b/src/station.h index 6a70bcc34..10ba050c1 100644 --- a/src/station.h +++ b/src/station.h @@ -12,34 +12,26 @@ #include "tile.h" #include "road.h" #include "newgrf_station.h" +#include "cargopacket.h" #include <list> -static const StationID INVALID_STATION = 0xFFFF; static const byte INITIAL_STATION_RATING = 175; struct GoodsEntry { GoodsEntry() : - waiting_acceptance(0), - unload_pending(0), + acceptance(false), days_since_pickup(0), rating(INITIAL_STATION_RATING), - enroute_from(INVALID_STATION), - enroute_from_xy(INVALID_TILE), last_speed(0), - last_age(255), - feeder_profit(0) + last_age(255) {} - uint16 waiting_acceptance; - uint16 unload_pending; ///< records how much cargo is awaiting transfer during gradual loading to allow correct fee calc + bool acceptance; byte days_since_pickup; byte rating; - StationID enroute_from; - TileIndex enroute_from_xy; - byte enroute_time; byte last_speed; byte last_age; - Money feeder_profit; + CargoList cargo; ///< The cargo packets of cargo waiting in this station }; /** A Stop for a Road Vehicle */ diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 5dd2b2a9b..c4fcff060 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -355,7 +355,7 @@ static uint GetAcceptanceMask(const Station *st) uint mask = 0; for (CargoID i = 0; i < NUM_CARGO; i++) { - if (st->goods[i].waiting_acceptance & 0x8000) mask |= 1 << i; + if (st->goods[i].acceptance) mask |= 1 << i; } return mask; } @@ -536,7 +536,7 @@ static void UpdateStationAcceptance(Station *st, bool show_msg) (is_passengers && !(st->facilities & (byte)~FACIL_TRUCK_STOP))) amt = 0; - SB(st->goods[i].waiting_acceptance, 12, 4, amt); + st->goods[i].acceptance = (amt >= 8); } // Only show a message in case the acceptance was actually changed. @@ -2347,11 +2347,10 @@ static void UpdateStationRating(Station *st) /* Slowly increase the rating back to his original level in the case we * didn't deliver cargo yet to this station. This happens when a bribe * failed while you didn't moved that cargo yet to a station. */ - if (ge->enroute_from == INVALID_STATION && ge->rating < INITIAL_STATION_RATING) + if (ge->days_since_pickup == 255 && ge->rating < INITIAL_STATION_RATING) ge->rating++; /* Only change the rating if we are moving this cargo */ - if (ge->enroute_from != INVALID_STATION) { - byte_inc_sat(&ge->enroute_time); + if (ge->last_speed != 0) { byte_inc_sat(&ge->days_since_pickup); int rating = 0; @@ -2383,7 +2382,7 @@ static void UpdateStationRating(Station *st) (rating += 35, true); } - int waiting = GB(ge->waiting_acceptance, 0, 12); + uint waiting = ge->cargo.Count(); (rating -= 90, waiting > 1500) || (rating += 55, waiting > 1000) || (rating += 35, waiting > 600) || @@ -2409,12 +2408,13 @@ static void UpdateStationRating(Station *st) if (rating <= 127 && waiting != 0) { uint32 r = Random(); if (rating <= (int)GB(r, 0, 7)) { - waiting = max(waiting - (int)GB(r, 8, 2) - 1, 0); + /* Need to have int, otherwise it will just overflow etc. */ + waiting = max((int)waiting - (int)GB(r, 8, 2) - 1, 0); waiting_changed = true; } } - if (waiting_changed) SB(ge->waiting_acceptance, 0, 12, waiting); + if (waiting_changed) ge->cargo.Truncate(waiting); } } } while (++ge != endof(st->goods)); @@ -2467,7 +2467,7 @@ void ModifyStationRatingAround(TileIndex tile, PlayerID owner, int amount, uint for (CargoID i = 0; i < NUM_CARGO; i++) { GoodsEntry* ge = &st->goods[i]; - if (ge->enroute_from != INVALID_STATION) { + if (ge->days_since_pickup != 255) { ge->rating = clamp(ge->rating + amount, 0, 255); } } @@ -2477,13 +2477,8 @@ void ModifyStationRatingAround(TileIndex tile, PlayerID owner, int amount, uint static void UpdateStationWaiting(Station *st, CargoID type, uint amount) { - SB(st->goods[type].waiting_acceptance, 0, 12, - min(0xFFF, GB(st->goods[type].waiting_acceptance, 0, 12) + amount) - ); + st->goods[type].cargo.Append(new CargoPacket(st->index, amount)); - st->goods[type].enroute_time = 0; - st->goods[type].enroute_from = st->index; - st->goods[type].enroute_from_xy = st->xy; InvalidateWindow(WC_STATION_VIEW, st->index); st->MarkTilesDirty(true); } @@ -2553,7 +2548,7 @@ uint MoveGoodsToStation(TileIndex tile, int w, int h, CargoID type, uint amount) if (around[i] == NULL) { if (!st->IsBuoy() && (st->town->exclusive_counter == 0 || st->town->exclusivity == st->owner) && // check exclusive transport rights - st->goods[type].rating != 0 && + st->goods[type].rating != 0 && st->goods[type].days_since_pickup != 255 && // we actually service the station (!_patches.selectgoods || st->goods[type].last_speed > 0) && // if last_speed is 0, no vehicle has been there. ((st->facilities & ~FACIL_BUS_STOP) != 0 || IsCargoInClass(type, CC_PASSENGERS)) && // if we have other fac. than a bus stop, or the cargo is passengers ((st->facilities & ~FACIL_TRUCK_STOP) != 0 || !IsCargoInClass(type, CC_PASSENGERS))) { // if we have other fac. than a cargo bay or the cargo is not passengers @@ -2683,10 +2678,8 @@ void BuildOilRig(TileIndex tile) st->build_date = _date; for (CargoID j = 0; j < NUM_CARGO; j++) { - st->goods[j].waiting_acceptance = 0; - st->goods[j].days_since_pickup = 0; - st->goods[j].enroute_from = INVALID_STATION; - st->goods[j].enroute_from_xy = INVALID_TILE; + st->goods[j].acceptance = false; + st->goods[j].days_since_pickup = 255; st->goods[j].rating = INITIAL_STATION_RATING; st->goods[j].last_speed = 0; st->goods[j].last_age = 255; @@ -2810,6 +2803,8 @@ void AfterLoadStations() st->speclist[i].spec = GetCustomStationSpecByGrf(st->speclist[i].grfid, st->speclist[i].localidx); } + + for (CargoID c = 0; c < NUM_CARGO; c++) st->goods[c].cargo.InvalidateCache(); } } @@ -2906,19 +2901,27 @@ static const SaveLoad _station_desc[] = { SLE_END() }; +static uint16 _waiting_acceptance; +static uint16 _cargo_source; +static uint32 _cargo_source_xy; +static uint16 _cargo_days; +static Money _cargo_feeder_share; + static const SaveLoad _goods_desc[] = { - SLE_VAR(GoodsEntry, waiting_acceptance, SLE_UINT16), - SLE_CONDVAR(GoodsEntry, unload_pending, SLE_UINT16, 51, SL_MAX_VERSION), - SLE_VAR(GoodsEntry, days_since_pickup, SLE_UINT8), - SLE_VAR(GoodsEntry, rating, SLE_UINT8), - SLE_CONDVAR(GoodsEntry, enroute_from, SLE_FILE_U8 | SLE_VAR_U16, 0, 6), - SLE_CONDVAR(GoodsEntry, enroute_from, SLE_UINT16, 7, SL_MAX_VERSION), - SLE_CONDVAR(GoodsEntry, enroute_from_xy, SLE_UINT32, 44, SL_MAX_VERSION), - SLE_VAR(GoodsEntry, enroute_time, SLE_UINT8), - SLE_VAR(GoodsEntry, last_speed, SLE_UINT8), - SLE_VAR(GoodsEntry, last_age, SLE_UINT8), - SLE_CONDVAR(GoodsEntry, feeder_profit, SLE_FILE_I32 | SLE_VAR_I64,14, 64), - SLE_CONDVAR(GoodsEntry, feeder_profit, SLE_INT64, 65, SL_MAX_VERSION), + SLEG_CONDVAR( _waiting_acceptance, SLE_UINT16, 0, 67), + SLE_CONDVAR(GoodsEntry, acceptance, SLE_BOOL, 68, SL_MAX_VERSION), + SLE_CONDNULL(2, 51, 67), + SLE_VAR(GoodsEntry, days_since_pickup, SLE_UINT8), + SLE_VAR(GoodsEntry, rating, SLE_UINT8), + SLEG_CONDVAR( _cargo_source, SLE_FILE_U8 | SLE_VAR_U16, 0, 6), + SLEG_CONDVAR( _cargo_source, SLE_UINT16, 7, 67), + SLEG_CONDVAR( _cargo_source_xy, SLE_UINT32, 44, 67), + SLEG_CONDVAR( _cargo_days, SLE_UINT8, 0, 67), + SLE_VAR(GoodsEntry, last_speed, SLE_UINT8), + SLE_VAR(GoodsEntry, last_age, SLE_UINT8), + SLEG_CONDVAR( _cargo_feeder_share, SLE_FILE_U32 | SLE_VAR_I64, 14, 64), + SLEG_CONDVAR( _cargo_feeder_share, SLE_INT64, 65, 67), + SLE_CONDLST(GoodsEntry, cargo, REF_CARGO_PACKET, 68, SL_MAX_VERSION), SLE_END() }; @@ -2935,9 +2938,28 @@ static void SaveLoad_STNS(Station *st) { SlObject(st, _station_desc); + _waiting_acceptance = 0; + uint num_cargo = CheckSavegameVersion(55) ? 12 : NUM_CARGO; for (CargoID i = 0; i < num_cargo; i++) { - SlObject(&st->goods[i], _goods_desc); + GoodsEntry *ge = &st->goods[i]; + SlObject(ge, _goods_desc); + if (_waiting_acceptance != 0) { + ge->acceptance = HASBIT(_waiting_acceptance, 15); + if (GB(_waiting_acceptance, 0, 12) != 0) { + /* Don't construct the packet with station here, because that'll fail with old savegames */ + CargoPacket *cp = new CargoPacket(); + /* In old versions, enroute_from used 0xFF as INVALID_STATION */ + cp->source = (CheckSavegameVersion(7) && _cargo_source == 0xFF) ? INVALID_STATION : _cargo_source; + cp->count = GB(_waiting_acceptance, 0, 12); + cp->days_in_transit = _cargo_days; + cp->feeder_share = _cargo_feeder_share; + cp->source_xy = _cargo_source_xy; + cp->days_in_transit = _cargo_days; + cp->feeder_share = _cargo_feeder_share; + ge->cargo.Append(cp); + } + } } if (st->num_specs != 0) { diff --git a/src/station_gui.cpp b/src/station_gui.cpp index dd8e8a99c..e8b332704 100644 --- a/src/station_gui.cpp +++ b/src/station_gui.cpp @@ -134,8 +134,8 @@ static int CDECL StationWaitingSorter(const void *a, const void *b) Money sum1 = 0, sum2 = 0; for (CargoID j = 0; j < NUM_CARGO; j++) { - if (st1->goods[j].waiting_acceptance & 0xfff) sum1 += GetTransportedGoodsIncome(st1->goods[j].waiting_acceptance & 0xfff, 20, 50, j); - if (st2->goods[j].waiting_acceptance & 0xfff) sum2 += GetTransportedGoodsIncome(st2->goods[j].waiting_acceptance & 0xfff, 20, 50, j); + if (!st1->goods[j].cargo.Empty()) sum1 += GetTransportedGoodsIncome(st1->goods[j].cargo.Count(), 20, 50, j); + if (!st2->goods[j].cargo.Empty()) sum2 += GetTransportedGoodsIncome(st2->goods[j].cargo.Count(), 20, 50, j); } return (_internal_sort_order & 1) ? ClampToI32(sum2 - sum1) : ClampToI32(sum1 - sum2); @@ -157,8 +157,8 @@ static int CDECL StationRatingMaxSorter(const void *a, const void *b) byte maxr2 = 0; for (CargoID j = 0; j < NUM_CARGO; j++) { - if (st1->goods[j].enroute_from != INVALID_STATION) maxr1 = max(maxr1, st1->goods[j].rating); - if (st2->goods[j].enroute_from != INVALID_STATION) maxr2 = max(maxr2, st2->goods[j].rating); + if (st1->goods[j].days_since_pickup != 255) maxr1 = max(maxr1, st1->goods[j].rating); + if (st2->goods[j].days_since_pickup != 255) maxr2 = max(maxr2, st2->goods[j].rating); } return (_internal_sort_order & 1) ? maxr2 - maxr1 : maxr1 - maxr2; @@ -225,7 +225,7 @@ static void BuildStationsList(plstations_d* sl, PlayerID owner, byte facilities, if (facilities & st->facilities) { //only stations with selected facilities int num_waiting_cargo = 0; for (CargoID j = 0; j < NUM_CARGO; j++) { - if (st->goods[j].waiting_acceptance & 0xFFF) { + if (!st->goods[j].cargo.Empty()) { num_waiting_cargo++; //count number of waiting cargo if (HASBIT(cargo_filter, j)) { station_sort[n++] = st; @@ -368,9 +368,8 @@ static void PlayerStationsWndProc(Window *w, WindowEvent *e) /* show cargo waiting and station ratings */ for (CargoID j = 0; j < NUM_CARGO; j++) { - uint amount = GB(st->goods[j].waiting_acceptance, 0, 12); - if (amount != 0) { - StationsWndShowStationRating(x, y, j, amount, st->goods[j].rating); + if (!st->goods[j].cargo.Empty()) { + StationsWndShowStationRating(x, y, j, st->goods[j].cargo.Count(), st->goods[j].rating); x += 20; } } @@ -705,9 +704,9 @@ static void DrawStationViewWindow(Window *w) num = 1; for (CargoID i = 0; i < NUM_CARGO; i++) { - if (GB(st->goods[i].waiting_acceptance, 0, 12) != 0) { + if (!st->goods[i].cargo.Empty()) { num++; - if (st->goods[i].enroute_from != station_id) num++; + if (st->goods[i].cargo.Source() != station_id) num++; } } SetVScrollCount(w, num); @@ -729,7 +728,7 @@ static void DrawStationViewWindow(Window *w) if (--pos < 0) { str = STR_00D0_NOTHING; for (CargoID i = 0; i < NUM_CARGO; i++) { - if (GB(st->goods[i].waiting_acceptance, 0, 12) != 0) str = STR_EMPTY; + if (!st->goods[i].cargo.Empty()) str = STR_EMPTY; } SetDParam(0, str); DrawString(x, y, STR_0008_WAITING, 0); @@ -737,10 +736,10 @@ static void DrawStationViewWindow(Window *w) } for (CargoID i = 0; i < NUM_CARGO && pos > -5; i++) { - uint waiting = GB(st->goods[i].waiting_acceptance, 0, 12); + uint waiting = st->goods[i].cargo.Count(); if (waiting == 0) continue; - if (st->goods[i].enroute_from == station_id) { + if (st->goods[i].cargo.Source() == station_id) { if (--pos < 0) { DrawCargoIcons(i, waiting, x, y); SetDParam(1, waiting); @@ -759,7 +758,7 @@ static void DrawStationViewWindow(Window *w) } if (pos > -5 && --pos < 0) { - SetDParam(0, st->goods[i].enroute_from); + SetDParam(0, st->goods[i].cargo.Source()); DrawStringRightAligned(x + 234, y, STR_000B, 0); y += 10; } @@ -774,7 +773,7 @@ static void DrawStationViewWindow(Window *w) for (CargoID i = 0; i < NUM_CARGO; i++) { if (b >= endof(_userstring) - 5 - 1) break; - if (st->goods[i].waiting_acceptance & 0x8000) { + if (st->goods[i].acceptance) { if (first) { first = false; } else { @@ -800,7 +799,7 @@ static void DrawStationViewWindow(Window *w) if (!cs->IsValid()) continue; const GoodsEntry *ge = &st->goods[i]; - if (ge->enroute_from == INVALID_STATION) continue; + if (ge->days_since_pickup == 255) continue; SetDParam(0, cs->name); SetDParam(2, ge->rating * 101 >> 8); diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 1d5a88877..d2a12e2c0 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -112,7 +112,7 @@ static void TrainCargoChanged(Vehicle* v) uint32 weight = 0; for (Vehicle *u = v; u != NULL; u = u->next) { - uint32 vweight = GetCargo(u->cargo_type)->weight * u->cargo_count * FreightWagonMult(u->cargo_type) / 16; + uint32 vweight = GetCargo(u->cargo_type)->weight * u->cargo.Count() * FreightWagonMult(u->cargo_type) / 16; /* Vehicle weight is not added for articulated parts. */ if (!IsArticulatedPart(u)) { @@ -463,7 +463,7 @@ int GetTrainImage(const Vehicle* v, Direction direction) base = _engine_sprite_base[img] + ((direction + _engine_sprite_add[img]) & _engine_sprite_and[img]); - if (v->cargo_count >= v->cargo_cap / 2) base += _wagon_full_adder[img]; + if (v->cargo.Count() >= v->cargo_cap / 2) base += _wagon_full_adder[img]; return base; } @@ -1761,7 +1761,7 @@ CommandCost CmdRefitRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 num += amount; if (flags & DC_EXEC) { - v->cargo_count = (v->cargo_type == new_cid) ? min(amount, v->cargo_count) : 0; + v->cargo.Truncate((v->cargo_type == new_cid) ? amount : 0); v->cargo_type = new_cid; v->cargo_cap = amount; v->cargo_subtype = new_subtype; @@ -2681,7 +2681,7 @@ static uint CountPassengersInTrain(const Vehicle* v) { uint num = 0; BEGIN_ENUM_WAGONS(v) - if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) num += v->cargo_count; + if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) num += v->cargo.Count(); END_ENUM_WAGONS(v) return num; } @@ -3294,8 +3294,7 @@ static void TrainLocoHandler(Vehicle *v, bool mode) void Train_Tick(Vehicle *v) { - if (_age_cargo_skip_counter == 0 && v->cargo_days != 0xff) - v->cargo_days++; + if (_age_cargo_skip_counter == 0) v->cargo.AgeCargo(); v->tick_counter++; diff --git a/src/train_gui.cpp b/src/train_gui.cpp index 1a1a475c8..095e51739 100644 --- a/src/train_gui.cpp +++ b/src/train_gui.cpp @@ -327,13 +327,12 @@ void ShowTrainViewWindow(const Vehicle *v) static void TrainDetailsCargoTab(const Vehicle *v, int x, int y) { if (v->cargo_cap != 0) { - uint num = v->cargo_count; StringID str = STR_8812_EMPTY; - if (num != 0) { + if (!v->cargo.Empty()) { SetDParam(0, v->cargo_type); - SetDParam(1, num); - SetDParam(2, v->cargo_source); + SetDParam(1, v->cargo.Count()); + SetDParam(2, v->cargo.Source()); SetDParam(3, _patches.freight_trains); str = FreightWagonMult(v->cargo_type) > 1 ? STR_FROM_MULT : STR_8813_FROM; } @@ -387,7 +386,7 @@ static void DrawTrainDetailsWindow(Window *w) } do { - act_cargo[u->cargo_type] += u->cargo_count; + act_cargo[u->cargo_type] += u->cargo.Count(); max_cargo[u->cargo_type] += u->cargo_cap; } while ((u = u->next) != NULL); @@ -504,7 +503,7 @@ static void DrawTrainDetailsWindow(Window *w) DrawString(x, y + 2, FreightWagonMult(i) > 1 ? STR_TOTAL_CAPACITY_MULT : STR_013F_TOTAL_CAPACITY, 0); } } - SetDParam(0, v->cargo_feeder_share); + SetDParam(0, v->cargo.FeederShare()); DrawString(x, y + 15, STR_FEEDER_CARGO_VALUE, 0); } } diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 53bdfdf51..f04a1a3ea 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -233,6 +233,8 @@ void AfterLoadVehicles() v->first = NULL; if (v->type == VEH_TRAIN) v->u.rail.first_engine = INVALID_ENGINE; if (v->type == VEH_ROAD) v->u.road.first_engine = INVALID_ENGINE; + + v->cargo.InvalidateCache(); } FOR_ALL_VEHICLES(v) { @@ -690,6 +692,7 @@ void DestroyVehicle(Vehicle *v) InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); } + v->cargo.Truncate(0); UpdateVehiclePosHash(v, INVALID_COORD, 0); v->next_hash = NULL; v->next_new_hash = NULL; @@ -2277,7 +2280,7 @@ uint8 CalcPercentVehicleFilled(Vehicle *v) /* Count up max and used */ for (; v != NULL; v = v->next) { - count += v->cargo_count; + count += v->cargo.Count(); max += v->cargo_cap; } @@ -2748,6 +2751,14 @@ SpriteID GetVehiclePalette(const Vehicle *v) return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v); } +static uint8 _cargo_days; +static uint16 _cargo_source; +static uint32 _cargo_source_xy; +static uint16 _cargo_count; +static uint16 _cargo_paid_for; +static Money _cargo_feeder_share; +static uint32 _cargo_loaded_at_xy; + /** Save and load of vehicles */ extern const SaveLoad _common_veh_desc[] = { SLE_VAR(Vehicle, subtype, SLE_UINT8), @@ -2784,14 +2795,15 @@ extern const SaveLoad _common_veh_desc[] = { SLE_CONDVAR(Vehicle, last_station_visited, SLE_FILE_U8 | SLE_VAR_U16, 0, 4), SLE_CONDVAR(Vehicle, last_station_visited, SLE_UINT16, 5, SL_MAX_VERSION), - SLE_VAR(Vehicle, cargo_type, SLE_UINT8), - SLE_CONDVAR(Vehicle, cargo_subtype, SLE_UINT8, 35, SL_MAX_VERSION), - SLE_VAR(Vehicle, cargo_days, SLE_UINT8), - SLE_CONDVAR(Vehicle, cargo_source, SLE_FILE_U8 | SLE_VAR_U16, 0, 6), - SLE_CONDVAR(Vehicle, cargo_source, SLE_UINT16, 7, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, cargo_source_xy, SLE_UINT32, 44, SL_MAX_VERSION), - SLE_VAR(Vehicle, cargo_cap, SLE_UINT16), - SLE_VAR(Vehicle, cargo_count, SLE_UINT16), + SLE_VAR(Vehicle, cargo_type, SLE_UINT8), + SLE_CONDVAR(Vehicle, cargo_subtype, SLE_UINT8, 35, SL_MAX_VERSION), + SLEG_CONDVAR( _cargo_days, SLE_UINT8, 0, 67), + SLEG_CONDVAR( _cargo_source, SLE_FILE_U8 | SLE_VAR_U16, 0, 6), + SLEG_CONDVAR( _cargo_source, SLE_UINT16, 7, 67), + SLEG_CONDVAR( _cargo_source_xy, SLE_UINT32, 44, 67), + SLE_VAR(Vehicle, cargo_cap, SLE_UINT16), + SLEG_CONDVAR( _cargo_count, SLE_UINT16, 0, 67), + SLE_CONDLST(Vehicle, cargo, REF_CARGO_PACKET, 68, SL_MAX_VERSION), SLE_VAR(Vehicle, day_counter, SLE_UINT8), SLE_VAR(Vehicle, tick_counter, SLE_UINT8), @@ -2837,19 +2849,19 @@ extern const SaveLoad _common_veh_desc[] = { SLE_CONDVAR(Vehicle, build_year, SLE_FILE_U8 | SLE_VAR_I32, 0, 30), SLE_CONDVAR(Vehicle, build_year, SLE_INT32, 31, SL_MAX_VERSION), - SLE_VAR(Vehicle, load_unload_time_rem, SLE_UINT16), - SLE_CONDVAR(Vehicle, cargo_paid_for, SLE_UINT16, 45, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, vehicle_flags, SLE_UINT8, 40, SL_MAX_VERSION), + SLE_VAR(Vehicle, load_unload_time_rem, SLE_UINT16), + SLEG_CONDVAR( _cargo_paid_for, SLE_UINT16, 45, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, vehicle_flags, SLE_UINT8, 40, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, profit_this_year, SLE_FILE_I32 | SLE_VAR_I64, 0, 64), - SLE_CONDVAR(Vehicle, profit_this_year, SLE_INT64, 65, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, profit_last_year, SLE_FILE_I32 | SLE_VAR_I64, 0, 64), - SLE_CONDVAR(Vehicle, profit_last_year, SLE_INT64, 65, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, cargo_feeder_share, SLE_FILE_I32 | SLE_VAR_I64,51, 64), - SLE_CONDVAR(Vehicle, cargo_feeder_share, SLE_INT64, 65, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, cargo_loaded_at_xy, SLE_UINT32, 51, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, value, SLE_FILE_I32 | SLE_VAR_I64, 0, 64), - SLE_CONDVAR(Vehicle, value, SLE_INT64, 65, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, profit_this_year, SLE_FILE_I32 | SLE_VAR_I64, 0, 64), + SLE_CONDVAR(Vehicle, profit_this_year, SLE_INT64, 65, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, profit_last_year, SLE_FILE_I32 | SLE_VAR_I64, 0, 64), + SLE_CONDVAR(Vehicle, profit_last_year, SLE_INT64, 65, SL_MAX_VERSION), + SLEG_CONDVAR( _cargo_feeder_share, SLE_FILE_I32 | SLE_VAR_I64,51, 64), + SLEG_CONDVAR( _cargo_feeder_share, SLE_INT64, 65, 67), + SLEG_CONDVAR( _cargo_loaded_at_xy, SLE_UINT32, 51, 67), + SLE_CONDVAR(Vehicle, value, SLE_FILE_I32 | SLE_VAR_I64, 0, 64), + SLE_CONDVAR(Vehicle, value, SLE_INT64, 65, SL_MAX_VERSION), SLE_VAR(Vehicle, random_bits, SLE_UINT8), SLE_VAR(Vehicle, waiting_triggers, SLE_UINT8), @@ -3030,6 +3042,8 @@ static void Load_VEHS() int index; Vehicle *v; + _cargo_count = 0; + while ((index = SlIterateArray()) != -1) { Vehicle *v; @@ -3038,9 +3052,8 @@ static void Load_VEHS() v = GetVehicle(index); VehicleType vtype = (VehicleType)SlReadByte(); - SlObject(v, (SaveLoad*)_veh_descs[vtype]); - switch (v->type) { + switch (vtype) { case VEH_TRAIN: v = new (v) Train(); break; case VEH_ROAD: v = new (v) RoadVehicle(); break; case VEH_SHIP: v = new (v) Ship(); break; @@ -3051,6 +3064,20 @@ static void Load_VEHS() default: NOT_REACHED(); } + SlObject(v, (SaveLoad*)_veh_descs[vtype]); + + if (_cargo_count != 0 && IsPlayerBuildableVehicleType(v)) { + /* Don't construct the packet with station here, because that'll fail with old savegames */ + CargoPacket *cp = new CargoPacket(); + cp->source = _cargo_source; + cp->source_xy = _cargo_source_xy; + cp->count = _cargo_count; + cp->days_in_transit = _cargo_days; + cp->feeder_share = _cargo_feeder_share; + cp->loaded_at_xy = _cargo_loaded_at_xy; + v->cargo.Append(cp); + } + /* Old savegames used 'last_station_visited = 0xFF' */ if (CheckSavegameVersion(5) && v->last_station_visited == 0xFF) v->last_station_visited = INVALID_STATION; diff --git a/src/vehicle.h b/src/vehicle.h index 9ab0bb6de..5efb14a7a 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -9,6 +9,7 @@ #include "order.h" #include "rail.h" #include "road.h" +#include "cargopacket.h" #include "texteff.hpp" /** The returned bits of VehicleEnterTile. */ @@ -265,12 +266,10 @@ struct Vehicle { StationID last_station_visited; CargoID cargo_type; // type of cargo this vehicle is carrying - byte cargo_days; // how many days have the pieces been in transit - StationID cargo_source; // source of cargo - TileIndex cargo_source_xy; //< stores the Tile where the source station is located, in case it is removed uint16 cargo_cap; // total capacity - uint16 cargo_count; // how many pieces are used byte cargo_subtype; ///< Used for livery refits (NewGRF variations) + CargoList cargo; ///< The cargo this vehicle is carrying + byte day_counter; // increased by one for each day byte tick_counter; // increased by one for each tick @@ -312,13 +311,10 @@ struct Vehicle { bool leave_depot_instantly; // NOSAVE: stores if the vehicle needs to leave the depot it just entered. Used by autoreplace uint16 load_unload_time_rem; - uint16 cargo_paid_for; // How much of the cargo currently on board has been paid for. byte vehicle_flags; // Used for gradual loading and other miscellaneous things (@see VehicleFlags enum) Money profit_this_year; Money profit_last_year; - Money cargo_feeder_share; ///< value of feeder pickup to be paid for on delivery of cargo - TileIndex cargo_loaded_at_xy; ///< tile index where feeder cargo was loaded Money value; GroupID group_id; ///< Index of group Pool array diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp index 15e05f78c..27001c8dc 100644 --- a/src/water_cmd.cpp +++ b/src/water_cmd.cpp @@ -670,7 +670,7 @@ static void FloodVehicle(Vehicle *v) /* crash all wagons, and count passengers */ BEGIN_ENUM_WAGONS(v) - if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo_count; + if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.Count(); v->vehstatus |= VS_CRASHED; MarkAllViewportsDirty(v->left_coord, v->top_coord, v->right_coord + 1, v->bottom_coord + 1); END_ENUM_WAGONS(v) |