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