OpenLoco/src/openloco/Windows/CompanyWindow.cpp

2714 lines
104 KiB
C++

#include "../Company.h"
#include "../CompanyManager.h"
#include "../Config.h"
#include "../Date.h"
#include "../GameCommands.h"
#include "../Graphics/ImageIds.h"
#include "../Input.h"
#include "../Interop/Interop.hpp"
#include "../Localisation/FormatArguments.hpp"
#include "../Localisation/StringIds.h"
#include "../Management/Expenditures.h"
#include "../Objects/CargoObject.h"
#include "../Objects/CompetitorObject.h"
#include "../Objects/InterfaceSkinObject.h"
#include "../Objects/ObjectManager.h"
#include "../OpenLoco.h"
#include "../Things/ThingManager.h"
#include "../Things/Vehicle.h"
#include "../Ui/Dropdown.h"
#include "../Ui/WindowManager.h"
#include "../ViewportManager.h"
#include "../Widget.h"
using namespace openloco::interop;
namespace openloco::ui::windows::CompanyWindow
{
static loco_global<string_id, 0x009C68E8> gGameCommandErrorTitle;
namespace common
{
enum widx
{
frame,
caption,
close_button,
panel,
tab_status,
tab_details,
tab_colour_scheme,
tab_finances,
tab_cargo_delivered,
tab_challenge,
company_select,
};
constexpr uint64_t enabledWidgets = (1 << widx::caption) | (1 << widx::close_button) | (1 << widx::tab_status) | (1 << widx::tab_details) | (1 << widx::tab_colour_scheme) | (1 << widx::tab_finances) | (1 << widx::tab_cargo_delivered) | (1 << widx::tab_challenge);
#define commonWidgets(frameWidth, frameHeight, windowCaptionId) \
makeWidget({ 0, 0 }, { frameWidth, frameHeight }, widget_type::frame, 0), \
makeWidget({ 1, 1 }, { frameWidth - 2, 13 }, widget_type::caption_24, 0, windowCaptionId), \
makeWidget({ frameWidth - 15, 2 }, { 13, 13 }, widget_type::wt_9, 0, image_ids::close_button, string_ids::tooltip_close_window), \
makeWidget({ 0, 41 }, { frameWidth, 120 }, widget_type::panel, 1), \
makeRemapWidget({ 3, 15 }, { 31, 27 }, widget_type::wt_8, 1, image_ids::tab, string_ids::tooltip_company_owner_and_status), \
makeRemapWidget({ 34, 15 }, { 31, 27 }, widget_type::wt_8, 1, image_ids::tab, string_ids::tooltip_company_headquarters_and_details), \
makeRemapWidget({ 65, 15 }, { 31, 27 }, widget_type::wt_8, 1, image_ids::tab, string_ids::tooltip_company_colour_scheme), \
makeRemapWidget({ 96, 15 }, { 31, 27 }, widget_type::wt_8, 1, image_ids::tab, string_ids::tooltip_company_finances), \
makeRemapWidget({ 127, 15 }, { 31, 27 }, widget_type::wt_8, 1, image_ids::tab, string_ids::tooltip_cargo_delivered), \
makeRemapWidget({ 158, 15 }, { 31, 27 }, widget_type::wt_8, 1, image_ids::tab, string_ids::tooltip_company_challenge_for_this_game), \
makeWidget({ 0, 14 }, { 26, 26 }, widget_type::wt_9, 0, image_ids::null, string_ids::tooltip_select_company)
// 0x004343FC
static void disableChallengeTab(window* self)
{
self->disabled_widgets = 0;
if (self->number != companymgr::getControllingId())
self->disabled_widgets |= (1 << widx::tab_challenge);
}
// 0x00431E9B
static void enableRenameByCaption(window* self)
{
if (isEditorMode() || self->number == companymgr::getControllingId())
{
self->enabled_widgets |= (1 << caption);
}
else
{
self->enabled_widgets &= ~(1 << caption);
}
}
// Defined at the bottom of this file.
static void initEvents();
static void renameCompanyPrompt(window* self, widget_index widgetIndex);
static void renameCompany(window* self, char* input);
static void switchCompany(window* self, int16_t itemIndex);
static void switchTab(window* self, widget_index widgetIndex);
static void switchTabWidgets(window* self);
static void drawCompanySelect(const window* const self, gfx::drawpixelinfo_t* const dpi);
static void drawTabs(window* self, gfx::drawpixelinfo_t* dpi);
static void repositionTabs(window* self);
}
namespace status
{
static const gfx::ui_size_t windowSize = { 270, 182 };
enum widx
{
unk_11 = 11,
viewport,
centre_on_viewport,
face,
change_owner_name,
};
static widget_t widgets[] = {
commonWidgets(270, 182, string_ids::title_company),
makeWidget({ 3, 160 }, { 242, 21 }, widget_type::wt_13, 1),
makeWidget({ 3, 44 }, { 96, 120 }, widget_type::viewport, 1, -2),
makeWidget({ 0, 0 }, { 24, 24 }, widget_type::wt_9, 1, image_ids::null, string_ids::move_main_view_to_show_this),
makeWidget({ 178, 57 }, { 66, 66 }, widget_type::wt_9, 1, image_ids::null),
makeWidget({ 154, 124 }, { 112, 22 }, widget_type::wt_9, 1, image_ids::null, string_ids::tooltip_change_owner_name),
widgetEnd(),
};
constexpr uint64_t enabledWidgets = common::enabledWidgets | (1 << common::widx::company_select) | (1 << widx::centre_on_viewport) | (1 << widx::face) | (1 << widx::change_owner_name);
static window_event_list events;
// 0x00431EBB
static void prepareDraw(window* self)
{
common::switchTabWidgets(self);
// Set company name in title.
auto company = companymgr::get(self->number);
FormatArguments args{};
args.push(company->name);
self->disabled_widgets &= ~((1 << widx::centre_on_viewport) | (1 << widx::face));
// No centering on a viewport that doesn't exist.
if (self->viewports[0] == nullptr)
self->disabled_widgets |= (1 << widx::centre_on_viewport);
// No changing other player's faces, unless we're editing a scenario.
if (self->number != companymgr::getControllingId() && !isEditorMode())
self->disabled_widgets |= (1 << widx::face);
self->widgets[common::widx::frame].right = self->width - 1;
self->widgets[common::widx::frame].bottom = self->height - 1;
self->widgets[common::widx::panel].right = self->width - 1;
self->widgets[common::widx::panel].bottom = self->height - 1;
self->widgets[common::widx::caption].right = self->width - 2;
self->widgets[common::widx::close_button].left = self->width - 15;
self->widgets[common::widx::close_button].right = self->width - 3;
self->widgets[widx::viewport].right = self->width - 119;
self->widgets[widx::viewport].bottom = self->height - 14;
self->widgets[widx::unk_11].top = self->height - 12;
self->widgets[widx::unk_11].bottom = self->height - 3;
self->widgets[widx::unk_11].right = self->width - 14;
self->widgets[widx::change_owner_name].right = self->width - 4;
self->widgets[widx::change_owner_name].left = self->width - 116;
self->widgets[widx::face].right = self->width - 28;
self->widgets[widx::face].left = self->width - 93;
self->widgets[common::widx::company_select].right = self->width - 3;
self->widgets[common::widx::company_select].left = self->width - 28;
if (self->number == companymgr::getControllingId())
self->widgets[widx::change_owner_name].type = widget_type::wt_9;
else
self->widgets[widx::change_owner_name].type = widget_type::none;
self->widgets[widx::centre_on_viewport].right = self->widgets[widx::viewport].right - 1;
self->widgets[widx::centre_on_viewport].bottom = self->widgets[widx::viewport].bottom - 1;
self->widgets[widx::centre_on_viewport].left = self->widgets[widx::viewport].right - 24;
self->widgets[widx::centre_on_viewport].top = self->widgets[widx::viewport].bottom - 24;
common::repositionTabs(self);
}
// 0x00432055
static void draw(window* self, gfx::drawpixelinfo_t* dpi)
{
self->draw(dpi);
common::drawTabs(self, dpi);
common::drawCompanySelect(self, dpi);
const auto company = companymgr::get(self->number);
const auto competitor = objectmgr::get<competitor_object>(company->competitor_id);
// Draw 'owner' label
{
auto& widget = self->widgets[widx::face];
gfx::drawStringCentred(
*dpi,
self->x + (widget.left + widget.right) / 2,
self->y + widget.top - 12,
colour::black,
string_ids::window_owner,
nullptr);
}
// Draw company owner image.
{
const uint32_t image = gfx::recolour(competitor->images[company->owner_emotion], company->mainColours.primary) + 1;
const uint16_t x = self->x + self->widgets[widx::face].left + 1;
const uint16_t y = self->y + self->widgets[widx::face].top + 1;
gfx::drawImage(dpi, x, y, image);
}
// If the owner's been naughty, draw some jail bars over them.
if (company->jail_status != 0)
{
const uint32_t image = image_ids::owner_jailed;
const uint16_t x = self->x + self->widgets[widx::face].left + 1;
const uint16_t y = self->y + self->widgets[widx::face].top + 1;
gfx::drawImage(dpi, x, y, image);
}
// Draw owner name
{
FormatArguments args{};
args.push(company->owner_name);
auto& widget = self->widgets[widx::change_owner_name];
auto origin = gfx::point_t(self->x + (widget.left + widget.right) / 2, self->y + widget.top + 5);
gfx::drawStringCentredWrapped(
dpi,
&origin,
widget.right - widget.left,
colour::black,
string_ids::black_stringid,
&args);
}
// Draw owner status
{
// TODO: df fix this
// Until format arguments can allow pushing to the front we will have to call twice once for the status
FormatArguments args{};
string_id status = companymgr::getOwnerStatus(self->number, args);
args = FormatArguments{};
args.push(status);
// and once for the args
companymgr::getOwnerStatus(self->number, args);
auto& widget = self->widgets[widx::unk_11];
gfx::drawString_494BBF(
*dpi,
self->x + widget.left - 1,
self->y + widget.top - 1,
widget.right - widget.left,
colour::black,
string_ids::black_stringid,
&args);
}
if (self->viewports[0] != nullptr)
{
self->drawViewports(dpi);
widget::drawViewportCentreButton(dpi, self, (widget_index)widx::centre_on_viewport);
}
}
// 0x00432244
static void onMouseUp(window* self, widget_index widgetIndex)
{
switch (widgetIndex)
{
case common::widx::caption:
common::renameCompanyPrompt(self, widgetIndex);
break;
case common::widx::close_button:
WindowManager::close(self);
break;
case common::widx::tab_status:
case common::widx::tab_details:
case common::widx::tab_colour_scheme:
case common::widx::tab_finances:
case common::widx::tab_cargo_delivered:
case common::widx::tab_challenge:
common::switchTab(self, widgetIndex);
break;
case widx::centre_on_viewport:
{
if (self->viewports[0] == nullptr)
break;
// Centre viewport on tile/thing.
auto main = WindowManager::getMainWindow();
if (self->saved_view.isThingView())
{
auto thing = thingmgr::get<Thing>(self->saved_view.thingId);
main->viewportCentreOnTile({ thing->x, thing->y, thing->z });
}
else
{
main->viewportCentreOnTile(self->saved_view.getPos());
}
break;
}
case widx::face:
{
CompanyFaceSelection::open(self->number);
break;
}
case widx::change_owner_name:
{
auto company = companymgr::get(self->number);
textinput::openTextinput(self, string_ids::title_name_owner, string_ids::prompt_enter_new_name_for_owner, company->owner_name, widgetIndex, nullptr);
break;
}
}
}
// 0x00432283
static void onMouseDown(window* self, widget_index widgetIndex)
{
if (widgetIndex == common::widx::company_select)
dropdown::populateCompanySelect(self, &self->widgets[widgetIndex]);
}
// 0x0043228E
static void onDropdown(window* self, widget_index widgetIndex, int16_t itemIndex)
{
if (widgetIndex == common::widx::company_select)
common::switchCompany(self, itemIndex);
}
// 0x004325DF
static void renameCompanyOwnerName(window* self, char* input)
{
if (strlen(input) == 0)
return;
gGameCommandErrorTitle = string_ids::cannot_change_owner_name;
bool success = false;
{
uint32_t* buffer = (uint32_t*)input;
game_commands::do_31(self->number, 1, buffer[0], buffer[1], buffer[2]);
game_commands::do_31(0, 2, buffer[3], buffer[4], buffer[5]);
success = game_commands::do_31(0, 0, buffer[6], buffer[7], buffer[8]);
}
// No need to propate the name if it could not be set.
if (!success)
return;
// Only name company after owner if this is a new company.
const auto& company = companymgr::get(self->number);
if (company->name != string_ids::new_company)
return;
// Temporarily store the new name in buffer string 2039.
// TODO: replace with a fixed length!
char* buffer = const_cast<char*>(stringmgr::getString(string_ids::buffer_2039));
strcpy(buffer, input);
FormatArguments args = {};
args.push(string_ids::buffer_2039);
// Add the ' Transport' suffix to the company name, and rename the company.
stringmgr::formatString(input, string_ids::company_owner_name_transport, const_cast<void*>(&args));
common::renameCompany(self, input);
}
// 0x004322F6
static void textInput(window* self, widget_index callingWidget, char* input)
{
if (callingWidget == common::widx::caption)
{
common::renameCompany(self, input);
}
else if (callingWidget == widx::change_owner_name)
{
renameCompanyOwnerName(self, input);
}
}
// 0x0043270A
static void onUpdate(window* self)
{
self->frame_no += 1;
self->callPrepareDraw();
WindowManager::invalidate(WindowType::company, self->number);
}
// 0x00432724
static void onResize(window* self)
{
common::enableRenameByCaption(self);
self->setSize(status::windowSize, gfx::ui_size_t(640, 400));
if (self->viewports[0] != nullptr)
{
gfx::ui_size_t proposedDims(self->width - 123, self->height - 59);
auto& viewport = self->viewports[0];
if (proposedDims.width != viewport->width || proposedDims.height != viewport->height)
{
viewport->width = proposedDims.width;
viewport->height = proposedDims.height;
viewport->view_width = proposedDims.width << viewport->zoom;
viewport->view_height = proposedDims.height << viewport->zoom;
self->saved_view.clear();
}
}
self->callViewportRotate();
}
static void sub_434336(window* self, const SavedView& view)
{
if (self->viewports[0] != nullptr)
{
return;
}
auto& widget = self->widgets[widx::viewport];
auto origin = gfx::point_t(widget.left + self->x + 1, widget.top + self->y + 1);
auto size = gfx::ui_size_t(widget.width() - 2, widget.height() - 2);
if (view.isThingView())
{
viewportmgr::create(self, 0, origin, size, self->saved_view.zoomLevel, view.thingId);
}
else
{
viewportmgr::create(self, 0, origin, size, self->saved_view.zoomLevel, view.getPos());
}
}
static void sub_434223(window* const self, const SavedView& view, const uint16_t vpFlags)
{
self->saved_view = view;
sub_434336(self, view);
self->viewports[0]->flags |= vpFlags;
self->invalidate();
}
static void differentViewportSettings(window* const self, const SavedView& view)
{
auto vpFlags = self->viewports[0]->flags;
self->viewports[0]->width = 0;
self->viewports[0] = nullptr;
viewportmgr::collectGarbage();
sub_434223(self, view, vpFlags);
}
static void noViewportPresent(window* const self, const SavedView& view)
{
uint16_t vpFlags = 0;
if (config::get().flags & config::flags::gridlines_on_landscape)
{
vpFlags |= viewport_flags::gridlines_on_landscape;
}
sub_434223(self, view, vpFlags);
}
static void invalidViewport(window* const self)
{
if (self->viewports[0] != nullptr)
{
self->viewports[0]->width = 0;
self->viewports[0] = nullptr;
self->invalidate();
}
}
// 0x004327C8
static void viewportRotate(window* self)
{
if (self->current_tab != 0)
{
return;
}
self->callPrepareDraw();
const auto& company = companymgr::get(self->number);
if (company->observation_thing == thing_id::null)
{
// Observing a certain location?
if (company->observation_x != -1)
{
auto tileZAndWater = openloco::map::tileElementHeight(company->observation_x, company->observation_y);
coord_t tileZ = tileZAndWater.landHeight;
coord_t waterZ = tileZAndWater.waterHeight;
if (waterZ != 0)
{
tileZ = waterZ;
}
// loc_43410A
int8_t rotation = static_cast<int8_t>(self->viewports[0]->getRotation());
SavedView view(
company->observation_x,
company->observation_y,
ZoomLevel::half,
rotation,
static_cast<int16_t>(tileZ + 16));
view.flags |= (1 << 14);
if (self->viewports[0] == nullptr)
{
noViewportPresent(self, view);
return;
}
if (self->saved_view.isThingView() || self->saved_view.rotation != view.rotation || self->saved_view.zoomLevel != view.zoomLevel)
{
if (self->saved_view != view)
{
differentViewportSettings(self, view);
return;
}
return;
}
self->saved_view = view;
self->viewportCentreOnTile(view.getPos());
return;
}
// Not observing anything at all?
else
{
invalidViewport(self);
}
}
else
{
// loc_434170
auto thing = thingmgr::get<openloco::vehicle_head>(company->observation_thing);
if (thing->base_type != thing_base_type::vehicle || thing->type != VehicleThingType::head || (thing->x == location::null))
{
invalidViewport(self);
return;
}
things::vehicle::Vehicle train(thing);
int8_t rotation = static_cast<int8_t>(self->viewports[0]->getRotation());
SavedView view(
train.cars.firstCar.body->id,
0xC000,
ZoomLevel::full,
rotation,
0);
if (self->viewports[0] == nullptr)
{
noViewportPresent(self, view);
return;
}
if (self->saved_view != view)
{
differentViewportSettings(self, view);
return;
}
}
}
static void initEvents()
{
events.prepare_draw = prepareDraw;
events.draw = draw;
events.on_mouse_up = onMouseUp;
events.on_mouse_down = onMouseDown;
events.on_dropdown = onDropdown;
events.text_input = textInput;
events.on_update = onUpdate;
events.on_resize = onResize;
events.viewport_rotate = viewportRotate;
}
}
// 0x004347D0
static window* create(company_id_t companyId)
{
const uint32_t newFlags = window_flags::flag_8 | window_flags::flag_11;
auto window = WindowManager::createWindow(WindowType::company, status::windowSize, newFlags, &status::events);
window->number = companyId;
window->owner = companyId;
window->current_tab = 0;
window->frame_no = 0;
window->saved_view.clear();
auto skin = objectmgr::get<interface_skin_object>();
window->colours[1] = skin->colour_0A;
window->flags |= window_flags::resizable;
return window;
}
// 0x0043454F
window* open(company_id_t companyId)
{
auto window = WindowManager::bringToFront(WindowType::company, companyId);
if (window != nullptr)
{
if (input::isToolActive(window->type, window->number))
{
input::toolCancel();
window = WindowManager::bringToFront(WindowType::company, companyId);
}
}
if (window == nullptr)
{
window = create(companyId);
}
// TODO(avgeffen): only needs to be called once.
common::initEvents();
window->current_tab = 0;
window->width = status::windowSize.width;
window->height = status::windowSize.height;
window->invalidate();
window->widgets = status::widgets;
window->enabled_widgets = status::enabledWidgets;
window->holdable_widgets = 0;
window->event_handlers = &status::events;
window->activated_widgets = 0;
common::disableChallengeTab(window);
window->initScrollWidgets();
window->moveInsideScreenEdges();
return window;
}
// 0x00435ACC
window* openAndSetName()
{
company_id_t companyId = companymgr::getControllingId();
window* self = open(companyId);
// Allow setting company owner name if no preferred owner name has been set.
if ((config::get().flags & config::flags::use_preferred_owner_name) == 0)
status::onMouseUp(self, status::widx::change_owner_name);
return self;
}
namespace details
{
const gfx::ui_size_t windowSize = { 340, 194 };
loco_global<coord_t, 0x009C68D6> _9C68D6; // likely tool x,y,z
loco_global<coord_t, 0x009C68D8> _9C68D8;
loco_global<coord_t, 0x009C68DA> _9C68DA;
loco_global<uint8_t, 0x009C68EF> _9C68EF;
enum widx
{
viewport = 11,
build_hq,
centre_on_viewport,
};
static widget_t widgets[] = {
commonWidgets(340, 194, string_ids::title_company_details),
makeWidget({ 219, 54 }, { 96, 120 }, widget_type::viewport, 1, -2),
makeWidget({ 315, 92 }, { 24, 24 }, widget_type::wt_9, 1, image_ids::null, string_ids::tooltip_build_or_move_headquarters),
makeWidget({ 0, 0 }, { 24, 24 }, widget_type::wt_9, 1, image_ids::null, string_ids::move_main_view_to_show_this),
widgetEnd(),
};
constexpr uint64_t enabledWidgets = common::enabledWidgets | (1 << common::widx::company_select) | (1 << build_hq) | (1 << centre_on_viewport);
static window_event_list events;
// 0x004327CF
static void prepareDraw(window* self)
{
common::switchTabWidgets(self);
// Set company name.
auto company = companymgr::get(self->number);
FormatArguments args{};
args.push(company->name);
auto companyColour = companymgr::getCompanyColour(self->number);
auto skin = objectmgr::get<interface_skin_object>();
uint32_t image = skin->img + interface_skin::image_ids::build_headquarters;
self->widgets[widx::build_hq].image = gfx::recolour(image, companyColour) | (1 << 30);
self->disabled_widgets &= ~(1 << widx::centre_on_viewport);
if (company->headquarters_x == -1)
{
self->disabled_widgets |= (1 << widx::centre_on_viewport);
}
self->widgets[common::widx::frame].right = self->width - 1;
self->widgets[common::widx::frame].bottom = self->height - 1;
self->widgets[common::widx::panel].right = self->width - 1;
self->widgets[common::widx::panel].bottom = self->height - 1;
self->widgets[common::widx::caption].right = self->width - 2;
self->widgets[common::widx::close_button].left = self->width - 15;
self->widgets[common::widx::close_button].right = self->width - 3;
self->widgets[widx::viewport].right = self->width - 26;
self->widgets[widx::viewport].bottom = self->height - 14;
self->widgets[common::widx::company_select].right = self->width - 3;
self->widgets[common::widx::company_select].left = self->width - 28;
if (self->number == companymgr::getControllingId())
self->widgets[widx::build_hq].type = widget_type::wt_9;
else
self->widgets[widx::build_hq].type = widget_type::none;
self->widgets[widx::centre_on_viewport].right = self->widgets[widx::viewport].right - 1;
self->widgets[widx::centre_on_viewport].bottom = self->widgets[widx::viewport].bottom - 1;
self->widgets[widx::centre_on_viewport].left = self->widgets[widx::viewport].right - 24;
self->widgets[widx::centre_on_viewport].top = self->widgets[widx::viewport].bottom - 24;
common::repositionTabs(self);
}
static std::array<string_id, 10> aiRatingToLevelArray = {
{
string_ids::low,
string_ids::low,
string_ids::low,
string_ids::low,
string_ids::medium,
string_ids::medium,
string_ids::medium,
string_ids::high,
string_ids::high,
string_ids::high,
}
};
constexpr string_id aiRatingToLevel(const uint8_t rating)
{
return aiRatingToLevelArray[std::min(rating, static_cast<uint8_t>(aiRatingToLevelArray.size()))];
}
static void drawAIdetails(gfx::drawpixelinfo_t& dpi, const int32_t x, int32_t& y, const openloco::company& company)
{
const auto competitor = objectmgr::get<competitor_object>(company.competitor_id);
{
FormatArguments args{};
args.push<uint16_t>(competitor->intelligence);
args.push(aiRatingToLevel(competitor->intelligence));
gfx::drawString_494B3F(dpi, x, y, colour::black, string_ids::company_details_intelligence, &args);
y += 10;
}
{
FormatArguments args{};
args.push<uint16_t>(competitor->agressiveness);
args.push(aiRatingToLevel(competitor->agressiveness));
gfx::drawString_494B3F(dpi, x, y, colour::black, string_ids::company_details_aggressiveness, &args);
y += 10;
}
{
FormatArguments args{};
args.push<uint16_t>(competitor->competitiveness);
args.push(aiRatingToLevel(competitor->competitiveness));
gfx::drawString_494B3F(dpi, x, y, colour::black, string_ids::company_details_competitiveness, &args);
y += 10;
}
}
static std::array<string_id, 6> transportTypeCountString = {
{
string_ids::company_details_trains_count,
string_ids::company_details_buses_count,
string_ids::company_details_trucks_count,
string_ids::company_details_trams_count,
string_ids::company_details_aircraft_count,
string_ids::company_details_ships_count,
}
};
// 0x00432919
static void draw(window* self, gfx::drawpixelinfo_t* dpi)
{
self->draw(dpi);
common::drawTabs(self, dpi);
common::drawCompanySelect(self, dpi);
auto company = companymgr::get(self->number);
auto x = self->x + 3;
auto y = self->y + 48;
{
FormatArguments args{};
args.push(company->startedDate);
gfx::drawString_494B3F(*dpi, x, y, colour::black, string_ids::company_details_started, &args);
y += 10;
}
{
FormatArguments args{};
formatPerformanceIndex(company->performance_index, args);
string_id formatId = string_ids::company_details_performance;
if (company->challenge_flags & company_flags::decreased_performance)
{
formatId = string_ids::company_details_performance_decreasing;
}
else if (company->challenge_flags & company_flags::increased_performance)
{
formatId = string_ids::company_details_performance_increasing;
}
gfx::drawString_494B3F(*dpi, x, y, colour::black, formatId, &args);
y += 25;
}
{
FormatArguments args{};
args.push(company->owner_name);
gfx::drawString_494BBF(*dpi, x, y, 213, colour::black, string_ids::owner_label, &args);
y += 10;
}
if (!isPlayerCompany(self->number))
{
drawAIdetails(*dpi, x + 5, y, *company);
}
y += 5;
{
for (auto i = 0; i < 6; ++i)
{
auto count = company->transportTypeCount[i];
if (count != 0)
{
FormatArguments args{};
args.push(count);
gfx::drawString_494B3F(*dpi, x, y, colour::black, transportTypeCountString[i], &args);
y += 10;
}
}
}
{
x = self->x + (self->widgets[widx::viewport].left + self->widgets[widx::viewport].right) / 2;
y = self->y + self->widgets[widx::viewport].top - 12;
gfx::drawStringCentred(*dpi, x, y, colour::black, string_ids::headquarters);
}
if (company->headquarters_x == -1)
{
auto width = self->widgets[widx::viewport].width();
gfx::point_t loc = {
static_cast<int16_t>(self->x + self->widgets[widx::viewport].left + width / 2),
static_cast<int16_t>(self->y + self->widgets[widx::viewport].top + self->widgets[widx::viewport].height() / 2 - 5)
};
width -= 2;
gfx::drawStringCentredWrapped(dpi, &loc, width, colour::black, string_ids::not_yet_constructed);
}
if (self->viewports[0] != nullptr)
{
self->drawViewports(dpi);
widget::drawViewportCentreButton(dpi, self, widx::centre_on_viewport);
}
}
// 0x00432BDD
static void onMouseUp(window* self, widget_index widgetIndex)
{
switch (widgetIndex)
{
case common::widx::caption:
common::renameCompanyPrompt(self, widgetIndex);
break;
case common::widx::close_button:
WindowManager::close(self);
break;
case common::widx::tab_status:
case common::widx::tab_details:
case common::widx::tab_colour_scheme:
case common::widx::tab_finances:
case common::widx::tab_cargo_delivered:
case common::widx::tab_challenge:
common::switchTab(self, widgetIndex);
break;
case widx::centre_on_viewport:
{
if (self->viewports[0] == nullptr)
break;
// Centre viewport on HQ.
// TODO(avgeffen): move/implement.
registers regs;
regs.esi = (int32_t)self;
call(0x00432C45, regs);
break;
}
}
}
// 0x00432C08
static void onMouseDown(window* self, widget_index widgetIndex)
{
switch (widgetIndex)
{
case common::widx::company_select:
dropdown::populateCompanySelect(self, &self->widgets[widgetIndex]);
break;
case widx::build_hq:
input::toolSet(self, widgetIndex, 43);
input::setFlag(input::input_flags::flag5);
break;
}
}
// 0x00432C19
static void onDropdown(window* self, widget_index widgetIndex, int16_t itemIndex)
{
if (widgetIndex == common::widx::company_select)
common::switchCompany(self, itemIndex);
}
// 0x00432C24
static void textInput(window* self, widget_index callingWidget, char* input)
{
if (callingWidget == common::widx::caption)
{
common::renameCompany(self, input);
}
}
static void sub_434E94()
{
if (_9C68EF & (1 << 0))
{
_9C68EF = _9C68EF & ~(1 << 0);
auto flags = game_commands::GameCommandFlag::apply | game_commands::GameCommandFlag::flag_3 | game_commands::GameCommandFlag::flag_5 | game_commands::GameCommandFlag::flag_6;
game_commands::do_55(flags, _9C68D6, _9C68D8, _9C68DA);
}
}
// 0x00468FFE hide_gridlines
static void sub_468FFE()
{
registers regs;
call(0x00468FFE, regs);
}
// 0x00434EC7
// input:
// regs.ax = mouseX;
// regs.bx = mouseY;
// output (not verified):
// regs.cx = tileX (tile coordinate)
// regs.ax = tileY (tile coordinate)
// regs.di = tileZ (height)
// regs.dx = dx - company index (value 1 in testing case)
static void sub_434EC7(const int16_t mouseX, const int16_t mouseY, coord_t& tileX, coord_t& tileY, coord_t& tileZ, int16_t& dx)
{
registers regs;
regs.ax = mouseX;
regs.bx = mouseY;
call(0x00434EC7, regs);
tileX = regs.cx;
tileY = regs.ax;
tileZ = regs.di;
dx = regs.dx;
}
// 0x00432CA1
static void onToolUpdate(window& self, const widget_index widgetIndex, const int16_t x, const int16_t y)
{
registers regs;
regs.esi = (int32_t)&self;
regs.dx = widgetIndex;
regs.ax = x;
regs.bx = y;
call(0x00432CA1, regs);
}
// 0x00432D45
// regs.esi = window* w;
// regs.dx = widgetIndex;
// regs.ax = mouseX;
// regs.bx = mouseY;
static void onToolDown(window& self, const widget_index widgetIndex, const int16_t mouseX, const int16_t mouseY)
{
sub_434E94();
coord_t tileX, tileY, tileZ;
int16_t dx;
sub_434EC7(mouseX, mouseY, tileX, tileY, tileZ, dx);
if (tileY == location::null)
{
return;
}
gGameCommandErrorTitle = string_ids::error_cant_build_this_here;
uint8_t flags = game_commands::GameCommandFlag::apply | game_commands::GameCommandFlag::flag_1;
auto commandResult = game_commands::do_54(flags, tileY, tileX, tileZ, dx);
if (commandResult != game_commands::FAILURE)
{
input::toolCancel();
}
}
// 0x00432D7A
static void onToolAbort(window& self, const widget_index widgetIndex)
{
sub_434E94();
sub_468FFE();
}
// 0x0432D85
static void onUpdate(window* self)
{
self->frame_no += 1;
self->callPrepareDraw();
WindowManager::invalidate(WindowType::company, self->number);
}
// 0x00432D9F
static void onResize(window* self)
{
common::enableRenameByCaption(self);
self->setSize(windowSize);
self->callViewportRotate();
}
static void sub_434377(window* self, const SavedView& view)
{
if (self->viewports[0] != nullptr)
{
return;
}
auto& widget = self->widgets[widx::viewport];
auto origin = gfx::point_t(widget.left + self->x + 1, widget.top + self->y + 1);
auto size = gfx::ui_size_t(widget.width() - 2, widget.height() - 2);
viewportmgr::create(self, 0, origin, size, self->saved_view.zoomLevel, view.getPos());
self->flags |= window_flags::viewport_no_scrolling;
self->invalidate();
}
// 0x00432E08
static void viewportRotate(window* self)
{
if (self->current_tab != common::tab_details - common::tab_status)
return;
self->callPrepareDraw();
auto company = companymgr::get(self->number);
if (company->headquarters_x == -1)
{
// If headquarters not placed destroy the viewport
if (self->viewports[0] == nullptr)
{
return;
}
self->viewports[0]->width = 0;
self->viewports[0] = nullptr;
self->invalidate();
return;
}
int8_t rotation = static_cast<int8_t>(self->viewports[0]->getRotation());
openloco::map::map_pos3 loc = {
static_cast<coord_t>(company->headquarters_x + 32),
static_cast<coord_t>(company->headquarters_y + 32),
static_cast<coord_t>((company->headquarters_z + 8) * 4)
};
SavedView view{
loc.x,
loc.y,
ZoomLevel::full,
rotation,
loc.z
};
view.flags |= (1 << 14);
uint16_t vpFlags = 0;
if (self->viewports[0] == nullptr)
{
if (config::get().flags & config::flags::gridlines_on_landscape)
{
vpFlags |= viewport_flags::gridlines_on_landscape;
}
}
else if (self->saved_view != view)
{
vpFlags = self->viewports[0]->flags;
self->viewports[0]->width = 0;
self->viewports[0] = nullptr;
viewportmgr::collectGarbage();
}
else
{
return;
}
self->saved_view = view;
sub_434377(self, view);
if (self->viewports[0] != nullptr)
{
self->viewports[0]->flags = vpFlags;
self->invalidate();
}
}
static void initEvents()
{
events.prepare_draw = prepareDraw;
events.draw = draw;
events.on_mouse_up = onMouseUp;
events.on_mouse_down = onMouseDown;
events.on_dropdown = onDropdown;
events.text_input = textInput;
events.on_tool_update = onToolUpdate;
events.on_tool_down = onToolDown;
events.on_tool_abort = onToolAbort;
events.on_update = onUpdate;
events.on_resize = onResize;
events.viewport_rotate = viewportRotate;
}
}
namespace colour_scheme
{
const gfx::ui_size_t windowSize = { 265, 252 };
enum widx
{
check_steam_locomotives = 11,
check_diesel_locomotives,
check_electric_locomotives,
check_multiple_units,
check_passenger_vehicles,
check_freight_vehicles,
check_buses,
check_trucks,
check_aircraft,
check_ships,
main_colour_scheme,
main_colour_steam_locomotives,
main_colour_diesel_locomotives,
main_colour_electric_locomotives,
main_colour_multiple_units,
main_colour_passenger_vehicles,
main_colour_freight_vehicles,
main_colour_buses,
main_colour_trucks,
main_colour_aircraft,
main_colour_ships,
secondary_colour_scheme,
secondary_colour_steam_locomotives,
secondary_colour_diesel_locomotives,
secondary_colour_electric_locomotives,
secondary_colour_multiple_units,
secondary_colour_passenger_vehicles,
secondary_colour_freight_vehicles,
secondary_colour_buses,
secondary_colour_trucks,
secondary_colour_aircraft,
secondary_colour_ships,
};
// clang-format off
constexpr uint64_t allMainColours = {
(1ULL << widx::main_colour_scheme) |
(1ULL << widx::main_colour_steam_locomotives) |
(1ULL << widx::main_colour_diesel_locomotives) |
(1ULL << widx::main_colour_electric_locomotives) |
(1ULL << widx::main_colour_multiple_units) |
(1ULL << widx::main_colour_passenger_vehicles) |
(1ULL << widx::main_colour_freight_vehicles) |
(1ULL << widx::main_colour_buses) |
(1ULL << widx::main_colour_trucks) |
(1ULL << widx::main_colour_aircraft) |
(1ULL << widx::main_colour_ships)
};
constexpr uint64_t allSecondaryColours = {
(1ULL << widx::secondary_colour_scheme) |
(1ULL << widx::secondary_colour_steam_locomotives) |
(1ULL << widx::secondary_colour_diesel_locomotives) |
(1ULL << widx::secondary_colour_electric_locomotives) |
(1ULL << widx::secondary_colour_multiple_units) |
(1ULL << widx::secondary_colour_passenger_vehicles) |
(1ULL << widx::secondary_colour_freight_vehicles) |
(1ULL << widx::secondary_colour_buses) |
(1ULL << widx::secondary_colour_trucks) |
(1ULL << widx::secondary_colour_aircraft) |
(1ULL << widx::secondary_colour_ships)
};
constexpr uint64_t allColourChecks = {
(1ULL << widx::check_steam_locomotives) |
(1ULL << widx::check_diesel_locomotives) |
(1ULL << widx::check_electric_locomotives) |
(1ULL << widx::check_multiple_units) |
(1ULL << widx::check_passenger_vehicles) |
(1ULL << widx::check_freight_vehicles) |
(1ULL << widx::check_buses) |
(1ULL << widx::check_trucks) |
(1ULL << widx::check_aircraft) |
(1ULL << widx::check_ships)
};
// clang-format on
static widget_t widgets[] = {
commonWidgets(265, 252, string_ids::title_company_colour_scheme),
makeWidget({ 15, 81 }, { 204, 12 }, widget_type::checkbox, 1, string_ids::colour_steam_locomotives, string_ids::tooltip_toggle_vehicle_colour_scheme),
makeWidget({ 15, 98 }, { 204, 12 }, widget_type::checkbox, 1, string_ids::colour_diesel_locomotives, string_ids::tooltip_toggle_vehicle_colour_scheme),
makeWidget({ 15, 115 }, { 204, 12 }, widget_type::checkbox, 1, string_ids::colour_electric_locomotives, string_ids::tooltip_toggle_vehicle_colour_scheme),
makeWidget({ 15, 132 }, { 204, 12 }, widget_type::checkbox, 1, string_ids::colour_multiple_units, string_ids::tooltip_toggle_vehicle_colour_scheme),
makeWidget({ 15, 149 }, { 204, 12 }, widget_type::checkbox, 1, string_ids::colour_passenger_vehicles, string_ids::tooltip_toggle_vehicle_colour_scheme),
makeWidget({ 15, 166 }, { 204, 12 }, widget_type::checkbox, 1, string_ids::colour_freight_vehicles, string_ids::tooltip_toggle_vehicle_colour_scheme),
makeWidget({ 15, 183 }, { 204, 12 }, widget_type::checkbox, 1, string_ids::colour_buses, string_ids::tooltip_toggle_vehicle_colour_scheme),
makeWidget({ 15, 200 }, { 204, 12 }, widget_type::checkbox, 1, string_ids::colour_trucks, string_ids::tooltip_toggle_vehicle_colour_scheme),
makeWidget({ 15, 217 }, { 204, 12 }, widget_type::checkbox, 1, string_ids::colour_aircraft, string_ids::tooltip_toggle_vehicle_colour_scheme),
makeWidget({ 15, 234 }, { 204, 12 }, widget_type::checkbox, 1, string_ids::colour_ships, string_ids::tooltip_toggle_vehicle_colour_scheme),
makeWidget({ 221, 48 }, { 16, 16 }, widget_type::wt_10, 1, image_ids::null, string_ids::tooltip_select_main_colour),
makeWidget({ 221, 78 }, { 16, 16 }, widget_type::wt_10, 1, image_ids::null, string_ids::tooltip_select_main_colour),
makeWidget({ 221, 95 }, { 16, 16 }, widget_type::wt_10, 1, image_ids::null, string_ids::tooltip_select_main_colour),
makeWidget({ 221, 112 }, { 16, 16 }, widget_type::wt_10, 1, image_ids::null, string_ids::tooltip_select_main_colour),
makeWidget({ 221, 129 }, { 16, 16 }, widget_type::wt_10, 1, image_ids::null, string_ids::tooltip_select_main_colour),
makeWidget({ 221, 146 }, { 16, 16 }, widget_type::wt_10, 1, image_ids::null, string_ids::tooltip_select_main_colour),
makeWidget({ 221, 163 }, { 16, 16 }, widget_type::wt_10, 1, image_ids::null, string_ids::tooltip_select_main_colour),
makeWidget({ 221, 180 }, { 16, 16 }, widget_type::wt_10, 1, image_ids::null, string_ids::tooltip_select_main_colour),
makeWidget({ 221, 197 }, { 16, 16 }, widget_type::wt_10, 1, image_ids::null, string_ids::tooltip_select_main_colour),
makeWidget({ 221, 214 }, { 16, 16 }, widget_type::wt_10, 1, image_ids::null, string_ids::tooltip_select_main_colour),
makeWidget({ 221, 231 }, { 16, 16 }, widget_type::wt_10, 1, image_ids::null, string_ids::tooltip_select_main_colour),
makeWidget({ 239, 48 }, { 16, 16 }, widget_type::wt_10, 1, image_ids::null, string_ids::tooltip_select_secondary_colour),
makeWidget({ 239, 78 }, { 16, 16 }, widget_type::wt_10, 1, image_ids::null, string_ids::tooltip_select_secondary_colour),
makeWidget({ 239, 95 }, { 16, 16 }, widget_type::wt_10, 1, image_ids::null, string_ids::tooltip_select_secondary_colour),
makeWidget({ 239, 112 }, { 16, 16 }, widget_type::wt_10, 1, image_ids::null, string_ids::tooltip_select_secondary_colour),
makeWidget({ 239, 129 }, { 16, 16 }, widget_type::wt_10, 1, image_ids::null, string_ids::tooltip_select_secondary_colour),
makeWidget({ 239, 146 }, { 16, 16 }, widget_type::wt_10, 1, image_ids::null, string_ids::tooltip_select_secondary_colour),
makeWidget({ 239, 163 }, { 16, 16 }, widget_type::wt_10, 1, image_ids::null, string_ids::tooltip_select_secondary_colour),
makeWidget({ 239, 180 }, { 16, 16 }, widget_type::wt_10, 1, image_ids::null, string_ids::tooltip_select_secondary_colour),
makeWidget({ 239, 197 }, { 16, 16 }, widget_type::wt_10, 1, image_ids::null, string_ids::tooltip_select_secondary_colour),
makeWidget({ 239, 214 }, { 16, 16 }, widget_type::wt_10, 1, image_ids::null, string_ids::tooltip_select_secondary_colour),
makeWidget({ 239, 231 }, { 16, 16 }, widget_type::wt_10, 1, image_ids::null, string_ids::tooltip_select_secondary_colour),
widgetEnd(),
};
constexpr uint64_t enabledWidgets = common::enabledWidgets | (1 << common::widx::company_select) | allMainColours | allSecondaryColours | allColourChecks;
static window_event_list events;
// 0x00432E0F
static void prepareDraw(window* self)
{
common::switchTabWidgets(self);
// Set company name.
auto company = companymgr::get(self->number);
FormatArguments args{};
args.push(company->name);
self->widgets[common::widx::frame].right = self->width - 1;
self->widgets[common::widx::frame].bottom = self->height - 1;
self->widgets[common::widx::panel].right = self->width - 1;
self->widgets[common::widx::panel].bottom = self->height - 1;
self->widgets[common::widx::caption].right = self->width - 2;
self->widgets[common::widx::close_button].left = self->width - 15;
self->widgets[common::widx::close_button].right = self->width - 3;
self->widgets[common::widx::company_select].right = self->width - 3;
self->widgets[common::widx::company_select].left = self->width - 28;
common::repositionTabs(self);
// Set company's main colour
self->widgets[widx::main_colour_scheme].image = (1 << 30) | gfx::recolour(image_ids::colour_swatch_recolourable, company->mainColours.primary);
// Set company's secondary colour
self->widgets[widx::secondary_colour_scheme].image = (1 << 30) | gfx::recolour(image_ids::colour_swatch_recolourable, company->mainColours.secondary);
struct ColourSchemeTuple
{
widget_index checkbox;
widget_index primary;
widget_index secondary;
};
// clang-format off
static const ColourSchemeTuple tuples[] =
{
{ widx::check_steam_locomotives, widx::main_colour_steam_locomotives, widx::secondary_colour_steam_locomotives },
{ widx::check_diesel_locomotives, widx::main_colour_diesel_locomotives, widx::secondary_colour_diesel_locomotives },
{ widx::check_electric_locomotives, widx::main_colour_electric_locomotives, widx::secondary_colour_electric_locomotives },
{ widx::check_multiple_units, widx::main_colour_multiple_units, widx::secondary_colour_multiple_units },
{ widx::check_passenger_vehicles, widx::main_colour_passenger_vehicles, widx::secondary_colour_passenger_vehicles },
{ widx::check_freight_vehicles, widx::main_colour_freight_vehicles, widx::secondary_colour_freight_vehicles },
{ widx::check_buses, widx::main_colour_buses, widx::secondary_colour_buses },
{ widx::check_trucks, widx::main_colour_trucks, widx::secondary_colour_trucks },
{ widx::check_aircraft, widx::main_colour_aircraft, widx::secondary_colour_aircraft },
{ widx::check_ships, widx::main_colour_ships, widx::secondary_colour_ships },
};
// clang-format on
for (uint8_t i = 0; i < static_cast<uint8_t>(std::size(tuples)); i++)
{
// customVehicleColoursSet appears to reserve its first bit for something else, so skip it.
if ((company->customVehicleColoursSet & (1 << (i + 1))) != 0)
{
self->activated_widgets |= (1ULL << tuples[i].checkbox);
self->widgets[tuples[i].primary].image = (1ULL << 30) | gfx::recolour(image_ids::colour_swatch_recolourable, company->vehicleColours[i].primary);
self->widgets[tuples[i].secondary].image = (1ULL << 30) | gfx::recolour(image_ids::colour_swatch_recolourable, company->vehicleColours[i].secondary);
self->widgets[tuples[i].primary].type = widget_type::wt_10;
self->widgets[tuples[i].secondary].type = widget_type::wt_10;
}
else
{
self->activated_widgets &= ~(1ULL << tuples[i].checkbox);
self->widgets[tuples[i].primary].type = widget_type::none;
self->widgets[tuples[i].secondary].type = widget_type::none;
}
}
if (self->number == companymgr::getControllingId())
self->enabled_widgets |= allColourChecks | allMainColours | allSecondaryColours;
else
self->enabled_widgets &= ~(allColourChecks | allMainColours | allSecondaryColours);
}
// 0x00432F9A
static void draw(window* self, gfx::drawpixelinfo_t* dpi)
{
self->draw(dpi);
common::drawTabs(self, dpi);
common::drawCompanySelect(self, dpi);
const auto& widget = self->widgets[widx::main_colour_scheme];
const uint16_t x = self->x + 6;
uint16_t y = self->y + widget.top + 3;
// 'Main colour scheme'
gfx::drawString_494B3F(
*dpi,
x,
y,
colour::black,
string_ids::main_colour_scheme);
// 'Special colour schemes used for'
y += 17;
gfx::drawString_494B3F(
*dpi,
x,
y,
colour::black,
string_ids::special_colour_schemes_used_for);
}
// 0x00433032
static void onMouseUp(window* self, widget_index widgetIndex)
{
switch (widgetIndex)
{
case common::widx::caption:
common::renameCompanyPrompt(self, widgetIndex);
break;
case common::widx::close_button:
WindowManager::close(self);
break;
case common::widx::tab_status:
case common::widx::tab_details:
case common::widx::tab_colour_scheme:
case common::widx::tab_finances:
case common::widx::tab_cargo_delivered:
case common::widx::tab_challenge:
common::switchTab(self, widgetIndex);
break;
case widx::check_steam_locomotives:
case widx::check_diesel_locomotives:
case widx::check_electric_locomotives:
case widx::check_multiple_units:
case widx::check_passenger_vehicles:
case widx::check_freight_vehicles:
case widx::check_buses:
case widx::check_trucks:
case widx::check_aircraft:
case widx::check_ships:
// customVehicleColoursSet reserves first bit for main colour scheme even though it can't be changed, so skip it.
const auto vehicleType = widgetIndex - widx::check_steam_locomotives + 1;
const auto company = companymgr::get(self->number);
const auto newMode = (company->customVehicleColoursSet & (1 << vehicleType)) == 0 ? 1 : 0;
gGameCommandErrorTitle = string_ids::error_cant_change_colour_scheme;
game_commands::do_19(0, newMode, vehicleType, 1, self->number);
break;
}
}
// 0x00433067
static void onMouseDown(window* self, widget_index widgetIndex)
{
switch (widgetIndex)
{
case common::widx::company_select:
dropdown::populateCompanySelect(self, &self->widgets[widgetIndex]);
break;
case main_colour_scheme:
case main_colour_steam_locomotives:
case main_colour_diesel_locomotives:
case main_colour_electric_locomotives:
case main_colour_multiple_units:
case main_colour_passenger_vehicles:
case main_colour_freight_vehicles:
case main_colour_buses:
case main_colour_trucks:
case main_colour_aircraft:
case main_colour_ships:
{
registers regs;
regs.edx = widgetIndex;
regs.esi = (int32_t)self;
regs.edi = (int32_t)&self->widgets[widgetIndex];
call(0x00433119, regs);
break;
}
case secondary_colour_scheme:
case secondary_colour_steam_locomotives:
case secondary_colour_diesel_locomotives:
case secondary_colour_electric_locomotives:
case secondary_colour_multiple_units:
case secondary_colour_passenger_vehicles:
case secondary_colour_freight_vehicles:
case secondary_colour_buses:
case secondary_colour_trucks:
case secondary_colour_aircraft:
case secondary_colour_ships:
{
registers regs;
regs.edx = widgetIndex;
regs.esi = (int32_t)self;
regs.edi = (int32_t)&self->widgets[widgetIndex];
call(0x00433183, regs);
break;
}
}
}
// 0x00433092
static void textInput(window* self, widget_index callingWidget, char* input)
{
if (callingWidget == common::widx::caption)
{
common::renameCompany(self, input);
}
}
// 0x0043309D
static void onDropdown(window* self, widget_index widgetIndex, int16_t itemIndex)
{
switch (widgetIndex)
{
case common::widx::company_select:
common::switchCompany(self, itemIndex);
break;
case widx::main_colour_scheme:
case widx::main_colour_steam_locomotives:
case widx::main_colour_diesel_locomotives:
case widx::main_colour_electric_locomotives:
case widx::main_colour_multiple_units:
case widx::main_colour_passenger_vehicles:
case widx::main_colour_freight_vehicles:
case widx::main_colour_buses:
case widx::main_colour_trucks:
case widx::main_colour_aircraft:
case widx::main_colour_ships:
{
if (itemIndex == -1)
return;
gGameCommandErrorTitle = string_ids::error_cant_change_colour_scheme;
const int8_t colour = dropdown::getItemArgument(itemIndex, 2);
const auto vehicleType = widgetIndex - widx::main_colour_scheme;
game_commands::do_19(0, colour, vehicleType, 0, self->number);
break;
}
case widx::secondary_colour_scheme:
case widx::secondary_colour_steam_locomotives:
case widx::secondary_colour_diesel_locomotives:
case widx::secondary_colour_electric_locomotives:
case widx::secondary_colour_multiple_units:
case widx::secondary_colour_passenger_vehicles:
case widx::secondary_colour_freight_vehicles:
case widx::secondary_colour_buses:
case widx::secondary_colour_trucks:
case widx::secondary_colour_aircraft:
case widx::secondary_colour_ships:
{
if (itemIndex == -1)
return;
gGameCommandErrorTitle = string_ids::error_cant_change_colour_scheme;
const int8_t colour = dropdown::getItemArgument(itemIndex, 2);
const auto vehicleType = widgetIndex - widx::secondary_colour_scheme;
game_commands::do_19(1, colour, vehicleType, 0, self->number);
break;
}
}
}
// 0x0043325F
static void onUpdate(window* self)
{
self->frame_no += 1;
self->callPrepareDraw();
WindowManager::invalidate(WindowType::company, self->number);
}
// 0x00433279
static void onResize(window* self)
{
common::enableRenameByCaption(self);
self->setSize(windowSize);
}
static void initEvents()
{
events.prepare_draw = prepareDraw;
events.draw = draw;
events.on_mouse_up = onMouseUp;
events.on_mouse_down = onMouseDown;
events.text_input = textInput;
events.on_dropdown = onDropdown;
events.on_update = onUpdate;
events.on_resize = onResize;
}
}
namespace finances
{
const gfx::ui_size_t windowSize = { 636, 319 };
enum widx
{
scrollview = 11,
current_loan,
loan_decrease,
loan_increase,
};
constexpr uint16_t expenditureColumnWidth = 128;
static widget_t widgets[] = {
commonWidgets(636, 319, string_ids::title_company_finances),
makeWidget({ 133, 45 }, { 499, 215 }, widget_type::scrollview, 1, horizontal),
makeStepperWidgets({ 87, 264 }, { 100, 12 }, widget_type::wt_17, 1, string_ids::company_current_loan_value),
widgetEnd(),
};
constexpr uint64_t enabledWidgets = common::enabledWidgets | (1 << common::widx::company_select) | (1 << widx::loan_decrease) | (1 << widx::loan_increase);
const uint64_t holdableWidgets = (1 << widx::loan_decrease) | (1 << widx::loan_increase);
static window_event_list events;
// 0x004332E4
static void prepareDraw(window* self)
{
common::switchTabWidgets(self);
// Set company name.
auto company = companymgr::get(self->number);
FormatArguments args{};
args.push(company->name);
args.push<uint32_t>(0);
args.push<uint16_t>(0);
// Used for the loan stepper current value at offset 4
args.push(company->current_loan);
self->widgets[common::widx::frame].right = self->width - 1;
self->widgets[common::widx::frame].bottom = self->height - 1;
self->widgets[common::widx::panel].right = self->width - 1;
self->widgets[common::widx::panel].bottom = self->height - 1;
self->widgets[common::widx::caption].right = self->width - 2;
self->widgets[common::widx::close_button].left = self->width - 15;
self->widgets[common::widx::close_button].right = self->width - 3;
self->widgets[common::widx::company_select].right = self->width - 3;
self->widgets[common::widx::company_select].left = self->width - 28;
if (self->number == companymgr::getControllingId())
{
self->widgets[widx::current_loan].type = widget_type::wt_17;
self->widgets[widx::loan_decrease].type = widget_type::wt_11;
self->widgets[widx::loan_increase].type = widget_type::wt_11;
}
else
{
self->widgets[widx::current_loan].type = widget_type::none;
self->widgets[widx::loan_decrease].type = widget_type::none;
self->widgets[widx::loan_increase].type = widget_type::none;
}
common::repositionTabs(self);
}
// 0x004333D0
static void draw(window* self, gfx::drawpixelinfo_t* dpi)
{
self->draw(dpi);
common::drawTabs(self, dpi);
common::drawCompanySelect(self, dpi);
const auto company = companymgr::get(self->number);
// Draw 'expenditure/income' label
{
gfx::drawStringLeftUnderline(
*dpi,
self->x + 5,
self->y + 47,
colour::black,
string_ids::expenditure_income,
nullptr);
}
const string_id ExpenditureLabels[] = {
string_ids::train_income,
string_ids::train_running_costs,
string_ids::bus_income,
string_ids::bus_running_costs,
string_ids::truck_income,
string_ids::truck_running_costs,
string_ids::tram_income,
string_ids::tram_running_costs,
string_ids::aircraft_income,
string_ids::aircraft_running_costs,
string_ids::ship_income,
string_ids::ship_running_costs,
string_ids::construction,
string_ids::vehicle_purchases,
string_ids::vehicle_disposals,
string_ids::loan_interest,
string_ids::miscellaneous,
};
uint16_t y = self->y + 62;
for (uint8_t i = 0; i < static_cast<uint8_t>(std::size(ExpenditureLabels)); i++)
{
// Add zebra stripes to even labels.
if (i % 2 == 0)
{
auto colour = colour::getShade(self->colours[1], 6) | 0x1000000;
gfx::fillRect(dpi, self->x + 4, y, self->x + 129, y + 9, colour);
}
FormatArguments args{};
args.push(ExpenditureLabels[i]);
gfx::drawString_494B3F(
*dpi,
self->x + 5,
y - 1,
colour::black,
string_ids::wcolour2_stringid,
&args);
y += 10;
}
// 'Current loan' label
{
gfx::drawString_494B3F(
*dpi,
self->x + 7,
self->y + self->widgets[widx::current_loan].top,
colour::black,
string_ids::company_current_loan);
}
// '@ X% interest per' label
{
loco_global<uint8_t, 0x00525FC6> loanInterestRate;
FormatArguments args{};
args.push<uint16_t>(loanInterestRate);
gfx::drawString_494B3F(
*dpi,
self->x + self->widgets[widx::current_loan].right + 3,
self->y + self->widgets[widx::current_loan].top + 1,
colour::black,
string_ids::interest_per_year,
&args);
}
// 'Cash' label with value
{
// Set cash value in format args.
FormatArguments args{};
args.push(company->cash);
string_id cash_format = string_ids::cash_positive;
if ((company->challenge_flags & company_flags::bankrupt) != 0)
cash_format = string_ids::cash_bankrupt;
if (company->cash.var_04 < 0)
cash_format = string_ids::cash_negative;
gfx::drawString_494B3F(
*dpi,
self->x + 7,
self->y + self->widgets[widx::current_loan].top + 13,
colour::black,
cash_format,
&args);
}
// 'Company value' label with value
{
// Set company value in format args.
FormatArguments args{};
args.push(company->companyValueHistory[0]);
gfx::drawString_494B3F(
*dpi,
self->x + 7,
self->y + self->widgets[widx::current_loan].top + 26,
colour::black,
string_ids::company_value,
&args);
}
// 'Profit from vehicles' label with value
{
// Set company value in format args.
FormatArguments args{};
args.push(company->vehicleProfit);
gfx::drawString_494B3F(
*dpi,
self->x + 7,
self->y + self->widgets[widx::current_loan].top + 39,
colour::black,
string_ids::profit_from_vehicles,
&args);
}
}
static void drawFinanceYear(gfx::drawpixelinfo_t* context, int16_t x, int16_t& y, uint16_t columnYear, uint16_t currentYear)
{
FormatArguments args = {};
args.push(string_ids::uint16_raw);
args.push(columnYear);
string_id format = string_ids::wcolour2_stringid;
if (columnYear != currentYear)
{
format = string_ids::black_stringid;
}
gfx::drawStringUnderline(
*context,
x,
y,
colour::black,
format,
&args);
y += 14;
}
static currency48_t drawFinanceExpenditureColumn(gfx::drawpixelinfo_t* context, const int16_t x, int16_t& y, uint8_t columnIndex, company& company)
{
currency48_t sum = 0;
for (auto j = 0; j < ExpenditureType::Count; j++)
{
currency48_t expenditures = company.expenditures[columnIndex][j];
sum += expenditures;
string_id mainFormat = string_ids::black_stringid;
string_id currFormat = string_ids::plus_currency48;
if (expenditures < 0)
{
mainFormat = string_ids::red_stringid;
currFormat = string_ids::currency48;
}
if (expenditures != 0)
{
FormatArguments args = {};
args.push<string_id>(currFormat);
args.push<currency48_t>(expenditures);
gfx::drawString_494C78(
*context,
x,
y,
colour::black,
mainFormat,
&args);
}
y += 10;
}
return sum;
}
static void drawFinanceSum(gfx::drawpixelinfo_t* context, int16_t x, int16_t& y, currency48_t sum)
{
FormatArguments args{};
auto mainFormat = string_ids::black_stringid;
auto sumFormat = string_ids::plus_currency48;
if (sum < 0)
{
mainFormat = string_ids::red_stringid;
sumFormat = string_ids::currency48;
}
args.push(sumFormat);
args.push(sum);
y += 4;
gfx::drawString_494C78(*context, x, y, colour::black, mainFormat, &args);
gfx::fillRect(context, x - expenditureColumnWidth + 10, y - 2, x, y - 2, colour::aquamarine);
}
// 0x0043361E
static void drawScroll(window* self, gfx::drawpixelinfo_t* context, uint32_t scrollIndex)
{
int16_t y = 47 - self->widgets[widx::scrollview].top + 14;
for (uint8_t i = 0; i < static_cast<uint8_t>(ExpenditureType::Count); i++)
{
// Add zebra stripes to even labels.
if (i % 2 == 0)
{
auto colour = colour::getShade(self->colours[1], 6) | 0x1000000;
gfx::fillRect(context, 0, y, expenditureColumnWidth * 17, y + 9, colour);
}
y += 10;
}
const auto company = companymgr::get(self->number);
uint32_t curYear = getCurrentYear();
uint8_t expenditureYears = std::min<uint8_t>(company->numExpenditureMonths, expenditureHistoryCapacity);
// Paint years on top of scroll area.
int16_t x = 132 - self->widgets[widx::scrollview].left + expenditureColumnWidth;
for (auto i = 0; i < expenditureYears; i++)
{
y = 46 - self->widgets[widx::scrollview].top;
uint16_t columnYear = curYear - (expenditureYears - i) + 1;
uint8_t columnIndex = expenditureYears - i - 1;
drawFinanceYear(context, x, y, columnYear, curYear);
auto sum = drawFinanceExpenditureColumn(context, x, y, columnIndex, *company);
drawFinanceSum(context, x, y, sum);
x += expenditureColumnWidth;
}
}
// 0x00433819
static void onMouseUp(window* self, widget_index widgetIndex)
{
switch (widgetIndex)
{
case common::widx::caption:
common::renameCompanyPrompt(self, widgetIndex);
break;
case common::widx::close_button:
WindowManager::close(self);
break;
case common::widx::tab_status:
case common::widx::tab_details:
case common::widx::tab_colour_scheme:
case common::widx::tab_finances:
case common::widx::tab_cargo_delivered:
case common::widx::tab_challenge:
common::switchTab(self, widgetIndex);
break;
}
}
// 0x0043383E
static void onMouseDown(window* self, widget_index widgetIndex)
{
static loco_global<uint16_t, 0x00523376> _clickRepeatTicks;
switch (widgetIndex)
{
case common::widx::company_select:
dropdown::populateCompanySelect(self, &self->widgets[widgetIndex]);
break;
case widx::loan_decrease:
{
auto company = companymgr::get(self->number);
if (company->current_loan == 0)
return;
currency32_t stepSize{};
if (*_clickRepeatTicks < 100)
stepSize = 1000;
else if (*_clickRepeatTicks >= 100)
stepSize = 10000;
else if (*_clickRepeatTicks >= 200)
stepSize = 100000;
auto newLoan = std::max<currency32_t>(0, company->current_loan - stepSize);
gGameCommandErrorTitle = string_ids::cant_pay_back_loan;
game_commands::do_9(newLoan);
break;
}
case widx::loan_increase:
{
currency32_t stepSize{};
if (*_clickRepeatTicks < 100)
stepSize = 1000;
else if (*_clickRepeatTicks >= 100)
stepSize = 10000;
else if (*_clickRepeatTicks >= 200)
stepSize = 100000;
currency32_t newLoan = companymgr::get(self->number)->current_loan + stepSize;
gGameCommandErrorTitle = string_ids::cant_borrow_any_more_money;
game_commands::do_9(newLoan);
break;
}
}
}
// 0x0043385D
static void textInput(window* self, widget_index callingWidget, char* input)
{
if (callingWidget == common::widx::caption)
{
common::renameCompany(self, input);
}
}
static void sub_4C8DBF(window* self)
{
registers regs;
regs.esi = (int32_t)self;
call(0x004C8DBF, regs);
}
// 0x00433868
static void onDropdown(window* self, widget_index widgetIndex, int16_t itemIndex)
{
if (widgetIndex == common::widx::company_select)
{
common::switchCompany(self, itemIndex);
sub_4C8DBF(self);
self->invalidate();
}
}
// 0x0043386F
static void getScrollSize(window* self, uint32_t scrollIndex, uint16_t* scrollWidth, uint16_t* scrollHeight)
{
const auto& company = companymgr::get(self->number);
*scrollWidth = company->numExpenditureMonths * expenditureColumnWidth;
}
// 0x00433887
static void tooltip(FormatArguments& args, ui::window* window, widget_index widgetIndex)
{
args.push(string_ids::tooltip_scroll_list);
}
// 0x0043399D
static void onUpdate(window* self)
{
self->frame_no += 1;
self->callPrepareDraw();
WindowManager::invalidate(WindowType::company, self->number);
}
// 0x004339B7
static void onResize(window* self)
{
common::enableRenameByCaption(self);
self->setSize(windowSize);
}
static void initEvents()
{
events.prepare_draw = prepareDraw;
events.draw = draw;
events.draw_scroll = drawScroll;
events.on_mouse_up = onMouseUp;
events.on_mouse_down = onMouseDown;
events.text_input = textInput;
events.on_dropdown = onDropdown;
events.get_scroll_size = getScrollSize;
events.tooltip = tooltip;
events.on_update = onUpdate;
events.on_resize = onResize;
}
}
// 0x004345EE
window* openFinances(company_id_t companyId)
{
auto window = WindowManager::bringToFront(WindowType::company, companyId);
if (window != nullptr)
{
if (input::isToolActive(window->type, window->number))
{
input::toolCancel();
window = WindowManager::bringToFront(WindowType::company, companyId);
}
}
if (window == nullptr)
{
window = create(companyId);
}
// TODO(avgeffen): only needs to be called once.
common::initEvents();
window->current_tab = common::tab_finances - common::tab_status;
window->width = finances::windowSize.width;
window->height = finances::windowSize.height;
window->invalidate();
window->widgets = finances::widgets;
window->enabled_widgets = finances::enabledWidgets;
window->holdable_widgets = finances::holdableWidgets;
window->event_handlers = &finances::events;
window->activated_widgets = 0;
common::disableChallengeTab(window);
window->initScrollWidgets();
window->moveInsideScreenEdges();
finances::sub_4C8DBF(window);
return window;
}
namespace CargoDelivered
{
const gfx::ui_size_t windowSize = { 240, 382 };
static widget_t widgets[] = {
commonWidgets(240, 382, string_ids::title_company_cargo_delivered),
widgetEnd(),
};
constexpr uint64_t enabledWidgets = common::enabledWidgets | (1 << common::widx::company_select);
static window_event_list events;
// 0x00433A22
static void prepareDraw(window* self)
{
common::switchTabWidgets(self);
// Set company name.
auto company = companymgr::get(self->number);
FormatArguments args{};
args.push(company->name);
self->widgets[common::widx::frame].right = self->width - 1;
self->widgets[common::widx::frame].bottom = self->height - 1;
self->widgets[common::widx::panel].right = self->width - 1;
self->widgets[common::widx::panel].bottom = self->height - 1;
self->widgets[common::widx::caption].right = self->width - 2;
self->widgets[common::widx::close_button].left = self->width - 15;
self->widgets[common::widx::close_button].right = self->width - 3;
common::repositionTabs(self);
}
// 0x00433ACD
static void draw(window* self, gfx::drawpixelinfo_t* dpi)
{
self->draw(dpi);
common::drawTabs(self, dpi);
uint16_t y = self->y + 47;
// 'Cargo delivered'
gfx::drawString_494B3F(
*dpi,
self->x + 5,
y,
colour::black,
string_ids::cargo_delivered);
y += 10;
uint8_t numPrinted = 0;
const auto company = companymgr::get(self->number);
for (uint8_t i = 0; i < static_cast<uint8_t>(std::size(company->cargoDelivered)); i++)
{
auto cargo = objectmgr::get<cargo_object>(i);
if (cargo == nullptr || company->cargoDelivered[i] == 0)
continue;
FormatArguments args{};
if (company->cargoDelivered[i] == 1)
args.push(cargo->unit_name_singular);
else
args.push(cargo->unit_name_plural);
args.push(company->cargoDelivered[i]);
gfx::drawString_494B3F(
*dpi,
self->x + 10,
y,
colour::black,
string_ids::black_stringid,
&args);
numPrinted++;
y += 10;
}
// No cargo delivered yet?
if (numPrinted == 0)
{
gfx::drawString_494B3F(
*dpi,
self->x + 10,
y,
colour::black,
string_ids::cargo_delivered_none);
}
}
// 0x00433BE6
static void onMouseUp(window* self, widget_index widgetIndex)
{
switch (widgetIndex)
{
case common::widx::caption:
common::renameCompanyPrompt(self, widgetIndex);
break;
case common::widx::close_button:
WindowManager::close(self);
break;
case common::widx::tab_status:
case common::widx::tab_details:
case common::widx::tab_colour_scheme:
case common::widx::tab_finances:
case common::widx::tab_cargo_delivered:
case common::widx::tab_challenge:
common::switchTab(self, widgetIndex);
break;
}
}
// 0x00433C0B
static void onMouseDown(window* self, widget_index widgetIndex)
{
if (widgetIndex == common::widx::company_select)
dropdown::populateCompanySelect(self, &self->widgets[widgetIndex]);
}
// 0x00433C16
static void textInput(window* self, widget_index callingWidget, char* input)
{
if (callingWidget == common::widx::caption)
{
common::renameCompany(self, input);
}
}
// 0x00433C21
static void onDropdown(window* self, widget_index widgetIndex, int16_t itemIndex)
{
if (widgetIndex == common::widx::company_select)
common::switchCompany(self, itemIndex);
}
// 0x00433C7D
static void onUpdate(window* self)
{
self->frame_no += 1;
self->callPrepareDraw();
WindowManager::invalidate(WindowType::company, self->number);
}
// 0x00433C97
static void onResize(window* self)
{
common::enableRenameByCaption(self);
uint16_t cargoHeight = 0;
const auto company = companymgr::get(self->number);
for (uint8_t i = 0; i < static_cast<uint8_t>(std::size(company->cargoDelivered)); i++)
{
auto cargo = objectmgr::get<cargo_object>(i);
if (cargo == nullptr || company->cargoDelivered[i] == 0)
continue;
cargoHeight += 10;
}
const uint16_t windowHeight = std::max<int16_t>(cargoHeight, 50) + 62;
self->setSize({ windowSize.width, windowHeight });
}
static void initEvents()
{
events.prepare_draw = prepareDraw;
events.draw = draw;
events.on_mouse_up = onMouseUp;
events.on_mouse_down = onMouseDown;
events.text_input = textInput;
events.on_dropdown = onDropdown;
events.on_update = onUpdate;
events.on_resize = onResize;
}
}
namespace challenge
{
const gfx::ui_size_t windowSize = { 320, 182 };
static widget_t widgets[] = {
commonWidgets(320, 182, string_ids::title_company_challenge),
widgetEnd(),
};
constexpr uint64_t enabledWidgets = common::enabledWidgets;
static window_event_list events;
// 0x00433D39
static void prepareDraw(window* self)
{
common::switchTabWidgets(self);
// Set company name.
auto company = companymgr::get(self->number);
FormatArguments args{};
args.push(company->name);
self->widgets[common::widx::frame].right = self->width - 1;
self->widgets[common::widx::frame].bottom = self->height - 1;
self->widgets[common::widx::panel].right = self->width - 1;
self->widgets[common::widx::panel].bottom = self->height - 1;
self->widgets[common::widx::caption].right = self->width - 2;
self->widgets[common::widx::close_button].left = self->width - 15;
self->widgets[common::widx::close_button].right = self->width - 3;
self->widgets[common::widx::company_select].right = self->width - 3;
self->widgets[common::widx::company_select].left = self->width - 28;
self->widgets[common::widx::company_select].type = widget_type::none;
common::repositionTabs(self);
}
// 0x00433DEB
static void draw(window* self, gfx::drawpixelinfo_t* dpi)
{
self->draw(dpi);
common::drawTabs(self, dpi);
registers regs;
regs.edi = (int32_t)dpi;
regs.esi = (int32_t)self;
call(0x00433DF5, regs);
}
// 0x00433FFE
static void onMouseUp(window* self, widget_index widgetIndex)
{
switch (widgetIndex)
{
case common::widx::caption:
common::renameCompanyPrompt(self, widgetIndex);
break;
case common::widx::close_button:
WindowManager::close(self);
break;
case common::widx::tab_status:
case common::widx::tab_details:
case common::widx::tab_colour_scheme:
case common::widx::tab_finances:
case common::widx::tab_cargo_delivered:
case common::widx::tab_challenge:
common::switchTab(self, widgetIndex);
break;
}
}
// 0x00434023
static void textInput(window* self, widget_index callingWidget, char* input)
{
if (callingWidget == common::widx::caption)
{
common::renameCompany(self, input);
}
}
// 0x0043402E
static void onUpdate(window* self)
{
self->frame_no += 1;
self->callPrepareDraw();
WindowManager::invalidate(WindowType::company, self->number);
}
// 0x00434048
static void onResize(window* self)
{
self->setSize(windowSize);
}
static void initEvents()
{
events.prepare_draw = prepareDraw;
events.draw = draw;
events.on_mouse_up = onMouseUp;
events.text_input = textInput;
events.on_update = onUpdate;
events.on_resize = onResize;
}
}
// 00434731
window* openChallenge(company_id_t companyId)
{
auto window = WindowManager::bringToFront(WindowType::company, companyId);
if (window != nullptr)
{
if (input::isToolActive(window->type, window->number))
{
input::toolCancel();
window = WindowManager::bringToFront(WindowType::company, companyId);
}
}
if (window == nullptr)
{
window = create(companyId);
}
// TODO(avgeffen): only needs to be called once.
common::initEvents();
window->current_tab = common::tab_challenge - common::tab_status;
window->width = challenge::windowSize.width;
window->height = challenge::windowSize.height;
window->invalidate();
window->widgets = challenge::widgets;
window->enabled_widgets = challenge::enabledWidgets;
window->holdable_widgets = 0;
window->event_handlers = &challenge::events;
window->activated_widgets = 0;
common::disableChallengeTab(window);
window->initScrollWidgets();
window->moveInsideScreenEdges();
return window;
}
namespace common
{
struct TabInformation
{
widget_t* widgets;
const widx widgetIndex;
window_event_list* events;
const uint64_t* enabledWidgets;
const gfx::ui_size_t* windowSize;
};
static TabInformation tabInformationByTabOffset[] = {
{ status::widgets, widx::tab_status, &status::events, &status::enabledWidgets, &status::windowSize },
{ details::widgets, widx::tab_details, &details::events, &details::enabledWidgets, &details::windowSize },
{ colour_scheme::widgets, widx::tab_colour_scheme, &colour_scheme::events, &colour_scheme::enabledWidgets, &colour_scheme::windowSize },
{ finances::widgets, widx::tab_finances, &finances::events, &finances::enabledWidgets, &finances::windowSize },
{ CargoDelivered::widgets, widx::tab_cargo_delivered, &CargoDelivered::events, &CargoDelivered::enabledWidgets, &CargoDelivered::windowSize },
{ challenge::widgets, widx::tab_challenge, &challenge::events, &challenge::enabledWidgets, &challenge::windowSize }
};
static void initEvents()
{
status::initEvents();
details::initEvents();
colour_scheme::initEvents();
finances::initEvents();
CargoDelivered::initEvents();
challenge::initEvents();
}
static void switchCompany(window* self, int16_t itemIndex)
{
if (itemIndex == -1)
return;
company_id_t companyId = dropdown::getCompanyIdFromSelection(itemIndex);
// Try to find an open company window for this company.
auto companyWindow = WindowManager::bringToFront(WindowType::company, companyId);
if (companyWindow != nullptr)
return;
// If not, we'll turn this window into a window for the company selected.
auto company = companymgr::get(companyId);
if (company->name == string_ids::empty)
return;
self->number = companyId;
self->owner = companyId;
common::disableChallengeTab(self);
self->invalidate();
}
static void switchTabWidgets(window* self)
{
self->activated_widgets = 0;
static widget_t* widgetCollectionsByTabId[] = {
status::widgets,
details::widgets,
colour_scheme::widgets,
finances::widgets,
CargoDelivered::widgets,
challenge::widgets,
};
widget_t* newWidgets = widgetCollectionsByTabId[self->current_tab];
if (self->widgets != newWidgets)
{
self->widgets = newWidgets;
// self->initScrollWidgets();
}
static const widx tabWidgetIdxByTabId[] = {
tab_status,
tab_details,
tab_colour_scheme,
tab_finances,
tab_cargo_delivered,
tab_challenge,
};
self->activated_widgets &= ~((1 << tab_status) | (1 << tab_details) | (1 << tab_colour_scheme) | (1 << tab_finances) | (1 << tab_cargo_delivered) | (1 << tab_challenge));
self->activated_widgets |= (1ULL << tabWidgetIdxByTabId[self->current_tab]);
}
// 0x0043230B
static void switchTab(window* self, widget_index widgetIndex)
{
if (input::isToolActive(self->type, self->number))
input::toolCancel();
textinput::sub_4CE6C9(self->type, self->number);
self->current_tab = widgetIndex - widx::tab_status;
self->frame_no = 0;
self->flags &= ~(window_flags::flag_16);
if (self->viewports[0] != nullptr)
{
self->viewports[0]->width = 0;
self->viewports[0] = nullptr;
}
auto tabIndex = widgetIndex - widx::tab_status;
auto tabInfo = tabInformationByTabOffset[tabIndex];
self->enabled_widgets = *tabInfo.enabledWidgets;
self->holdable_widgets = 0;
self->event_handlers = tabInfo.events;
self->activated_widgets = 0;
self->widgets = tabInfo.widgets;
if (tabInfo.widgetIndex == widx::tab_finances)
self->holdable_widgets = finances::holdableWidgets;
common::disableChallengeTab(self);
self->invalidate();
self->setSize(*tabInfo.windowSize);
self->callOnResize();
self->callPrepareDraw();
self->initScrollWidgets();
self->invalidate();
self->moveInsideScreenEdges();
if (tabInfo.widgetIndex == widx::tab_finances)
finances::sub_4C8DBF(self);
}
// 0x0043252E
static void renameCompanyPrompt(window* self, widget_index widgetIndex)
{
auto company = companymgr::get(self->number);
textinput::openTextinput(self, string_ids::title_name_company, string_ids::prompt_enter_new_company_name, company->name, widgetIndex, nullptr);
}
// 0x0043254F
static void renameCompany(window* self, char* input)
{
if (strlen(input) == 0)
return;
gGameCommandErrorTitle = string_ids::cannot_rename_this_company;
uint32_t* buffer = (uint32_t*)input;
game_commands::do_30(self->number, 1, buffer[0], buffer[1], buffer[2]);
game_commands::do_30(0, 2, buffer[3], buffer[4], buffer[5]);
game_commands::do_30(0, 0, buffer[6], buffer[7], buffer[8]);
}
static void drawCompanySelect(const window* const self, gfx::drawpixelinfo_t* const dpi)
{
const auto company = companymgr::get(self->number);
const auto competitor = objectmgr::get<competitor_object>(company->competitor_id);
// Draw company owner face.
const uint32_t image = gfx::recolour(competitor->images[company->owner_emotion], company->mainColours.primary);
const uint16_t x = self->x + self->widgets[common::widx::company_select].left + 1;
const uint16_t y = self->y + self->widgets[common::widx::company_select].top + 1;
gfx::drawImage(dpi, x, y, image);
}
// 0x00434413
static void drawTabs(window* self, gfx::drawpixelinfo_t* dpi)
{
auto skin = objectmgr::get<interface_skin_object>();
// Status tab
{
const uint32_t imageId = skin->img + interface_skin::image_ids::tab_company;
widget::draw_tab(self, dpi, imageId, widx::tab_status);
}
// Details tab
{
const uint32_t imageId = gfx::recolour(skin->img + interface_skin::image_ids::tab_company_details, self->colours[0]);
widget::draw_tab(self, dpi, imageId, widx::tab_details);
}
// Colour scheme tab
{
static const uint32_t colourSchemeTabImageIds[] = {
interface_skin::image_ids::tab_colour_scheme_frame0,
interface_skin::image_ids::tab_colour_scheme_frame1,
interface_skin::image_ids::tab_colour_scheme_frame2,
interface_skin::image_ids::tab_colour_scheme_frame3,
interface_skin::image_ids::tab_colour_scheme_frame4,
interface_skin::image_ids::tab_colour_scheme_frame5,
interface_skin::image_ids::tab_colour_scheme_frame6,
interface_skin::image_ids::tab_colour_scheme_frame7,
};
uint32_t imageId = skin->img;
if (self->current_tab == widx::tab_colour_scheme - widx::tab_status)
imageId += colourSchemeTabImageIds[(self->frame_no / 4) % std::size(colourSchemeTabImageIds)];
else
imageId += colourSchemeTabImageIds[0];
widget::draw_tab(self, dpi, imageId, widx::tab_colour_scheme);
}
// Finances tab
{
static const uint32_t financesTabImageIds[] = {
interface_skin::image_ids::tab_finances_frame0,
interface_skin::image_ids::tab_finances_frame1,
interface_skin::image_ids::tab_finances_frame2,
interface_skin::image_ids::tab_finances_frame3,
interface_skin::image_ids::tab_finances_frame4,
interface_skin::image_ids::tab_finances_frame5,
interface_skin::image_ids::tab_finances_frame6,
interface_skin::image_ids::tab_finances_frame7,
interface_skin::image_ids::tab_finances_frame8,
interface_skin::image_ids::tab_finances_frame9,
interface_skin::image_ids::tab_finances_frame10,
interface_skin::image_ids::tab_finances_frame11,
interface_skin::image_ids::tab_finances_frame12,
interface_skin::image_ids::tab_finances_frame13,
interface_skin::image_ids::tab_finances_frame14,
interface_skin::image_ids::tab_finances_frame15,
};
uint32_t imageId = skin->img;
if (self->current_tab == widx::tab_finances - widx::tab_status)
imageId += financesTabImageIds[(self->frame_no / 2) % std::size(financesTabImageIds)];
else
imageId += financesTabImageIds[0];
widget::draw_tab(self, dpi, imageId, widx::tab_finances);
}
// Cargo delivered tab
{
static const uint32_t cargoDeliveredTabImageIds[] = {
interface_skin::image_ids::tab_cargo_delivered_frame0,
interface_skin::image_ids::tab_cargo_delivered_frame1,
interface_skin::image_ids::tab_cargo_delivered_frame2,
interface_skin::image_ids::tab_cargo_delivered_frame3,
};
uint32_t imageId = skin->img;
if (self->current_tab == widx::tab_cargo_delivered - widx::tab_status)
imageId += cargoDeliveredTabImageIds[(self->frame_no / 4) % std::size(cargoDeliveredTabImageIds)];
else
imageId += cargoDeliveredTabImageIds[0];
widget::draw_tab(self, dpi, imageId, widx::tab_cargo_delivered);
}
// Challenge tab
{
static const uint32_t challengeTabImageIds[] = {
interface_skin::image_ids::tab_cup_frame0,
interface_skin::image_ids::tab_cup_frame1,
interface_skin::image_ids::tab_cup_frame2,
interface_skin::image_ids::tab_cup_frame3,
interface_skin::image_ids::tab_cup_frame4,
interface_skin::image_ids::tab_cup_frame5,
interface_skin::image_ids::tab_cup_frame6,
interface_skin::image_ids::tab_cup_frame7,
interface_skin::image_ids::tab_cup_frame8,
interface_skin::image_ids::tab_cup_frame9,
interface_skin::image_ids::tab_cup_frame10,
interface_skin::image_ids::tab_cup_frame11,
interface_skin::image_ids::tab_cup_frame12,
interface_skin::image_ids::tab_cup_frame13,
interface_skin::image_ids::tab_cup_frame14,
interface_skin::image_ids::tab_cup_frame15,
};
uint32_t imageId = skin->img;
if (self->current_tab == widx::tab_challenge - widx::tab_status)
imageId += challengeTabImageIds[(self->frame_no / 4) % std::size(challengeTabImageIds)];
else
imageId += challengeTabImageIds[0];
widget::draw_tab(self, dpi, imageId, widx::tab_challenge);
}
}
// 0x004343BC
static void repositionTabs(window* self)
{
int16_t xPos = self->widgets[widx::tab_status].left;
const int16_t tabWidth = self->widgets[widx::tab_status].right - xPos;
for (uint8_t i = widx::tab_status; i <= widx::tab_challenge; i++)
{
if (self->isDisabled(i))
continue;
self->widgets[i].left = xPos;
self->widgets[i].right = xPos + tabWidth;
xPos = self->widgets[i].right + 1;
}
}
}
}