// Ryzom - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "stdpch.h" #include "actions.h" #include "events_listener.h" #include "interface_v3/interface_manager.h" #ifdef DEBUG_NEW #define new DEBUG_NEW #endif using namespace std; using namespace NLMISC; extern CEventsListener EventsListener; //////////// // GLOBAL // //////////// // Hierarchical timer H_AUTO_DECL ( RZ_Client_Actions_Context_Mngr_Update ) static bool getParam (CBaseAction::CParameter::TType type, ucstring ¶mName, ucstring ¶mValue, const std::string &argu, uint paramId); ///////////////////////////////////////////////////// ///////////////////////////////////////////////////// // CAction ////////////////////////////////////////// ///////////////////////////////////////////////////// ///////////////////////////////////////////////////// //--------------------------------------------------- // CAction : // Constructor //--------------------------------------------------- CAction::CAction() { Valide = false; Repeat = false; KeyDown = true; KeyUp = false; }// CAction // void CAction::runAction () { CInterfaceManager *IM = CInterfaceManager::getInstance (); if (IM) { CAHManager::getInstance()->runActionHandler (Name.Name, NULL, Name.Argu); } } ///////////////////////////////////////////////////// ///////////////////////////////////////////////////// // CActionsManager ////////////////////////////////// ///////////////////////////////////////////////////// ///////////////////////////////////////////////////// //--------------------------------------------------- // CActionsManager : // Constructor //--------------------------------------------------- CActionsManager::CActionsManager() { clear (); _Enabled = false; }// CActionsManager // //--------------------------------------------------- // addAction : // Add a new Action in the context. //--------------------------------------------------- bool CActionsManager::addAction(const CAction::CName &name) { // Try to allocate memory for action CAction action; action.Name = name; // Find the base action const CBaseAction *baseAction = getBaseAction(name); if (baseAction) { // Copy the repeat flag action.Repeat = baseAction->Repeat; action.KeyUp = baseAction->KeyUp; action.KeyDown = baseAction->KeyDown; } // Try to insert the new action. pair p = _Actions.insert(TActionsMap::value_type(name, action)); if (!p.second) { nlwarning ("Action (%s %s) already exist in the action manager.", name.Name.c_str (), name.Argu.c_str ()); } return p.second; }// addAction // // *************************************************************************** CAction* CActionsManager::getAction(const CAction::CName &name) { TActionsMap::iterator it = _Actions.find(name); if (it == _Actions.end()) return NULL; return &(it->second); } // *************************************************************************** void CActionsManager::clear () { _Actions.clear (); _ActionForceDisplay.clear (); _ActionCombo.clear (); _ComboAction.clear (); _KeyAction.clear (); _WatchedActions.clear (); _ActionCategory.clear (); _Categories.clear (); } // *************************************************************************** void CActionsManager::removeCombo (const CCombo &combo) { // Get the old action of the combo TComboActionMap::iterator itePreviousCombo = _ComboAction.find (combo); if (itePreviousCombo != _ComboAction.end ()) { const CAction::CName oldName = itePreviousCombo->second; // Remove all affected keys TKeyActionMap::iterator ite = _KeyAction.find (combo.Key); while ((ite != _KeyAction.end ()) && (ite->first == combo.Key)) { TKeyActionMap::iterator copyToDelete = ite; #ifdef NL_ISO_CPP0X_AVAILABLE if (copyToDelete->second == oldName) ite = _KeyAction.erase (copyToDelete); else ++ite; #else ++ite; if (copyToDelete->second == oldName) _KeyAction.erase (copyToDelete); #endif } // Remove the action _ActionCombo.erase (oldName); // Remove the combo _ComboAction.erase (itePreviousCombo); } } // *************************************************************************** void CActionsManager::removeAllCombos() { _Actions.clear (); _ActionCombo.clear (); _ComboAction.clear (); _KeyAction.clear (); _WatchedActions.clear (); _ActionForceDisplay.clear(); } // *************************************************************************** bool CActionsManager::addCombo(const CAction::CName &name, const CCombo &combo, bool createAction) { // If createAction == "true" -> Create the Action before to add te combo. if(createAction) addAction(name); // Erase previous values TActionComboMap::iterator itePreviousCombo = _ActionCombo.find (name); if (itePreviousCombo != _ActionCombo.end ()) { // Remove the old action affected to the combo removeCombo (itePreviousCombo->second); } // Remove the new combo removeCombo (combo); _ComboAction.insert (TComboActionMap::value_type (combo, name)); _ActionCombo.insert (TActionComboMap::value_type (name, combo)); _KeyAction.insert (TKeyActionMap::value_type (combo.Key, name)); return true; }// addCombo // // *************************************************************************** bool CActionsManager::valide(const CAction::CName &name) const { // Recover the pointer on "actionName" if it exists. TActionsMap::const_iterator it = _Actions.find(name); if(it != _Actions.end()) { return it->second.Valide; } // No action of this name found. else return false; }// valide // // *************************************************************************** bool CActionsManager::isActionPresentInContext(const CAction::CName &name) const { CInterfaceManager *im = CInterfaceManager::getInstance(); if (!im->isInGame()) return true; // no filtering done when outgame, because actions.xml not loaded yet (no base actions where added) { const CBaseAction *baseAction = getBaseAction(name); if (!baseAction) return false; // see if action valid in current context if (!ActionsContext.matchContext(baseAction->Contexts)) return false; // all parameters must be valid in current context for (uint i=0; iParameters.size (); i++) { const CBaseAction::CParameter ¶meter = baseAction->Parameters[i]; if (parameter.Type == CBaseAction::CParameter::Constant) { ucstring paramName; ucstring paramValue = parameter.DefaultValue; // Get the param from the argu getParam (parameter.Type, paramName, paramValue, name.Argu, i); bool found = true; for (uint k = 0; k < parameter.Values.size(); ++k) { if (parameter.Values[k].Value == paramValue.toUtf8()) { if (!ActionsContext.matchContext(parameter.Values[k].Contexts)) return false; found = true; break; } } if (!found) return false; } } return true; } }// valide // // *************************************************************************** bool CActionsManager::keyPushed (const CEventKeyDown &keyDown) { bool actionExist = false; if (_Enabled) { CCombo combo; combo.Key = keyDown.Key; combo.KeyButtons = keyDown.Button; // Scan action binded to this key TKeyActionMap::iterator iteKeyAction = _KeyAction.find (keyDown.Key); while ((iteKeyAction != _KeyAction.end ()) && (iteKeyAction->first == keyDown.Key)) { if (isActionPresentInContext(iteKeyAction->second)) { // Add it to the set of actions to watch _WatchedActions.insert (*iteKeyAction); } iteKeyAction++; } // Update special valide actions updateKeyButton (keyDown.Button); // Get the key down TComboActionMap::iterator ite = _ComboAction.find (combo); if (ite != _ComboAction.end ()) { // Get the action TActionsMap::iterator iteAction = _Actions.find (ite->second); nlassert (iteAction != _Actions.end ()); if (isActionPresentInContext(iteAction->first)) { // Run the action if (iteAction->second.KeyDown && (iteAction->second.Repeat || keyDown.FirstTime)) { iteAction->second.runAction (); } // The action exist actionExist = true; } else { // The action exist actionExist = false; } } } return actionExist; } // *************************************************************************** void CActionsManager::keyReleased (const CEventKeyUp &keyUp) { if (_Enabled) { // Update special keys state updateKeyButton (keyUp.Button); // For each watched actions TKeyActionMap::iterator iteWatchedAction = _WatchedActions.begin (); while (iteWatchedAction != _WatchedActions.end ()) { TKeyActionMap::iterator iteToDelete = iteWatchedAction; // Get the combo for this action TActionComboMap::iterator iteCombo = _ActionCombo.find (iteToDelete->second); nlassert (iteCombo != _ActionCombo.end()); // This combo released ? if (iteCombo->second.Key == keyUp.Key) { // Get the action TActionsMap::iterator iteAction = _Actions.find (iteToDelete->second); nlassert (iteAction != _Actions.end()); // Remove this action from watching #ifdef NL_ISO_CPP0X_AVAILABLE // C++11 return the next item iteWatchedAction = _WatchedActions.erase (iteToDelete); #else // remember the next iterator only if not using C++11 ++iteWatchedAction; _WatchedActions.erase (iteToDelete); #endif // Invalidate the action bool LastValid = iteAction->second.Valide; iteAction->second.Valide = false; if ((LastValid == true) && (iteAction->second.Valide == false)) { // Run the action if (iteAction->second.KeyUp) { iteAction->second.runAction (); } } } else { ++iteWatchedAction; } } } } // *************************************************************************** void CActionsManager::releaseAllKeyNoRunning() { // For each watched actions TKeyActionMap::iterator iteWatchedAction = _WatchedActions.begin (); while (iteWatchedAction != _WatchedActions.end ()) { TKeyActionMap::iterator iteToDelete = iteWatchedAction++; // Invalidate the action TActionsMap::iterator iteAction = _Actions.find (iteToDelete->second); nlassert (iteAction != _Actions.end()); iteAction->second.Valide = false; // Remove this action from watching _WatchedActions.erase (iteToDelete); } } // *************************************************************************** sint getMatchingNote (sint keyButton, sint newButton) { // At least all the needed key if ((keyButton & newButton) != keyButton) return -1; // If exactly the same, we want it if (keyButton == newButton) return 10; // Else count the number of bits used const uint flags[3] = { ctrlKeyButton, shiftKeyButton, altKeyButton }; sint count = 0; for (uint i=0; i<3; i++) { if (keyButton & flags[i]) count++; } return count; } // *************************************************************************** void CActionsManager::updateKeyButton (NLMISC::TKeyButton newButtons) { // For each watched actions TKeyActionMap::iterator iteWatchedAction = _WatchedActions.begin (); while (iteWatchedAction != _WatchedActions.end ()) { // First action for this key TKeyActionMap::iterator iteWatchedActionBegin = iteWatchedAction; // Current Key NLMISC::TKey key = iteWatchedAction->first; // Best matching action CAction *bestMatching = NULL; sint bestMatchingNote = -1; // For each action with the same key, search the best combo while ((iteWatchedAction != _WatchedActions.end ()) && (key == iteWatchedAction->first)) { // Get the action combo TActionComboMap::iterator iteCombo = _ActionCombo.find (iteWatchedAction->second); if (iteCombo == _ActionCombo.end()) nlwarning("try to find Name:%s , Argu:%s",iteWatchedAction->second.Name.c_str(), iteWatchedAction->second.Argu.c_str()); nlassert (iteCombo != _ActionCombo.end()); // Get the matching note sint matchingNote = getMatchingNote (iteCombo->second.KeyButtons, newButtons); if (matchingNote > bestMatchingNote) { // Get the action TActionsMap::iterator iteAction = _Actions.find (iteWatchedAction->second); nlassert (iteAction != _Actions.end()); // Memorise the best action bestMatching = &(iteAction->second); bestMatchingNote = matchingNote; } iteWatchedAction++; } // Invalide or valide actions while (iteWatchedActionBegin != iteWatchedAction) { // Get the action TActionsMap::iterator iteAction = _Actions.find (iteWatchedActionBegin->second); nlassert (iteAction != _Actions.end()); // Valide or invalide it bool LastValid = iteAction->second.Valide; iteAction->second.Valide = (&(iteAction->second) == bestMatching); if ((LastValid == true) && (iteAction->second.Valide == false)) { // Run the action on keyup if (iteAction->second.KeyUp) { iteAction->second.runAction (); } } iteWatchedActionBegin++; } } } // *************************************************************************** void CCombo::init (NLMISC::TKey key, NLMISC::TKeyButton keyButtons) { Key = key; KeyButtons = keyButtons; } // *************************************************************************** ucstring CCombo::toUCString() const { ucstring ret; if ((KeyButtons & shiftKeyButton) && (Key != 0x10)) ret += CI18N::get("uiKeySHIFT") + "+"; if ((KeyButtons & ctrlKeyButton) && (Key != 0x11)) ret += CI18N::get("uiKeyCONTROL") + "+"; if ((KeyButtons & altKeyButton) && (Key != 0x12)) ret += CI18N::get("uiKeyMENU") + "+"; if (CI18N::hasTranslation("ui"+CEventKey::getStringFromKey(Key))) ret += CI18N::get("ui"+CEventKey::getStringFromKey(Key)); else ret += CEventKey::getStringFromKey(Key); return ret; } // *************************************************************************** const std::vector &CActionsManager::getCategories () const { return _Categories; } // *************************************************************************** void CActionsManager::reserveCategories (uint space) { _Categories.reserve (space); } // *************************************************************************** void CActionsManager::addCategory (const CCategory &category) { _Categories.push_back (category); // Add an entry in the map to get the base action by the action name uint i; for (i=0; ifirst == name.Name)) { // Ref on the base action const CCategory &cat = _Categories[ite->second.CategoryId]; uint baseActionId = ite->second.BaseActionId; uint baseActionSize = cat.BaseActions.size(); if( ite->second.BaseActionId >= cat.BaseActions.size() ) return NULL; const CBaseAction &baseAction = cat.BaseActions[ite->second.BaseActionId]; // Check parameters uint i; uint s = baseAction.Parameters.size(); for (i=0; isecond; ite++; } return NULL; } // *************************************************************************** const CBaseAction *CActionsManager::getBaseAction (const CAction::CName &name) const { const CCategoryLocator *pCL = getActionLocator(name); if (pCL == NULL) return NULL; return &_Categories[pCL->CategoryId].BaseActions[pCL->BaseActionId]; } // *************************************************************************** void CActionsManager::removeBaseAction(const CAction::CName &name) { const CCategoryLocator *pCL = getActionLocator(name); if (pCL == NULL) { nlwarning("Action %s %s not found.", name.Name.c_str(), name.Argu.c_str()); return; } std::vector &baseActions = _Categories[pCL->CategoryId].BaseActions; baseActions.erase(baseActions.begin() + pCL->BaseActionId); } // *************************************************************************** const CCategory *CActionsManager::getCategory (const CAction::CName &name) const { const CCategoryLocator *pCL = getActionLocator(name); if (pCL == NULL) return NULL; return &_Categories[pCL->CategoryId]; } // *************************************************************************** void CActionsManager::forceDisplayForAction(const CAction::CName &name, bool state) { if(state) _ActionForceDisplay.insert(name); else _ActionForceDisplay.erase(name); } // *************************************************************************** bool CActionsManager::isActionDisplayForced(const CAction::CName &name) const { return _ActionForceDisplay.find(name)!=_ActionForceDisplay.end(); } // *************************************************************************** ucstring CActionsManager::getActionLocalizedText (const CAction::CName &name) const { const CBaseAction *baseAction= getBaseAction(name); if(!baseAction) return ucstring(); return baseAction->getActionLocalizedText(name); } // *************************************************************************** // CActionsContext // *************************************************************************** CActionsContext::CActionsContext() { _Context = "game"; } bool CActionsContext::addActionsManager (CActionsManager *actionManager, const std::string &category) { return _ActionsManagers.insert (TActionsManagerMap::value_type(category, actionManager)).second; } // *************************************************************************** CActionsManager *CActionsContext::getActionsManager (const std::string &category) const { TActionsManagerMap::const_iterator ite = _ActionsManagers.find (category); if (ite != _ActionsManagers.end()) return ite->second; else { ite = _ActionsManagers.find (""); if (ite != _ActionsManagers.end()) return ite->second; return NULL; } } // *************************************************************************** bool CActionsContext::matchContext(const std::string &contexts) const { std::vector contextList; splitString(contexts, ",", contextList); for (uint k = 0; k < contextList.size(); ++k) { std::string currContext = contextList[k]; while(strFindReplace(currContext, " ", "")); while(strFindReplace(currContext, "\t", "")); if (nlstricmp(currContext, _Context) == 0) return true; } return false; } // *************************************************************************** void CActionsContext::removeAllCombos() { for (TActionsManagerMap::iterator it = _ActionsManagers.begin(); it != _ActionsManagers.end(); ++it) { it->second->removeAllCombos(); } } // ***************************************************************************