Implement Tree Paint (#1115)

* Implement tree drawing

Co-authored-by: Marijn van der Werf <marijn.vanderwerf@gmail.com>
This commit is contained in:
Duncan 2021-08-18 22:09:34 +01:00 committed by GitHub
parent 18422de137
commit 65895bcab1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 214 additions and 54 deletions

View File

@ -1,4 +1,5 @@
#include "Gfx.h"
#include "../Config.h"
#include "../Console.h"
#include "../Drawing/SoftwareDrawingEngine.h"
#include "../Environment.h"
@ -75,6 +76,19 @@ namespace OpenLoco::Gfx
return _screenContext;
}
// 0x004FFAE8
uint32_t applyGhostToImage(uint32_t imageId)
{
if (Config::get().construction_marker)
{
return Gfx::recolourTranslucent(imageId, PaletteIndex::index_31);
}
else
{
return Gfx::recolour(imageId, PaletteIndex::index_2C);
}
}
const PaletteMap& PaletteMap::getDefault()
{
static bool initialised = false;

View File

@ -234,6 +234,7 @@ namespace OpenLoco::Gfx
uint32_t recolour2(uint32_t image, uint8_t colour1, uint8_t colour2);
uint32_t recolour2(uint32_t image, ColourScheme colourScheme);
uint32_t recolourTranslucent(uint32_t image, uint8_t colour);
uint32_t applyGhostToImage(uint32_t imageId);
void invalidateScreen();
void setDirtyBlocks(int32_t left, int32_t top, int32_t right, int32_t bottom);

View File

@ -246,6 +246,13 @@ namespace OpenLoco::Map
public:
uint8_t treeObjectId() const { return _4; } // _4
uint8_t rotation() const { return _type & 0x03; }
uint8_t quadrant() const { return (_type >> 6) & 0x03; } // _0_C0
uint8_t unk5l() const { return _5 & 0xF; }
uint8_t colour() const { return _6 & 0x1F; } //_6l
bool hasSnow() const { return _6 & 0x40; } //_6_40
uint8_t unk7l() const { return _7 & 0x7; }
uint8_t season() const { return (_7 >> 3) & 0x7; } // unsure of &0x7
};
struct WallElement : public TileElementBase

View File

@ -7,10 +7,10 @@ namespace OpenLoco
// 0x004BE2A2
void TreeObject::drawPreviewImage(Gfx::Context& context, const int16_t x, const int16_t y) const
{
uint32_t image = treeGrowth[growth] * num_rotations;
uint32_t image = getTreeGrowthDisplayOffset() * num_rotations;
auto rotation = (num_rotations - 1) & 2;
image += rotation;
image += sprites[season_state];
image += sprites[0][season_state];
auto colourOptions = colours;
if (colourOptions != 0)
@ -30,9 +30,9 @@ namespace OpenLoco
if (flags & TreeObjectFlags::hasSnowVariation)
{
auto snowImage = treeGrowth[growth] * num_rotations;
auto snowImage = getTreeGrowthDisplayOffset() * num_rotations;
snowImage += rotation;
snowImage += sprites[season_state + 6];
snowImage += sprites[1][season_state];
if (colourOptions != 0)
{
@ -52,4 +52,24 @@ namespace OpenLoco
}
Gfx::drawImage(&context, treePos.x, treePos.y, image);
}
// 0x00500775
constexpr std::array<uint8_t, 11> treeGrowth = { {
1,
0,
1,
2,
2,
3,
4,
5,
6,
0,
0,
} };
uint8_t TreeObject::getTreeGrowthDisplayOffset() const
{
return treeGrowth[growth];
}
}

View File

@ -10,20 +10,6 @@ namespace OpenLoco
struct Context;
}
const std::array<uint8_t, 11> treeGrowth = { {
1,
0,
1,
2,
2,
3,
4,
5,
6,
0,
0,
} };
namespace TreeObjectFlags
{
constexpr uint16_t hasSnowVariation = (1 << 0);
@ -33,7 +19,7 @@ namespace OpenLoco
constexpr uint16_t requiresWater = (1 << 4);
constexpr uint16_t unk5 = (1 << 5);
constexpr uint16_t droughtResistant = (1 << 6);
constexpr uint16_t unk7 = (1 << 7);
constexpr uint16_t hasShadow = (1 << 7);
}
#pragma pack(push, 1)
@ -44,11 +30,12 @@ namespace OpenLoco
uint8_t height; // 0x03
uint8_t var_04;
uint8_t var_05;
uint8_t num_rotations; // 0x06 (1,2,4)
uint8_t growth; // 0x07 (number of tree size images)
uint16_t flags; // 0x08
uint32_t sprites[12]; // 0x0A
uint8_t pad_3A[0x3D - 0x3A];
uint8_t num_rotations; // 0x06 (1,2,4)
uint8_t growth; // 0x07 (number of tree size images)
uint16_t flags; // 0x08
uint32_t sprites[2][6]; // 0x0A
uint16_t shadowImageOffset; // 0x3A
uint8_t var_3C;
uint8_t season_state; // 0x3D (index for sprites, seasons + dying)
uint8_t var_3E;
uint8_t cost_index; // 0x3F
@ -59,6 +46,7 @@ namespace OpenLoco
uint16_t var_4A;
void drawPreviewImage(Gfx::Context& context, const int16_t x, const int16_t y) const;
uint8_t getTreeGrowthDisplayOffset() const;
};
#pragma pack(pop)
}

View File

@ -56,6 +56,7 @@ namespace OpenLoco::Paint
loco_global<int32_t[4], 0x4FD120> _4FD120;
loco_global<int32_t[4], 0x4FD130> _4FD130;
loco_global<int32_t[4], 0x4FD140> _4FD140;
loco_global<int32_t[4], 0x4FD1E0> _4FD1E0;
loco_global<int32_t[4], 0x4FD200> _4FD200;
// 0x004FD120
@ -139,6 +140,26 @@ namespace OpenLoco::Paint
call(_4FD200[currentRotation], regs);
}
// 0x004FD1E0
void PaintSession::addToPlotList4FD1E0(uint32_t imageId, const Map::Pos3& offset, const Map::Pos3& boundBoxOffset, const Map::Pos3& boundBoxSize)
{
registers regs;
regs.ebx = imageId;
regs.al = offset.x;
regs.cl = offset.y;
regs.dx = offset.z;
regs.di = boundBoxSize.x;
regs.si = boundBoxSize.y;
regs.ah = boundBoxSize.z;
addr<0xE3F0A0, int16_t>() = boundBoxOffset.x;
addr<0xE3F0A2, int16_t>() = boundBoxOffset.y;
addr<0xE3F0A4, uint16_t>() = boundBoxOffset.z;
call(_4FD1E0[currentRotation], regs);
}
// 0x0045E779
void PaintSession::attachToPrevious(uint32_t imageId, const Map::Pos2& offset)
{

View File

@ -235,6 +235,21 @@ namespace OpenLoco::Paint
*/
void addToPlotListAsParent(uint32_t imageId, const Map::Pos3& offset, const Map::Pos3& boundBoxOffset, const Map::Pos3& boundBoxSize);
/*
* @param rotation @<ebp>
* @param imageId @<ebx>
* @param offset_x @<al>
* @param offset_y @<cl>
* @param offset_z @<dx>
* @param boundBoxLength_x @<di>
* @param boundBoxLength_y @<si>
* @param boundBoxLength_z @<ah>
* @param boundBoxOffset_x @<0xE3F0A0>
* @param boundBoxOffset_y @<0xE3F0A2>
* @param boundBoxOffset_z @<0xE3F0A4>
*/
void addToPlotList4FD1E0(uint32_t imageId, const Map::Pos3& offset, const Map::Pos3& boundBoxOffset, const Map::Pos3& boundBoxSize);
/*
* @param rotation @<ebp>
* @param imageId @<ebx>

View File

@ -5,6 +5,7 @@
#include "../Map/TileManager.h"
#include "../Ui.h"
#include "Paint.h"
#include "PaintTree.h"
using namespace OpenLoco::Interop;
using namespace OpenLoco::Ui::ViewportInteraction;
@ -156,16 +157,6 @@ namespace OpenLoco::Paint
call(0x0042C6C4, regs);
}
// 0x004BAEDA
static void paintTree(PaintSession& session, Map::TreeElement& elTree)
{
registers regs;
regs.esi = X86Pointer(&elTree);
regs.ecx = (session.getRotation() + (elTree.data()[0] & 0x3)) & 0x3;
regs.dx = elTree.baseZ() * 4;
call(0x004BAEDA, regs);
}
// 0x004C3D7C
static void paintWall(PaintSession& session, Map::WallElement& elWall)
{

View File

@ -0,0 +1,103 @@
#include "PaintTree.h"
#include "../Config.h"
#include "../Graphics/Colour.h"
#include "../Map/Tile.h"
#include "../Objects/ObjectManager.h"
#include "../Objects/TreeObject.h"
#include "../Ui.h"
#include "Paint.h"
using namespace OpenLoco::Interop;
using namespace OpenLoco::Ui::ViewportInteraction;
namespace OpenLoco::Paint
{
constexpr std::array<uint8_t, 6> _50076A = { 3, 0, 1, 2, 1, 4 };
constexpr std::array<bool, 5> _500770 = { true, true, false, false, true };
constexpr std::array<Map::Pos2, 4> _treeQuadrantOffset = {
Map::Pos2{ 7, 7 },
Map::Pos2{ 7, 23 },
Map::Pos2{ 23, 23 },
Map::Pos2{ 23, 7 },
};
// 0x004BAEDA
void paintTree(PaintSession& session, const Map::TreeElement& elTree)
{
session.setItemType(InteractionItem::tree);
const auto* treeObj = ObjectManager::get<TreeObject>(elTree.treeObjectId());
const uint8_t viewableRotation = (session.getRotation() + elTree.rotation()) & 0x3;
const uint32_t treeFrameNum = (viewableRotation % treeObj->num_rotations) + elTree.unk5l() * treeObj->num_rotations;
uint8_t season = elTree.season();
const uint8_t altSeason = elTree.hasSnow() ? 1 : 0;
bool hasImage2 = false;
uint32_t imageId2 = 0;
if (elTree.unk7l() != 7)
{
hasImage2 = true;
uint32_t edx = (elTree.unk7l()) + 1;
season = _50076A[season];
auto image2Season = elTree.season();
if (!_500770[season])
{
image2Season = season;
season = elTree.season();
edx = (~edx) & 0b111;
}
// Unlikely to do anything as no remap flag set
edx = edx << 26;
imageId2 = edx | (treeFrameNum + treeObj->sprites[altSeason][image2Season]);
}
const auto seasonBaseImageId = treeObj->sprites[altSeason][season];
std::optional<uint32_t> shadowImageId = std::nullopt;
if (treeObj->flags & TreeObjectFlags::hasShadow)
{
shadowImageId = Gfx::recolourTranslucent(treeObj->shadowImageOffset + treeFrameNum + seasonBaseImageId, PaletteIndex::index_32);
}
const uint8_t quadrant = (elTree.quadrant() + session.getRotation()) % 4;
const auto imageOffset = Map::Pos3(_treeQuadrantOffset[quadrant].x, _treeQuadrantOffset[quadrant].y, elTree.baseZ() * 4);
const int16_t boundBoxSizeZ = std::min(elTree.clearZ() - elTree.baseZ(), 32) * 4 - 3;
uint32_t imageId1 = treeFrameNum + seasonBaseImageId;
if (treeObj->colours != 0)
{
// No vanilla object has this property set
const uint8_t colour = elTree.colour();
imageId2 = Gfx::recolour(imageId2, colour);
imageId1 = Gfx::recolour(imageId1, colour);
}
if (elTree.isGhost())
{
session.setItemType(InteractionItem::noInteraction);
imageId2 = Gfx::applyGhostToImage(imageId2);
imageId1 = Gfx::applyGhostToImage(imageId1);
}
if (shadowImageId)
{
if (session.getContext()->zoom_level <= 1)
{
session.addToPlotListAsParent(*shadowImageId, imageOffset, { 18, 18, 1 }, imageOffset);
}
}
session.addToPlotListAsParent(imageId1, imageOffset, imageOffset + Map::Pos3(0, 0, 2), { 2, 2, boundBoxSizeZ });
if (hasImage2)
{
session.addToPlotList4FD1E0(imageId2, imageOffset, imageOffset + Map::Pos3(0, 0, 2), { 2, 2, boundBoxSizeZ });
}
}
}

View File

@ -0,0 +1,12 @@
#pragma once
namespace OpenLoco::Map
{
struct TreeElement;
}
namespace OpenLoco::Paint
{
struct PaintSession;
void paintTree(PaintSession& session, const Map::TreeElement& elTree);
}

View File

@ -34,19 +34,6 @@ namespace OpenLoco::Paint
4, 3, 2, 1, 0, 0, 0, 0
};
// 0x004FFAE8
static uint32_t applyGhostToImage(uint32_t imageId)
{
if (Config::get().construction_marker)
{
return Gfx::recolourTranslucent(imageId, PaletteIndex::index_31);
}
else
{
return Gfx::recolour(imageId, PaletteIndex::index_2C);
}
}
// 0x004B0CFC
static void paintBogie(PaintSession& session, VehicleBogie* bogie)
{
@ -100,7 +87,7 @@ namespace OpenLoco::Paint
if (bogie->getFlags38() & Flags38::isGhost)
{
session.setItemType(Ui::ViewportInteraction::InteractionItem::noInteraction);
imageId = applyGhostToImage(imageId);
imageId = Gfx::applyGhostToImage(imageId);
}
else if (bogie->var_0C & Flags0C::unk_5)
{
@ -146,7 +133,7 @@ namespace OpenLoco::Paint
if (bogie->getFlags38() & Flags38::isGhost)
{
session.setItemType(Ui::ViewportInteraction::InteractionItem::noInteraction);
imageId = applyGhostToImage(imageId);
imageId = Gfx::applyGhostToImage(imageId);
}
else
{
@ -170,7 +157,7 @@ namespace OpenLoco::Paint
if (bogie->getFlags38() & Flags38::isGhost)
{
session.setItemType(Ui::ViewportInteraction::InteractionItem::noInteraction);
imageId = applyGhostToImage(imageId);
imageId = Gfx::applyGhostToImage(imageId);
}
else
{
@ -441,7 +428,7 @@ namespace OpenLoco::Paint
uint32_t imageId = 0;
if (body->getFlags38() & Flags38::isGhost)
{
imageId = applyGhostToImage(bodyImage);
imageId = Gfx::applyGhostToImage(bodyImage);
}
else if (body->var_0C & Flags0C::unk_5)
{

View File

@ -30,7 +30,6 @@ using namespace OpenLoco::GameCommands;
namespace OpenLoco::Ui::Windows::Terraform
{
static loco_global<std::uint8_t[10], 0x00500775> _byte_500775;
static loco_global<int16_t, 0x0052337A> _dragLastY;
static loco_global<Ui::WindowType, 0x00523392> _toolWindowType;
static loco_global<CompanyId_t, 0x00525E3C> _player_company;
@ -870,10 +869,10 @@ namespace OpenLoco::Ui::Windows::Terraform
static void drawTreeThumb(TreeObject* treeObj, Gfx::Context* clipped)
{
uint32_t image = _byte_500775[treeObj->growth] * treeObj->num_rotations;
uint32_t image = treeObj->getTreeGrowthDisplayOffset() * treeObj->num_rotations;
auto rotation = (treeObj->num_rotations - 1) & _treeRotation;
image += rotation;
image += treeObj->sprites[treeObj->season_state];
image += treeObj->sprites[0][treeObj->season_state];
auto colourOptions = treeObj->colours;
if (colourOptions != 0)

View File

@ -108,6 +108,7 @@
<ClCompile Include="Paint\PaintEntity.cpp" />
<ClCompile Include="Paint\PaintMiscEntity.cpp" />
<ClCompile Include="Paint\PaintTile.cpp" />
<ClCompile Include="Paint\PaintTree.cpp" />
<ClCompile Include="Paint\PaintVehicle.cpp" />
<ClCompile Include="Platform\Crash.cpp" />
<ClCompile Include="Platform\Platform.Posix.cpp" />
@ -315,6 +316,7 @@
<ClInclude Include="Paint\PaintEntity.h" />
<ClInclude Include="Paint\PaintMiscEntity.h" />
<ClInclude Include="Paint\PaintTile.h" />
<ClInclude Include="Paint\PaintTree.h" />
<ClInclude Include="Paint\PaintVehicle.h" />
<ClInclude Include="Platform/Crash.h" />
<ClInclude Include="Platform\Platform.h" />