Building tile element update (#1100)

Co-authored-by: Marijn van der Werf <marijn.vanderwerf@gmail.com>
This commit is contained in:
Duncan 2021-08-11 20:52:21 +01:00 committed by GitHub
parent 498911fd81
commit 904abdecce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 304 additions and 17 deletions

View File

@ -0,0 +1,211 @@
#include "../GameCommands/GameCommands.h"
#include "../Objects/BuildingObject.h"
#include "../StationManager.h"
#include "../TownManager.h"
#include "../ViewportManager.h"
#include "Tile.h"
#include "TileManager.h"
namespace OpenLoco::Map
{
static void sub_497DC1(const Map::Pos2& loc, uint32_t population, uint32_t unk1, uint16_t rating, uint16_t unk3)
{
registers regs;
regs.edi = population;
regs.esi = unk1;
regs.ebp = rating | (unk3 << 16);
regs.ax = loc.x;
regs.cx = loc.y;
call(0x00497DC1, regs);
}
// 0x0042DF8B
bool BuildingElement::update(const Map::Pos2& loc)
{
// Only update from tile index 0 of multi tile buildings
if (multiTileIndex())
{
return true;
}
if (isGhost())
{
return true;
}
auto* buildingObj = object();
if (!isConstructed())
{
auto newUnk5u = unk5u();
auto newAge = age();
auto isConstructed = false;
if (unk5u() != 7)
{
newUnk5u++;
}
else
{
auto* unkVariation = buildingObj->variationsArr10[variation()];
if (unkVariation[age() + 1] != 0xFF)
{
newUnk5u = 0;
newAge++;
}
else
{
auto totalHeight = 3;
for (; *unkVariation != 0xFF; unkVariation++)
{
totalHeight += buildingObj->varationHeights[*unkVariation];
}
Ui::ViewportManager::invalidate(loc, baseZ() * 4, clearZ() * 4, ZoomLevel::quarter);
const auto newClearHeight = baseZ() + totalHeight / 4;
setClearZ(newClearHeight);
if (buildingObj->var_AD != 0)
{
TileManager::createAnimation(5, loc, baseZ());
}
if (buildingObj->flags & BuildingObjectFlags::large_tile)
{
for (auto i = 1; i < 4; ++i)
{
const auto pos = loc + Map::offsets[i];
auto tile = TileManager::get(pos);
for (auto& el : tile)
{
auto* elBuilding2 = el.asBuilding();
if (elBuilding2 == nullptr)
{
continue;
}
if (elBuilding2->baseZ() != baseZ())
{
continue;
}
Ui::ViewportManager::invalidate(pos, elBuilding2->baseZ() * 4, elBuilding2->clearZ() * 4, ZoomLevel::quarter);
elBuilding2->setClearZ(newClearHeight);
}
}
}
sub_497DC1(loc, buildingObj->producedQuantity[0], 0, 0, 0);
newUnk5u = 0;
newAge = 0;
isConstructed = true;
}
}
setConstructed(isConstructed);
setUnk5u(newUnk5u);
setAge(newAge);
Ui::ViewportManager::invalidate(loc, baseZ() * 4, clearZ() * 4, ZoomLevel::quarter);
if (buildingObj->flags & BuildingObjectFlags::large_tile)
{
for (auto i = 1; i < 4; ++i)
{
const auto pos = loc + Map::offsets[i];
auto tile = TileManager::get(pos);
for (auto& el : tile)
{
auto* elBuilding2 = el.asBuilding();
if (elBuilding2 == nullptr)
{
continue;
}
if (elBuilding2->baseZ() != baseZ())
{
continue;
}
elBuilding2->setConstructed(isConstructed);
elBuilding2->setUnk5u(newUnk5u);
elBuilding2->setAge(newAge);
Ui::ViewportManager::invalidate(pos, elBuilding2->baseZ() * 4, elBuilding2->clearZ() * 4, ZoomLevel::quarter);
}
}
}
}
if (has_40())
{
return true;
}
auto res = TownManager::getClosestTownAndUnk(loc);
if (!res)
{
return true;
}
auto* town = TownManager::get(res->first);
if (isConstructed())
{
setUnk5u(unk5u() + 1);
if (!unk5u() && age() != 63)
{
const auto newAge = age() + 1;
setAge(newAge);
if (buildingObj->flags & BuildingObjectFlags::large_tile)
{
for (auto i = 1; i < 4; ++i)
{
const auto pos = loc + Map::offsets[i];
auto tile = TileManager::get(pos);
for (auto& el : tile)
{
auto* elBuilding2 = el.asBuilding();
if (elBuilding2 == nullptr)
{
continue;
}
if (elBuilding2->baseZ() != baseZ())
{
continue;
}
elBuilding2->setAge(newAge);
}
}
}
}
}
if (isConstructed() && age() >= 40)
{
if (town->prng.randNext(0xFFFF) <= 16)
{
GameCommands::BuildingRemovalArgs args;
args.pos = Map::Pos3(loc.x, loc.y, baseZ() * 4);
GameCommands::do_45(GameCommands::Flags::apply, args);
return false;
}
}
if (isConstructed())
{
// This is purely to keep things in sync when confirmed no desync move
// rand call into loop and force a desync.
const uint16_t randVal = town->prng.randNext(0xFFFF);
const uint8_t randArr[2] = { static_cast<uint8_t>(randVal), static_cast<uint8_t>(randVal >> 8) };
for (auto i = 0; i < 2; ++i)
{
if (randArr[i] >= buildingObj->producedQuantity[i])
{
continue;
}
auto producedAmount = randArr[i] / 4 + 1;
if ((addr<0x00525E28, uint32_t>() & (1 << 1)) != 0)
{
producedAmount = (producedAmount + 1) / 2;
}
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;
}
}
return true;
}
}

View File

@ -210,11 +210,30 @@ namespace OpenLoco::Map
public:
bool has_40() const { return (_type & 0x40) != 0; }
bool isConstructed() const { return (_type & 0x80) != 0; }
void setConstructed(bool state)
{
_type &= ~0x80;
_type |= state ? 0x80 : 0;
}
uint8_t colour() const { return _6 >> 11; }
void setColour(Colour_t colour) { _6 = (_6 & 0x7FF) | (colour << 11); }
uint8_t objectId() const { return _4; }
BuildingObject* object() const;
uint8_t multiTileIndex() const { return _5 & 3; }
uint8_t unk5u() const { return _5 >> 5; } // likely age related as well (higher precision)
void setUnk5u(uint8_t value)
{
_5 &= ~0xE0;
_5 |= value << 5;
}
uint8_t variation() const { return (_6 >> 6) & 0x1F; }
uint8_t age() const { return _6 & 0x3F; } // 6l
void setAge(uint8_t value) // 6l
{
_6 &= ~0x3F;
_6 |= value & 0x3F;
}
bool update(const Map::Pos2& loc);
};
struct TreeElement : public TileElementBase

View File

@ -627,8 +627,15 @@ namespace OpenLoco::Map::TileManager
call(0x004691FA, regs);
break;
case ElementType::building:
call(0x0042DF8B, regs);
break;
{
auto* elBuilding = el.asBuilding();
if (elBuilding == nullptr) // Can never happen
{
return false;
}
return elBuilding->update(loc);
}
case ElementType::tree:
call(0x004BD52B, regs);
break;

View File

@ -23,23 +23,27 @@ namespace OpenLoco
{
string_id name;
uint8_t pad_02[0x07 - 0x02];
uint8_t numVariations; //0x7
uint8_t pad_08[0x90 - 0x08];
uint32_t colours; // 0x90
uint16_t designedYear; // 0x94
uint16_t obsoleteYear; // 0x96
uint8_t flags; // 0x98
uint8_t numVariations; //0x7
uint8_t* varationHeights; // 0x8
uint8_t pad_0C[0x10 - 0x0C];
uint8_t* variationsArr10[32]; // 0x10
uint32_t colours; // 0x90
uint16_t designedYear; // 0x94
uint16_t obsoleteYear; // 0x96
uint8_t flags; // 0x98
uint8_t pad_99[0xA0 - 0x99];
uint8_t var_A0[2];
uint8_t producedCargoType[2];
uint8_t producedQuantity[2]; // 0xA0
uint8_t producedCargoType[2]; // 0xA2
uint8_t var_A4[2];
uint8_t var_A6[2];
uint8_t var_A8[2];
uint8_t pad_AA[0xAD - 0xAA];
uint8_t var_AD;
void drawPreviewImage(Gfx::Context& context, const int16_t x, const int16_t y) const;
void drawBuilding(Gfx::Context* clipped, uint8_t buildingRotation, int16_t x, int16_t y, Colour_t colour) const;
void drawDescription(Gfx::Context& context, const int16_t x, const int16_t y, [[maybe_unused]] const int16_t width) const;
};
#pragma pack(pop)
static_assert(sizeof(BuildingObject) == 0xAA);
static_assert(sizeof(BuildingObject) == 0xAE);
}

View File

@ -377,7 +377,7 @@ namespace OpenLoco
{
cargoSearchState.addScore(cargoId, obj->var_A6[i]);
if (obj->var_A0[i] != 0)
if (obj->producedQuantity[i] != 0)
{
cargoSearchState.addProducedCargoType(cargoId);
}

View File

@ -411,6 +411,18 @@ namespace OpenLoco::StationManager
}
}
// 0x0042F2FE
uint16_t sendProducedCargoToStations(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;
}
void registerHooks()
{
// Can be removed once the createStation function has been implemented (used by place.*Station game commands)

View File

@ -18,4 +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);
}

View File

@ -38,8 +38,8 @@ namespace OpenLoco
coord_t y; // 0x04
uint16_t flags; // 0x06
LabelFrame labelFrame; // 0x08
uint8_t pad_28[0x30 - 0x28];
uint32_t population; // 0x30
Utility::prng prng; // 0x28
uint32_t population; // 0x30
uint8_t pad_34[0x38 - 0x34];
uint16_t var_38;
int16_t company_ratings[15]; // 0x3A
@ -51,7 +51,7 @@ namespace OpenLoco
uint8_t pad_150[0x158 - 0x150];
uint16_t monthly_cargo_delivered[32];
uint32_t cargo_influence_flags;
uint8_t pad_19C[0x1A4 - 0x19C];
uint16_t var_19C[2][2];
uint8_t build_speed; // 0x1A4, 1=slow build speed, 4=fast build speed
uint8_t unk_1A5;
uint16_t num_stations; // 0x1A6

View File

@ -150,6 +150,37 @@ namespace OpenLoco::TownManager
Ui::WindowManager::invalidate(Ui::WindowType::town);
}
// 0x00497DC1
std::optional<std::pair<TownId_t, uint8_t>> getClosestTownAndUnk(const Map::Pos2& loc)
{
int32_t closestDistance = std::numeric_limits<uint16_t>::max();
auto closestTown = TownId::null; // ebx
for (const auto& town : towns())
{
const auto distance = Math::Vector::manhattanDistance(Map::Pos2(town.x, town.y), loc);
if (distance < closestDistance)
{
closestDistance = distance;
closestTown = town.id();
}
}
if (closestDistance == std::numeric_limits<uint16_t>::max())
{
return std::nullopt;
}
const auto* town = get(closestTown);
if (town == nullptr)
{
return std::nullopt;
}
const int32_t realDistance = Math::Vector::distance(Map::Pos2(town->x, town->y), loc);
const auto unk = std::clamp((realDistance - town->var_38 * 4 + 512) / 128, 0, 4);
const uint8_t invUnk = std::min(4 - unk, 3); //edx
return { std::make_pair(town->id(), invUnk) };
}
}
OpenLoco::TownId_t OpenLoco::Town::id() const

View File

@ -11,6 +11,7 @@ namespace OpenLoco::TownManager
void reset();
LocoFixedVector<Town> towns();
Town* get(TownId_t id);
std::optional<std::pair<TownId_t, uint8_t>> getClosestTownAndUnk(const Map::Pos2& loc);
void update();
void updateLabels();
void updateMonthly();

View File

@ -806,13 +806,13 @@ namespace OpenLoco::Ui::ViewportInteraction
}
else
{
if (buildingObj->var_A0[0] != 0 || buildingObj->var_A0[1] != 0)
if (buildingObj->producedQuantity[0] != 0 || buildingObj->producedQuantity[1] != 0)
{
buffer = StringManager::formatString(buffer, StringIds::produces);
bool requiresComma = false;
for (auto i = 0; i < 2; ++i)
{
if (buildingObj->var_A0[i] != 0)
if (buildingObj->producedQuantity[i] != 0)
{
if (requiresComma)
{

View File

@ -62,6 +62,7 @@
<ClCompile Include="Localisation\Languages.cpp" />
<ClCompile Include="Localisation\StringManager.cpp" />
<ClCompile Include="Localisation\Unicode.cpp" />
<ClCompile Include="Map\BuildingTile.cpp" />
<ClCompile Include="Map\MapGenerator.cpp" />
<ClCompile Include="Map\SurfaceTile.cpp" />
<ClCompile Include="Map\Tile.cpp" />