Implement Tree Paint (#1115)
* Implement tree drawing Co-authored-by: Marijn van der Werf <marijn.vanderwerf@gmail.com>
This commit is contained in:
parent
18422de137
commit
65895bcab1
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 });
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
namespace OpenLoco::Map
|
||||
{
|
||||
struct TreeElement;
|
||||
}
|
||||
namespace OpenLoco::Paint
|
||||
{
|
||||
struct PaintSession;
|
||||
|
||||
void paintTree(PaintSession& session, const Map::TreeElement& elTree);
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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" />
|
||||
|
|
Loading…
Reference in New Issue