diff --git a/code/ryzom/client/data/gamedev/interfaces_v3/commands.xml b/code/ryzom/client/data/gamedev/interfaces_v3/commands.xml index 13356083c..e236e43c8 100644 --- a/code/ryzom/client/data/gamedev/interfaces_v3/commands.xml +++ b/code/ryzom/client/data/gamedev/interfaces_v3/commands.xml @@ -78,17 +78,11 @@ - - + + - - - - - - - - + + diff --git a/code/ryzom/client/src/entities.cpp b/code/ryzom/client/src/entities.cpp index 7d16e51dc..495471da1 100644 --- a/code/ryzom/client/src/entities.cpp +++ b/code/ryzom/client/src/entities.cpp @@ -2334,7 +2334,63 @@ CEntityCL *CEntityManager::getEntityByName (uint32 stringId) const } //----------------------------------------------- +CEntityCL *CEntityManager::getEntityByKeywords (const std::vector &keywords, bool onlySelectable) const +{ + if (keywords.empty()) return NULL; + std::vector lcKeywords; + lcKeywords.resize(keywords.size()); + for(uint k = 0; k < keywords.size(); k++) + { + lcKeywords[k] = toLower(keywords[k]); + } + + const NLMISC::CVectorD &userPosD = UserEntity->pos(); + const uint count = (uint)_Entities.size(); + uint selectedEntityId = 0; + float selectedEntityDist = FLT_MAX; + for(uint i = 0; i < count; ++i) + { + if (!_Entities[i]) continue; + + if (onlySelectable && !_Entities[i]->properties().selectable()) continue; + + ucstring lcName; + lcName = toLower(_Entities[i]->getDisplayName()); + if (lcName.empty()) continue; + + bool match = true; + for (uint k = 0; k < lcKeywords.size(); ++k) + { + if (lcName.find(lcKeywords[k]) == ucstring::npos) + { + match = false; + break; + } + } + + if (match) + { + const NLMISC::CVectorD &targetPosD = _Entities[i]->pos(); + + float deltaX = (float) targetPosD.x - (float) userPosD.x; + float deltaY = (float) targetPosD.y - (float) userPosD.y; + float dist = (float)sqrt(deltaX * deltaX + deltaY * deltaY); + if (dist < selectedEntityDist) + { + selectedEntityDist = dist; + selectedEntityId = i; + } + } + } + + if (selectedEntityDist != FLT_MAX) + return _Entities[selectedEntityId]; + else + return NULL; +} + +//----------------------------------------------- CEntityCL *CEntityManager::getEntityByName (const ucstring &name, bool caseSensitive, bool complete) const { ucstring source = name; diff --git a/code/ryzom/client/src/entities.h b/code/ryzom/client/src/entities.h index fb8504fc1..1151a367d 100644 --- a/code/ryzom/client/src/entities.h +++ b/code/ryzom/client/src/entities.h @@ -302,6 +302,12 @@ public: * \param complete : if true, the name must match the full name of the entity. */ CEntityCL *getEntityByName (const ucstring &name, bool caseSensitive, bool complete) const; + /** + * Case insensitive match against entity name. All listed keywords must match. + * \param keywords to match + * \param onlySelectable : if true, match only entity that can be selected + */ + CEntityCL *getEntityByKeywords (const std::vector &keywords, bool onlySelectable) const; CEntityCL *getEntityBySheetName (const std::string &sheet) const; /// Get an entity by dataset index. Returns NULL if the entity is not found. CEntityCL *getEntityByCompressedIndex(TDataSetIndex compressedIndex) const; 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 c5faa16d9..2f91cb9a5 100644 --- a/code/ryzom/client/src/interface_v3/action_handler_game.cpp +++ b/code/ryzom/client/src/interface_v3/action_handler_game.cpp @@ -2416,70 +2416,66 @@ class CAHTarget : public IActionHandler { virtual void execute (CCtrlBase * /* pCaller */, const string &Params) { - // Get the entity name to target ucstring entityName; - entityName.fromUtf8 (getParam (Params, "entity")); - bool preferCompleteMatch = (getParam (Params, "prefer_complete_match") != "0"); + entityName.fromUtf8(getParam(Params, "entity")); + if (entityName.empty()) return; + + string completeMatch = getParam(Params, "prefer_complete_match"); bool quiet = (getParam (Params, "quiet") == "true"); - if (!entityName.empty()) + vector keywords; + NLMISC::splitUCString(entityName, ucstring(" "), keywords); + if (!keywords.empty() && keywords[0].size() > 0 && keywords[0][0] == (ucchar)'"') { - CEntityCL *entity = NULL; - if (preferCompleteMatch) - { - // Try to get the entity with complete match first - entity = EntitiesMngr.getEntityByName (entityName, false, true); - } - - if (entity == NULL) - { - // Get the entity with a partial match - entity = EntitiesMngr.getEntityByName (entityName, false, false); - } + // entity name is in quotes, do old style match with 'starts with' filter + // search for optional second parameter from old command for prefer_complete_match param + keywords.clear(); - if (entity == NULL) - { - //Get the entity with a sheetName - entity = EntitiesMngr.getEntityBySheetName(entityName.toUtf8()); - } - - if (entity) - { - CCharacterCL *character = dynamic_cast(entity); - if (character != NULL) - { - if(character->isSelectableBySpace()) - { - nldebug("isSelectableBySpace"); - } - else - { - nldebug("is not isSelectableBySpace"); - } - } - if(entity->properties().selectable()) - { - nldebug("is prop selectable"); - } - else - { - // to avoid campfire selection exploit #316 - nldebug("is not prop selectable"); - CInterfaceManager *pIM= CInterfaceManager::getInstance(); - if(!quiet) - pIM->displaySystemInfo(CI18N::get("uiTargetErrorCmd")); - return; - } + ucstring::size_type lastOf = entityName.rfind(ucstring("\"")); + if (lastOf == 0) + lastOf = ucstring::npos; - // Select the entity - UserEntity->selection(entity->slot()); - } - else - { - CInterfaceManager *pIM= CInterfaceManager::getInstance(); - if(!quiet) - pIM->displaySystemInfo(CI18N::get("uiTargetErrorCmd")); - } + // override the value only when there is no 'prefer_complete_match' parameter set + if (completeMatch.empty() && lastOf < entityName.size()) + completeMatch = trim(entityName.substr(lastOf+1).toUtf8()); + + entityName = entityName.substr(1, lastOf-1); + } + + // late check because only possible if doing 'starts-with' search + bool preferCompleteMatch = (completeMatch != "0"); + + CEntityCL *entity = NULL; + if (preferCompleteMatch) + { + // Try to get the entity with complete match first + entity = EntitiesMngr.getEntityByName (entityName, false, true); + } + + if (entity == NULL && !keywords.empty()) + { + entity = EntitiesMngr.getEntityByKeywords(keywords, true); + } + + if (entity == NULL) + { + // Get the entity with a partial match using 'starts with' search + entity = EntitiesMngr.getEntityByName(entityName, false, false); + } + + if (entity == NULL) + { + //Get the entity with a sheetName + entity = EntitiesMngr.getEntityBySheetName(entityName.toUtf8()); + } + + if (entity && entity->properties().selectable() && !entity->getDisplayName().empty()) + { + UserEntity->selection(entity->slot()); + } + else if (!quiet) + { + CInterfaceManager::getInstance()->displaySystemInfo(CI18N::get("uiTargetErrorCmd")); } } };