Skip to content

Commit

Permalink
CommandStringPicMenu - Inline buttons
Browse files Browse the repository at this point in the history
the substrings "\/" are the tag that points to a link

CommandStringPicMenu - Fix glitch related to the command is at the end of a list of commands
  • Loading branch information
jetrotal committed Jul 28, 2024
1 parent fe7f49d commit c0d7560
Show file tree
Hide file tree
Showing 4 changed files with 245 additions and 2 deletions.
37 changes: 35 additions & 2 deletions src/game_interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5207,6 +5207,10 @@ bool Game_Interpreter::CommandStringPicMenu(const lcf::rpg::EventCommand& com) {
const int output_var_current_item = ValueOrVariable(com.parameters[3], com.parameters[4]);
const int output_var_input_state = ValueOrVariable(com.parameters[5], com.parameters[6]);

if (strpic_index <= 0) {
Output::Warning("CommandStringPicMenu: Requested invalid picture id ({})", strpic_index);
return true;
}
auto& window_data = Main_Data::game_windows->GetWindow(strpic_index);

if (!window_data.window) {
Expand Down Expand Up @@ -5286,7 +5290,11 @@ bool Game_Interpreter::CommandStringPicMenu(const lcf::rpg::EventCommand& com) {
auto& current_index = frame.current_command;
int indent = commands[current_index].indent;
int ui_choice_index = -1;
const auto& choice_controller = commands[current_index + 1];

const auto& choice_controller = (current_index + 1 < commands.size()) ?
commands[current_index + 1] :
commands[current_index];

bool has_choice_list = (static_cast<Cmd>(choice_controller.code) == Cmd::ShowChoice);

UpdateVariables(window->GetIndex(), 0);
Expand Down Expand Up @@ -5321,12 +5329,37 @@ bool Game_Interpreter::CommandStringPicMenu(const lcf::rpg::EventCommand& com) {
auto pending_message = Main_Data::game_windows->GeneratePendingMessage(ToString(text_data.text));
const auto& lines = pending_message.GetLines();

bool use_substring_mode = false;

// Detect mode by checking for "\/" in the entire text
for (const auto& line : lines) {
if (line.find("\\/") != std::string::npos) {
use_substring_mode = true;
break;
}
}

// Now apply the appropriate counting method
for (const auto& line : lines) {
std::stringstream ss(line);
std::string sub_line;

while (Utils::ReadLine(ss, sub_line)) {
max_item++;
if (use_substring_mode) {
size_t pos = 0;
int count = 0;
while ((pos = sub_line.find("\\/", pos)) != std::string::npos) {
count++;
if (count % 2 != 0) { // Check if count is odd
max_item++;
}
pos += 2; // Move past the found substring
}
}
else {
max_item++; // Increment once per sub_line in original mode
}

// Output::Warning("{}", sub_line);
}
}
Expand Down
35 changes: 35 additions & 0 deletions src/game_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,10 @@ void Game_Windows::Window_User::Refresh(bool& async_wait) {
int x_max = 0;
int y_max = 0;

bool is_drawing_rect = false;
Rect current_rect;
std::vector<Rect> inline_rects = {};

auto ProcessText = [&](ProcessTextMode mode) {
for (size_t i = 0; i < data.texts.size(); ++i) {
auto& font = fonts[i];
Expand Down Expand Up @@ -335,13 +339,41 @@ void Game_Windows::Window_User::Refresh(bool& async_wait) {
text_index = pres.next;
}
break;
case '/':
{
if (mode == ProcessTextMode::TextDrawing) continue;
// Toggle rect drawing mode
if (!is_drawing_rect) {
// Start drawing a new rect
current_rect.x = x;
current_rect.y = y;
is_drawing_rect = true;
}
else {
// Finish the current rect
current_rect.width = x + 1 - current_rect.x;
if (current_rect.width > x_max && x_max != 0 ) current_rect.width -=1;
current_rect.height = y - current_rect.y;
inline_rects.push_back(current_rect);
is_drawing_rect = false;
}
}
break;
}
continue;
}

line32 += static_cast<char32_t>(ch);
}

// After the loop, check if there's an unfinished rect
if (is_drawing_rect) {
current_rect.width = x_max -1;
current_rect.height = y;
inline_rects.push_back(current_rect);
is_drawing_rect = false;
}

if (!line32.empty()) {
if (mode == ProcessTextMode::TextDrawing) {
Text::Draw(*window->GetContents(), x, y, *font, *system, text_color, Utils::EncodeUTF(line32));
Expand Down Expand Up @@ -389,6 +421,9 @@ void Game_Windows::Window_User::Refresh(bool& async_wait) {
}

window = std::make_unique<Window_Selectable>(0, 0, data.width, data.height);

if (!inline_rects.empty()) window->inline_rects = inline_rects;

if (!data.flags.border_margin) {
window->SetBorderX(0);
// FIXME: Figure out why 0 does not work here (bug in Window class)
Expand Down
167 changes: 167 additions & 0 deletions src/window_selectable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "input.h"
#include "util_macro.h"
#include "bitmap.h"
#include "output.h"

constexpr int arrow_animation_frames = 20;

Expand Down Expand Up @@ -99,6 +100,13 @@ void Window_Selectable::UpdateHelp() {

// Update Cursor Rect
void Window_Selectable::UpdateCursorRect() {

bool cursor_is_inline = !inline_rects.empty() && index < static_cast<int>(inline_rects.size());
if (cursor_is_inline) {
InlineUpdateCursorRect();
return;
}

int cursor_width = 0;
int x = 0;
if (index < 0) {
Expand Down Expand Up @@ -128,6 +136,67 @@ void Window_Selectable::UpdateCursorRect() {
SetCursorRect(Rect(x, y, cursor_width, item_height));
}

void Window_Selectable::InlineUpdateCursorRect() {
if (index < 0 || index >= static_cast<int>(inline_rects.size())) {
SetCursorRect(Rect());
return;
}

const auto& rect = inline_rects[index];
int cursor_width = rect.width + 9;
int x = rect.x - 4;
int y = rect.y;

int height = this->height - border_y * 2;
int max_y = std::max_element(inline_rects.begin(), inline_rects.end(),
[](const auto& a, const auto& b) { return a.y + a.height < b.y + b.height; })->y + menu_item_height;
int max_oy = std::max(0, max_y - height);

int target_oy = oy;
if (y < oy) {
target_oy = y;
scroll_dir = scroll_dir == 0 ? -1 : scroll_dir;
}
else if (y + menu_item_height > oy + height) {
target_oy = std::min(y + menu_item_height - height, max_oy);
scroll_dir = scroll_dir == 0 ? 1 : scroll_dir;
}
else {
scroll_dir = 0;
}

if (scroll_dir != 0) {
scroll_progress = std::min(scroll_progress + 1, 4);
int scroll_amount = (menu_item_height * scroll_progress / 4 - menu_item_height * (scroll_progress - 1) / 4) * scroll_dir;
oy = std::clamp(oy + scroll_amount, 0, max_oy);
if (scroll_progress == 4) {
scroll_dir = 0;
scroll_progress = 0;
oy = target_oy;
}
}
else {
oy = target_oy;
}

y -= oy;
if (border_x == 0) {
x += border_x;
cursor_width -= border_x * 2;
}

//workaround to deal with the cursor appearing over the borders while scrolling
int output_y = scroll_dir == -1 ? y / 2 : y;
int output_height = scroll_dir == 1 ? menu_item_height / 2 : menu_item_height;

SetCursorRect(Rect(x, output_y, cursor_width, output_height));

UpdateArrows();
bool latest_rect_is_oob = inline_rects.back().y + menu_item_height != oy + height;
bool arrow_visible = (arrow_frame < arrow_animation_frames);
SetDownArrow(latest_rect_is_oob && arrow_visible);
}

void Window_Selectable::UpdateArrows() {
bool show_up_arrow = (GetTopRow() > 0);
bool show_down_arrow = (GetTopRow() < (GetRowMax() - GetPageRowMax()));
Expand All @@ -143,6 +212,13 @@ void Window_Selectable::UpdateArrows() {
// Update
void Window_Selectable::Update() {
Window_Base::Update();

bool cursor_is_inline = !inline_rects.empty() && index < static_cast<int>(inline_rects.size());
if (cursor_is_inline) {
InlineUpdate();
return;
}

if (active && item_max > 0 && index >= 0) {
if (scroll_dir != 0) {
scroll_progress++;
Expand Down Expand Up @@ -234,6 +310,97 @@ void Window_Selectable::Update() {
UpdateArrows();
}

void Window_Selectable::InlineUpdate() {
if (active && !inline_rects.empty()) {
int old_index = index;
if (Input::IsRepeated(Input::DOWN) || Input::IsTriggered(Input::SCROLL_DOWN)) {
MoveIndexVertical(1);
}
if (Input::IsRepeated(Input::UP) || Input::IsTriggered(Input::SCROLL_UP)) {
MoveIndexVertical(-1);
}
if (Input::IsRepeated(Input::RIGHT)) {
MoveIndexHorizontal(1);
}
if (Input::IsRepeated(Input::LEFT)) {
MoveIndexHorizontal(-1);
}
if (index != old_index) {
Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Cursor));
if (active && help_window != NULL) {
UpdateHelp();
}
}
}
InlineUpdateCursorRect();

}

void Window_Selectable::MoveIndexHorizontal(int direction) {
int current_x = inline_rects[index].x;
int current_y = inline_rects[index].y;
int nearest_index = -1;
int min_distance = std::numeric_limits<int>::max();

for (size_t i = 0; i < inline_rects.size(); ++i) {
if (i != index && inline_rects[i].y == current_y) {
if ((direction > 0 && inline_rects[i].x > current_x) ||
(direction < 0 && inline_rects[i].x < current_x)) {
int distance = std::abs(inline_rects[i].x - current_x);
if (distance < min_distance) {
min_distance = distance;
nearest_index = i;
}
}
}
}

if (nearest_index != -1) {
Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Cursor));
index = nearest_index;
}
}

void Window_Selectable::MoveIndexVertical(int direction) {
int current_x = inline_rects[index].x;
int current_y = inline_rects[index].y;
int nearest_index = -1;
int min_y_distance = std::numeric_limits<int>::max();
int min_x = std::numeric_limits<int>::max();

// First pass: find the minimum y distance in the correct direction
for (size_t i = 0; i < inline_rects.size(); ++i) {
if (i != index) {
if ((direction > 0 && inline_rects[i].y > current_y) ||
(direction < 0 && inline_rects[i].y < current_y)) {
int y_distance = std::abs(inline_rects[i].y - current_y);
if (y_distance < min_y_distance) {
min_y_distance = y_distance;
}
}
}
}

// Second pass: among buttons with minimum y distance, find the one with smallest x value
for (size_t i = 0; i < inline_rects.size(); ++i) {
if (i != index) {
if ((direction > 0 && inline_rects[i].y > current_y) ||
(direction < 0 && inline_rects[i].y < current_y)) {
int y_distance = std::abs(inline_rects[i].y - current_y);
if (y_distance == min_y_distance && inline_rects[i].x < min_x) {
min_x = inline_rects[i].x;
nearest_index = i;
}
}
}
}

if (nearest_index != -1) {
Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Cursor));
index = nearest_index;
}
}

// Set endless scrolling state
void Window_Selectable::SetEndlessScrolling(bool state) {
endless_scrolling = state;
Expand Down
8 changes: 8 additions & 0 deletions src/window_selectable.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class Window_Selectable: public Window_Base {
public:
Window_Selectable(int ix, int iy, int iwidth, int iheight);

std::vector<Rect> inline_rects = {};

/**
* Creates the contents based on how many items
* are currently in the window.
Expand Down Expand Up @@ -76,6 +78,12 @@ class Window_Selectable: public Window_Base {
virtual void UpdateCursorRect();
void Update() override;

void InlineUpdateCursorRect();
void InlineUpdate();

void MoveIndexVertical(int direction);
void MoveIndexHorizontal(int direction);

virtual void UpdateHelp();

/**
Expand Down

0 comments on commit c0d7560

Please sign in to comment.