Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions port/include/stubs/menu_save.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#pragma once

// Keep the item picker's game logic available to the port's pointer wrapper.
#define EventItemSelectLoop PortEventItemSelectLoop

// memcard.cpp's SaveMenuFunc table names the save menu's steps, which this unit defines static:
// the PS2 link makes them global (object_fixups.json, globalize_symbols). Each gets a global
// symbol under the name the table references, which also keeps clang from dropping the static
Expand Down
3 changes: 2 additions & 1 deletion port/src/battlemenu/main_ring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -707,4 +707,5 @@ bool TakeBattleMenuLoadRequest() {
}
ingame_load_pending = false;
return true;
}
}
void BattleMenuIconPosition(int index, float &x, float &y) { GetSafeIconPos(index, x, y); }
98 changes: 98 additions & 0 deletions port/src/menu_event_pointer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#include "menu_event_pointer.hpp"

#include "menu_pointer.hpp"
#include "menu_save.hpp"
#include "platform/input.hpp"

/**
* Runs the original item picker selection and drawing logic.
*/
int PortEventItemSelectLoop(int *result);

/** The item picker's selection and transition state. */
extern MINI_MENU_INFO MiniMenu;
/** The item board's logical screen origin. */
extern float EventBoardPos[2];
/** The animated vertical origin of the first item row. */
extern int EventItemMoveY;
/** The item picker's animated hand position. */
extern float MiniCur[2];
/** Whether the item picker textures are ready. */
extern int MiniEventTexReadFlag;

namespace {

/** The pointer owned by the door/object item picker. */
MenuPointer event_pointer;
/** Whether the item picker remained active after its last update. */
bool event_active = false;

/**
* Finds an occupied item cell within the animated board's visible clip.
*/
int ItemHit(float x, float y) {
const float left = EventBoardPos[0] + 28.0f;
const float top = EventBoardPos[1] + 56.0f;
if (x < left || x >= left + 210.0f || y < top || y >= top + 80.0f) {
return -1;
}
for (int index = 0; index < MiniMenu.event_item_num; ++index) {
const float cell_left = left + (index % 5) * 42.0f;
const float cell_top = static_cast<float>(EventItemMoveY + (index / 5) * 40);
if (x >= cell_left && x < cell_left + 42.0f && y >= cell_top && y < cell_top + 40.0f) {
return index;
}
}
return -1;
}

} // namespace

/**
* Tracks the item picker's lifetime while preserving its original item handling.
*/
int EventItemSelectLoop(int *result) {
const int done = PortEventItemSelectLoop(result);
event_active = done == 0;
return done;
}

bool EventItemPointerUpdate() {
const bool active = event_active;
event_active = false;
if (!active) {
return !event_pointer.Close();
}
if (!event_pointer.IsOpen()) {
event_pointer.Open();
}
const MenuPointerTake take = event_pointer.Take(MiniCur[0], MiniCur[1]);
MenuPointerShow(&event_pointer);
if (!MiniEventTexReadFlag) {
return true;
}
if (MiniMenu.state == MINI_MENU_REFUSED) {
if ((take.clicked & 3) != 0) {
MenuPointerPress(kInputCircle);
}
return true;
}
if (MiniMenu.state != MINI_MENU_CHOOSING) {
return true;
}
if ((take.clicked & 2) != 0) {
MenuPointerPress(kInputCircle);
return true;
}
const int hit = take.pointing ? ItemHit(event_pointer.x, event_pointer.y) : -1;
if (hit >= 0 && take.moved && take.wheel == 0.0f) {
MiniMenu.cursor = hit;
if ((take.clicked & 1) != 0) {
MenuPointerPress(kInputCross);
}
}
if (take.pointing && take.wheel != 0.0f) {
MenuPointerPress(take.wheel > 0.0f ? kInputUp : kInputDown);
}
return true;
}
6 changes: 6 additions & 0 deletions port/src/menu_event_pointer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#pragma once

/**
* Handles pointer input while the door/object item picker owns the menu.
*/
bool EventItemPointerUpdate();
14 changes: 14 additions & 0 deletions port/src/menu_journal_chest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,20 @@ void Open() {
s_cursor.SnapTo(48.0f, static_cast<float>(kLeftStartY - 2));
}

void HandlePointer(float x, float y) {
if (x >= 84 && x < 248 && y >= kLeftStartY - 4 &&
y < kLeftStartY - 4 + TRACKER_TOWN_COUNT * kRowSpacing) {
const int town = static_cast<int>((y - (kLeftStartY - 4)) / kRowSpacing);
if (town != s_selected_town) {
s_selected_town = town;
ComMenuSePlay(MENU_SOUND_CURSOR);
}
} else if (x >= 180 && x < 460 && y >= 100 && y < 128) {
s_filter_miracle_only = !s_filter_miracle_only;
ComMenuSePlay(MENU_SOUND_CONFIRM);
}
}

bool HandleKey() {
if (GamePad.Down(PAD_CIRCLE)) {
ComMenuSePlay(MENU_SOUND_REFUSE);
Expand Down
5 changes: 4 additions & 1 deletion port/src/menu_journal_chest.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ void Open();
/// Handles gamepad input while the Chest Tracker is active. Returns true if the user exited back to the Journal menu.
bool HandleKey();

// Select a settlement or toggle the filter at the displayed pointer position.
void HandlePointer(float x, float y);

/// Renders the Chest Tracker dashboard using the UI framework.
void Draw();

/// Appends the Chest Tracker's localization keys and default English strings to `out`.
void AppendStrings(std::vector<std::pair<std::string, std::string>>& out);

} // namespace JournalChest
} // namespace JournalChest
102 changes: 82 additions & 20 deletions port/src/menu_mouse.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#include "menu_pointer.hpp"
#include "menu_event_pointer.hpp"

#include <algorithm>
#include <cmath>

#include "battlemenu.hpp"
#include "clsmes.hpp"
#include "itemdata.hpp"
#include "dngstatusdata.hpp"
#include "editpartsinfo.hpp"
Expand All @@ -13,6 +15,7 @@
#include "menu_draw.hpp"
#include "menu_inventory.hpp"
#include "menu_manual.hpp"
#include "menu_journal_chest.hpp"
#include "weapon_buildup.hpp"
#include "menu_misc.hpp"
#include "menu_option.hpp"
Expand Down Expand Up @@ -53,23 +56,10 @@ int RingIconCount() {
return BtlMenuMode == BATTLE_MENU_MODE_DUNGEON ? 8 : 9;
}

// Matches port/src/battlemenu.cpp GetSafeIconPos() so icons >= orig_max have valid hitboxes.
void GetRingIconPos(int index, float &out_x, float &out_y) {
const int orig_max = (BtlMenuMode == BATTLE_MENU_MODE_DUNGEON) ? 7 : 8;
if (index < orig_max) {
out_x = NorMenuIcon[index].x;
out_y = NorMenuIcon[index].y;
} else {
const int diff = index - (orig_max - 1);
out_x = NorMenuIcon[orig_max - 1].x - (16.0f * diff);
out_y = NorMenuIcon[orig_max - 1].y + (40.0f * diff);
}
}

// The row of one ring icon: its icon and the label beside it, where the brackets stand.
Rect RingRow(int icon) {
float ix = 0.0f, iy = 0.0f;
GetRingIconPos(icon, ix, iy);
BattleMenuIconPosition(icon, ix, iy);
int left = static_cast<int>(ix) - 18;
int top = static_cast<int>(iy) - 8;
return Rect{left, top, left + 250, top + 34};
Expand Down Expand Up @@ -329,6 +319,10 @@ void SaveMouse() {
}
MenuPointerTake take = g_save.Take(320.0f, 240.0f);
MenuPointerShow(&g_save);
if ((take.clicked & 2) != 0) {
MenuPointerPress(SaveMenu.key_no == SAVE_KEY_END_SAVE_ENDING ? kInputCross : kInputCircle);
return;
Comment on lines +322 to +324

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Dismiss the ending completion on right click

When the ending-only save reaches SAVE_KEY_END_SAVE_ENDING, this branch turns every right click into PAD_CIRCLE and returns, but SaveMenuKeyEndSaveEnding in port/src/memcard.cpp only checks PAD_CROSS. Consequently the completion message cannot be dismissed through the right-click/Back path (unlike SAVE_KEY_END_SAVE); send the acknowledgement input for this state or make its state handler accept Circle.

Useful? React with 👍 / 👎.

}
bool slots = SaveMenu.key_no == SAVE_KEY_FILE_SELECT;
if (slots) {
int count = SaveSlotRows(SaveSlots, SaveMenu.access_kind == SAVE_ACCESS_SAVE);
Expand All @@ -349,12 +343,35 @@ void SaveMouse() {
} else {
bool confirm = SaveMenu.key_no == SAVE_KEY_LOAD_DECIDE || SaveMenu.key_no == SAVE_KEY_SAVE_DECIDE ||
SaveMenu.key_no == SAVE_KEY_AFTER_ENDING;
int x = SaveMenu.key_no == SAVE_KEY_AFTER_ENDING ? 196 : 246;
int y = SaveMenu.key_no == SAVE_KEY_AFTER_ENDING ? 140 : 156;
int hit = take.pointing ? YesNoHit(x, y, g_save.x, g_save.y) : -1;
if (confirm && hit >= 0) {
if ((take.clicked & 1) != 0) MenuPointerPress(hit == 0 ? kInputCross : kInputCircle);
if ((take.clicked & 2) != 0) MenuPointerPress(kInputCircle);
if (!take.pointing || (take.clicked & 1) == 0) return;
const auto &mes = CommonMenuMes2;
if (confirm) {
// These are inline Cross/Yes and Circle/No prompts in a centred message,
// not the dungeon's vertical yes/no plate. Follow the laid-out glyphs.
for (int i = 0; i < mes.win_line_num; ++i) {
const auto &line = mes.win_line[i];
if (line.code != -0x2F8 && line.code != -0x2FA) continue;
float left = mes.text_x + line.x;
const int row_offset = mes.fukidashi == 0 && mes.centre_rows != 0
? (mes.char_height * (mes.rows - mes.text_rows)) >> 1
: 0;
float top = mes.text_y + row_offset + line.y;
float right = mes.text_x + mes.text_width;
for (int j = i + 1; j < mes.win_line_num && mes.win_line[j].y == line.y; ++j) {
if (mes.win_line[j].code == -0x2F8 || mes.win_line[j].code == -0x2FA) {
right = mes.text_x + mes.win_line[j].x;
break;
}
}
if (g_save.x >= left && g_save.x < right && g_save.y >= top && g_save.y < top + mes.char_height) {
MenuPointerPress(line.code == -0x2F8 ? kInputCross : kInputCircle);
break;
}
}
} else if (SaveMenu.key_no == SAVE_KEY_END_SAVE || SaveMenu.key_no == SAVE_KEY_END_SAVE_ENDING ||
SaveMenu.key_no == SAVE_KEY_ALERT) {
// Informational messages have no choice: a click anywhere acknowledges them.
MenuPointerPress(kInputCross);
}
}
}
Expand Down Expand Up @@ -989,6 +1006,35 @@ bool WeaponMouse(const MenuPointerTake &take) {
}
}

// The new journal categories and tracker use their displayed row positions.
void JournalMouse(const MenuPointerTake &take) {
if ((take.clicked & 2) != 0) {
MenuPointerPress(kInputCircle);
return;
}
if (!take.pointing || (take.clicked & 1) == 0) return;
const float x = g_battle.x, y = g_battle.y;
if (ManualMenu.mode == 100) {
JournalChest::HandlePointer(x, y);
} else if (ManualMenu.mode == MANUAL_MODE_SELECT && ManualMenu.selection_level == -2) {
if (x >= 135 && x < 355 && y >= 96 && y < 236) {
ManualMenu.category = static_cast<int>((y - 96) / 28);
MenuPointerPress(kInputCross);
}
} else if (ManualMenu.mode == MANUAL_MODE_SELECT && ManualMenu.selection_level == -1) {
constexpr int counts[] = {5, 5, 4, 3};
const auto &text = CommonMenuMes3;
if (ManualMenu.category >= 0 && ManualMenu.category < 4 && text.char_height > 0 &&
x >= text.text_x && x < text.text_x + text.char_width * text.text_columns &&
y >= text.text_y && y < text.text_y + counts[ManualMenu.category] * text.char_height) {
ManualMenu.entry = static_cast<int>((y - text.text_y) / text.char_height);
MenuPointerPress(kInputCross);
}
} else if (ManualMenu.mode == MANUAL_MODE_READING && x >= 0 && x < 640 && y >= 0 && y < 480) {
MenuPointerPress(x < 160 ? kInputLeft : kInputCross);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Render the pointer on Journal reading pages

When the battle-menu Journal reaches MANUAL_MODE_READING, this splits the captured relative pointer into a 160-pixel previous region and an everywhere-else next region, but MenuManualDraw only renders the page, message, and fixed previous/next arrows; no renderer in this mode consumes MenuPointerHand. The mouse hand therefore stops following the pointer while these spatial hitboxes are active, so users cannot tell which action a click will take, especially whether they have reached the narrow previous region; render the menu pointer on reading pages.

Useful? React with 👍 / 👎.

}
}

void BattleMouse() {
if (!g_battle.IsOpen()) {
g_battle.Open();
Expand Down Expand Up @@ -1021,6 +1067,11 @@ void BattleMouse() {
case BTLMENU_STATE_CHARA:
AlliesMouse(take);
break;
case BTLMENU_STATE_MANUAL:
case BTLMENU_STATE_MANUAL_OPEN:
// Like Georama, the manual keeps its OPEN state after the slide finishes.
if (BtlEffectFlag == BTLEFFECT_NONE) JournalMouse(take);
break;
case BTLMENU_STATE_MOVE:
MoveMouse(take);
break;
Expand All @@ -1047,6 +1098,13 @@ void MenuMouseUpdate() {
bool battle = g_battle_drawn;
g_battle_drawn = false;

if (EventItemPointerUpdate()) {
g_battle.Abandon();
g_save.Abandon();
g_save_open = false;
return;
}

bool save = SaveMenu.texture_ready != 0 && SaveMenu.key_no != SAVE_KEY_FADE_OUT;
if (save) {
g_battle.Abandon();
Expand Down Expand Up @@ -1082,6 +1140,10 @@ void MenuMouseUpdate() {
}

void MenuMouseDrawOverlay() {
if ((BattleMenuFlag == BTLMENU_STATE_MANUAL || BattleMenuFlag == BTLMENU_STATE_MANUAL_OPEN) &&
ManualMenu.mode == MANUAL_MODE_READING && g_battle.IsOpen() && g_battle.pointing) {
DrawMenuObjectVibe(0, 0, 1, 0x40);
}
if (!g_discard.active) {
return;
}
Expand Down
8 changes: 6 additions & 2 deletions port/src/menu_mouse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
// The pause menu was drawn this tick, so its screen takes the mouse next tick.
void MenuMouseNoteBattleMenu();

// Draws what the mouse adds to the pause menu's own picture: the question asked before an item is
// thrown away. Called at the end of BattleMenuDraw.
/**
* Draws the Journal reading pointer and item-discard confirmation over the pause menu.
*/
void MenuMouseDrawOverlay();

// The same animated icon coordinates used by the renderer and pointer hit testing.
void BattleMenuIconPosition(int index, float &x, float &y);
Loading
Loading