496 lines
18 KiB
C++
496 lines
18 KiB
C++
#include "../EditorController.h"
|
|
#include "../GameCommands/GameCommands.h"
|
|
#include "../Graphics/Colour.h"
|
|
#include "../Graphics/Gfx.h"
|
|
#include "../Graphics/ImageIds.h"
|
|
#include "../Gui.h"
|
|
#include "../Input.h"
|
|
#include "../Interop/Interop.hpp"
|
|
#include "../Intro.h"
|
|
#include "../Localisation/StringIds.h"
|
|
#include "../Map/Tile.h"
|
|
#include "../MultiPlayer.h"
|
|
#include "../Objects/InterfaceSkinObject.h"
|
|
#include "../Objects/ObjectManager.h"
|
|
#include "../OpenLoco.h"
|
|
#include "../Tutorial.h"
|
|
#include "../Ui.h"
|
|
#include "../Ui/Dropdown.h"
|
|
#include "../Ui/WindowManager.h"
|
|
#include "../ViewportManager.h"
|
|
#include "../Widget.h"
|
|
|
|
using namespace OpenLoco::Interop;
|
|
|
|
namespace OpenLoco::Ui::Windows::TitleMenu
|
|
{
|
|
static const uint8_t btn_main_size = 74;
|
|
static const uint8_t btn_sub_height = 18;
|
|
static const uint16_t ww = btn_main_size * 4;
|
|
static const uint16_t wh = btn_main_size + btn_sub_height;
|
|
|
|
static const std::vector<uint32_t> globe_spin = {
|
|
ImageIds::title_menu_globe_spin_0,
|
|
ImageIds::title_menu_globe_spin_1,
|
|
ImageIds::title_menu_globe_spin_2,
|
|
ImageIds::title_menu_globe_spin_3,
|
|
ImageIds::title_menu_globe_spin_4,
|
|
ImageIds::title_menu_globe_spin_5,
|
|
ImageIds::title_menu_globe_spin_6,
|
|
ImageIds::title_menu_globe_spin_7,
|
|
ImageIds::title_menu_globe_spin_8,
|
|
ImageIds::title_menu_globe_spin_9,
|
|
ImageIds::title_menu_globe_spin_10,
|
|
ImageIds::title_menu_globe_spin_11,
|
|
ImageIds::title_menu_globe_spin_12,
|
|
ImageIds::title_menu_globe_spin_13,
|
|
ImageIds::title_menu_globe_spin_14,
|
|
ImageIds::title_menu_globe_spin_15,
|
|
ImageIds::title_menu_globe_spin_16,
|
|
ImageIds::title_menu_globe_spin_17,
|
|
ImageIds::title_menu_globe_spin_18,
|
|
ImageIds::title_menu_globe_spin_19,
|
|
ImageIds::title_menu_globe_spin_20,
|
|
ImageIds::title_menu_globe_spin_21,
|
|
ImageIds::title_menu_globe_spin_22,
|
|
ImageIds::title_menu_globe_spin_23,
|
|
ImageIds::title_menu_globe_spin_24,
|
|
ImageIds::title_menu_globe_spin_25,
|
|
ImageIds::title_menu_globe_spin_26,
|
|
ImageIds::title_menu_globe_spin_27,
|
|
ImageIds::title_menu_globe_spin_28,
|
|
ImageIds::title_menu_globe_spin_29,
|
|
ImageIds::title_menu_globe_spin_30,
|
|
ImageIds::title_menu_globe_spin_31,
|
|
};
|
|
|
|
static const std::vector<uint32_t> globe_construct = {
|
|
ImageIds::title_menu_globe_construct_0,
|
|
ImageIds::title_menu_globe_construct_1,
|
|
ImageIds::title_menu_globe_construct_2,
|
|
ImageIds::title_menu_globe_construct_3,
|
|
ImageIds::title_menu_globe_construct_4,
|
|
ImageIds::title_menu_globe_construct_5,
|
|
ImageIds::title_menu_globe_construct_6,
|
|
ImageIds::title_menu_globe_construct_7,
|
|
ImageIds::title_menu_globe_construct_8,
|
|
ImageIds::title_menu_globe_construct_9,
|
|
ImageIds::title_menu_globe_construct_10,
|
|
ImageIds::title_menu_globe_construct_11,
|
|
ImageIds::title_menu_globe_construct_12,
|
|
ImageIds::title_menu_globe_construct_13,
|
|
ImageIds::title_menu_globe_construct_14,
|
|
ImageIds::title_menu_globe_construct_15,
|
|
ImageIds::title_menu_globe_construct_16,
|
|
ImageIds::title_menu_globe_construct_17,
|
|
ImageIds::title_menu_globe_construct_18,
|
|
ImageIds::title_menu_globe_construct_19,
|
|
ImageIds::title_menu_globe_construct_20,
|
|
ImageIds::title_menu_globe_construct_21,
|
|
ImageIds::title_menu_globe_construct_22,
|
|
ImageIds::title_menu_globe_construct_23,
|
|
ImageIds::title_menu_globe_construct_24,
|
|
ImageIds::title_menu_globe_construct_25,
|
|
ImageIds::title_menu_globe_construct_26,
|
|
ImageIds::title_menu_globe_construct_27,
|
|
ImageIds::title_menu_globe_construct_28,
|
|
ImageIds::title_menu_globe_construct_29,
|
|
ImageIds::title_menu_globe_construct_30,
|
|
ImageIds::title_menu_globe_construct_31,
|
|
};
|
|
|
|
namespace Widx
|
|
{
|
|
enum
|
|
{
|
|
scenario_list_btn,
|
|
load_game_btn,
|
|
tutorial_btn,
|
|
scenario_editor_btn,
|
|
chat_btn,
|
|
multiplayer_toggle_btn,
|
|
};
|
|
}
|
|
|
|
static Widget _widgets[] = {
|
|
makeWidget({ 0, 0 }, { btn_main_size, btn_main_size }, WidgetType::wt_9, WindowColour::secondary, StringIds::null, StringIds::title_menu_new_game),
|
|
makeWidget({ btn_main_size, 0 }, { btn_main_size, btn_main_size }, WidgetType::wt_9, WindowColour::secondary, StringIds::null, StringIds::title_menu_load_game),
|
|
makeWidget({ btn_main_size * 2, 0 }, { btn_main_size, btn_main_size }, WidgetType::wt_9, WindowColour::secondary, StringIds::null, StringIds::title_menu_show_tutorial),
|
|
makeWidget({ btn_main_size * 3, 0 }, { btn_main_size, btn_main_size }, WidgetType::wt_9, WindowColour::secondary, StringIds::null, StringIds::title_menu_scenario_editor),
|
|
makeWidget({ btn_main_size * 4 - 31, btn_main_size - 27 }, { 31, 27 }, WidgetType::wt_9, WindowColour::secondary, StringIds::null, StringIds::title_menu_chat_tooltip),
|
|
makeWidget({ 0, btn_main_size }, { ww, btn_sub_height }, WidgetType::none, WindowColour::secondary, StringIds::null, StringIds::title_multiplayer_toggle_tooltip),
|
|
widgetEnd(),
|
|
};
|
|
|
|
static WindowEventList _events;
|
|
|
|
static void sub_439112(Window* window);
|
|
static void sub_4391CC(int16_t itemIndex);
|
|
static void sub_43918F(const char* string);
|
|
static void sub_4391DA();
|
|
static void sub_4391E2();
|
|
static void sub_43910A();
|
|
static void sub_439163(Ui::Window* callingWindow, WidgetIndex_t callingWidget);
|
|
static void sub_439102();
|
|
static void sub_46E328();
|
|
|
|
static void onMouseUp(Ui::Window* window, WidgetIndex_t widgetIndex);
|
|
static void onMouseDown(Ui::Window* window, WidgetIndex_t widgetIndex);
|
|
static void onDropdown(Ui::Window* window, WidgetIndex_t widgetIndex, int16_t itemIndex);
|
|
static void onUpdate(Window* window);
|
|
static void onTextInput(Window* window, WidgetIndex_t widgetIndex, const char* input);
|
|
static Ui::CursorId onCursor(Window* window, int16_t widgetIdx, int16_t xPos, int16_t yPos, Ui::CursorId fallback);
|
|
static void draw(Ui::Window* window, Gfx::Context* context);
|
|
static void prepareDraw(Ui::Window* window);
|
|
|
|
// static loco_global<WindowEventList[1], 0x004f9ec8> _events;
|
|
|
|
Window* open()
|
|
{
|
|
_events.on_mouse_up = onMouseUp;
|
|
_events.on_mouse_down = onMouseDown;
|
|
_events.on_dropdown = onDropdown;
|
|
_events.text_input = onTextInput;
|
|
_events.cursor = onCursor;
|
|
_events.on_update = onUpdate;
|
|
_events.prepare_draw = prepareDraw;
|
|
_events.draw = draw;
|
|
|
|
auto window = OpenLoco::Ui::WindowManager::createWindow(
|
|
WindowType::titleMenu,
|
|
Gfx::point_t((Ui::width() - ww) / 2, Ui::height() - wh - 25),
|
|
{ ww, wh },
|
|
WindowFlags::stick_to_front | WindowFlags::transparent | WindowFlags::no_background | WindowFlags::flag_6,
|
|
&_events);
|
|
|
|
window->widgets = _widgets;
|
|
window->enabled_widgets = (1 << Widx::scenario_list_btn) | (1 << Widx::load_game_btn) | (1 << Widx::tutorial_btn) | (1 << Widx::scenario_editor_btn) | (1 << Widx::chat_btn);
|
|
|
|
window->initScrollWidgets();
|
|
|
|
window->setColour(WindowColour::primary, Colour::translucent(Colour::saturated_green));
|
|
window->setColour(WindowColour::secondary, Colour::translucent(Colour::saturated_green));
|
|
window->var_846 = 0;
|
|
|
|
return window;
|
|
}
|
|
|
|
// 0x00438E0B
|
|
static void prepareDraw(Ui::Window* window)
|
|
{
|
|
window->disabled_widgets = 0;
|
|
window->widgets[Widx::tutorial_btn].type = Ui::WidgetType::wt_9;
|
|
window->widgets[Widx::scenario_editor_btn].type = Ui::WidgetType::wt_9;
|
|
|
|
// TODO: add widget::set_origin()
|
|
window->widgets[Widx::scenario_list_btn].left = 0;
|
|
window->widgets[Widx::scenario_list_btn].right = btn_main_size - 1;
|
|
window->widgets[Widx::load_game_btn].left = btn_main_size;
|
|
window->widgets[Widx::load_game_btn].right = btn_main_size * 2 - 1;
|
|
window->widgets[Widx::tutorial_btn].left = btn_main_size * 2;
|
|
window->widgets[Widx::tutorial_btn].right = btn_main_size * 3 - 1;
|
|
window->widgets[Widx::scenario_editor_btn].left = btn_main_size * 3;
|
|
window->widgets[Widx::scenario_editor_btn].right = btn_main_size * 4 - 1;
|
|
window->widgets[Widx::chat_btn].type = Ui::WidgetType::none;
|
|
|
|
if (OpenLoco::isNetworked())
|
|
{
|
|
window->widgets[Widx::tutorial_btn].type = Ui::WidgetType::none;
|
|
window->widgets[Widx::scenario_editor_btn].type = Ui::WidgetType::none;
|
|
|
|
window->widgets[Widx::scenario_list_btn].left = btn_main_size;
|
|
window->widgets[Widx::scenario_list_btn].right = btn_main_size * 2 - 1;
|
|
window->widgets[Widx::load_game_btn].left = btn_main_size * 2;
|
|
window->widgets[Widx::load_game_btn].right = btn_main_size * 3 - 1;
|
|
|
|
window->widgets[Widx::chat_btn].type = Ui::WidgetType::wt_9;
|
|
auto* skin = ObjectManager::get<InterfaceSkinObject>();
|
|
window->widgets[Widx::chat_btn].image = skin->img + InterfaceSkin::ImageIds::phone;
|
|
}
|
|
}
|
|
|
|
// 0x00438EC7
|
|
static void draw(Ui::Window* window, Gfx::Context* context)
|
|
{
|
|
// Draw widgets.
|
|
window->draw(context);
|
|
|
|
if (window->widgets[Widx::scenario_list_btn].type != Ui::WidgetType::none)
|
|
{
|
|
int16_t x = window->widgets[Widx::scenario_list_btn].left + window->x;
|
|
int16_t y = window->widgets[Widx::scenario_list_btn].top + window->y;
|
|
|
|
uint32_t image_id = ImageIds::title_menu_globe_spin_0;
|
|
if (Input::isHovering(WindowType::titleMenu, 0, Widx::scenario_list_btn))
|
|
{
|
|
image_id = globe_spin[((window->var_846 / 2) % globe_spin.size())];
|
|
}
|
|
|
|
OpenLoco::Gfx::drawImage(context, x, y, image_id);
|
|
OpenLoco::Gfx::drawImage(context, x, y, ImageIds::title_menu_sparkle);
|
|
}
|
|
|
|
if (window->widgets[Widx::load_game_btn].type != Ui::WidgetType::none)
|
|
{
|
|
int16_t x = window->widgets[Widx::load_game_btn].left + window->x;
|
|
int16_t y = window->widgets[Widx::load_game_btn].top + window->y;
|
|
|
|
uint32_t image_id = ImageIds::title_menu_globe_spin_0;
|
|
if (Input::isHovering(WindowType::titleMenu, 0, Widx::load_game_btn))
|
|
{
|
|
image_id = globe_spin[((window->var_846 / 2) % globe_spin.size())];
|
|
}
|
|
|
|
OpenLoco::Gfx::drawImage(context, x, y, image_id);
|
|
OpenLoco::Gfx::drawImage(context, x, y, ImageIds::title_menu_save);
|
|
}
|
|
|
|
if (window->widgets[Widx::tutorial_btn].type != Ui::WidgetType::none)
|
|
{
|
|
int16_t x = window->widgets[Widx::tutorial_btn].left + window->x;
|
|
int16_t y = window->widgets[Widx::tutorial_btn].top + window->y;
|
|
|
|
uint32_t image_id = ImageIds::title_menu_globe_spin_0;
|
|
if (Input::isHovering(WindowType::titleMenu, 0, Widx::tutorial_btn))
|
|
{
|
|
image_id = globe_spin[((window->var_846 / 2) % globe_spin.size())];
|
|
}
|
|
|
|
OpenLoco::Gfx::drawImage(context, x, y, image_id);
|
|
|
|
// TODO: base lesson overlay on language
|
|
OpenLoco::Gfx::drawImage(context, x, y, ImageIds::title_menu_lesson_l);
|
|
}
|
|
|
|
if (window->widgets[Widx::scenario_editor_btn].type != Ui::WidgetType::none)
|
|
{
|
|
int16_t x = window->widgets[Widx::scenario_editor_btn].left + window->x;
|
|
int16_t y = window->widgets[Widx::scenario_editor_btn].top + window->y;
|
|
|
|
uint32_t image_id = ImageIds::title_menu_globe_construct_24;
|
|
if (Input::isHovering(WindowType::titleMenu, 0, Widx::scenario_editor_btn))
|
|
{
|
|
image_id = globe_construct[((window->var_846 / 2) % globe_construct.size())];
|
|
}
|
|
|
|
OpenLoco::Gfx::drawImage(context, x, y, image_id);
|
|
}
|
|
|
|
if (window->widgets[Widx::multiplayer_toggle_btn].type != Ui::WidgetType::none)
|
|
{
|
|
int16_t y = window->widgets[Widx::multiplayer_toggle_btn].top + 3 + window->y;
|
|
int16_t x = window->width / 2 + window->x;
|
|
|
|
string_id string = StringIds::single_player_mode;
|
|
|
|
if (OpenLoco::isNetworked())
|
|
{
|
|
// char[512+1]
|
|
auto buffer = StringManager::getString(StringIds::buffer_2039);
|
|
|
|
char* playerName = (char*)0xF254D0;
|
|
|
|
strcpy((char*)buffer, playerName);
|
|
|
|
addr<0x112C826, string_id>() = StringIds::buffer_2039;
|
|
string = StringIds::two_player_mode_connected;
|
|
}
|
|
|
|
drawStringCentredClipped(*context, x, y, ww - 4, Colour::black, string, (char*)0x112c826);
|
|
}
|
|
}
|
|
|
|
// 0x00439094
|
|
static void onMouseUp(Ui::Window* window, WidgetIndex_t widgetIndex)
|
|
{
|
|
if (Intro::isActive())
|
|
{
|
|
return;
|
|
}
|
|
|
|
sub_46E328();
|
|
|
|
switch (widgetIndex)
|
|
{
|
|
case Widx::scenario_list_btn:
|
|
sub_4391DA();
|
|
break;
|
|
case Widx::load_game_btn:
|
|
sub_4391E2();
|
|
break;
|
|
case Widx::scenario_editor_btn:
|
|
sub_43910A();
|
|
break;
|
|
case Widx::chat_btn:
|
|
sub_439163(window, widgetIndex);
|
|
break;
|
|
case Widx::multiplayer_toggle_btn:
|
|
sub_439102();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 0x004390D1
|
|
static void onMouseDown(Ui::Window* window, WidgetIndex_t widgetIndex)
|
|
{
|
|
sub_46E328();
|
|
switch (widgetIndex)
|
|
{
|
|
case Widx::tutorial_btn:
|
|
sub_439112(window);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 0x004390DD
|
|
static void onDropdown(Ui::Window* window, WidgetIndex_t widgetIndex, int16_t itemIndex)
|
|
{
|
|
sub_46E328();
|
|
switch (widgetIndex)
|
|
{
|
|
case Widx::tutorial_btn:
|
|
sub_4391CC(itemIndex);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 0x004390ED
|
|
static void onTextInput(Window* window, WidgetIndex_t widgetIndex, const char* input)
|
|
{
|
|
switch (widgetIndex)
|
|
{
|
|
case Widx::chat_btn:
|
|
sub_43918F(input);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 0x004390f8
|
|
static Ui::CursorId onCursor(Window* window, int16_t widgetIdx, int16_t xPos, int16_t yPos, Ui::CursorId fallback)
|
|
{
|
|
// Reset tooltip timeout to keep tooltips open.
|
|
addr<0x0052338A, uint16_t>() = 2000;
|
|
return fallback;
|
|
}
|
|
|
|
static void sub_439102()
|
|
{
|
|
call(0x0046e639); // window_multiplayer::open
|
|
}
|
|
|
|
// 0x0043CB9F
|
|
void editorInit()
|
|
{
|
|
Main::open();
|
|
|
|
addr<0x00F2533F, int8_t>() = 0; // grid lines
|
|
addr<0x0112C2e1, int8_t>() = 0;
|
|
addr<0x009c870E, int8_t>() = 0;
|
|
addr<0x009c870F, int8_t>() = 2;
|
|
addr<0x009c8710, int8_t>() = 1;
|
|
|
|
ToolbarTop::Editor::open();
|
|
ToolbarBottom::Editor::open();
|
|
Gui::resize();
|
|
}
|
|
|
|
static void sub_43910A()
|
|
{
|
|
EditorController::init();
|
|
}
|
|
|
|
static void sub_439112(Window* window)
|
|
{
|
|
Dropdown::add(0, StringIds::tutorial_1_title);
|
|
Dropdown::add(1, StringIds::tutorial_2_title);
|
|
Dropdown::add(2, StringIds::tutorial_3_title);
|
|
|
|
Widget* widget = &window->widgets[Widx::tutorial_btn];
|
|
Dropdown::showText(
|
|
window->x + widget->left,
|
|
window->y + widget->top,
|
|
widget->width(),
|
|
widget->height(),
|
|
Colour::translucent(window->getColour(WindowColour::primary)),
|
|
3,
|
|
0x80);
|
|
}
|
|
|
|
static void sub_439163(Ui::Window* callingWindow, WidgetIndex_t callingWidget)
|
|
{
|
|
WindowManager::close(WindowType::multiplayer);
|
|
|
|
addr<0x112C826 + 8, string_id>() = StringIds::the_other_player;
|
|
|
|
// TODO: convert this to a builder pattern, with chainable functions to set the different string ids and arguments
|
|
TextInput::openTextInput(callingWindow, StringIds::chat_title, StringIds::chat_instructions, StringIds::empty, callingWidget, (void*)0x112C826);
|
|
}
|
|
|
|
static void sub_43918F(const char* string)
|
|
{
|
|
// Identical to processChatMessage
|
|
GameCommands::setErrorTitle(StringIds::empty);
|
|
|
|
for (int i = 0; i < 32; i++)
|
|
{
|
|
GameCommands::do_71(i, &string[i * 16]);
|
|
}
|
|
}
|
|
|
|
static void sub_4391CC(int16_t itemIndex)
|
|
{
|
|
// DROPDOWN_ITEM_UNDEFINED
|
|
if (itemIndex == -1)
|
|
return;
|
|
|
|
OpenLoco::Tutorial::start(itemIndex);
|
|
}
|
|
|
|
static void sub_4391DA()
|
|
{
|
|
ScenarioSelect::open();
|
|
}
|
|
|
|
static void sub_4391E2()
|
|
{
|
|
GameCommands::do_21(0, 0);
|
|
}
|
|
|
|
static void sub_46E328()
|
|
{
|
|
call(0x0046e328);
|
|
}
|
|
|
|
// 0x004391F9
|
|
static void onUpdate(Window* window)
|
|
{
|
|
window->var_846++;
|
|
|
|
if (Intro::isActive())
|
|
{
|
|
window->invalidate();
|
|
return;
|
|
}
|
|
|
|
if (!MultiPlayer::hasFlag(MultiPlayer::flags::flag_8) && !MultiPlayer::hasFlag(MultiPlayer::flags::flag_9))
|
|
{
|
|
window->invalidate();
|
|
return;
|
|
}
|
|
|
|
if (addr<0x0050C1AE, int32_t>() < 20)
|
|
{
|
|
window->invalidate();
|
|
return;
|
|
}
|
|
|
|
auto multiplayer = WindowManager::find(WindowType::multiplayer);
|
|
if (multiplayer == nullptr)
|
|
{
|
|
call(0x0046e639); // window_multiplayer::open
|
|
}
|
|
|
|
window->invalidate();
|
|
}
|
|
}
|