diff --git a/code/ryzom/client/src/interface_v3/action_handler_game.cpp b/code/ryzom/client/src/interface_v3/action_handler_game.cpp index 3ad721809..73b15a1d8 100644 --- a/code/ryzom/client/src/interface_v3/action_handler_game.cpp +++ b/code/ryzom/client/src/interface_v3/action_handler_game.cpp @@ -2493,6 +2493,33 @@ class CAHTarget : public IActionHandler }; REGISTER_ACTION_HANDLER (CAHTarget, "target"); +// *************************************************************************** +class CAHTargetLandmark : public IActionHandler +{ + virtual void execute (CCtrlBase * /* pCaller */, const string &Params) + { + string search = getParam(Params, "search"); + if (search.empty()) return; + + bool startsWith = false; + if (search.size() > 0 && (search[0] == '\'' || search[0] == '"') && search[0] == search[search.size()-1]) + { + startsWith = true; + search = trimQuotes(search); + } + + const std::string mapid = "ui:interface:map:content:map_content:actual_map"; + CGroupMap* cgMap = dynamic_cast(CWidgetManager::getInstance()->getElementFromId(mapid)); + if (cgMap) + { + if (!cgMap->targetLandmarkByName(search, startsWith)) + { + CInterfaceManager::getInstance()->displaySystemInfo(CI18N::get("uiTargetErrorCmd")); + } + } + } +}; +REGISTER_ACTION_HANDLER (CAHTargetLandmark, "target_landmark"); class CAHAddShape : public IActionHandler diff --git a/code/ryzom/client/src/interface_v3/group_map.cpp b/code/ryzom/client/src/interface_v3/group_map.cpp index e9864b61b..87dbb38f5 100644 --- a/code/ryzom/client/src/interface_v3/group_map.cpp +++ b/code/ryzom/client/src/interface_v3/group_map.cpp @@ -100,6 +100,12 @@ const uint32 ISLAND_PIXEL_PER_METER = 2; static void setupFromZoom(CViewBase *pVB, CContLandMark::TContLMType t, float fMeterPerPixel); +// calculate distance (squared) between two points +static float distsqr(const CVector2f a, const CVector2f b) +{ + return pow(a.x - b.x, 2) + pow(a.y - b.y, 2); +} + // popup the landmark name dialog static void popupLandMarkNameDialog() @@ -2671,21 +2677,38 @@ void CGroupMap::updateLandMarkButton(CLandMarkButton *lmb, const CLandMarkOption } //============================================================================================================ -bool CGroupMap::filterLandmark(const ucstring &title) const +bool CGroupMap::filterLandmark(const ucstring &title, const std::vector filter, bool startsWith) const { - if (_LandmarkFilter.size() > 0) + if (filter.size() > 0) { ucstring lcTitle = toLower(title); - for(uint i = 0; i< _LandmarkFilter.size(); ++i) { - if (lcTitle.find(_LandmarkFilter[i]) == ucstring::npos) { + if (startsWith) + { + if (lcTitle.find(filter[0]) != 0) + { return false; } } + else + { + for(uint i = 0; i< filter.size(); ++i) + { + if (lcTitle.find(filter[i]) == ucstring::npos) + { + return false; + } + } + } } - return true; } +//============================================================================================================ +bool CGroupMap::filterLandmark(const ucstring &title) const +{ + return filterLandmark(title, _LandmarkFilter); +} + //============================================================================================================ void CGroupMap::addLandMark(TLandMarkButtonVect &destList, const NLMISC::CVector2f &pos, const ucstring &title, const CLandMarkOptions &options) { @@ -3186,6 +3209,129 @@ void CGroupMap::targetLandmarkResult(uint32 index) } } +//========================================================================================================= +CGroupMap::CLandMarkButton* CGroupMap::findClosestLandmark(const CVector2f ¢er, const ucstring &search, bool startsWith, const TLandMarkButtonVect &landmarks, float &closest) const +{ + CLandMarkButton *ret = NULL; + + std::vector keywords; + if (startsWith) + keywords.push_back(search); + else + splitUCString(toLower(search), ucstring(" "), keywords); + + closest = std::numeric_limits::max(); + for(TLandMarkButtonVect::const_iterator it = landmarks.begin(); it != landmarks.end(); ++it) + { + ucstring lc; + (*it)->getContextHelp(lc); + if(filterLandmark(lc, keywords, startsWith)) { + CVector2f pos; + mapToWorld(pos, (*it)->Pos); + float dist = distsqr(center, pos); + if (dist < closest) + { + ret = (*it); + closest = dist; + } + } + } + + return ret; +} + +//========================================================================================================= +CGroupMap::CLandMarkText* CGroupMap::findClosestLandmark(const CVector2f ¢er, const ucstring &search, bool startsWith, const TLandMarkTextVect &landmarks, float &closest) const +{ + CLandMarkText *ret = NULL; + + std::vector keywords; + if (startsWith) + keywords.push_back(search); + else + splitUCString(toLower(search), ucstring(" "), keywords); + + closest = std::numeric_limits::max(); + for(TLandMarkTextVect::const_iterator it = landmarks.begin(); it != landmarks.end(); ++it) + { + ucstring lc; + lc = (*it)->getText(); + if(filterLandmark(lc, keywords, startsWith)) { + CVector2f pos; + mapToWorld(pos, (*it)->Pos); + float dist = distsqr(center, pos); + if (dist < closest) + { + ret = (*it); + closest = dist; + } + } + } + + return ret; +} + +bool CGroupMap::targetLandmarkByName(const ucstring &search, bool startsWith) const +{ + CCompassTarget ct; + CLandMarkButton* lm; + float dist; + float closest = std::numeric_limits::max(); + bool found = false; + CVector2f center; + mapToWorld(center, _PlayerPos); + + lm = findClosestLandmark(center, search, startsWith, _UserLM, dist); + if (lm && dist < closest) + { + ct.setType(CCompassTarget::UserLandMark); + mapToWorld(ct.Pos, lm->Pos); + lm->getContextHelp(ct.Name); + closest = dist; + found = true; + } + + // only check other types if user landmark was not found + if (!found) + { + lm = findClosestLandmark(center, search, startsWith, _ContinentLM, dist); + if (lm && dist < closest) + { + ct.setType(CCompassTarget::ContinentLandMark); + mapToWorld(ct.Pos, lm->Pos); + lm->getContextHelp(ct.Name); + closest = dist; + found = true; + } + + CLandMarkText* lmt; + lmt = findClosestLandmark(center, search, startsWith, _ContinentText, dist); + if (lmt && dist < closest) + { + ct.setType(CCompassTarget::ContinentLandMark); + mapToWorld(ct.Pos, lmt->Pos); + ct.Name = lmt->getText(); + closest = dist; + found = true; + } + } + + if (found) + { + CInterfaceManager *im = CInterfaceManager::getInstance(); + CGroupCompas *gc = dynamic_cast(CWidgetManager::getInstance()->getElementFromId(_CompassId)); + if (gc) + { + gc->setActive(true); + gc->setTarget(ct); + gc->blink(); + CWidgetManager::getInstance()->setTopWindow(gc); + } + } + + return found; +} + //========================================================================================================= void CGroupMap::getLandmarkPosition(const CCtrlButton *lm, NLMISC::CVector2f &worldPos) { diff --git a/code/ryzom/client/src/interface_v3/group_map.h b/code/ryzom/client/src/interface_v3/group_map.h index baf273779..ebfde5c36 100644 --- a/code/ryzom/client/src/interface_v3/group_map.h +++ b/code/ryzom/client/src/interface_v3/group_map.h @@ -192,6 +192,8 @@ public: // target the given landmark void targetLandmark(CCtrlButton *lm); void targetLandmarkResult(uint32 index); + // search matching landmark and target it. return true if landmark was targeted + bool targetLandmarkByName(const ucstring &search, bool startsWith) const; // get the world position of a landmark or return vector Null if not found void getLandmarkPosition(const CCtrlButton *lm, NLMISC::CVector2f &worldPos); @@ -551,6 +553,12 @@ private: // Test title against landmark filter bool filterLandmark(const ucstring &title) const; + bool filterLandmark(const ucstring &title, const std::vector filter, bool startsWith = false) const; + + // return closest landmark which matches (case insensitive) search string + // center position must be in world coordindates + CLandMarkButton* findClosestLandmark(const NLMISC::CVector2f ¢er, const ucstring &search, bool startsWith, const TLandMarkButtonVect &landmarks, float &closest) const; + CLandMarkText* findClosestLandmark(const NLMISC::CVector2f ¢er, const ucstring &search, bool startsWith, const TLandMarkTextVect &landmarks, float &closest) const; // update the scale depending on the window size and the user scale void updateScale();