Building tile element update (#1100)
Co-authored-by: Marijn van der Werf <marijn.vanderwerf@gmail.com>
This commit is contained in:
parent
498911fd81
commit
904abdecce
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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" />
|
||||
|
|
Loading…
Reference in New Issue