rubidium@7010: /* $Id$ */ rubidium@7010: rubidium@7010: /** @file cargopacket.cpp Implementation of the cargo packets */ rubidium@7010: rubidium@7010: #include "stdafx.h" rubidium@7010: #include "openttd.h" rubidium@8785: #include "station_base.h" rubidium@7010: #include "cargopacket.h" rubidium@7010: #include "saveload.h" smatz@8847: #include "oldpool_func.h" rubidium@7010: rubidium@7010: /* Initialize the cargopacket-pool */ rubidium@7380: DEFINE_OLD_POOL_GENERIC(CargoPacket, CargoPacket) rubidium@7010: rubidium@7010: void InitializeCargoPackets() rubidium@7010: { rubidium@7010: /* Clean the cargo packet pool and create 1 block in it */ rubidium@7380: _CargoPacket_pool.CleanPool(); rubidium@7380: _CargoPacket_pool.AddBlockToPool(); rubidium@7010: } rubidium@7010: rubidium@7010: CargoPacket::CargoPacket(StationID source, uint16 count) rubidium@7010: { rubidium@7010: if (source != INVALID_STATION) assert(count != 0); rubidium@7010: rubidium@7010: this->source = source; rubidium@7010: this->source_xy = (source != INVALID_STATION) ? GetStation(source)->xy : 0; rubidium@7010: this->loaded_at_xy = this->source_xy; rubidium@7010: rubidium@7010: this->count = count; rubidium@7010: this->days_in_transit = 0; rubidium@7010: this->feeder_share = 0; rubidium@7010: this->paid_for = false; rubidium@7010: } rubidium@7010: rubidium@7010: CargoPacket::~CargoPacket() rubidium@7010: { rubidium@7010: this->count = 0; rubidium@7010: } rubidium@7010: peter1138@8542: bool CargoPacket::SameSource(const CargoPacket *cp) const rubidium@7010: { rubidium@7010: return this->source_xy == cp->source_xy && this->days_in_transit == cp->days_in_transit && this->paid_for == cp->paid_for; rubidium@7010: } rubidium@7010: rubidium@7010: static const SaveLoad _cargopacket_desc[] = { rubidium@7010: SLE_VAR(CargoPacket, source, SLE_UINT16), rubidium@7010: SLE_VAR(CargoPacket, source_xy, SLE_UINT32), rubidium@7010: SLE_VAR(CargoPacket, loaded_at_xy, SLE_UINT32), rubidium@7010: SLE_VAR(CargoPacket, count, SLE_UINT16), rubidium@7010: SLE_VAR(CargoPacket, days_in_transit, SLE_UINT8), rubidium@7010: SLE_VAR(CargoPacket, feeder_share, SLE_INT64), rubidium@7010: SLE_VAR(CargoPacket, paid_for, SLE_BOOL), rubidium@7010: rubidium@7010: SLE_END() rubidium@7010: }; rubidium@7010: rubidium@7010: static void Save_CAPA() rubidium@7010: { rubidium@7010: CargoPacket *cp; rubidium@7010: rubidium@7010: FOR_ALL_CARGOPACKETS(cp) { rubidium@7010: SlSetArrayIndex(cp->index); rubidium@7010: SlObject(cp, _cargopacket_desc); rubidium@7010: } rubidium@7010: } rubidium@7010: rubidium@7010: static void Load_CAPA() rubidium@7010: { rubidium@7010: int index; rubidium@7010: rubidium@7010: while ((index = SlIterateArray()) != -1) { rubidium@7380: CargoPacket *cp = new (index) CargoPacket(); rubidium@7010: SlObject(cp, _cargopacket_desc); rubidium@7010: } rubidium@7010: } rubidium@7010: rubidium@7010: extern const ChunkHandler _cargopacket_chunk_handlers[] = { rubidium@7010: { 'CAPA', Save_CAPA, Load_CAPA, CH_ARRAY | CH_LAST}, rubidium@7010: }; rubidium@7010: rubidium@7010: /* rubidium@7010: * rubidium@7010: * Cargo list implementation rubidium@7010: * rubidium@7010: */ rubidium@7010: rubidium@7010: CargoList::~CargoList() rubidium@7010: { rubidium@7010: while (!packets.empty()) { rubidium@7010: delete packets.front(); rubidium@7010: packets.pop_front(); rubidium@7010: } rubidium@7010: } rubidium@7010: rubidium@7010: const CargoList::List *CargoList::Packets() const rubidium@7010: { rubidium@7010: return &packets; rubidium@7010: } rubidium@7010: rubidium@7010: void CargoList::AgeCargo() rubidium@7010: { rubidium@7010: if (empty) return; rubidium@7010: rubidium@7010: uint dit = 0; rubidium@7010: for (List::const_iterator it = packets.begin(); it != packets.end(); it++) { rubidium@7010: if ((*it)->days_in_transit != 0xFF) (*it)->days_in_transit++; rubidium@7010: dit += (*it)->days_in_transit * (*it)->count; rubidium@7010: } rubidium@7010: days_in_transit = dit / count; rubidium@7010: } rubidium@7010: rubidium@7010: bool CargoList::Empty() const rubidium@7010: { rubidium@7010: return empty; rubidium@7010: } rubidium@7010: rubidium@7010: uint CargoList::Count() const rubidium@7010: { rubidium@7010: return count; rubidium@7010: } rubidium@7010: rubidium@7010: bool CargoList::UnpaidCargo() const rubidium@7010: { rubidium@7010: return unpaid_cargo; rubidium@7010: } rubidium@7010: rubidium@7010: Money CargoList::FeederShare() const rubidium@7010: { rubidium@7010: return feeder_share; rubidium@7010: } rubidium@7010: rubidium@7010: StationID CargoList::Source() const rubidium@7010: { rubidium@7010: return source; rubidium@7010: } rubidium@7010: rubidium@7010: uint CargoList::DaysInTransit() const rubidium@7010: { rubidium@7010: return days_in_transit; rubidium@7010: } rubidium@7010: rubidium@7010: void CargoList::Append(CargoPacket *cp) rubidium@7010: { rubidium@7010: assert(cp != NULL); rubidium@7010: assert(cp->IsValid()); rubidium@7010: rubidium@7010: for (List::iterator it = packets.begin(); it != packets.end(); it++) { peter1138@7130: if ((*it)->SameSource(cp) && (*it)->count + cp->count <= 65535) { rubidium@7010: (*it)->count += cp->count; rubidium@7010: (*it)->feeder_share += cp->feeder_share; rubidium@7010: delete cp; rubidium@7010: rubidium@7010: InvalidateCache(); rubidium@7010: return; rubidium@7010: } rubidium@7010: } rubidium@7010: rubidium@7010: /* The packet could not be merged with another one */ rubidium@7010: packets.push_back(cp); rubidium@7010: InvalidateCache(); rubidium@7010: } rubidium@7010: rubidium@7010: rubidium@7010: void CargoList::Truncate(uint count) rubidium@7010: { rubidium@7010: for (List::iterator it = packets.begin(); it != packets.end(); it++) { rubidium@7010: uint local_count = (*it)->count; rubidium@7010: if (local_count <= count) { rubidium@7010: count -= local_count; rubidium@7010: continue; rubidium@7010: } rubidium@7010: rubidium@7010: (*it)->count = count; rubidium@7010: count = 0; rubidium@7010: } rubidium@7010: rubidium@7010: while (!packets.empty()) { rubidium@7010: CargoPacket *cp = packets.back(); rubidium@7010: if (cp->count != 0) break; rubidium@7010: delete cp; rubidium@7010: packets.pop_back(); rubidium@7010: } rubidium@7010: rubidium@7010: InvalidateCache(); rubidium@7010: } rubidium@7010: rubidium@7010: bool CargoList::MoveTo(CargoList *dest, uint count, CargoList::MoveToAction mta, uint data) rubidium@7010: { rubidium@7010: assert(mta == MTA_FINAL_DELIVERY || dest != NULL); rubidium@7010: CargoList tmp; rubidium@7010: rubidium@7010: while (!packets.empty() && count > 0) { rubidium@7010: CargoPacket *cp = *packets.begin(); rubidium@7010: if (cp->count <= count) { rubidium@7010: /* Can move the complete packet */ rubidium@7010: packets.remove(cp); rubidium@7010: switch (mta) { rubidium@7010: case MTA_FINAL_DELIVERY: rubidium@7010: if (cp->source == data) { rubidium@7010: tmp.Append(cp); rubidium@7010: } else { rubidium@7010: count -= cp->count; rubidium@7010: delete cp; rubidium@7010: } rubidium@7010: break; rubidium@7010: case MTA_CARGO_LOAD: rubidium@7010: cp->loaded_at_xy = data; rubidium@7010: /* When cargo is moved into another vehicle you have *always* paid for it */ rubidium@7010: cp->paid_for = false; rubidium@7010: /* FALL THROUGH */ rubidium@7010: case MTA_OTHER: rubidium@7010: count -= cp->count; rubidium@7010: dest->packets.push_back(cp); rubidium@7010: break; rubidium@7010: } rubidium@7010: } else { rubidium@7010: /* Can move only part of the packet, so split it into two pieces */ rubidium@7010: if (mta != MTA_FINAL_DELIVERY) { rubidium@7010: CargoPacket *cp_new = new CargoPacket(); rubidium@7010: cp_new->source = cp->source; rubidium@7010: cp_new->source_xy = cp->source_xy; rubidium@7010: cp_new->loaded_at_xy = (mta == MTA_CARGO_LOAD) ? data : cp->loaded_at_xy; rubidium@7010: rubidium@7010: cp_new->days_in_transit = cp->days_in_transit; rubidium@7010: cp_new->feeder_share = cp->feeder_share / count; rubidium@7010: /* When cargo is moved into another vehicle you have *always* paid for it */ rubidium@7010: cp_new->paid_for = (mta == MTA_CARGO_LOAD) ? false : cp->paid_for; rubidium@7010: rubidium@7010: cp_new->count = count; rubidium@7010: dest->packets.push_back(cp_new); rubidium@7010: rubidium@7010: cp->feeder_share /= cp->count - count; rubidium@7010: } rubidium@7010: cp->count -= count; rubidium@7010: rubidium@7010: count = 0; rubidium@7010: } rubidium@7010: } rubidium@7010: rubidium@7010: bool remaining = !packets.empty(); rubidium@7010: rubidium@7010: if (mta == MTA_FINAL_DELIVERY && !tmp.Empty()) { rubidium@7010: /* There are some packets that could not be delivered at the station, put them back */ rubidium@7010: tmp.MoveTo(this, MAX_UVALUE(uint)); rubidium@7010: tmp.packets.clear(); rubidium@7010: } rubidium@7010: rubidium@7010: if (dest != NULL) dest->InvalidateCache(); rubidium@7010: InvalidateCache(); rubidium@7010: rubidium@7010: return remaining; rubidium@7010: } rubidium@7010: rubidium@7010: void CargoList::InvalidateCache() rubidium@7010: { rubidium@7010: empty = packets.empty(); rubidium@7010: count = 0; rubidium@7010: unpaid_cargo = false; rubidium@7010: feeder_share = 0; rubidium@7010: source = INVALID_STATION; rubidium@7010: days_in_transit = 0; rubidium@7010: rubidium@7010: if (empty) return; rubidium@7010: rubidium@7010: uint dit = 0; rubidium@7010: for (List::const_iterator it = packets.begin(); it != packets.end(); it++) { rubidium@7010: count += (*it)->count; rubidium@7010: unpaid_cargo |= !(*it)->paid_for; rubidium@7010: dit += (*it)->days_in_transit * (*it)->count; rubidium@7010: feeder_share += (*it)->feeder_share; rubidium@7010: } rubidium@7010: days_in_transit = dit / count; rubidium@7010: source = (*packets.begin())->source; rubidium@7010: } bjarni@9081: bjarni@9081: /** Restore an array of cargo packets from a backup bjarni@9081: * The end of the row should be marked by an invalid packet bjarni@9081: */ bjarni@9081: void CargoPacket::RestoreBackup() const bjarni@9081: { bjarni@9081: for (const CargoPacket *cargo = this; cargo->IsValid(); cargo++) { bjarni@9081: CargoPacket *dest = GetCargoPacket(cargo->index); bjarni@9081: assert(!dest->IsValid()); bjarni@9081: memcpy(dest, cargo, sizeof(CargoPacket)); bjarni@9081: } bjarni@9081: }