Fix: #366. Implement deliver cargo to nearby stations (#1101)

* Fix: #366. Implement deliver cargo to nearby stations

Original game had a bug where it would write to bad memory and end up
delivering cargo to stations that it shouldn't

* Add note and break

* Update changelog

* Adjust variable name
This commit is contained in:
Duncan 2021-08-12 11:52:41 +01:00 committed by GitHub
parent 44f5210c2f
commit cba3bd06d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 102 additions and 11 deletions

View File

@ -1,5 +1,6 @@
21.07+ (???)
------------------------------------------------------------------------
- Fix: [#366] People and mail cargo incorrectly delivered to far away stations.
- Fix: [#1035] Incorrect colour selection when building buildings.
- Fix: [#1070] Crash when naming stations after exhausting natural names.
- Fix: [#1094] Repeated clicking on construction window not always working.

View File

@ -203,7 +203,7 @@ namespace OpenLoco::Map
town->var_19C[i][0] += producedAmount;
const auto size = (buildingObj->flags & BuildingObjectFlags::large_tile) ? Map::TilePos2(2, 2) : Map::TilePos2(1, 1);
town->var_19C[i][1] += StationManager::sendProducedCargoToStations(buildingObj->producedCargoType[i], producedAmount, loc, size) & 0xFF;
town->var_19C[i][1] += StationManager::deliverCargoToNearbyStations(buildingObj->producedCargoType[i], producedAmount, loc, size) & 0xFF;
}
}
return true;

View File

@ -528,6 +528,16 @@ namespace OpenLoco
twn->monthly_cargo_delivered[cargoType] = Math::Bound::add(twn->monthly_cargo_delivered[cargoType], cargoQuantity);
}
// 0x0042F489
void Station::deliverCargoToStation(const uint8_t cargoType, const uint8_t cargoQuantity)
{
auto& cargoStats = cargo_stats[cargoType];
cargoStats.quantity = Math::Bound::add(cargoStats.quantity, cargoQuantity);
cargoStats.enroute_age = 0;
cargoStats.origin = id();
updateCargoDistribution();
}
// 0x0048F7D1
void Station::sub_48F7D1()
{

View File

@ -111,6 +111,7 @@ namespace OpenLoco
void invalidate();
void invalidateWindow();
void setCatchmentDisplay(uint8_t flags);
void deliverCargoToStation(const uint8_t cargoType, const uint8_t cargoQuantity);
void deliverCargoToTown(uint8_t cargoType, uint16_t cargoQuantity);
void updateCargoDistribution();

View File

@ -12,6 +12,7 @@
#include "Window.h"
#include <bitset>
#include <numeric>
using namespace OpenLoco::Interop;
using namespace OpenLoco::Ui;
@ -412,17 +413,95 @@ namespace OpenLoco::StationManager
}
// 0x0042F2FE
uint16_t sendProducedCargoToStations(const uint8_t cargoType, const uint8_t cargoQty, const Map::Pos2& pos, const Map::TilePos2& size)
uint16_t deliverCargoToNearbyStations(const uint8_t cargoType, const uint8_t cargoQty, const Map::Pos2& pos, const Map::TilePos2& size)
{
registers regs;
regs.ax = pos.x;
regs.cx = pos.y;
regs.dx = size.x | (size.y << 8);
regs.bh = cargoType;
regs.bl = cargoQty;
call(0x0042F2FE, regs);
return regs.bx;
const auto initialLoc = TilePos2(pos) - TilePos2(4, 4);
const auto catchmentSize = size + TilePos2(8, 8);
// TODO: Use a fixed size array (max size 15)
std::vector<std::pair<StationId_t, uint8_t>> foundStations;
for (TilePos2 searchOffset{ 0, 0 }; searchOffset.y < catchmentSize.y; ++searchOffset.y)
{
for (; searchOffset.x < catchmentSize.x; ++searchOffset.x)
{
const auto searchLoc = initialLoc + searchOffset;
if (!Map::validCoords(searchLoc))
{
continue;
}
const auto tile = TileManager::get(searchLoc);
for (const auto& el : tile)
{
auto* elStation = el.asStation();
if (elStation == nullptr)
{
continue;
}
if (elStation->isFlag5() || elStation->isGhost())
{
continue;
}
if (foundStations.size() > 15)
{
break;
}
auto res = std::find_if(foundStations.begin(), foundStations.end(), [stationId = elStation->stationId()](const std::pair<StationId_t, uint8_t>& item) { return item.first == stationId; });
if (res != foundStations.end())
{
continue;
}
auto* station = get(elStation->stationId());
if (station == nullptr)
{
continue;
}
if (!(station->cargo_stats[cargoType].flags & (1 << 1)))
{
continue;
}
foundStations.push_back(std::make_pair(elStation->stationId(), station->cargo_stats[cargoType].rating));
}
}
searchOffset.x = 0;
}
if (foundStations.empty())
{
return 0;
}
const auto ratingTotal = std::accumulate(foundStations.begin(), foundStations.end(), 0, [](const int32_t a, const std::pair<StationId_t, uint8_t>& b) { return a + b.second * b.second; });
if (ratingTotal == 0)
{
return 0;
}
uint16_t cargoQtyDelivered = 0;
for (const auto& [stationId, rating] : foundStations)
{
auto* station = get(stationId);
if (station == nullptr)
{
continue;
}
const auto defaultShare = (rating * rating * cargoQty) / ratingTotal;
const auto alternateShare = (rating * cargoQty) / 256;
auto share = std::min(defaultShare, alternateShare);
if (rating > 66)
{
share++;
}
cargoQtyDelivered += share;
station->deliverCargoToStation(cargoType, share);
}
return std::min<uint16_t>(cargoQtyDelivered, cargoQty);
}
void registerHooks()
{
// Can be removed once the createStation function has been implemented (used by place.*Station game commands)

View File

@ -18,5 +18,5 @@ namespace OpenLoco::StationManager
string_id generateNewStationName(StationId_t stationId, TownId_t townId, Map::Pos3 position, uint8_t mode);
void zeroUnused();
void registerHooks();
uint16_t sendProducedCargoToStations(const uint8_t cargoType, const uint8_t cargoQty, const Map::Pos2& pos, const Map::TilePos2& size);
uint16_t deliverCargoToNearbyStations(const uint8_t cargoType, const uint8_t cargoQty, const Map::Pos2& pos, const Map::TilePos2& size);
}