1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Merge pull request #4705 from Laserlicht/adv_search

map object search
This commit is contained in:
Ivan Savenko 2024-10-14 21:51:41 +03:00 committed by GitHub
commit abf9d1017e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 153 additions and 20 deletions

View File

@ -15,6 +15,8 @@
"vcmi.adventureMap.monsterLevel" : "\n\nLevel %LEVEL %TOWN %ATTACK_TYPE unit",
"vcmi.adventureMap.monsterMeleeType" : "melee",
"vcmi.adventureMap.monsterRangedType" : "ranged",
"vcmi.adventureMap.search.hover" : "Search map object",
"vcmi.adventureMap.search.help" : "Select object to search on map.",
"vcmi.adventureMap.confirmRestartGame" : "Are you sure you want to restart the game?",
"vcmi.adventureMap.noTownWithMarket" : "There are no available marketplaces!",

View File

@ -15,6 +15,8 @@
"vcmi.adventureMap.monsterLevel" : "\n\nStufe %LEVEL %TOWN-Einheit (%ATTACK_TYPE)",
"vcmi.adventureMap.monsterMeleeType" : "Nahkampf",
"vcmi.adventureMap.monsterRangedType" : "Fernkampf",
"vcmi.adventureMap.search.hover" : "Suche Kartenobjekt",
"vcmi.adventureMap.search.help" : "Wähle Objekt das gesucht werden soll.",
"vcmi.adventureMap.confirmRestartGame" : "Seid Ihr sicher, dass Ihr das Spiel neu starten wollt?",
"vcmi.adventureMap.noTownWithMarket" : "Kein Marktplatz verfügbar!",

View File

@ -24,6 +24,7 @@
#include "../windows/CKingdomInterface.h"
#include "../windows/CSpellWindow.h"
#include "../windows/CMarketWindow.h"
#include "../windows/GUIClasses.h"
#include "../windows/settings/SettingsMainWindow.h"
#include "AdventureMapInterface.h"
#include "AdventureOptions.h"
@ -36,11 +37,14 @@
#include "../../lib/mapObjects/CGTownInstance.h"
#include "../../lib/mapping/CMap.h"
#include "../../lib/pathfinder/CGPathNode.h"
#include "../../lib/mapObjectConstructors/CObjectClassesHandler.h"
AdventureMapShortcuts::AdventureMapShortcuts(AdventureMapInterface & owner)
: owner(owner)
, state(EAdventureState::NOT_INITIALIZED)
, mapLevel(0)
, searchLast("")
, searchPos(0)
{}
void AdventureMapShortcuts::setState(EAdventureState newState)
@ -109,7 +113,9 @@ std::vector<AdventureMapShortcutState> AdventureMapShortcuts::getShortcuts()
{ EShortcut::ADVENTURE_MOVE_HERO_EE, optionHeroSelected(), [this]() { this->moveHeroDirectional({+1, 0}); } },
{ EShortcut::ADVENTURE_MOVE_HERO_NW, optionHeroSelected(), [this]() { this->moveHeroDirectional({-1, -1}); } },
{ EShortcut::ADVENTURE_MOVE_HERO_NN, optionHeroSelected(), [this]() { this->moveHeroDirectional({ 0, -1}); } },
{ EShortcut::ADVENTURE_MOVE_HERO_NE, optionHeroSelected(), [this]() { this->moveHeroDirectional({+1, -1}); } }
{ EShortcut::ADVENTURE_MOVE_HERO_NE, optionHeroSelected(), [this]() { this->moveHeroDirectional({+1, -1}); } },
{ EShortcut::ADVENTURE_SEARCH, optionSidePanelActive(),[this]() { this->search(false); } },
{ EShortcut::ADVENTURE_SEARCH_CONTINUE, optionSidePanelActive(),[this]() { this->search(true); } }
};
return result;
}
@ -457,6 +463,62 @@ void AdventureMapShortcuts::zoom( int distance)
owner.hotkeyZoom(distance, false);
}
void AdventureMapShortcuts::search(bool next)
{
// get all relevant objects
std::vector<ObjectInstanceID> visitableObjInstances;
for(auto & obj : LOCPLINT->cb->getAllVisitableObjs())
if(obj->ID != MapObjectID::MONSTER && obj->ID != MapObjectID::HERO && obj->ID != MapObjectID::TOWN)
visitableObjInstances.push_back(obj->id);
// count of elements for each group (map is already sorted)
std::map<std::string, int> mapObjCount;
for(auto & obj : visitableObjInstances)
mapObjCount[{ LOCPLINT->cb->getObjInstance(obj)->getObjectName() }]++;
// convert to vector for indexed access
std::vector<std::pair<std::string, int>> textCountList;
for (auto itr = mapObjCount.begin(); itr != mapObjCount.end(); ++itr)
textCountList.push_back(*itr);
// get pos of last selection
int lastSel = 0;
for(int i = 0; i < textCountList.size(); i++)
if(textCountList[i].first == searchLast)
lastSel = i;
// create texts
std::vector<std::string> texts;
for(auto & obj : textCountList)
texts.push_back(obj.first + " (" + std::to_string(obj.second) + ")");
// function to center element from list on map
auto selectObjOnMap = [this, textCountList, visitableObjInstances](int index)
{
auto selObj = textCountList[index].first;
// filter for matching objects
std::vector<ObjectInstanceID> selVisitableObjInstances;
for(auto & obj : visitableObjInstances)
if(selObj == LOCPLINT->cb->getObjInstance(obj)->getObjectName())
selVisitableObjInstances.push_back(obj);
if(searchPos + 1 < selVisitableObjInstances.size() && searchLast == selObj)
searchPos++;
else
searchPos = 0;
auto objInst = LOCPLINT->cb->getObjInstance(selVisitableObjInstances[searchPos]);
owner.centerOnObject(objInst);
searchLast = objInst->getObjectName();
};
if(next)
selectObjOnMap(lastSel);
else
GH.windows().createAndPushWindow<CObjectListWindow>(texts, nullptr, CGI->generaltexth->translate("vcmi.adventureMap.search.hover"), CGI->generaltexth->translate("vcmi.adventureMap.search.help"), [selectObjOnMap](int index){ selectObjOnMap(index); }, lastSel, std::vector<std::shared_ptr<IImage>>(), true);
}
void AdventureMapShortcuts::nextObject()
{
const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero();

View File

@ -33,6 +33,9 @@ class AdventureMapShortcuts
EAdventureState state;
int mapLevel;
std::string searchLast;
int searchPos;
void showOverview();
void worldViewBack();
void worldViewScale1x();
@ -71,6 +74,7 @@ class AdventureMapShortcuts
void nextTown();
void nextObject();
void zoom( int distance);
void search(bool next);
void moveHeroDirectional(const Point & direction);
public:

View File

@ -161,6 +161,8 @@ enum class EShortcut
ADVENTURE_RESTART_GAME,
ADVENTURE_TO_MAIN_MENU,
ADVENTURE_QUIT_GAME,
ADVENTURE_SEARCH,
ADVENTURE_SEARCH_CONTINUE,
// Move hero one tile in specified direction. Bound to cursors & numpad buttons
ADVENTURE_MOVE_HERO_SW,

View File

@ -209,6 +209,8 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const
{"adventureZoomIn", EShortcut::ADVENTURE_ZOOM_IN },
{"adventureZoomOut", EShortcut::ADVENTURE_ZOOM_OUT },
{"adventureZoomReset", EShortcut::ADVENTURE_ZOOM_RESET },
{"adventureSearch", EShortcut::ADVENTURE_SEARCH },
{"adventureSearchContinue", EShortcut::ADVENTURE_SEARCH_CONTINUE },
{"battleToggleHeroesStats", EShortcut::BATTLE_TOGGLE_HEROES_STATS},
{"battleToggleQueue", EShortcut::BATTLE_TOGGLE_QUEUE },
{"battleUseCreatureSpell", EShortcut::BATTLE_USE_CREATURE_SPELL },

View File

@ -185,6 +185,9 @@ void CListBox::scrollTo(size_t which)
//scroll down
else if (first + items.size() <= which && which < totalSize)
moveToPos(which - items.size() + 1);
if(slider)
slider->scrollTo(which);
}
void CListBox::moveToPos(size_t which)

View File

@ -1480,39 +1480,47 @@ void CObjectListWindow::CItem::showPopupWindow(const Point & cursorPosition)
parent->onPopup(index);
}
CObjectListWindow::CObjectListWindow(const std::vector<int> & _items, std::shared_ptr<CIntObject> titleWidget_, std::string _title, std::string _descr, std::function<void(int)> Callback, size_t initialSelection, std::vector<std::shared_ptr<IImage>> images)
CObjectListWindow::CObjectListWindow(const std::vector<int> & _items, std::shared_ptr<CIntObject> titleWidget_, std::string _title, std::string _descr, std::function<void(int)> Callback, size_t initialSelection, std::vector<std::shared_ptr<IImage>> images, bool searchBoxEnabled)
: CWindowObject(PLAYER_COLORED, ImagePath::builtin("TPGATE")),
onSelect(Callback),
selected(initialSelection),
images(images)
{
OBJECT_CONSTRUCTION;
addUsedEvents(KEYBOARD);
items.reserve(_items.size());
for(int id : _items)
{
items.push_back(std::make_pair(id, LOCPLINT->cb->getObjInstance(ObjectInstanceID(id))->getObjectName()));
}
itemsVisible = items;
init(titleWidget_, _title, _descr);
init(titleWidget_, _title, _descr, searchBoxEnabled);
list->scrollTo(initialSelection - 4); // -4 is for centering (list have 9 elements)
}
CObjectListWindow::CObjectListWindow(const std::vector<std::string> & _items, std::shared_ptr<CIntObject> titleWidget_, std::string _title, std::string _descr, std::function<void(int)> Callback, size_t initialSelection, std::vector<std::shared_ptr<IImage>> images)
CObjectListWindow::CObjectListWindow(const std::vector<std::string> & _items, std::shared_ptr<CIntObject> titleWidget_, std::string _title, std::string _descr, std::function<void(int)> Callback, size_t initialSelection, std::vector<std::shared_ptr<IImage>> images, bool searchBoxEnabled)
: CWindowObject(PLAYER_COLORED, ImagePath::builtin("TPGATE")),
onSelect(Callback),
selected(initialSelection),
images(images)
{
OBJECT_CONSTRUCTION;
addUsedEvents(KEYBOARD);
items.reserve(_items.size());
for(size_t i=0; i<_items.size(); i++)
items.push_back(std::make_pair(int(i), _items[i]));
itemsVisible = items;
init(titleWidget_, _title, _descr);
init(titleWidget_, _title, _descr, searchBoxEnabled);
list->scrollTo(initialSelection - 4); // -4 is for centering (list have 9 elements)
}
void CObjectListWindow::init(std::shared_ptr<CIntObject> titleWidget_, std::string _title, std::string _descr)
void CObjectListWindow::init(std::shared_ptr<CIntObject> titleWidget_, std::string _title, std::string _descr, bool searchBoxEnabled)
{
titleWidget = titleWidget_;
@ -1527,24 +1535,51 @@ void CObjectListWindow::init(std::shared_ptr<CIntObject> titleWidget_, std::stri
titleWidget->pos.y =75 + pos.y - titleWidget->pos.h/2;
}
list = std::make_shared<CListBox>(std::bind(&CObjectListWindow::genItem, this, _1),
Point(14, 151), Point(0, 25), 9, items.size(), 0, 1, Rect(262, -32, 256, 256) );
Point(14, 151), Point(0, 25), 9, itemsVisible.size(), 0, 1, Rect(262, -32, 256, 256) );
list->setRedrawParent(true);
ok = std::make_shared<CButton>(Point(15, 402), AnimationPath::builtin("IOKAY.DEF"), CButton::tooltip(), std::bind(&CObjectListWindow::elementSelected, this), EShortcut::GLOBAL_ACCEPT);
ok->block(!list->size());
if(!searchBoxEnabled)
return;
Rect r(50, 90, pos.w - 100, 16);
const ColorRGBA rectangleColor = ColorRGBA(0, 0, 0, 75);
const ColorRGBA borderColor = ColorRGBA(128, 100, 75);
const ColorRGBA grayedColor = ColorRGBA(158, 130, 105);
searchBoxRectangle = std::make_shared<TransparentFilledRectangle>(r.resize(1), rectangleColor, borderColor);
searchBoxDescription = std::make_shared<CLabel>(r.center().x, r.center().y, FONT_SMALL, ETextAlignment::CENTER, grayedColor, CGI->generaltexth->translate("vcmi.spellBook.search"));
searchBox = std::make_shared<CTextInput>(r, FONT_SMALL, ETextAlignment::CENTER, true);
searchBox->setCallback([this](const std::string & text){
searchBoxDescription->setEnabled(text.empty());
itemsVisible.clear();
for(auto & item : items)
if(boost::algorithm::contains(boost::algorithm::to_lower_copy(item.second), boost::algorithm::to_lower_copy(text)))
itemsVisible.push_back(item);
selected = 0;
list->resize(itemsVisible.size());
list->scrollTo(0);
ok->block(!itemsVisible.size());
redraw();
});
}
std::shared_ptr<CIntObject> CObjectListWindow::genItem(size_t index)
{
if(index < items.size())
return std::make_shared<CItem>(this, index, items[index].second);
if(index < itemsVisible.size())
return std::make_shared<CItem>(this, index, itemsVisible[index].second);
return std::shared_ptr<CIntObject>();
}
void CObjectListWindow::elementSelected()
{
std::function<void(int)> toCall = onSelect;//save
int where = items[selected].first; //required variables
int where = itemsVisible[selected].first; //required variables
close();//then destroy window
toCall(where);//and send selected object
}
@ -1600,14 +1635,14 @@ void CObjectListWindow::keyPressed(EShortcut key)
sel = 0;
break; case EShortcut::MOVE_LAST:
sel = static_cast<int>(items.size());
sel = static_cast<int>(itemsVisible.size());
break; default:
return;
}
vstd::abetween<int>(sel, 0, items.size()-1);
list->scrollTo(sel);
vstd::abetween<int>(sel, 0, itemsVisible.size()-1);
list->scrollTo(sel - 4); // -4 is for centering (list have 9 elements)
changeSelection(sel);
}

View File

@ -45,6 +45,7 @@ class IImage;
class VideoWidget;
class VideoWidgetOnce;
class GraphicalPrimitiveCanvas;
class TransparentFilledRectangle;
enum class EUserEvent;
@ -186,9 +187,14 @@ class CObjectListWindow : public CWindowObject
std::shared_ptr<CButton> ok;
std::shared_ptr<CButton> exit;
std::vector< std::pair<int, std::string> > items;//all items present in list
std::shared_ptr<CTextInput> searchBox;
std::shared_ptr<TransparentFilledRectangle> searchBoxRectangle;
std::shared_ptr<CLabel> searchBoxDescription;
void init(std::shared_ptr<CIntObject> titleWidget_, std::string _title, std::string _descr);
std::vector< std::pair<int, std::string> > items; //all items present in list
std::vector< std::pair<int, std::string> > itemsVisible; //visible items present in list
void init(std::shared_ptr<CIntObject> titleWidget_, std::string _title, std::string _descr, bool searchBoxEnabled);
void exitPressed();
public:
size_t selected;//index of currently selected item
@ -200,8 +206,8 @@ public:
/// Callback will be called when OK button is pressed, returns id of selected item. initState = initially selected item
/// Image can be nullptr
///item names will be taken from map objects
CObjectListWindow(const std::vector<int> &_items, std::shared_ptr<CIntObject> titleWidget_, std::string _title, std::string _descr, std::function<void(int)> Callback, size_t initialSelection = 0, std::vector<std::shared_ptr<IImage>> images = {});
CObjectListWindow(const std::vector<std::string> &_items, std::shared_ptr<CIntObject> titleWidget_, std::string _title, std::string _descr, std::function<void(int)> Callback, size_t initialSelection = 0, std::vector<std::shared_ptr<IImage>> images = {});
CObjectListWindow(const std::vector<int> &_items, std::shared_ptr<CIntObject> titleWidget_, std::string _title, std::string _descr, std::function<void(int)> Callback, size_t initialSelection = 0, std::vector<std::shared_ptr<IImage>> images = {}, bool searchBoxEnabled = false);
CObjectListWindow(const std::vector<std::string> &_items, std::shared_ptr<CIntObject> titleWidget_, std::string _title, std::string _descr, std::function<void(int)> Callback, size_t initialSelection = 0, std::vector<std::shared_ptr<IImage>> images = {}, bool searchBoxEnabled = false);
std::shared_ptr<CIntObject> genItem(size_t index);
void elementSelected();//call callback and close this window

View File

@ -56,6 +56,8 @@
"adventureZoomIn": "Keypad +",
"adventureZoomOut": "Keypad -",
"adventureZoomReset": "Backspace",
"adventureSearch": "Ctrl+F",
"adventureSearchContinue": "Alt+F",
"battleAutocombat": "A",
"battleAutocombatEnd": "Q",
"battleCastSpell": "C",

View File

@ -479,6 +479,17 @@ std::vector <const CGObjectInstance *> CGameInfoCallback::getVisitableObjs(int3
return ret;
}
std::vector<ConstTransitivePtr<CGObjectInstance>> CGameInfoCallback::getAllVisitableObjs() const
{
std::vector<ConstTransitivePtr<CGObjectInstance>> ret;
for(auto & obj : gs->map->objects)
if(obj->isVisitable() && obj->ID != Obj::EVENT && isVisible(obj))
ret.push_back(obj);
return ret;
}
const CGObjectInstance * CGameInfoCallback::getTopObj (int3 pos) const
{
return vstd::backOrNull(getVisitableObjs(pos));

View File

@ -11,6 +11,7 @@
#include "int3.h"
#include "ResourceSet.h" // for Res
#include "ConstTransitivePtr.h"
#define ASSERT_IF_CALLED_WITH_PLAYER if(!getPlayerID()) {logGlobal->error(BOOST_CURRENT_FUNCTION); assert(0);}
@ -189,6 +190,7 @@ public:
const CGObjectInstance * getObj(ObjectInstanceID objid, bool verbose = true) const override;
virtual std::vector <const CGObjectInstance * > getBlockingObjs(int3 pos)const;
std::vector <const CGObjectInstance * > getVisitableObjs(int3 pos, bool verbose = true) const override;
std::vector<ConstTransitivePtr<CGObjectInstance>> getAllVisitableObjs() const;
virtual std::vector <const CGObjectInstance * > getFlaggableObjects(int3 pos) const;
virtual const CGObjectInstance * getTopObj (int3 pos) const;
virtual PlayerColor getOwner(ObjectInstanceID heroID) const;

View File

@ -358,7 +358,7 @@ TObjectTypeHandler CObjectClassesHandler::getHandlerFor(MapObjectID type, MapObj
return mapObjectTypes.front()->objectTypeHandlers.front();
auto subID = subtype.getNum();
if (type == Obj::PRISON || type == Obj::HERO_PLACEHOLDER)
if (type == Obj::PRISON || type == Obj::HERO_PLACEHOLDER || type == Obj::SPELL_SCROLL)
subID = 0;
auto result = mapObjectTypes.at(type.getNum())->objectTypeHandlers.at(subID);