OpenLoco/src/OpenLoco/Windows/Error.cpp

273 lines
8.5 KiB
C++

#include "../Audio/Audio.h"
#include "../CompanyManager.h"
#include "../Graphics/Colour.h"
#include "../Graphics/ImageIds.h"
#include "../Interop/Interop.hpp"
#include "../Localisation/FormatArguments.hpp"
#include "../Localisation/StringManager.h"
#include "../Objects/CompetitorObject.h"
#include "../Objects/InterfaceSkinObject.h"
#include "../Objects/ObjectManager.h"
#include "../OpenLoco.h"
#include "../Ui/WindowManager.h"
#include "../Widget.h"
using namespace OpenLoco::Interop;
namespace OpenLoco::Ui::Windows::Error
{
static loco_global<uint8_t, 0x00508F09> _suppressErrorSound;
static loco_global<char[512], 0x009C64B3> _byte_9C64B3;
static loco_global<uint16_t, 0x009C66B3> _word_9C66B3;
static loco_global<uint8_t, 0x009C68EC> _errorCompetitorId;
static loco_global<int32_t, 0x112C876> gCurrentFontSpriteBase;
static loco_global<int32_t, 0x0113E72C> _cursorX;
static loco_global<int32_t, 0x0113E730> _cursorY;
namespace Common
{
static WindowEventList events;
static void draw(Ui::Window* self, Gfx::Context* context);
static void onPeriodicUpdate(Ui::Window* self);
static void initEvents();
}
namespace Error
{
enum widx
{
frame,
};
Widget widgets[] = {
makeWidget({ 0, 0 }, { 200, 42 }, WidgetType::wt_3, WindowColour::primary),
widgetEnd(),
};
}
namespace ErrorCompetitor
{
enum widx
{
frame,
innerFrame,
};
Widget widgets[] = {
makeWidget({ 0, 0 }, { 250, 70 }, WidgetType::wt_3, WindowColour::primary),
makeWidget({ 3, 3 }, { 64, 64 }, WidgetType::wt_3, WindowColour::secondary),
widgetEnd(),
};
}
static char* formatErrorString(string_id title, string_id message, FormatArguments args, char* buffer)
{
char* ptr = (char*)buffer;
ptr[0] = ControlCodes::colour_black;
ptr++;
if (title != StringIds::null)
{
ptr = StringManager::formatString(ptr, title, &args);
}
if (message != StringIds::null)
{
if (title != StringIds::null)
{
*ptr = ControlCodes::newline;
ptr++;
}
StringManager::formatString(ptr, message, &args);
}
return ptr;
}
static void createErrorWindow(string_id title, string_id message)
{
WindowManager::close(WindowType::error);
char* buffer = _byte_9C64B3;
auto args = FormatArguments();
buffer = formatErrorString(title, message, args, buffer);
if (buffer != &_byte_9C64B3[0])
{
gCurrentFontSpriteBase = Font::medium_bold;
int16_t strWidth;
{
strWidth = Gfx::getStringWidthNewLined(&_byte_9C64B3[0]);
}
strWidth = std::min<int16_t>(strWidth, 196);
gCurrentFontSpriteBase = Font::medium_bold;
{
uint16_t breakLineCount = 0;
std::tie(strWidth, breakLineCount) = Gfx::wrapString(&_byte_9C64B3[0], strWidth);
_word_9C66B3 = breakLineCount;
}
uint16_t frameWidth = strWidth + 3;
uint16_t frameHeight = (_word_9C66B3 + 1) * 10 + 4;
uint16_t width = frameWidth;
uint16_t height = frameHeight;
if (_errorCompetitorId != 0xFF)
{
width = 250;
height = 70;
}
int x, y;
int maxY = Ui::height() - height;
y = _cursorY + 26; // Normally, we'd display the tooltip 26 lower
if (y > maxY)
{
// If y is too large, the tooltip could be forced below the cursor if we'd just clamped y,
// so we'll subtract a bit more
y -= height + 40;
}
y = std::clamp(y, 22, maxY);
x = std::clamp(_cursorX - (width / 2), 0, Ui::width() - width);
Ui::Size windowSize = { width, height };
auto error = WindowManager::createWindow(
WindowType::error,
Ui::Point(x, y),
windowSize,
WindowFlags::stick_to_front | WindowFlags::transparent | WindowFlags::flag_7,
&Common::events);
Common::initEvents();
if (_errorCompetitorId != 0xFF)
{
error->widgets = ErrorCompetitor::widgets;
}
else
{
error->widgets = Error::widgets;
}
error->widgets[Error::widx::frame].right = frameWidth;
error->widgets[Error::widx::frame].bottom = frameHeight;
error->var_846 = 0;
if (!(_suppressErrorSound & (1 << 0)))
{
int32_t pan = (error->width / 2) + error->x;
Audio::playSound(Audio::SoundId::error, pan);
}
}
}
// 0x00431A8A
void open(string_id title, string_id message)
{
_errorCompetitorId = 0xFF;
createErrorWindow(title, message);
}
// 0x00431908
void openWithCompetitor(string_id title, string_id message, uint8_t competitorId)
{
_errorCompetitorId = competitorId;
createErrorWindow(title, message);
}
void registerHooks()
{
registerHook(
0x00431A8A,
[](registers& regs) FORCE_ALIGN_ARG_POINTER -> uint8_t {
Ui::Windows::Error::open(regs.bx, regs.dx);
return 0;
});
registerHook(
0x00431908,
[](registers& regs) FORCE_ALIGN_ARG_POINTER -> uint8_t {
Ui::Windows::Error::openWithCompetitor(regs.bx, regs.dx, regs.al);
return 0;
});
}
namespace Common
{
// 0x00431C05
static void draw(Ui::Window* self, Gfx::Context* context)
{
uint16_t x = self->x;
uint16_t y = self->y;
uint16_t width = self->width;
uint16_t height = self->height;
auto skin = ObjectManager::get<InterfaceSkinObject>()->colour_09;
Gfx::drawRect(context, x + 1, y + 1, width - 2, height - 2, 0x2000000 | 45);
Gfx::drawRect(context, x + 1, y + 1, width - 2, height - 2, 0x2000000 | (116 + skin));
Gfx::drawRect(context, x, y + 2, 1, height - 4, 0x2000000 | 46);
Gfx::drawRect(context, x + width - 1, y + 2, 1, height - 4, 0x2000000 | 46);
Gfx::drawRect(context, x + 2, y + height - 1, width - 4, 1, 0x2000000 | 46);
Gfx::drawRect(context, x + 2, y, width - 4, 1, 0x2000000 | 46);
Gfx::drawRect(context, x + 1, y + 1, 1, 1, 0x2000000 | 46);
Gfx::drawRect(context, x + width - 1 - 1, y + 1, 1, 1, 0x2000000 | 46);
Gfx::drawRect(context, x + 1, y + height - 1 - 1, 1, 1, 0x2000000 | 46);
Gfx::drawRect(context, x + width - 1 - 1, y + height - 1 - 1, 1, 1, 0x2000000 | 46);
if (_errorCompetitorId == 0xFF)
{
Gfx::drawStringCentredRaw(*context, ((width + 1) / 2) + x - 1, y + 1, _word_9C66B3, Colour::black, &_byte_9C64B3[0]);
}
else
{
auto xPos = self->widgets[ErrorCompetitor::widx::innerFrame].left + self->x;
auto yPos = self->widgets[ErrorCompetitor::widx::innerFrame].top + self->y;
auto company = CompanyManager::get(_errorCompetitorId);
auto companyObj = ObjectManager::get<CompetitorObject>(company->id());
auto imageId = companyObj->images[company->owner_emotion];
imageId = Gfx::recolour(imageId, company->mainColours.primary);
imageId++;
Gfx::drawImage(context, xPos, yPos, imageId);
if (company->jail_status != 0)
{
Gfx::drawImage(context, xPos, yPos, ImageIds::owner_jailed);
}
Gfx::drawStringCentredRaw(*context, self->x + 156, self->y + 20, _word_9C66B3, Colour::black, &_byte_9C64B3[0]);
}
}
// 0x00431E1B
static void onPeriodicUpdate(Ui::Window* self)
{
self->var_846++;
if (self->var_846 >= 7)
{
WindowManager::close(self);
}
}
static void initEvents()
{
events.draw = Common::draw;
events.on_periodic_update = Common::onPeriodicUpdate;
}
}
}