summaryrefslogtreecommitdiff
path: root/src/cargopacket.cpp
diff options
context:
space:
mode:
authorrubidium <rubidium@openttd.org>2007-06-22 11:58:59 +0000
committerrubidium <rubidium@openttd.org>2007-06-22 11:58:59 +0000
commitfc201d4ad8268234ce67d2ea1e49d470ab0c2004 (patch)
treefd6fbc96b8d23bef18ed7267fa8174e1689c8e78 /src/cargopacket.cpp
parentdc82eeb2ae166f3284cda1aa265045c10c29b4c2 (diff)
downloadopenttd-fc201d4ad8268234ce67d2ea1e49d470ab0c2004.tar.xz
(svn r10266) -Codechange: keep track of the origin, time of travel and accumulated feeder share (transfers) of individual pieces of cargo. This means that cargo isn't thrown on a big pile when it's put in a station or unloaded at a station, however the GUI does not reflect these changes yet so you will not actually see it.
Diffstat (limited to 'src/cargopacket.cpp')
-rw-r--r--src/cargopacket.cpp351
1 files changed, 351 insertions, 0 deletions
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;
+}