From 99688bdfc458ef0ea90a2c1b1f266e1d925e1434 Mon Sep 17 00:00:00 2001 From: Ayrton Denner Date: Mon, 2 Mar 2026 16:03:54 -0300 Subject: [PATCH] Right-click inventory item opens UFOpaedia entry In the original X-COM: Apocalypse, right-clicking an item in the equip screen opens its UFOpaedia entry. This restores that behavior for both the agent and vehicle equipment screens. Resolves OpenApoc/OpenApoc#1571 Closes ayrtondenner/OpenApoc#8 Co-Authored-By: Claude Opus 4.6 --- game/ui/base/vequipscreen.cpp | 64 ++++++++++++++++++++++++- game/ui/general/aequipscreen.cpp | 80 ++++++++++++++++++++++++++++++-- 2 files changed, 139 insertions(+), 5 deletions(-) diff --git a/game/ui/base/vequipscreen.cpp b/game/ui/base/vequipscreen.cpp index 66e5ee5db..e4ce39bda 100644 --- a/game/ui/base/vequipscreen.cpp +++ b/game/ui/base/vequipscreen.cpp @@ -18,10 +18,12 @@ #include "game/state/city/vehicle.h" #include "game/state/city/vequipment.h" #include "game/state/gamestate.h" +#include "game/state/rules/city/ufopaedia.h" #include "game/state/rules/city/vehicletype.h" #include "game/ui/components/equipscreen.h" #include "game/ui/general/messagebox.h" #include "game/ui/general/vehiclesheet.h" +#include "game/ui/ufopaedia/ufopaediacategoryview.h" #include "library/strings_format.h" namespace OpenApoc @@ -281,8 +283,68 @@ void VEquipScreen::eventOccurred(Event *e) // Find the base this vehicle is landed in StateRef base = selected->currentBuilding ? selected->currentBuilding->base : nullptr; + // Right-click: open UFOpaedia entry for the item under cursor + if (e->type() == EVENT_MOUSE_DOWN && + Event::isPressed(e->mouse().Button, Event::MouseButton::Right) && !this->draggedEquipment) + { + Vec2 mousePos{e->mouse().X, e->mouse().Y}; + StateRef clickedType; + + // Check if we're over any equipment in the paper doll + auto mouseSlotPos = this->paperDoll->getSlotPositionFromScreenPosition(mousePos); + auto equipment = + std::dynamic_pointer_cast(this->selected->getEquipmentAt(mouseSlotPos)); + if (equipment) + { + clickedType = equipment->type; + } + else + { + // Check if we're over any equipment in the inventory bar + for (auto &pair : this->inventoryItems) + { + if (pair.first.within(mousePos)) + { + clickedType = pair.second; + break; + } + } + } + + if (clickedType) + { + sp ufopaediaCategory; + sp ufopaediaEntry; + for (auto &cat : state->ufopaedia) + { + for (auto &entry : cat.second->entries) + { + if (entry.second->data_type == UfopaediaEntry::Data::VehicleEquipment && + entry.second->data_id == clickedType.id) + { + ufopaediaEntry = entry.second; + ufopaediaCategory = cat.second; + break; + } + } + if (ufopaediaCategory) + { + break; + } + } + if (ufopaediaEntry && ufopaediaEntry->dependency.satisfied()) + { + fw().stageQueueCommand( + {StageCmd::Command::PUSH, + mksp(state, ufopaediaCategory, ufopaediaEntry)}); + } + } + return; + } + // Only allow removing equipment if we're in a base, otherwise it'll disappear - if (e->type() == EVENT_MOUSE_DOWN && base) + if (e->type() == EVENT_MOUSE_DOWN && + Event::isPressed(e->mouse().Button, Event::MouseButton::Left) && base) { Vec2 mousePos{e->mouse().X, e->mouse().Y}; diff --git a/game/ui/general/aequipscreen.cpp b/game/ui/general/aequipscreen.cpp index dba972a65..84d982e2c 100644 --- a/game/ui/general/aequipscreen.cpp +++ b/game/ui/general/aequipscreen.cpp @@ -21,6 +21,7 @@ #include "game/state/city/city.h" #include "game/state/city/vehicle.h" #include "game/state/gamestate.h" +#include "game/state/rules/city/ufopaedia.h" #include "game/state/shared/aequipment.h" #include "game/state/shared/agent.h" #include "game/state/tilemap/tileobject_battleunit.h" @@ -30,6 +31,7 @@ #include "game/ui/general/aequipmentsheet.h" #include "game/ui/general/agentsheet.h" #include "game/ui/general/messagebox.h" +#include "game/ui/ufopaedia/ufopaediacategoryview.h" #include namespace OpenApoc @@ -443,17 +445,87 @@ void AEquipScreen::eventOccurred(Event *e) // Item manipulation if (currentAgent->type->inventory && getMode() != Mode::Enemy) { - // Picking up items - if (e->type() == EVENT_MOUSE_DOWN && !this->draggedEquipment) + // Picking up items (left-click) + if (e->type() == EVENT_MOUSE_DOWN && !this->draggedEquipment && + Event::isPressed(e->mouse().Button, Event::MouseButton::Left)) { handleItemPickup({e->mouse().X, e->mouse().Y}); } - // Placing items - if (e->type() == EVENT_MOUSE_UP && draggedEquipment) + // Placing items (left-click) + if (e->type() == EVENT_MOUSE_UP && draggedEquipment && + Event::isPressed(e->mouse().Button, Event::MouseButton::Left)) { handleItemPlacement({e->mouse().X, e->mouse().Y}); } + + // Right-click: open UFOpaedia entry for the item under cursor + if (e->type() == EVENT_MOUSE_DOWN && !this->draggedEquipment && + Event::isPressed(e->mouse().Button, Event::MouseButton::Right)) + { + Vec2 mousePos{e->mouse().X, e->mouse().Y}; + sp clickedItem; + + // Check if we're over any equipment in the paper doll + auto mouseSlotPos = this->paperDoll->getSlotPositionFromScreenPosition(mousePos); + auto equipment = + std::dynamic_pointer_cast(currentAgent->getEquipmentAt(mouseSlotPos)); + if (equipment) + { + clickedItem = equipment; + } + else + { + // Check if we're over any equipment in the inventory bar + auto posWithinInventory = mousePos; + posWithinInventory.x += inventoryPage * inventoryControl->Size.x; + for (auto &tuple : this->inventoryItems) + { + if (std::get<0>(tuple).within(posWithinInventory)) + { + auto pos = std::get<0>(tuple).p0; + pos.x -= inventoryPage * inventoryControl->Size.x; + if (pos.x >= inventoryControl->Location.x + formMain->Location.x && + pos.x < inventoryControl->Location.x + inventoryControl->Size.x + + formMain->Location.x) + { + clickedItem = std::get<2>(tuple); + } + break; + } + } + } + + if (clickedItem) + { + const auto &itemTypeId = clickedItem->type.id; + sp ufopaediaCategory; + sp ufopaediaEntry; + for (auto &cat : state->ufopaedia) + { + for (auto &entry : cat.second->entries) + { + if (entry.second->data_type == UfopaediaEntry::Data::Equipment && + entry.second->data_id == itemTypeId) + { + ufopaediaEntry = entry.second; + ufopaediaCategory = cat.second; + break; + } + } + if (ufopaediaCategory) + { + break; + } + } + if (ufopaediaEntry && ufopaediaEntry->dependency.satisfied()) + { + fw().stageQueueCommand( + {StageCmd::Command::PUSH, + mksp(state, ufopaediaCategory, ufopaediaEntry)}); + } + } + } } }