src/cargopacket.cpp
changeset 7506 e52d89f5c7c1
child 7529 5a277cbe8d8f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cargopacket.cpp	Fri Jun 22 11:58:59 2007 +0000
@@ -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;
+}