/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. #include "StdAfx.h" #include #include #include "IViewPane.h" #include "Objects/EntityObject.h" #include "Objects/SmartObject.h" #include "Objects/SelectionGroup.h" #include "GameEngine.h" #include "QtViewPaneManager.h" #include "AI/AIManager.h" #include "ItemDescriptionDlg.h" #include "SmartObjectClassDialog.h" #include "SmartObjectHelperDialog.h" #include "SmartObjectTemplateDialog.h" #include #include "SmartObjectHelperObject.h" #include "SmartObjectsEditorDialog.h" #include #include #include #include #include // for getting the game folder #include #include #include #include #include #include #include #include #include #include #include #include #include #define SOED_DIALOGFRAME_CLASSNAME "SmartObjectsEditorDialog" #define CLASS_TEMPLATES_FOLDER "Libs/SmartObjects/ClassTemplates/" #define IDC_REPORTCONTROL 1234 static const char* const s_smartObjectsDialogName = "Smart Objects Editor"; #define COLUMN_TYPE -1 #define COLUMN_NAME 0 #define COLUMN_DESCRIPTION 1 #define COLUMN_USER_CLASS 2 #define COLUMN_USER_STATE 3 #define COLUMN_OBJECT_CLASS 4 #define COLUMN_OBJECT_STATE 5 #define COLUMN_ACTION 6 #define COLUMN_ORDER 7 // static members SmartObjectConditions CSOLibrary::m_Conditions; CSOLibrary::VectorHelperData CSOLibrary::m_vHelpers; CSOLibrary::VectorEventData CSOLibrary::m_vEvents; CSOLibrary::VectorStateData CSOLibrary::m_vStates; CSOLibrary::VectorClassTemplateData CSOLibrary::m_vClassTemplates; CSOLibrary::VectorClassData CSOLibrary::m_vClasses; bool CSOLibrary::m_bSaveNeeded = false; bool CSOLibrary::m_bLoadNeeded = true; int CSOLibrary::m_iNumEditors = 0; CSOLibrary* CSOLibrary::m_pInstance = NULL; // functor used for comparing and ordering data structures by name template< class T > struct less_name { bool operator() (const T& _Left, const T& _Right) const { return _Left.name < _Right.name; } }; // functor used for comparing and ordering data structures by name (case insensitive) template< class T > struct less_name_no_case { bool operator() (const T& _Left, const T& _Right) const { return _stricmp(_Left.name, _Right.name) < 0; } }; void ReloadSmartObjects(IConsoleCmdArgs* /* pArgs */) { CSOLibrary::Reload(); } ////////////////////////////////////////////////////////////////////////// // Called by the editor to notify the listener about the specified event. void CSOLibrary::OnEditorNotifyEvent(EEditorNotifyEvent event) { switch (event) { case eNotify_OnBeginGameMode: // Sent when editor goes to game mode. case eNotify_OnBeginSimulationMode: // Sent when simulation mode is started. case eNotify_OnBeginSceneSave: // Sent when document is about to be saved. case eNotify_OnQuit: // Sent before editor quits. case eNotify_OnBeginNewScene: // Sent when the document is begin to be cleared. case eNotify_OnBeginSceneOpen: // Sent when document is about to be opened. case eNotify_OnCloseScene: // Send when the document is about to close. if (m_bSaveNeeded && m_iNumEditors == 0 && Save()) { m_bSaveNeeded = false; m_bLoadNeeded = false; } if (event == eNotify_OnQuit) { GetIEditor()->UnregisterNotifyListener(this); m_pInstance = NULL; delete this; } break; case eNotify_OnInit: // Sent after editor fully initialized. REGISTER_COMMAND("so_reload", ReloadSmartObjects, VF_NULL, ""); break; case eNotify_OnEndSceneOpen: // Sent after document have been opened. case eNotify_OnEndSceneSave: // Sent after document have been saved. case eNotify_OnEndNewScene: // Sent after the document have been cleared. case eNotify_OnMissionChange: // Send when the current mission changes. case eNotify_OnEditModeChange: // Sent when editing mode change (move,rotate,scale,....) case eNotify_OnEditToolChange: // Sent when edit tool is changed (ObjectMode,TerrainModify,....) case eNotify_OnEndGameMode: // Send when editor goes out of game mode. case eNotify_OnUpdateViewports: // Sent when editor needs to update data in the viewports. case eNotify_OnReloadTrackView: // Sent when editor needs to update the track view. case eNotify_OnInvalidateControls: // Sent when editor needs to update some of the data that can be cached by controls like combo boxes. case eNotify_OnSelectionChange: // Sent when object selection change. case eNotify_OnPlaySequence: // Sent when editor start playing animation sequence. case eNotify_OnStopSequence: // Sent when editor stop playing animation sequence. break; } } void CSOLibrary::Reload() { m_bLoadNeeded = true; CSmartObjectsEditorDialog* pView = FindViewPane(s_smartObjectsDialogName); if (pView) { pView->ReloadEntries(true); } else { Load(); } InvalidateSOEntities(); if (gEnv->pAISystem && !GetIEditor()->IsInGameMode() && !GetIEditor()->GetGameEngine()->GetSimulationMode()) { gEnv->pAISystem->GetSmartObjectManager()->ReloadSmartObjectRules(); } } void CSOLibrary::InvalidateSOEntities() { CObjectClassDesc* pClass = GetIEditor()->GetObjectManager()->FindClass("SmartObject"); if (!pClass) { return; } CBaseObjectsArray objects; GetIEditor()->GetObjectManager()->GetObjects(objects); CBaseObjectsArray::iterator it, itEnd = objects.end(); for (it = objects.begin(); it != itEnd; ++it) { CBaseObject* pBaseObject = *it; if (pBaseObject->GetClassDesc() == pClass) { CSmartObject* pSOEntity = (CSmartObject*) pBaseObject; pSOEntity->OnPropertyChange(0); } } } bool CSOLibrary::Load() { if (!m_bLoadNeeded) { return true; } m_bLoadNeeded = false; if (!m_pInstance) { m_pInstance = new CSOLibrary; GetIEditor()->RegisterNotifyListener(m_pInstance); } LoadClassTemplates(); // load the rules from SmartObjects.xml, if it exists QString smartObjectsXmlPath = Path::Make(Path::GetEditingGameDataFolder().c_str(), SMART_OBJECTS_XML); if (QFileInfo(smartObjectsXmlPath).exists()) { if (!LoadFromFile(smartObjectsXmlPath.toUtf8().data())) { m_bLoadNeeded = true; Warning("CSOLibrary::Load() failed to load from %s", smartObjectsXmlPath.toUtf8().constData()); return false; } m_bSaveNeeded = false; } else { // SMART_OBJECTS_XML file didn't exist in the game folder, set m_bSaveNeeded flag so an empty file will be saved later // in the game folder m_bSaveNeeded = true; } return true; } void CSOLibrary::LoadClassTemplates() { m_vClassTemplates.clear(); string sLibPath = PathUtil::Make(CLASS_TEMPLATES_FOLDER, "*", "xml"); ICryPak* pack = gEnv->pCryPak; _finddata_t fd; intptr_t handle = pack->FindFirst(sLibPath, &fd); int nCount = 0; if (handle < 0) { return; } do { XmlNodeRef root = GetISystem()->LoadXmlFromFile(string(CLASS_TEMPLATES_FOLDER) + fd.name); if (!root) { continue; } CClassTemplateData* data = AddClassTemplate(PathUtil::GetFileName(fd.name)); if (!data) { continue; } int count = root->getChildCount(); for (int i = 0; i < count; ++i) { XmlNodeRef node = root->getChild(i); if (node->isTag("object")) { data->model = node->getAttr("model"); } else if (node->isTag("helper")) { CClassTemplateData::CTemplateHelper helper; helper.name = node->getAttr("name"); node->getAttr("pos", helper.qt.t); node->getAttr("rot", helper.qt.q); node->getAttr("radius", helper.radius); node->getAttr("projectOnGround", helper.project); data->helpers.push_back(helper); } } } while (pack->FindNext(handle, &fd) >= 0); pack->FindClose(handle); } CSOLibrary::CClassTemplateData* CSOLibrary::AddClassTemplate(const char* name) { CClassTemplateData classTemplateData; classTemplateData.name = name; VectorClassTemplateData::iterator it = std::lower_bound(m_vClassTemplates.begin(), m_vClassTemplates.end(), classTemplateData, less_name_no_case< CClassTemplateData >()); if (it != m_vClassTemplates.end() && _stricmp(it->name, name) == 0) { return NULL; } return &*m_vClassTemplates.insert(it, classTemplateData); } ////////////////////////////////////////////////////////////////////////// // Smart Objects UI structures. ////////////////////////////////////////////////////////////////////////// /** User Interface definition of Smart Objects */ class CSmartObjectsUIDefinition { public: CVariable< QString > sName; CVariable< QString > sFolder; CVariable< QString > sDescription; CVariable< bool > bEnabled; CVariable< QString > sTemplate; CVariable< bool > bNavigationRule; CVariable< QString > sEvent; CVariable< QString > sChainedUserEvent; CVariable< QString > sChainedObjectEvent; CVariableArray objectTable; CVariableArray userTable; CVariableArray navigationTable; CVariableArray limitsTable; CVariableArray delayTable; CVariableArray multipliersTable; CVariableArray actionTable; ////////////////////////////////////////////////////////////////////////// // Object variables CVariable< QString > objectClass; CVariable< QString > objectState; CVariable< QString > objectHelper; ////////////////////////////////////////////////////////////////////////// // User variables CVariable< QString > userClass; CVariable< QString > userState; CVariable< QString > userHelper; CVariable< int > iMaxAlertness; ////////////////////////////////////////////////////////////////////////// // Navigation variables CVariable< QString > entranceHelper; CVariable< QString > exitHelper; ////////////////////////////////////////////////////////////////////////// // Limits variables CVariable< float > limitsDistanceFrom; CVariable< float > limitsDistanceTo; CVariable< float > limitsOrientation; CVariable< bool > horizLimitOnly; CVariable< float > limitsOrientationToTarget; ////////////////////////////////////////////////////////////////////////// // Delay variables CVariable< float > delayMinimum; CVariable< float > delayMaximum; CVariable< float > delayMemory; ////////////////////////////////////////////////////////////////////////// // Multipliers variables CVariable< float > multipliersProximity; CVariable< float > multipliersOrientation; CVariable< float > multipliersVisibility; CVariable< float > multipliersRandomness; ////////////////////////////////////////////////////////////////////////// // Action variables CVariable< float > fLookAtOnPerc; CVariable< QString > userPreActionState; CVariable< QString > objectPreActionState; CVariableEnum< int > actionType; CVariable< QString > actionName; CVariable< QString > userPostActionState; CVariable< QString > objectPostActionState; ////////////////////////////////////////////////////////////////////////// // Exact positioning CVariableEnum< int > approachSpeed; CVariableEnum< int > approachStance; CVariable< QString > animationHelper; CVariable< QString > approachHelper; CVariable< float > fStartWidth; CVariable< float > fStartDirectionTolerance; CVariable< float > fStartArcAngle; // CVarEnumList enumType; // CVarEnumList enumBlendType; CVarBlockPtr m_vars; IVariable::OnSetCallback m_onSetCallback; ////////////////////////////////////////////////////////////////////////// CVarBlock* CreateVars() { m_vars = new CVarBlock; /* ////////////////////////////////////////////////////////////////////////// // Init enums. ////////////////////////////////////////////////////////////////////////// enumBlendType.AddRef(); // We not using pointer. enumBlendType.AddItem( "AlphaBlend",ParticleBlendType_AlphaBased ); enumBlendType.AddItem( "ColorBased",ParticleBlendType_ColorBased ); enumBlendType.AddItem( "Additive",ParticleBlendType_Additive ); enumBlendType.AddItem( "None",ParticleBlendType_None ); enumType.AddRef(); // We not using pointer. enumType.AddItem( "Billboard",PART_FLAG_BILLBOARD ); enumType.AddItem( "Horizontal",PART_FLAG_HORIZONTAL ); enumType.AddItem( "Underwater",PART_FLAG_UNDERWATER ); enumType.AddItem( "Align To Direction",PART_FLAG_ALIGN_TO_DIR ); enumType.AddItem( "Line",PART_FLAG_LINEPARTICLE ); */ AddVariable(m_vars, sName, "Name", IVariable::DT_SIMPLE, "The name for this smart object rule"); AddVariable(m_vars, sDescription, "Description", IVariable::DT_SIMPLE, "A description for this smart object rule"); AddVariable(m_vars, sFolder, "Location", IVariable::DT_SIMPLE, "The path to folder where this smart object rule is located"); AddVariable(m_vars, bEnabled, "Enabled", IVariable::DT_SIMPLE, "Is this smart object rule active?"); AddVariable(m_vars, sTemplate, "Template", IVariable::DT_SOTEMPLATE, "Template used on this smart object rule"); AddVariable(m_vars, bNavigationRule, "Navigation Rule", IVariable::DT_SIMPLE, "Is this a navigation smart object rule?"); AddVariable(m_vars, sEvent, "Event", IVariable::DT_SOEVENT, "Event on which this rule is triggered"); AddVariable(m_vars, sChainedUserEvent, "Chained User Event", IVariable::DT_SOEVENT, "Event chained with the user"); AddVariable(m_vars, sChainedObjectEvent, "Chained Object Event", IVariable::DT_SOEVENT, "Event chained with the object"); // AddVariable( m_vars, bRelativeToTarget, "Relative to Target" ); // setup the limits iMaxAlertness.SetLimits(0.0f, 2.0f); //passRadius.SetLimits( 0.0f, 1000.0f ); limitsDistanceFrom.SetLimits(0.0f, 1000.0f); limitsDistanceTo.SetLimits(0.0f, 1000.0f); limitsOrientation.SetLimits(-360.0f, 360.0f); limitsOrientationToTarget.SetLimits(-360.0f, 360.0f); delayMinimum.SetLimits(0.0f, 1000.0f); delayMaximum.SetLimits(0.0f, 1000.0f); delayMemory.SetLimits(0.0f, 1000.0f); multipliersRandomness.SetLimits(0.0f, 100.0f); fLookAtOnPerc.SetLimits(0.0f, 100.0f); fStartWidth.SetLimits(0.0f, 10.0f); fStartDirectionTolerance.SetLimits(0.0f, 180.0f); fStartArcAngle.SetLimits(-360.0f, 360.0f); // setup the enumeration lists actionType.AddEnumItem("None", 0); // eAT_None actionType.AddEnumItem("AI Action", 1); // eAT_Action actionType.AddEnumItem("High Priority Action", 2); // eAT_PriorityAction // actionType.AddEnumItem( "Go To Object", 3 ); // eAT_Approach // actionType.AddEnumItem( "High Priority Go To", 4 ); // eAT_PriorityApproach // actionType.AddEnumItem( "Go To + Action", 5 ); // eAT_ApproachAction // actionType.AddEnumItem( "H.P. Go To + Action", 6 ); // eAT_PriorityApproachAction actionType.AddEnumItem("AI Signal", 7); // eAT_AISignal actionType.AddEnumItem("One Shot Animation", 8); // eAT_AnimationSignal actionType.AddEnumItem("Looping Animation", 9); // eAT_AnimationAction actionType.AddEnumItem("HP One Shot Animation", 10); // eAT_PriorityAnimationSignal actionType.AddEnumItem("HP Looping Animation", 11); // eAT_PriorityAnimationAction approachSpeed.AddEnumItem("", -1); approachSpeed.AddEnumItem("Normal/Walk", 0); approachSpeed.AddEnumItem("Fast/Run", 1); approachSpeed.AddEnumItem("Very Fast/Sprint", 2); approachStance.AddEnumItem("", -1); approachStance.AddEnumItem("Combat", STANCE_STAND); approachStance.AddEnumItem("Crouch", STANCE_CROUCH); approachStance.AddEnumItem("Prone", STANCE_PRONE); approachStance.AddEnumItem("Relaxed", STANCE_RELAXED); approachStance.AddEnumItem("Stealth", STANCE_STEALTH); approachStance.AddEnumItem("Alerted", STANCE_ALERTED); approachStance.AddEnumItem("LowCover", STANCE_LOW_COVER); approachStance.AddEnumItem("HighCover", STANCE_HIGH_COVER); ////////////////////////////////////////////////////////////////////////// // Init tables. ////////////////////////////////////////////////////////////////////////// objectTable.DeleteAllVariables(); userTable.DeleteAllVariables(); navigationTable.DeleteAllVariables(); limitsTable.DeleteAllVariables(); delayTable.DeleteAllVariables(); multipliersTable.DeleteAllVariables(); actionTable.DeleteAllVariables(); AddVariable(m_vars, userTable, "User", IVariable::DT_SIMPLE, "Define the smart object user"); AddVariable(m_vars, objectTable, "Object", IVariable::DT_SIMPLE, "Define the smart object to be used\nIf empty the user will use himself"); AddVariable(m_vars, navigationTable, "Navigation", IVariable::DT_SIMPLE, "An AI navigation link will be created between these two helpers"); AddVariable(m_vars, limitsTable, "Limits", IVariable::DT_SIMPLE, "Only objects within this range will be considered"); AddVariable(m_vars, delayTable, "Delay", IVariable::DT_SIMPLE, ""); AddVariable(m_vars, multipliersTable, "Multipliers", IVariable::DT_SIMPLE, ""); AddVariable(m_vars, actionTable, "Action", IVariable::DT_SIMPLE, ""); userTable.SetFlags(IVariable::UI_BOLD); objectTable.SetFlags(IVariable::UI_BOLD); navigationTable.SetFlags(IVariable::UI_BOLD); limitsTable.SetFlags(IVariable::UI_BOLD); delayTable.SetFlags(IVariable::UI_BOLD); multipliersTable.SetFlags(IVariable::UI_BOLD); actionTable.SetFlags(IVariable::UI_BOLD); AddVariable(userTable, userClass, "Class", IVariable::DT_SOCLASS, "Only users of this class will be considered"); AddVariable(userTable, userState, "State Pattern", IVariable::DT_SOSTATEPATTERN, "Only users which state matches this pattern will be considered"); AddVariable(userTable, userHelper, "Helper", IVariable::DT_SOHELPER, "User's helper point used in calculations"); AddVariable(userTable, iMaxAlertness, "Max. Alertness", IVariable::DT_SIMPLE, "Consider only users which alertness state is not higher"); AddVariable(objectTable, objectClass, "Class", IVariable::DT_SOCLASS, "Only objects of this class will be considered"); AddVariable(objectTable, objectState, "State Pattern", IVariable::DT_SOSTATEPATTERN, "Only objects which state matches this pattern will be considered"); AddVariable(objectTable, objectHelper, "Helper", IVariable::DT_SOHELPER, "Object's helper point used in calculations"); // AddVariable( navigationTable, passRadius, "Pass Radius", IVariable::DT_SIMPLE, "Defines a navigational smart object if higher than zero" ); AddVariable(navigationTable, entranceHelper, "Entrance", IVariable::DT_SONAVHELPER, "Entrance point of navigational smart object"); AddVariable(navigationTable, exitHelper, "Exit", IVariable::DT_SONAVHELPER, "Exit point of navigational smart object"); AddVariable(limitsTable, limitsDistanceFrom, "Distance from", IVariable::DT_SIMPLE, "Range at which objects could be used"); AddVariable(limitsTable, limitsDistanceTo, "Distance to", IVariable::DT_SIMPLE, "Range at which objects could be used"); AddVariable(limitsTable, limitsOrientation, "Orientation", IVariable::DT_SIMPLE, "Orientation limit at which objects could be used"); AddVariable(limitsTable, horizLimitOnly, "Horizontal Limit Only", IVariable::DT_SIMPLE, "When checked, the limit only applies to horizontal orientation (vertical orientation is excluded)"); AddVariable(limitsTable, limitsOrientationToTarget, "Orient. to Target", IVariable::DT_SIMPLE, "Object's orientation limit towards the user's attention target"); AddVariable(delayTable, delayMinimum, "Minimum", IVariable::DT_SIMPLE, "Shortest time needed for interaction to happen"); AddVariable(delayTable, delayMaximum, "Maximum", IVariable::DT_SIMPLE, "Longest time needed for interaction to happen"); AddVariable(delayTable, delayMemory, "Memory", IVariable::DT_SIMPLE, "Time needed to forget"); AddVariable(multipliersTable, multipliersProximity, "Proximity", IVariable::DT_SIMPLE, "How important is the proximity"); AddVariable(multipliersTable, multipliersOrientation, "Orientation", IVariable::DT_SIMPLE, "How important is the orientation of the user towards the object"); AddVariable(multipliersTable, multipliersVisibility, "Visibility", IVariable::DT_SIMPLE, "How important is the visibility between user and object"); AddVariable(multipliersTable, multipliersRandomness, "Randomness", IVariable::DT_PERCENT, "How much randomness will be added"); AddVariable(actionTable, fLookAtOnPerc, "LookAt On %", IVariable::DT_PERCENT, "Look at the object before using it (only when the user is an AI agent)"); AddVariable(actionTable, userPreActionState, "User: Pre-Action State", IVariable::DT_SOSTATES, "How user's states will change before executing the action"); AddVariable(actionTable, objectPreActionState, "Object: Pre-Action State", IVariable::DT_SOSTATES, "How object's states will change before executing the action"); AddVariable(actionTable, actionType, "Action Type", IVariable::DT_SIMPLE, "What type of action to execute"); AddVariable(actionTable, actionName, "Action Name", IVariable::DT_SOACTION, "The action to be executed on interaction"); AddVariable(actionTable, userPostActionState, "User: Post-Action State", IVariable::DT_SOSTATES, "How user's states will change after executing the action"); AddVariable(actionTable, objectPostActionState, "Object: Post-Action State", IVariable::DT_SOSTATES, "How object's states will change after executing the action"); AddVariable(actionTable, approachSpeed, "Speed", IVariable::DT_SIMPLE, "Movement speed while approaching the helper point"); AddVariable(actionTable, approachStance, "Stance", IVariable::DT_SIMPLE, "Body stance while approaching the helper point"); AddVariable(objectTable, animationHelper, "Animation Helper", IVariable::DT_SOANIMHELPER, "Object's helper point at which the animation will be played"); AddVariable(objectTable, approachHelper, "Approach Helper", IVariable::DT_SOHELPER, "Object's helper point to approach before the animation play (if empty Animation Helper is used)"); AddVariable(actionTable, fStartWidth, "Start Line Width", IVariable::DT_SIMPLE, "Width of the starting line for playing animation"); AddVariable(actionTable, fStartArcAngle, "Arc Angle", IVariable::DT_SIMPLE, "Arc angle if the start line need to be bended"); AddVariable(actionTable, fStartDirectionTolerance, "Direction Tolerance", IVariable::DT_SIMPLE, "Start direction tolerance for playing animation"); return m_vars; } ////////////////////////////////////////////////////////////////////////// // returns true if folder was edited bool GetConditionFromUI(SmartObjectCondition& condition) const { QString temp; bool result = false; condition.sName = static_cast(sName).toUtf8().data(); condition.sDescription = static_cast(sDescription).toUtf8().data(); QString newLocation; QString location = sFolder; int i = 0; for (auto folder : location.split(QRegularExpression(QStringLiteral(R"([/\\])")), Qt::SkipEmptyParts)) { newLocation += '/'; newLocation += folder; } if (newLocation != condition.sFolder.c_str()) { result = true; condition.sFolder = newLocation.toUtf8().data(); } condition.bEnabled = bEnabled; condition.iRuleType = bNavigationRule ? 1 : 0; condition.sEvent = static_cast(sEvent).toUtf8().data(); condition.sChainedUserEvent = static_cast(sChainedUserEvent).toUtf8().data(); condition.sChainedObjectEvent = static_cast(sChainedObjectEvent).toUtf8().data(); QRegularExpression reg("(^[ :]+)|([ :]+$)"); userClass.Get(temp); condition.sUserClass = temp.remove(reg).toUtf8().data(); userState.Get(temp); condition.sUserState = temp.toUtf8().data(); userHelper.Get(temp); temp.remove(0, temp.lastIndexOf(':') + 1); condition.sUserHelper = temp.toUtf8().data(); condition.iMaxAlertness = iMaxAlertness; objectClass.Get(temp); condition.sObjectClass = temp.remove(reg).toUtf8().data(); objectState.Get(temp); condition.sObjectState = temp.toUtf8().data(); objectHelper.Get(temp); temp.remove(0, temp.lastIndexOf(':') + 1); condition.sObjectHelper = temp.toUtf8().data(); entranceHelper.Get(temp); temp.remove(0, temp.lastIndexOf(':') + 1); condition.sEntranceHelper = temp.toUtf8().data(); exitHelper.Get(temp); temp.remove(0, temp.lastIndexOf(':') + 1); condition.sExitHelper = temp.toUtf8().data(); condition.fDistanceFrom = limitsDistanceFrom; condition.fDistanceTo = limitsDistanceTo; condition.fOrientationLimit = limitsOrientation; condition.bHorizLimitOnly = horizLimitOnly; condition.fOrientationToTargetLimit = limitsOrientationToTarget; condition.fMinDelay = delayMinimum; condition.fMaxDelay = delayMaximum; condition.fMemory = delayMemory; condition.fProximityFactor = multipliersProximity; condition.fOrientationFactor = multipliersOrientation; condition.fVisibilityFactor = multipliersVisibility; condition.fRandomnessFactor = multipliersRandomness; condition.fLookAtOnPerc = fLookAtOnPerc; objectPreActionState.Get(temp); condition.sObjectPreActionState = temp.toUtf8().data(); userPreActionState.Get(temp); condition.sUserPreActionState = temp.toUtf8().data(); condition.eActionType = (EActionType) (int) actionType; actionName.Get(temp); condition.sAction = temp.toUtf8().data(); objectPostActionState.Get(temp); condition.sObjectPostActionState = temp.toUtf8().data(); userPostActionState.Get(temp); condition.sUserPostActionState = temp.toUtf8().data(); condition.fApproachSpeed = approachSpeed; condition.iApproachStance = approachStance; animationHelper.Get(temp); temp.remove(0, temp.lastIndexOf(':') + 1); condition.sAnimationHelper = temp.toUtf8().data(); approachHelper.Get(temp); temp.remove(0, temp.lastIndexOf(':') + 1); condition.sApproachHelper = temp.toUtf8().data(); condition.fStartWidth = fStartWidth; condition.fDirectionTolerance = fStartDirectionTolerance; condition.fStartArcAngle = fStartArcAngle; return result; } ////////////////////////////////////////////////////////////////////////// void SetConditionToUI(const SmartObjectCondition& condition, ReflectedPropertyControl* pPropertyCtrl, QWidget* messageBoxParent) { const MapTemplates& mapTemplates = GetIEditor()->GetAI()->GetMapTemplates(); MapTemplates::const_iterator find = mapTemplates.find(condition.iTemplateId); if (find == mapTemplates.end()) { find = mapTemplates.find(0); } // template id 0 must always exist! // if doesn't then probably templates aren't loaded... assert(find != mapTemplates.end()); CSOTemplate* pTemplate = find->second; sTemplate = pTemplate->name; sName = condition.sName.c_str(); sFolder = condition.sFolder.c_str(); sDescription = condition.sDescription.c_str(); bEnabled = condition.bEnabled; bNavigationRule = condition.iRuleType == 1; sEvent = condition.sEvent.c_str(); sChainedUserEvent = condition.sChainedUserEvent.c_str(); sChainedObjectEvent = condition.sChainedObjectEvent.c_str(); QRegularExpression reg("(^[ :]+)|([ :]+$)"); QString temp = QtUtil::ToQString(condition.sUserClass); userClass = temp.remove(reg).toUtf8().data(); userState = condition.sUserState.c_str(); userHelper = temp.isEmpty() ? "" : temp + ':' + condition.sUserHelper.c_str(); iMaxAlertness = condition.iMaxAlertness; temp = condition.sObjectClass; objectClass = temp.remove(reg).toUtf8().data(); objectState = condition.sObjectState.c_str(); objectHelper = temp.isEmpty() ? "" : temp + ':' + condition.sObjectHelper.c_str(); entranceHelper = temp + ':' + condition.sEntranceHelper.c_str(); exitHelper = temp + ':' + condition.sExitHelper.c_str(); limitsDistanceFrom = condition.fDistanceFrom; limitsDistanceTo = condition.fDistanceTo; limitsOrientation = condition.fOrientationLimit; horizLimitOnly = condition.bHorizLimitOnly; limitsOrientationToTarget = condition.fOrientationToTargetLimit; delayMinimum = condition.fMinDelay; delayMaximum = condition.fMaxDelay; delayMemory = condition.fMemory; multipliersProximity = condition.fProximityFactor; multipliersOrientation = condition.fOrientationFactor; multipliersVisibility = condition.fVisibilityFactor; multipliersRandomness = condition.fRandomnessFactor; fLookAtOnPerc = condition.fLookAtOnPerc; userPreActionState = condition.sUserPreActionState.c_str(); objectPreActionState = condition.sObjectPreActionState.c_str(); actionType = condition.eActionType; actionName = condition.sAction.c_str(); userPostActionState = condition.sUserPostActionState.c_str(); objectPostActionState = condition.sObjectPostActionState.c_str(); approachSpeed = condition.fApproachSpeed; approachStance = condition.iApproachStance; animationHelper = temp.isEmpty() ? "" : temp + ':' + condition.sAnimationHelper.c_str(); approachHelper = temp.isEmpty() ? "" : temp + ':' + condition.sApproachHelper.c_str(); fStartWidth = condition.fStartWidth; fStartDirectionTolerance = condition.fDirectionTolerance; fStartArcAngle = condition.fStartArcAngle; // remove all variables from UI m_vars->DeleteAllVariables(); // add standard variables m_vars->AddVariable(&sName); m_vars->AddVariable(&sDescription); m_vars->AddVariable(&sFolder); m_vars->AddVariable(&bEnabled); m_vars->AddVariable(&sTemplate); // add template variables AddVariablesFromTemplate(m_vars, pTemplate->params, messageBoxParent); pPropertyCtrl->RemoveAllItems(); pPropertyCtrl->AddVarBlock(m_vars); pPropertyCtrl->ExpandAll(); for (int i = 0; i < m_collapsedItems.size(); ++i) { pPropertyCtrl->Expand(pPropertyCtrl->FindItemByVar(m_collapsedItems[i]), false); } m_collapsedItems.clear(); } IVariable* ResolveVariable(const CSOParam* pParam, QWidget* messageBoxParent) { IVariable* pVar = NULL; if (pParam->sName == "bNavigationRule") { return &bNavigationRule; } else if (pParam->sName == "sEvent") { return &sEvent; } else if (pParam->sName == "sChainedUserEvent") { return &sChainedUserEvent; } else if (pParam->sName == "sChainedObjectEvent") { return &sChainedObjectEvent; } else if (pParam->sName == "userClass") { return &userClass; } else if (pParam->sName == "userState") { return &userState; } else if (pParam->sName == "userHelper") { return &userHelper; } else if (pParam->sName == "iMaxAlertness") { return &iMaxAlertness; } else if (pParam->sName == "objectClass") { return &objectClass; } else if (pParam->sName == "objectState") { return &objectState; } else if (pParam->sName == "objectHelper") { return &objectHelper; } else if (pParam->sName == "entranceHelper") { return &entranceHelper; } else if (pParam->sName == "exitHelper") { return &exitHelper; } else if (pParam->sName == "limitsDistanceFrom") { return &limitsDistanceFrom; } else if (pParam->sName == "limitsDistanceTo") { return &limitsDistanceTo; } else if (pParam->sName == "limitsOrientation") { return &limitsOrientation; } else if (pParam->sName == "horizLimitOnly") { return &horizLimitOnly; } else if (pParam->sName == "limitsOrientationToTarget") { return &limitsOrientationToTarget; } else if (pParam->sName == "delayMinimum") { return &delayMinimum; } else if (pParam->sName == "delayMaximum") { return &delayMaximum; } else if (pParam->sName == "delayMemory") { return &delayMemory; } else if (pParam->sName == "multipliersProximity") { return &multipliersProximity; } else if (pParam->sName == "multipliersOrientation") { return &multipliersOrientation; } else if (pParam->sName == "multipliersVisibility") { return &multipliersVisibility; } else if (pParam->sName == "multipliersRandomness") { return &multipliersRandomness; } else if (pParam->sName == "fLookAtOnPerc") { return &fLookAtOnPerc; } else if (pParam->sName == "userPreActionState") { return &userPreActionState; } else if (pParam->sName == "objectPreActionState") { return &objectPreActionState; } else if (pParam->sName == "actionType") { return &actionType; } else if (pParam->sName == "actionName") { return &actionName; } else if (pParam->sName == "userPostActionState") { return &userPostActionState; } else if (pParam->sName == "objectPostActionState") { return &objectPostActionState; } else if (pParam->sName == "approachSpeed") { return &approachSpeed; } else if (pParam->sName == "approachStance") { return &approachStance; } else if (pParam->sName == "animationHelper") { return &animationHelper; } else if (pParam->sName == "approachHelper") { return &approachHelper; } else if (pParam->sName == "fStartRadiusTolerance") // backward compatibility { return &fStartWidth; } else if (pParam->sName == "fStartWidth") { return &fStartWidth; } else if (pParam->sName == "fStartDirectionTolerance") { return &fStartDirectionTolerance; } else if (pParam->sName == "fTargetRadiusTolerance") // backward compatibility { return &fStartArcAngle; } else if (pParam->sName == "fStartArcAngle") { return &fStartArcAngle; } QMessageBox::critical(messageBoxParent, QString(), QObject::tr("WARNING:\nSmart object template has a Param tag named %1 which is not recognized as valid name!\nThe Param will be ignored...").arg(pParam->sName)); return NULL; } private: std::vector< IVariable* > m_collapsedItems; void AddVariablesFromTemplate(CVarBlock* var, CSOParamBase* param, QWidget* messageBoxParent) { while (param) { if (!param->bIsGroup) { CSOParam* pParam = static_cast< CSOParam* >(param); if (pParam->bVisible) { if (!pParam->pVariable) { pParam->pVariable = ResolveVariable(pParam, messageBoxParent); } if (pParam->pVariable) { pParam->pVariable->SetHumanName(pParam->sCaption); pParam->pVariable->SetDescription(pParam->sHelp); pParam->pVariable->SetFlags(pParam->bEditable ? 0 : IVariable::UI_DISABLED); var->AddVariable(param->pVariable); } } } else { CSOParamGroup* pParam = static_cast< CSOParamGroup* >(param); pParam->pVariable->DeleteAllVariables(); pParam->pVariable->SetName(pParam->sName); pParam->pVariable->SetDescription(pParam->sHelp); var->AddVariable(pParam->pVariable); AddVariablesFromTemplate(pParam->pVariable, pParam->pChildren, messageBoxParent); if (!pParam->bExpand) { m_collapsedItems.push_back(pParam->pVariable); } } param = param->pNext; } } ////////////////////////////////////////////////////////////////////////// void AddVariablesFromTemplate(IVariable* var, CSOParamBase* param, QWidget* messageBoxParent) { while (param) { if (!param->bIsGroup) { CSOParam* pParam = static_cast< CSOParam* >(param); if (pParam->bVisible) { if (!pParam->pVariable) { pParam->pVariable = ResolveVariable(pParam, messageBoxParent); } if (pParam->pVariable) { pParam->pVariable->SetHumanName(pParam->sCaption); pParam->pVariable->SetDescription(pParam->sHelp); pParam->pVariable->SetFlags(pParam->bEditable ? 0 : IVariable::UI_DISABLED); var->AddVariable(param->pVariable); } } } else { CSOParamGroup* pParam = static_cast< CSOParamGroup* >(param); pParam->pVariable->DeleteAllVariables(); pParam->pVariable->SetName(pParam->sName); pParam->pVariable->SetDescription(pParam->sHelp); var->AddVariable(pParam->pVariable); AddVariablesFromTemplate(pParam->pVariable, pParam->pChildren, messageBoxParent); if (!pParam->bExpand) { m_collapsedItems.push_back(pParam->pVariable); } } param = param->pNext; } } ////////////////////////////////////////////////////////////////////////// void AddVariable(CVariableArray& varArray, CVariableBase& var, const char* varName, unsigned char dataType = IVariable::DT_SIMPLE, const char* description = NULL) { var.AddRef(); // Variables are local and must not be released by CVarBlock. if (varName) { var.SetName(varName); } var.SetDataType(dataType); if (description) { var.SetDescription(description); } if (m_onSetCallback) { var.AddOnSetCallback(m_onSetCallback); } varArray.AddVariable(&var); } ////////////////////////////////////////////////////////////////////////// void AddVariable(CVarBlock* vars, CVariableBase& var, const char* varName, unsigned char dataType = IVariable::DT_SIMPLE, const char* description = NULL) { var.AddRef(); // Variables are local and must not be released by CVarBlock. if (varName) { var.SetName(varName); } var.SetDataType(dataType); if (description) { var.SetDescription(description); } if (m_onSetCallback) { var.AddOnSetCallback(m_onSetCallback); } vars->AddVariable(&var); } }; static CSmartObjectsUIDefinition gSmartObjectsUI; void CSmartObjectsEditorDialog::OnContextMenu(const QPoint& point) { int selCount = m_View->selectionModel()->selectedRows().count(); QMenu menu; QAction* actionNew = menu.addAction(tr("New...")); QAction* actionDuplicate = menu.addAction(tr("Duplicate...")); QAction* actionDelete = menu.addAction(tr("Delete")); actionDuplicate->setEnabled(selCount == 1); actionDelete->setEnabled(selCount > 0); connect(actionNew, &QAction::triggered, this, &CSmartObjectsEditorDialog::OnAddEntry); connect(actionDuplicate, &QAction::triggered, this, &CSmartObjectsEditorDialog::OnDuplicateEntry); connect(actionDelete, &QAction::triggered, this, &CSmartObjectsEditorDialog::OnRemoveEntry); //menu.AppendMenu( MF_SEPARATOR ); /* // create columns items CXTPReportColumns* pColumns = m_View.GetColumns(); int nColumnCount = pColumns->GetCount(); for ( int i = 0; i < nColumnCount; ++i ) { CXTPReportColumn* pCol = pColumns->GetAt(i); CString sCaption = pCol->GetCaption(); if ( !sCaption.IsEmpty() ) { menu.AppendMenu( MF_STRING, ID_COLUMN_SHOW + i, sCaption ); menu.CheckMenuItem( ID_COLUMN_SHOW + i, MF_BYCOMMAND | (pCol->IsVisible() ? MF_CHECKED : MF_UNCHECKED) ); } } */ // track menu menu.exec(m_View->viewport()->mapToGlobal(point)); } ////////////////////////////////////////////////////////////////////////// class SmartObjectsEditorModel : public QAbstractTableModel { public: SmartObjectsEditorModel(QObject* parent = nullptr) : QAbstractTableModel(parent) { } enum Column { ColumnName, ColumnDescription, ColumnUserClass, ColumnUserState, ColumnObjectClass, ColumnObjectState, ColumnAction, ColumnOrder }; void MoveAllRules(const QString& from, const QString& to); QMimeData* mimeData(const QModelIndexList& indexes) const override { QMimeData* d = new QMimeData; QVector rows; std::for_each(indexes.begin(), indexes.end(), [&](const QModelIndex& index) { if (!rows.contains(index.row())) { rows.push_back(index.row()); } }); d->setData(QStringLiteral("x-application/x-smartobjects-rows"), QByteArray(reinterpret_cast(rows.data()), rows.count() * sizeof(int))); return d; } QStringList mimeTypes() const override { return QStringList() << QStringLiteral("x-application/x-smartobjects-rows"); } void addCondition(const SmartObjectCondition& condition) { const int row = rowCount(); beginInsertRows(QModelIndex(), row, row); CSOLibrary::GetConditions().push_back(condition); endInsertRows(); } bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override { if (row < 0 || row + count > rowCount(parent)) { return false; } auto first = CSOLibrary::GetConditions().begin(); std::advance(first, row); auto last = CSOLibrary::GetConditions().begin(); std::advance(last, row + count); beginRemoveRows(QModelIndex(), row, row + count - 1); CSOLibrary::GetConditions().erase(first, last); endRemoveRows(); return true; } void reloadItems() { beginResetModel(); endResetModel(); } int columnCount(const QModelIndex& parent = QModelIndex()) const override { return parent.isValid() ? 0 : 8; } int rowCount(const QModelIndex& parent = QModelIndex()) const override { return parent.isValid() ? 0 : CSOLibrary::GetConditions().size(); } Qt::ItemFlags flags(const QModelIndex& index) const override { Qt::ItemFlags f = QAbstractTableModel::flags(index) | Qt::ItemIsDragEnabled; if (index.column() < 2) { f |= Qt::ItemIsEditable; } if (index.column() == 0) { f |= Qt::ItemIsUserCheckable; } return f; } QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override { if (!index.isValid() || index.row() >= rowCount(index.parent()) || index.column() >= columnCount(index.parent())) { return QVariant(); } auto condition = CSOLibrary::GetConditions().begin(); std::advance(condition, index.row()); static QFont underline; underline.setUnderline(true); if (role == Qt::CheckStateRole && index.column() == 0) { return condition->bEnabled ? Qt::Checked : Qt::Unchecked; } if (role == Qt::UserRole) { return QVariant::fromValue(&(*condition)); } if (role == Qt::ForegroundRole && index.column() == ColumnAction && condition->eActionType == eAT_Action) { return QPalette().color(QPalette::Link); } if (role == Qt::FontRole && index.column() == ColumnAction && condition->eActionType == eAT_Action) { return underline; } if (role == Qt::ForegroundRole) { return condition->bEnabled ? QVariant() : QPalette().color(QPalette::Disabled, QPalette::WindowText); } if (role == Qt::DisplayRole || role == Qt::EditRole) { switch (index.column()) { case ColumnName: return QString(condition->sName.c_str()); case ColumnDescription: return QString(condition->sDescription.c_str()); case ColumnUserClass: return QString(condition->sUserClass.c_str()); case ColumnUserState: return QString(condition->sUserState.c_str()); case ColumnObjectClass: return QString(condition->sObjectClass.c_str()); case ColumnObjectState: return QString(condition->sObjectState.c_str()); case ColumnAction: return QString(condition->sAction.c_str()); case ColumnOrder: return condition->iOrder; } } return QVariant(); } bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override { if (!index.isValid() || index.row() >= rowCount(index.parent()) || index.column() >= columnCount(index.parent())) { return false; } if (!CSOLibrary::StartEditing()) { return false; } auto condition = CSOLibrary::GetConditions().begin(); std::advance(condition, index.row()); if (role == Qt::EditRole) { switch (index.column()) { case ColumnName: if (condition->sName == value.toString().toUtf8().data()) { return true; } condition->sName = value.toString().toUtf8().data(); emit dataChanged(index, index); return true; case ColumnDescription: if (condition->sDescription == value.toString().toUtf8().data()) { return true; } condition->sDescription = value.toString().toUtf8().data(); emit dataChanged(index, index); return true; } } else if (role == Qt::UserRole) { auto cond = value.value(); Q_ASSERT(cond == &(*condition)); emit dataChanged(index, index.sibling(index.row(), columnCount() - 1)); return true; } else if (role == Qt::CheckStateRole) { if (condition->bEnabled == (value.toInt() == Qt::Checked)) { return true; } condition->bEnabled = (value.toInt() == Qt::Checked); emit dataChanged(index, index.sibling(index.row(), columnCount() - 1)); return true; } return false; } QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override { if (orientation == Qt::Horizontal) { if (role == Qt::DisplayRole) { switch (section) { case ColumnName: return tr("Name"); case ColumnDescription: return tr("Description"); case ColumnUserClass: return tr("User's Class"); case ColumnUserState: return tr("User's State Pattern"); case ColumnObjectClass: return tr("Object's Class"); case ColumnObjectState: return tr("Object's State Pattern"); case ColumnAction: return tr("Action"); case ColumnOrder: return QString(); default: return QVariant(); } } } return QAbstractTableModel::headerData(section, orientation, role); } }; class SmartObjectsEditorTreeModel : public AbstractGroupProxyModel { public: SmartObjectsEditorTreeModel(QObject* parent) : AbstractGroupProxyModel(parent) { } bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override { if (row != -1 || column != -1) { return false; } const QByteArray d = data->data(QStringLiteral("x-application/x-smartobjects-rows")); QVector rows(d.count() / sizeof(int)); AZStd::copy(d.begin(), d.end(), reinterpret_cast(rows.data())); const QString path = parent.data(Qt::UserRole).toString(); for (auto i : rows) { const QModelIndex index = sourceModel()->index(i, 0); auto cond = index.data(Qt::UserRole).value(); cond->sFolder = path.toUtf8().data(); sourceModel()->setData(index, QVariant::fromValue(cond), Qt::UserRole); } return true; } QStringList mimeTypes() const override { return QStringList() << QStringLiteral("x-application/x-smartobjects-rows"); } Qt::ItemFlags flags(const QModelIndex& index) const override { const QModelIndex sourceIndex = mapToSource(index); if (!sourceIndex.isValid() && index.isValid()) { return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDropEnabled; } return sourceModel()->flags(sourceIndex); } QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override { if (role == Qt::DecorationRole) { return QIcon(":/HyperGraph/graphs/graph_folder.png"); } else if (role == Qt::UserRole && !index.parent().isValid()) { return QVariant(); } else if (role == Qt::UserRole) { return data(index.parent(), Qt::UserRole).toString() + QString("/") + data(index).toString(); } else { return AbstractGroupProxyModel::data(index, role == Qt::EditRole ? Qt::DisplayRole : role); } } bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override { if (role == Qt::EditRole) { if (!index.parent().isValid()) { return false; } const QString folder = value.toString(); // first check is there already a folder with than name QModelIndexList matches = match(SmartObjectsEditorTreeModel::index(0, 0, index.parent()), Qt::DisplayRole, folder, 1, Qt::MatchFixedString); matches.removeAll(index); if (!matches.isEmpty()) { QMessageBox::warning(qobject_cast(QObject::parent()), QString(), tr("There is already a sibling folder with that name!")); return false; } // renaming is allowed => update paths const QString from = data(index, Qt::UserRole).toString(); const QString to = data(index.parent(), Qt::UserRole).toString() + QStringLiteral("/") + folder; static_cast(sourceModel())->MoveAllRules(from, to); } return false; } int columnCount(const QModelIndex& parent = QModelIndex()) const override { Q_UNUSED(parent); return 1; } QStringList GroupForSourceIndex(const QModelIndex& sourceIndex) const override { auto cond = sourceIndex.data(Qt::UserRole).value(); QStringList group = QString(cond->sFolder.c_str()).split(QRegularExpression(QStringLiteral("[\\/]"))); if (group.isEmpty()) { return QStringList(); } if (group.first().isEmpty()) { group[0] = QStringLiteral("/"); } return group; } }; class LeafRemovingFilterModel : public QSortFilterProxyModel { public: LeafRemovingFilterModel(QObject* parent = nullptr) : QSortFilterProxyModel(parent) { } protected: bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override { return sourceModel()->hasChildren(sourceModel()->index(source_row, 0, source_parent)); } }; class SmartObjectsPathFilterModel : public QSortFilterProxyModel { public: SmartObjectsPathFilterModel(QObject* parent = nullptr) : QSortFilterProxyModel(parent) { } void setFilterClasses(const SetStrings& classes) { m_filterClasses = classes; invalidateFilter(); } void setPath(const QString& path) { QString p = path; p.replace('\\', '/'); while (p.startsWith('/')) { p.remove(0, 1); } if (m_path == p) { return; } m_path = p; invalidateFilter(); } protected: bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override { auto condition = sourceModel()->index(source_row, 0, source_parent).data(Qt::UserRole).value(); QString folder(condition->sFolder); folder.replace('\\', '/'); while (folder.startsWith('/')) { folder.remove(0, 1); } bool folderMatches = folder.compare(m_path, Qt::CaseInsensitive) == 0; if (!folderMatches) { return false; } if (m_filterClasses.empty()) { return true; } return m_filterClasses.find(condition->sUserClass) != m_filterClasses.end() || !condition->sObjectClass.empty() && m_filterClasses.find(condition->sObjectClass) != m_filterClasses.end(); } private: QString m_path; SetStrings m_filterClasses; }; ////////////////////////////////////////////////////////////////////////// void CSmartObjectsEditorDialog::RegisterViewClass() { AzToolsFramework::ViewPaneOptions options; options.paneRect = QRect(100, 250, 500, 500); options.sendViewPaneNameBackToAmazonAnalyticsServers = true; options.isLegacy = true; AzToolsFramework::RegisterViewPane(s_smartObjectsDialogName, LyViewPane::CategoryOther, options); GetIEditor()->GetSettingsManager()->AddToolName("SmartObjectsLayout", "Smart Objects"); } const GUID& CSmartObjectsEditorDialog::GetClassID() { // {9bc75ea1-6556-42b7-a23a-55036906027c} static const GUID guid = { 0x9bc75ea1, 0x6556, 0x42b7, { 0xa2, 0x3a, 0x55, 0x03, 0x69, 0x06, 0x02, 0x7c } }; return guid; } ////////////////////////////////////////////////////////////////////////// CSmartObjectsEditorDialog::CSmartObjectsEditorDialog(QWidget* parent) : QMainWindow(parent) , m_bIgnoreNotifications(false) , m_View(new QTreeView(this)) , m_topLabel(new SmartObjectsEditorDialogLabel) , m_Tree(new QTreeView(this)) { QWidget* centralWidget = new QWidget(this); centralWidget->setLayout(new QVBoxLayout); centralWidget->layout()->setSpacing(0); centralWidget->layout()->addWidget(m_topLabel); centralWidget->layout()->addWidget(m_View); setCentralWidget(centralWidget); connect(m_topLabel, &SmartObjectsEditorDialogLabel::closeRequested, this, [this]() { m_bFilterCanceled = true; m_bSinkNeeded = true; }); GetIEditor()->RegisterNotifyListener(this); ++CSOLibrary::m_iNumEditors; GetIEditor()->GetObjectManager()->AddObjectEventListener(functor(*this, &CSmartObjectsEditorDialog::OnObjectEvent)); m_bSinkNeeded = true; OnInitDialog(); m_View->setContextMenuPolicy(Qt::CustomContextMenu); m_View->header()->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_View, &QWidget::customContextMenuRequested, this, &CSmartObjectsEditorDialog::OnContextMenu); connect(m_View->header(), &QWidget::customContextMenuRequested, this, &CSmartObjectsEditorDialog::OnReportColumnRClick); connect(m_View->selectionModel(), &QItemSelectionModel::selectionChanged, this, &CSmartObjectsEditorDialog::OnReportSelChanged); connect(m_Tree->selectionModel(), &QItemSelectionModel::currentChanged, this, &CSmartObjectsEditorDialog::OnTreeSelChanged); connect(m_Description, &QTextEdit::textChanged, this, &CSmartObjectsEditorDialog::OnDescriptionEdit); connect(m_View->model(), &QAbstractItemModel::dataChanged, this, &CSmartObjectsEditorDialog::OnTreeViewDataChanged); } ////////////////////////////////////////////////////////////////////////// CSmartObjectsEditorDialog::~CSmartObjectsEditorDialog() { OnDestroy(); if (CSOLibrary::m_bSaveNeeded && SaveSOLibrary(false)) { CSOLibrary::m_bSaveNeeded = false; CSOLibrary::m_bLoadNeeded = false; } --CSOLibrary::m_iNumEditors; GetIEditor()->GetObjectManager()->RemoveObjectEventListener(functor(*this, &CSmartObjectsEditorDialog::OnObjectEvent)); GetIEditor()->UnregisterNotifyListener(this); } ////////////////////////////////////////////////////////////////////////// SmartObjectConditions::iterator CSmartObjectsEditorDialog::FindRuleByPtr(const SmartObjectCondition* pRule) { SmartObjectConditions::iterator it, itEnd = CSOLibrary::m_Conditions.end(); for (it = CSOLibrary::m_Conditions.begin(); it != itEnd; ++it) { if (&*it == pRule) { return it; } } // if this ever happen it means that the rule is already deleted! assert(!"Rule not found in CSmartObjectsEditorDialog::FindRuleByPtr!"); return itEnd; } ////////////////////////////////////////////////////////////////////////// void CSmartObjectsEditorDialog::OnInitDialog() { m_bFilterCanceled = false; // root variable block // block = new CVarBlock; // block->AddRef(); // init panes CreatePanes(); // init client pane m_model = new SmartObjectsEditorModel(this); SmartObjectsEditorTreeModel* treeModel = new SmartObjectsEditorTreeModel(this); m_pathModel = new SmartObjectsPathFilterModel(this); m_pathModel->setSourceModel(m_model); m_pathModel->setSortCaseSensitivity(Qt::CaseInsensitive); m_View->setRootIsDecorated(false); m_View->setModel(m_pathModel); m_View->header()->setSectionHidden(SmartObjectsEditorModel::ColumnDescription, true); m_View->header()->setSectionHidden(SmartObjectsEditorModel::ColumnOrder, true); m_View->header()->resizeSection(SmartObjectsEditorModel::ColumnName, 120); m_View->header()->resizeSection(SmartObjectsEditorModel::ColumnDescription, 320); m_View->header()->resizeSection(SmartObjectsEditorModel::ColumnUserClass, 120); m_View->header()->resizeSection(SmartObjectsEditorModel::ColumnUserState, 120); m_View->header()->resizeSection(SmartObjectsEditorModel::ColumnObjectClass, 120); m_View->header()->resizeSection(SmartObjectsEditorModel::ColumnObjectState, 120); m_View->header()->resizeSection(SmartObjectsEditorModel::ColumnAction, 120); auto treeFilterModel = new LeafRemovingFilterModel(this); treeModel->setSourceModel(m_model); treeFilterModel->setSourceModel(treeModel); m_Tree->setModel(treeFilterModel); m_Tree->setHeaderHidden(true); m_View->setSortingEnabled(true); m_pathModel->setDynamicSortFilter(true); m_pathModel->sort(SmartObjectsEditorModel::ColumnOrder); m_View->setDragEnabled(true); m_Tree->setDragDropMode(QAbstractItemView::DragOnly); m_Tree->viewport()->setAcceptDrops(true); m_Tree->setDragDropMode(QAbstractItemView::DropOnly); m_View->setSelectionMode(QAbstractItemView::ExtendedSelection); const QSettings settings("Amazon", "Lumberyard"); restoreState(settings.value(QStringLiteral("SmartObjectsLayout")).toByteArray()); // // Load templates // ReloadTemplates(); } ////////////////////////////////////////////////////////////////////////// void CSmartObjectsEditorDialog::CreatePanes() { ///////////////////////////////////////////////////////////////////////// // Docking Pane for Properties QDockWidget* pDockPane = new AzQtComponents::StyledDockWidget; addDockWidget(Qt::RightDockWidgetArea, pDockPane); pDockPane->setWindowTitle(tr("Properties")); pDockPane->setFeatures(QDockWidget::NoDockWidgetFeatures); ////////////////////////////////////////////////////////////////////////// // Create properties control. ////////////////////////////////////////////////////////////////////////// pDockPane->setWidget(m_Properties = new ReflectedPropertyControl(this)); m_Properties->Setup(); //m_Properties->m_props.SetFlags( CPropertyCtrl::F_VS_DOT_NET_STYLE ); m_Properties->ExpandAll(); m_vars = gSmartObjectsUI.CreateVars(); m_Properties->AddVarBlock(m_vars); m_Properties->ExpandAll(); m_Properties->setEnabled(false); m_Properties->SetUpdateCallback(functor(*this, &CSmartObjectsEditorDialog::OnUpdateProperties)); m_Properties->EnableUpdateCallback(true); pDockPane->resize(120, m_Properties->GetContentHeight()); ///////////////////////////////////////////////////////////////////////// // Docking Pane for Tree Control pDockPane = new AzQtComponents::StyledDockWidget; addDockWidget(Qt::LeftDockWidgetArea, pDockPane); pDockPane->setWindowTitle(tr("Rules")); pDockPane->setFeatures(QDockWidget::NoDockWidgetFeatures); ///////////////////////////////////////////////////////////////////////// // Create Tree Control pDockPane->setWidget(m_Tree); ///////////////////////////////////////////////////////////////////////// // Docking Pane for TaskPanel QDockWidget* pTaskPane = new AzQtComponents::StyledDockWidget; addDockWidget(Qt::LeftDockWidgetArea, pTaskPane, Qt::Vertical); pTaskPane->setWindowTitle(tr("Tasks")); pTaskPane->setFeatures(QDockWidget::NoDockWidgetFeatures); ///////////////////////////////////////////////////////////////////////// // Create empty Task Panel pTaskPane->setWidget(m_taskPanel = new QWidget); m_taskPanel->setLayout(new QVBoxLayout); ClickableLabel* pItem = NULL; QCollapsibleGroupBox* pGroup = NULL; int groupId = 0; m_taskPanel->layout()->addWidget(pGroup = new QCollapsibleGroupBox(this)); pGroup->setLayout(new QVBoxLayout); pGroup->setTitle(tr("Rules")); pGroup->layout()->addWidget(pItem = new ClickableLabel(tr("New..."), this)); pItem->setToolTip(tr("Adds a new entry")); connect(pItem, &ClickableLabel::linkActivated, this, &CSmartObjectsEditorDialog::OnAddEntry); pGroup->layout()->addWidget(m_pDuplicate = new ClickableLabel(tr("Duplicate..."))); m_pDuplicate->setToolTip(tr("Duplicates selected entry")); connect(m_pDuplicate, &ClickableLabel::linkActivated, this, &CSmartObjectsEditorDialog::OnDuplicateEntry); pGroup->layout()->addWidget(m_pDelete = new ClickableLabel(tr("Delete"))); m_pDelete->setToolTip(tr("Removes selected entries")); connect(m_pDelete, &ClickableLabel::linkActivated, this, &CSmartObjectsEditorDialog::OnRemoveEntry); m_taskPanel->layout()->addWidget(pGroup = new QCollapsibleGroupBox(this)); pGroup->setLayout(new QVBoxLayout); pGroup->setTitle(tr("Helpers")); pGroup->layout()->addWidget(m_pItemHelpersEdit = new ClickableLabel(tr("Edit..."))); m_pItemHelpersEdit->setToolTip(tr("Displays Smart Object helpers for editing")); connect(m_pItemHelpersEdit, &ClickableLabel::linkActivated, this, &CSmartObjectsEditorDialog::OnHelpersEdit); pGroup->layout()->addWidget(m_pItemHelpersNew = new ClickableLabel(tr("New..."))); m_pItemHelpersNew->setToolTip(tr("Adds a new Smart Object helper")); connect(m_pItemHelpersNew, &ClickableLabel::linkActivated, this, &CSmartObjectsEditorDialog::OnHelpersNew); pGroup->layout()->addWidget(m_pItemHelpersDelete = new ClickableLabel(tr("Delete..."))); m_pItemHelpersDelete->setToolTip(tr("Removes selected Smart Object helper(s)")); connect(m_pItemHelpersDelete, &ClickableLabel::linkActivated, this, &CSmartObjectsEditorDialog::OnHelpersDelete); pGroup->layout()->addWidget(m_pItemHelpersDone = new ClickableLabel(tr("Done"))); m_pItemHelpersDone->setToolTip(tr("Ends helper edit mode")); connect(m_pItemHelpersDone, &ClickableLabel::linkActivated, this, &CSmartObjectsEditorDialog::OnHelpersDone); m_taskPanel->layout()->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::Expanding)); ///////////////////////////////////////////////////////////////////////// // Docking Pane for description pDockPane = new AzQtComponents::StyledDockWidget; addDockWidget(Qt::BottomDockWidgetArea, pDockPane); pDockPane->setWindowTitle(tr("Description")); pDockPane->setFeatures(QDockWidget::NoDockWidgetFeatures); ///////////////////////////////////////////////////////////////////////// // Create Edit Control m_Description = new QTextEdit; pDockPane->setWidget(m_Description); } ////////////////////////////////////////////////////////////////////////// QString CSmartObjectsEditorDialog::GetFolderPath(const QModelIndex& index) const { return index.data(Qt::UserRole).toString(); } ////////////////////////////////////////////////////////////////////////// QString CSmartObjectsEditorDialog::GetCurrentFolderPath() const { return GetFolderPath(m_Tree->currentIndex()); } ////////////////////////////////////////////////////////////////////////// QModelIndex CSmartObjectsEditorDialog::SetCurrentFolder(const QString& folder) { const QModelIndexList matches = m_Tree->model()->match(m_Tree->model()->index(0, 0), Qt::UserRole, folder, 1, Qt::MatchFixedString | Qt::MatchRecursive); if (matches.isEmpty()) { return QModelIndex(); } m_Tree->setCurrentIndex(matches.first()); return matches.first(); } ////////////////////////////////////////////////////////////////////////// void CSmartObjectsEditorDialog::ReloadEntries(bool bFromFile) { m_model->reloadItems(); } ////////////////////////////////////////////////////////////////////////// bool CSmartObjectsEditorDialog::ChangeTemplate(SmartObjectCondition* pRule, const CSOParamBase* pParam) const { QString msg; SetTemplateDefaults(*pRule, pParam, &msg); if (msg.isEmpty()) { SetTemplateDefaults(*pRule, pParam); return true; } msg = tr("WARNING!\n\nThe change of the template will implicitly alter the following field(s):\n%1\nDo you really want to change the template?").arg(msg); if (QMessageBox::warning(const_cast(this), QString(), msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) { return false; } SetTemplateDefaults(*pRule, pParam); return true; } void CSmartObjectsEditorDialog::OnTreeViewDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) { if (m_View->currentIndex().row() >= topLeft.row() && m_View->currentIndex().row() <= bottomRight.row()) { m_Properties->EnableUpdateCallback(false); auto condition = m_View->currentIndex().data(Qt::UserRole).value(); gSmartObjectsUI.sName = condition->sName.c_str(); gSmartObjectsUI.sDescription = condition->sDescription.c_str(); gSmartObjectsUI.bEnabled = condition->bEnabled; if (m_Description->toPlainText() != condition->sDescription.c_str()) { m_Description->setPlainText(condition->sDescription.c_str()); } m_Properties->EnableUpdateCallback(true); } } ////////////////////////////////////////////////////////////////////////// void CSmartObjectsEditorDialog::OnUpdateProperties(IVariable* pVar) { auto currentCondition = m_View->currentIndex().data(Qt::UserRole).value(); if (currentCondition) { if (!CSOLibrary::StartEditing()) { return; } if (pVar->GetDataType() == IVariable::DT_SOTEMPLATE) { // Template changed -> special handling here... QString name; pVar->Get(name); const MapTemplates& mapTemplates = GetIEditor()->GetAI()->GetMapTemplates(); MapTemplates::const_iterator it, itEnd = mapTemplates.end(); for (it = mapTemplates.begin(); it != itEnd; ++it) { if (it->second->name == name) { break; } } // is it a valid template name? if (it == itEnd) { QMessageBox::critical(this, QString(), tr("Specified template is not defined!")); it = mapTemplates.find(currentCondition->iTemplateId); if (it != itEnd) { m_Properties->EnableUpdateCallback(false); pVar->Set(it->second->name); m_Properties->EnableUpdateCallback(true); } return; } // does it differs from current? if (currentCondition->iTemplateId != it->first) { // try to set default template values if (ChangeTemplate(currentCondition, it->second->params)) { currentCondition->iTemplateId = it->first; // Update variables. //m_Properties.EnableUpdateCallback( false ); //gSmartObjectsUI.SetConditionToUI( *m_pEditedEntry->m_pCondition, &m_Properties ); //m_Properties.EnableUpdateCallback( true ); } } return; } // Update variables. //m_Properties.EnableUpdateCallback( false ); if (gSmartObjectsUI.GetConditionFromUI(*currentCondition)) { // folder property was edited m_bSinkNeeded = true; // make sure the folder exists in the tree view findChild()->setSourceModel(m_model); SetCurrentFolder(currentCondition->sFolder.c_str()); const auto matches = m_model->match(m_model->index(0, 0), Qt::UserRole, QVariant::fromValue(currentCondition), 1, Qt::MatchRecursive | Qt::MatchWrap); m_model->setData(matches.first(), QVariant::fromValue(currentCondition), Qt::UserRole); /* HTREEITEM root = m_Tree.GetRootItem(); CString folder = m_pEditedEntry->m_pCondition->sFolder.c_str(); CString realFolder; HTREEITEM item = root; int token = 0; CString name = folder.Tokenize( _T("/\\"), token ); while ( !name.IsEmpty() ) { m_Tree.Expand( item, TVE_EXPAND ); HTREEITEM child = m_Tree.GetChildItem( item ); while ( child && m_Tree.GetItemText( child ).CompareNoCase( name ) != 0 ) child = m_Tree.GetNextSiblingItem( child ); if ( child ) { name = m_Tree.GetItemText( child ); item = child; } else item = m_Tree.InsertItem( name, 2, 2, item, TVI_SORT ); realFolder += _T('/'); realFolder += name; name = folder.Tokenize( _T("/\\"), token ); } m_pEditedEntry->m_pCondition->sFolder = realFolder; m_Tree.SelectItem( item ); */ } //m_Properties.EnableUpdateCallback( true ); if (pVar->GetDataType() == IVariable::DT_SOCLASS || // pVar->GetDataType() == IVariable::DT_SOSTATE || pVar->GetDataType() == IVariable::DT_SOSTATES || pVar->GetDataType() == IVariable::DT_SOSTATEPATTERN || pVar->GetDataType() == IVariable::DT_SOACTION || pVar == &gSmartObjectsUI.sName || pVar == &gSmartObjectsUI.sDescription || pVar == &gSmartObjectsUI.bEnabled) // pVar->GetDataType() == IVariable::DT_OBJECT ) { m_View->model()->setData(m_View->currentIndex(), QVariant::fromValue(currentCondition), Qt::UserRole); m_Description->setPlainText(gSmartObjectsUI.sDescription); } struct Local { static void CopyClassToHelper(const QString& sClass, CVariable< QString >& var) { QString sHelper; if (!sClass.isEmpty()) { var.Get(sHelper); int f = sHelper.lastIndexOf(':'); if (f >= 0) { sHelper.remove(0, f + 1); } sHelper = sClass + ':' + sHelper; } var.Set(sHelper); } }; // if class was changed update helper value //m_Properties.EnableUpdateCallback( false ); if (pVar->GetDataType() == IVariable::DT_SOCLASS) { if (pVar == &gSmartObjectsUI.userClass) { QString sHelper, sClass; pVar->Get(sClass); Local::CopyClassToHelper(sClass, gSmartObjectsUI.userHelper); } else if (pVar == &gSmartObjectsUI.objectClass) { QString sHelper, sClass; pVar->Get(sClass); Local::CopyClassToHelper(sClass, gSmartObjectsUI.objectHelper); Local::CopyClassToHelper(sClass, gSmartObjectsUI.entranceHelper); Local::CopyClassToHelper(sClass, gSmartObjectsUI.exitHelper); Local::CopyClassToHelper(sClass, gSmartObjectsUI.animationHelper); Local::CopyClassToHelper(sClass, gSmartObjectsUI.approachHelper); } } //m_Properties.EnableUpdateCallback( true ); if (pVar == &gSmartObjectsUI.bNavigationRule) { UpdatePropertyTables(); } } } ////////////////////////////////////////////////////////////////////////// // Called by the editor to notify the listener about the specified event. void CSmartObjectsEditorDialog::OnEditorNotifyEvent(EEditorNotifyEvent event) { if (event == eNotify_OnIdleUpdate) // Sent every frame while editor is idle. { if (m_bSinkNeeded) { SinkSelection(); } return; } switch (event) { case eNotify_OnBeginGameMode: // Sent when editor goes to game mode. case eNotify_OnBeginSimulationMode: // Sent when simulation mode is started. case eNotify_OnBeginSceneSave: // Sent when document is about to be saved. case eNotify_OnQuit: // Sent before editor quits. case eNotify_OnBeginNewScene: // Sent when the document is begin to be cleared. case eNotify_OnBeginSceneOpen: // Sent when document is about to be opened. case eNotify_OnCloseScene: // Send when the document is about to close. OnHelpersDone(); if (CSOLibrary::m_bSaveNeeded && SaveSOLibrary()) { CSOLibrary::m_bSaveNeeded = false; CSOLibrary::m_bLoadNeeded = false; } break; case eNotify_OnInit: // Sent after editor fully initialized. case eNotify_OnEndSceneOpen: // Sent after document have been opened. // ReloadEntries(); break; case eNotify_OnEndSceneSave: // Sent after document have been saved. case eNotify_OnEndNewScene: // Sent after the document have been cleared. case eNotify_OnMissionChange: // Send when the current mission changes. break; ////////////////////////////////////////////////////////////////////////// // Editing events. ////////////////////////////////////////////////////////////////////////// case eNotify_OnEditModeChange: // Sent when editing mode change (move,rotate,scale,....) case eNotify_OnEditToolChange: // Sent when edit tool is changed (ObjectMode,TerrainModify,....) break; // Game related events. case eNotify_OnEndGameMode: // Send when editor goes out of game mode. break; // UI events. case eNotify_OnUpdateViewports: // Sent when editor needs to update data in the viewports. case eNotify_OnReloadTrackView: // Sent when editor needs to update the track view. case eNotify_OnInvalidateControls: // Sent when editor needs to update some of the data that can be cached by controls like combo boxes. break; // Object events. case eNotify_OnSelectionChange: // Sent when object selection change. // Unfortunately I have never received this notification!!! // SinkSelection(); break; case eNotify_OnPlaySequence: // Sent when editor start playing animation sequence. case eNotify_OnStopSequence: // Sent when editor stop playing animation sequence. break; } } void CSOLibrary::String2Classes(const string& sClass, SetStrings& classes) { Load(); int start = 0, end, length = sClass.length(); string single; while (start < length) { start = sClass.find_first_of("ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz", start); if (start < 0) { break; } end = sClass.find_first_not_of("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz", start); if (end == -1) { end = length; } single = sClass.substr(start, end - start); classes.insert(single); start = end + 1; } ; } void CSmartObjectsEditorDialog::ParseClassesFromProperties(CBaseObject* pObject, SetStrings& classes) { if ((pObject->GetType() & OBJTYPE_ENTITY) || (pObject->GetType() & OBJTYPE_AIPOINT) && (!QString::compare(pObject->GetTypeName(), "AIAnchor") || !QString::compare(pObject->GetTypeName(), "SmartObject"))) { CEntityObject* pEntity = static_cast< CEntityObject* >(pObject); CEntityPrototype* pPrototype = pEntity->GetPrototype(); CVarBlock* pVars = pPrototype ? pPrototype->GetProperties() : pEntity->GetProperties(); if (pVars) { IVariablePtr pVariable = pVars->FindVariable("soclasses_SmartObjectClass", false); if (pVariable) { QString value; pVariable->Get(value); if (!value.isEmpty()) { CSOLibrary::String2Classes(value.toUtf8().data(), classes); } } } pVars = pEntity->GetProperties2(); if (pVars) { IVariablePtr pVariable = pVars->FindVariable("soclasses_SmartObjectClass", false); if (pVariable) { QString value; pVariable->Get(value); if (!value.isEmpty()) { CSOLibrary::String2Classes(value.toUtf8().data(), classes); } } } } } void CSmartObjectsEditorDialog::SinkSelection() { m_bSinkNeeded = false; // get selected folder QString folder = GetCurrentFolderPath(); m_sNewObjectClass.clear(); m_sFilterClasses.clear(); m_sFirstFilterClass.clear(); SetStrings filterClasses; CSelectionGroup* pSelection = GetIEditor()->GetSelection(); int selCount = pSelection->GetCount(); m_pItemHelpersDelete->setEnabled(!m_sEditedClass.isEmpty() && selCount == 1 && qobject_cast(pSelection->GetObject(0))); // Update helpers m_bIgnoreNotifications = true; if (m_sEditedClass.isEmpty()) { MapHelperObjects::iterator it, itEnd = m_mapHelperObjects.end(); for (it = m_mapHelperObjects.begin(); it != itEnd; ++it) { CSmartObjectHelperObject* pHelperObject = it->second; pHelperObject->RemoveEventListener(functor(*this, &CSmartObjectsEditorDialog::OnObjectEvent)); pHelperObject->DetachThis(); pHelperObject->Release(); GetIEditor()->GetObjectManager()->DeleteObject(pHelperObject); } m_mapHelperObjects.clear(); } else { typedef std::set< CEntityObject* > SetEntities; SetEntities setEntities; int i = selCount; // build a list of selected entities which smart object class matches 'm_sEditedClass' while (i--) { CBaseObject* pSelected = pSelection->GetObject(i); if (pSelected->GetType() & OBJTYPE_OTHER) { if (qobject_cast(pSelected)) { pSelected = pSelected->GetParent(); } } SetStrings classes; ParseClassesFromProperties(pSelected, classes); if (classes.find(m_sEditedClass.toUtf8().data()) != classes.end()) { setEntities.insert(static_cast< CEntityObject* >(pSelected)); } } // remove not needed helpers MapHelperObjects::iterator it, next, itEnd = m_mapHelperObjects.end(); for (it = m_mapHelperObjects.begin(); it != itEnd; ++it) { CEntityObject* const pEntity = it->first; CSmartObjectHelperObject* pHelperObject = it->second; if (setEntities.find(pEntity) == setEntities.end()) { // remove all helpers for this entity it->second = NULL; pHelperObject->RemoveEventListener(functor(*this, &CSmartObjectsEditorDialog::OnObjectEvent)); pHelperObject->DetachThis(); pHelperObject->Release(); GetIEditor()->GetObjectManager()->DeleteObject(pHelperObject); } else { // remove this helper if it isn't in the list of helpers for this class if (CSOLibrary::FindHelper(m_sEditedClass, pHelperObject->GetName()) == CSOLibrary::m_vHelpers.end()) { it->second = NULL; pHelperObject->RemoveEventListener(functor(*this, &CSmartObjectsEditorDialog::OnObjectEvent)); pHelperObject->DetachThis(); pHelperObject->Release(); GetIEditor()->GetObjectManager()->DeleteObject(pHelperObject); } } } // remove NULL helpers it = m_mapHelperObjects.begin(); while (it != itEnd) { next = it; next++; if (!it->second) { m_mapHelperObjects.erase(it); } it = next; } // for each helper of edited class... CSOLibrary::VectorHelperData::iterator itHelpers, itHelpersEnd = CSOLibrary::HelpersUpperBound(m_sEditedClass); for (itHelpers = CSOLibrary::HelpersLowerBound(m_sEditedClass); itHelpers != itHelpersEnd; ++itHelpers) { const CSOLibrary::CHelperData& helper = *itHelpers; // ... and for each entity SetEntities::iterator it, itEnd = setEntities.end(); for (it = setEntities.begin(); it != itEnd; ++it) { CEntityObject* pEntity = *it; // create/update child helper object CSmartObjectHelperObject* pHelperObject = NULL; int i = pEntity->GetChildCount(); while (i--) { CBaseObject* pChild = pEntity->GetChild(i); if ((pChild->GetType() & OBJTYPE_OTHER) != 0 && qobject_cast(pChild)) { pHelperObject = static_cast< CSmartObjectHelperObject* >(pChild); if (helper.name == pHelperObject->GetName()) { break; } else { pHelperObject = NULL; } } } if (!pHelperObject) { pHelperObject = static_cast< CSmartObjectHelperObject* >(GetIEditor()->GetObjectManager()->NewObject("SmartObjectHelper")); if (pHelperObject) { pHelperObject->AddRef(); pEntity->AttachChild(pHelperObject); m_mapHelperObjects.insert(std::make_pair(pEntity, pHelperObject)); pHelperObject->AddEventListener(functor(*this, &CSmartObjectsEditorDialog::OnObjectEvent)); } } if (pHelperObject) { pHelperObject->SetSmartObjectEntity(pEntity); GetIEditor()->GetObjectManager()->ChangeObjectName(pHelperObject, helper.name); //Vec3 pos = pHelper->qt.t //Vec3 pos = pEntity->GetWorldTM().GetInvertedFast().TransformPoint( GetWorldPos() ); //pVar->Set( pos ); pHelperObject->SetPos(helper.qt.t, eObjectUpdateFlags_PositionChanged); pHelperObject->SetRotation(helper.qt.q, eObjectUpdateFlags_RotationChanged); } } } } m_bIgnoreNotifications = false; // Get the selection once again since it might be changed pSelection = GetIEditor()->GetSelection(); selCount = pSelection->GetCount(); m_pItemHelpersDelete->setEnabled(!m_sEditedClass.isEmpty() && selCount == 1 && qobject_cast(pSelection->GetObject(0))); // make a list of all selected SmartObjectClass properties int j = selCount; while (j--) { CBaseObject* pObject = pSelection->GetObject(j); SetStrings classes; ParseClassesFromProperties(pObject, classes); if (classes.empty()) { filterClasses.clear(); break; } if (!m_bFilterCanceled) { filterClasses.insert(classes.begin(), classes.end()); } } // Update report table m_pathModel->setFilterClasses(filterClasses); SetStrings::iterator it, itEnd = filterClasses.end(); for (it = filterClasses.begin(); it != itEnd; ++it) { if (m_sFilterClasses.isEmpty()) { m_sFilterClasses += *it; m_sFirstFilterClass = *it; } else { m_sFilterClasses += ',' + *it; } } m_topLabel->setText(m_sFilterClasses); m_topLabel->setVisible(!m_sFilterClasses.isEmpty()); } ////////////////////////////////////////////////////////////////////////// void CSmartObjectsEditorDialog::DeleteHelper(const QString& className, const QString& helperName) { m_bSinkNeeded = true; CSOLibrary::m_bSaveNeeded = true; CSOLibrary::VectorHelperData::iterator it = CSOLibrary::FindHelper(className, helperName); if (it != CSOLibrary::m_vHelpers.end()) { CSOLibrary::m_vHelpers.erase(it); } } ////////////////////////////////////////////////////////////////////////// CSOLibrary::VectorHelperData::iterator CSOLibrary::FindHelper(const QString& className, const QString& helperName) { Load(); VectorHelperData::iterator it, itEnd = m_vHelpers.end(); for (it = m_vHelpers.begin(); it != itEnd; ++it) { if (it->className == className && it->name == helperName) { return it; } } return itEnd; } ////////////////////////////////////////////////////////////////////////// CSOLibrary::VectorHelperData::iterator CSOLibrary::HelpersLowerBound(const QString& className) { Load(); VectorHelperData::iterator it, itEnd = m_vHelpers.end(); for (it = m_vHelpers.begin(); it != itEnd; ++it) { if (it->className >= className) { return it; } } return itEnd; } ////////////////////////////////////////////////////////////////////////// CSOLibrary::VectorHelperData::iterator CSOLibrary::HelpersUpperBound(const QString& className) { Load(); VectorHelperData::iterator it, itEnd = m_vHelpers.end(); for (it = m_vHelpers.begin(); it != itEnd; ++it) { if (it->className > className) { return it; } } return itEnd; } ////////////////////////////////////////////////////////////////////////// CSOLibrary::VectorEventData::iterator CSOLibrary::FindEvent(const char* name) { Load(); VectorEventData::iterator it, itEnd = m_vEvents.end(); for (it = m_vEvents.begin(); it != itEnd; ++it) { if (it->name == name) { return it; } } return itEnd; } ////////////////////////////////////////////////////////////////////////// void CSOLibrary::AddEvent(const char* name, const char* description) { Load(); m_bSaveNeeded = true; VectorEventData::iterator it, itEnd = m_vEvents.end(); for (it = m_vEvents.begin(); it != itEnd; ++it) { if (it->name > name) { break; } } CEventData event; event.name = name; event.description = description; m_vEvents.insert(it, event); } ////////////////////////////////////////////////////////////////////////// CSOLibrary::VectorStateData::iterator CSOLibrary::FindState(const char* name) { Load(); CStateData stateData; stateData.name = name; VectorStateData::iterator it = std::lower_bound(m_vStates.begin(), m_vStates.end(), stateData, less_name< CStateData >()); if (it != m_vStates.end() && it->name == name) { return it; } else { return m_vStates.end(); } } ////////////////////////////////////////////////////////////////////////// void CSOLibrary::AddState(const char* name, const char* description, const char* location) { Load(); m_bSaveNeeded = true; CStateData stateData = { name, description, location }; VectorStateData::iterator it = std::lower_bound(m_vStates.begin(), m_vStates.end(), stateData, less_name< CStateData >()); if (it != m_vStates.end() && it->name == name) { it->description = description; it->location = location; } else { m_vStates.insert(it, stateData); } } ////////////////////////////////////////////////////////////////////////// CSOLibrary::VectorClassData::iterator CSOLibrary::FindClass(const char* name) { Load(); CClassData classData; classData.name = name; VectorClassData::iterator it = std::lower_bound(m_vClasses.begin(), m_vClasses.end(), classData, less_name< CClassData >()); if (it != m_vClasses.end() && it->name == name) { return it; } else { return m_vClasses.end(); } } ////////////////////////////////////////////////////////////////////////// void CSOLibrary::AddClass(const char* name, const char* description, const char* location, const char* templateName) { Load(); m_bSaveNeeded = true; CClassTemplateData const* pTemplate = FindClassTemplate(templateName); CClassData classData = { name, description, location, templateName, pTemplate }; VectorClassData::iterator it = std::lower_bound(m_vClasses.begin(), m_vClasses.end(), classData, less_name< CClassData >()); if (it != m_vClasses.end() && it->name == name) { it->description = description; it->location = location; it->templateName = templateName; it->pClassTemplateData = pTemplate; } else { m_vClasses.insert(it, classData); } } ////////////////////////////////////////////////////////////////////////// CSOLibrary::CClassTemplateData const* CSOLibrary::FindClassTemplate(const char* name) { if (!name || !*name) { return NULL; } Load(); CClassTemplateData classTemplateData; classTemplateData.name = name; VectorClassTemplateData::iterator it = std::lower_bound(m_vClassTemplates.begin(), m_vClassTemplates.end(), classTemplateData, less_name_no_case< CClassTemplateData >()); if (it != m_vClassTemplates.end() && _stricmp(it->name, name) == 0) { it->name = name; return &*it; } else { return NULL; } } ////////////////////////////////////////////////////////////////////////// void CSmartObjectsEditorDialog::OnObjectEvent(CBaseObject* object, int event) { if (!m_bIgnoreNotifications) { switch (event) { case CBaseObject::ON_DELETE: // Sent after object was deleted from object manager. if (!m_sEditedClass.isEmpty() && qobject_cast(object)) { CSmartObjectHelperObject* pHelperObject = (CSmartObjectHelperObject*) object; DeleteHelper(m_sEditedClass, object->GetName()); } m_bSinkNeeded = true; break; case CBaseObject::ON_SELECT: // Sent when objects becomes selected. case CBaseObject::ON_UNSELECT: // Sent when objects unselected. m_bSinkNeeded = true; m_bFilterCanceled = false; break; case CBaseObject::ON_TRANSFORM: // Sent when object transformed. if (static_cast(object)) { CSmartObjectHelperObject* pHelperObject = (CSmartObjectHelperObject*) object; CSOLibrary::VectorHelperData::iterator it = CSOLibrary::FindHelper(m_sEditedClass, pHelperObject->GetName()); if (it != CSOLibrary::m_vHelpers.end()) { it->qt.t = object->GetPos(); it->qt.q = object->GetRotation(); m_bSinkNeeded = true; CSOLibrary::m_bSaveNeeded = true; } } break; case CBaseObject::ON_VISIBILITY: // Sent when object visibility changes. case CBaseObject::ON_RENAME: // Sent when object changes name. case CBaseObject::ON_ADD: // Sent after object was added to object manager. break; } } } ////////////////////////////////////////////////////////////////////////// void CSmartObjectsEditorDialog::ModifyRuleOrder(int from, int to) { SmartObjectConditions::iterator it = CSOLibrary::m_Conditions.begin(), itEnd = CSOLibrary::m_Conditions.end(); if (from < to) { for (; it != itEnd; ++it) { int order = it->iOrder; if (it->iOrder == from) { it->iOrder = to; } else if (from < it->iOrder && it->iOrder <= to) { it->iOrder--; } } } else { for (; it != itEnd; ++it) { if (it->iOrder == from) { it->iOrder = to; } else if (to <= it->iOrder && it->iOrder < from) { it->iOrder++; } } } } ////////////////////////////////////////////////////////////////////////// void CSmartObjectsEditorDialog::OnAddEntry() { } ////////////////////////////////////////////////////////////////////////// void CSmartObjectsEditorDialog::OnEditEntry() { // ReloadEntries(); CSOLibrary::m_bSaveNeeded = true; } ////////////////////////////////////////////////////////////////////////// void CSmartObjectsEditorDialog::OnRemoveEntry() { } ////////////////////////////////////////////////////////////////////////// void CSmartObjectsEditorDialog::OnDuplicateEntry() { auto cond = m_View->currentIndex().data(Qt::UserRole).value(); if (!cond) { return; } SmartObjectCondition condition = *cond; int order = condition.iOrder + 1; condition.iOrder = INT_MAX; CItemDescriptionDlg dlg(this, true, true); dlg.setWindowTitle(tr("Duplicate Smart Object Rule")); dlg.setItemCaption(tr("Rule name:")); dlg.setItem(tr("Copy of %1").arg(condition.sName.c_str())); dlg.setDescription(condition.sDescription.c_str()); if (!dlg.exec()) { return; } condition.sName = dlg.item().toUtf8().data(); condition.sDescription = dlg.description().toUtf8().data(); m_model->addCondition(condition); ModifyRuleOrder(INT_MAX, order); condition.iOrder = order; // to select the right row // now select duplicated row const QModelIndexList matches = m_View->model()->match(m_View->model()->index(0, 0), Qt::DisplayRole, QString(condition.sName.c_str()), 1, Qt::MatchFixedString); if (!matches.isEmpty()) { m_View->setCurrentIndex(matches.first()); } CSOLibrary::m_bSaveNeeded = true; } ////////////////////////////////////////////////////////////////////////// void CSmartObjectsEditorDialog::OnHelpersEdit() { if (!CheckOutLibrary()) { return; } if (m_sEditedClass.isEmpty()) { QString sClass = m_sFirstFilterClass; auto condition = m_View->currentIndex().data(Qt::UserRole).value(); if (condition && sClass.isEmpty()) { if (!condition->sObjectClass.empty()) { sClass = condition->sObjectClass; } else { sClass = condition->sUserClass; } } CSmartObjectClassDialog dlg(this); dlg.SetSOClass(sClass); dlg.EnableOK(); if (dlg.exec() == QDialog::Accepted) { sClass = dlg.GetSOClass(); if (!sClass.isEmpty()) { m_sEditedClass = sClass; m_pItemHelpersNew->setEnabled(true); m_pItemHelpersDone->setEnabled(true); m_pItemHelpersEdit->setText(tr("Editing %1").arg(sClass)); QFont f; f.setBold(true); m_pItemHelpersEdit->setFont(f); m_bSinkNeeded = true; } } } else if (!m_mapHelperObjects.empty()) { QString sSelectedHelper; CBaseObject* pSelectedObject = GetIEditor()->GetSelectedObject(); if (!pSelectedObject) { return; } if (qobject_cast(pSelectedObject)) { sSelectedHelper = pSelectedObject->GetName(); } CSmartObjectHelperDialog dlg(this, false, false, false); dlg.SetSOHelper(m_sEditedClass, sSelectedHelper); if (dlg.exec() == QDialog::Accepted) { sSelectedHelper = dlg.GetSOHelper(); MapHelperObjects::iterator it, itEnd = m_mapHelperObjects.end(); for (it = m_mapHelperObjects.begin(); it != itEnd; ++it) { if (it->second->GetName() == sSelectedHelper) { GetIEditor()->SelectObject(it->second); GetIEditor()->GetObjectManager()->UnselectObject(pSelectedObject); break; } } } } } ////////////////////////////////////////////////////////////////////////// void CSmartObjectsEditorDialog::OnHelpersNew() { if (!m_sEditedClass.isEmpty()) { CItemDescriptionDlg dlg(this); dlg.setWindowTitle(QObject::tr("New Helper")); dlg.setItemCaption(QObject::tr("Helper name:")); if (dlg.exec()) { CSOLibrary::VectorHelperData::iterator it = CSOLibrary::FindHelper(m_sEditedClass, dlg.item()); if (it != CSOLibrary::m_vHelpers.end()) { QMessageBox::information(this, QString(), tr("The specified smart object helper name is already used.\n\nCreation canceled...")); return; } CSOLibrary::CHelperData helper; helper.className = m_sEditedClass; helper.qt.t.zero(); helper.qt.q.SetIdentity(); helper.name = dlg.item(); helper.description = dlg.description(); it = CSOLibrary::HelpersUpperBound(m_sEditedClass); CSOLibrary::m_vHelpers.insert(it, helper); CSOLibrary::m_bSaveNeeded = true; // this will eventually show the new helper m_bSinkNeeded = true; SinkSelection(); // try to select the newly created helper CBaseObject* pSelected = GetIEditor()->GetSelectedObject(); if (pSelected) { if (qobject_cast(pSelected)) { pSelected = pSelected->GetParent(); } int i = pSelected->GetChildCount(); while (i--) { CBaseObject* pChild = pSelected->GetChild(i); if (pChild->GetName() == helper.name) { GetIEditor()->ClearSelection(); GetIEditor()->SelectObject(pChild); break; } } } } } } ////////////////////////////////////////////////////////////////////////// void CSmartObjectsEditorDialog::OnHelpersDelete() { CSelectionGroup* pSelection = GetIEditor()->GetSelection(); int selCount = pSelection->GetCount(); if (!m_sEditedClass.isEmpty() && selCount == 1 && qobject_cast(pSelection->GetObject(0))) { const QString msg = tr("Delete helper '%1'?").arg(pSelection->GetObject(0)->GetName()); if (QMessageBox::warning(this, QString(), msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) { DeleteHelper(m_sEditedClass, pSelection->GetObject(0)->GetName()); } } } ////////////////////////////////////////////////////////////////////////// void CSmartObjectsEditorDialog::OnHelpersDone() { if (!m_sEditedClass.isEmpty()) { m_bIgnoreNotifications = true; MapHelperObjects::iterator it, itEnd = m_mapHelperObjects.end(); for (it = m_mapHelperObjects.begin(); it != itEnd; ++it) { CSmartObjectHelperObject* pHelperObject = it->second; pHelperObject->RemoveEventListener(functor(*this, &CSmartObjectsEditorDialog::OnObjectEvent)); pHelperObject->DetachThis(); pHelperObject->Release(); GetIEditor()->GetObjectManager()->DeleteObject(pHelperObject); } m_mapHelperObjects.clear(); m_bIgnoreNotifications = false; m_sEditedClass.clear(); m_pItemHelpersEdit->setText(tr("Edit...")); m_pItemHelpersEdit->setFont(QFont()); m_pItemHelpersNew->setEnabled(false); m_pItemHelpersDelete->setEnabled(false); m_pItemHelpersDone->setEnabled(false); m_bSinkNeeded = true; } } ////////////////////////////////////////////////////////////////////////// void CSmartObjectsEditorDialog::EnableIfOneSelected(QWidget* target) { target->setEnabled(m_View->selectionModel()->selectedRows().count() == 1); } ////////////////////////////////////////////////////////////////////////// void CSmartObjectsEditorDialog::EnableIfSelected(QWidget* target) { target->setEnabled(m_View->selectionModel()->hasSelection()); } ////////////////////////////////////////////////////////////////////////// void CSmartObjectsEditorDialog::closeEvent(QCloseEvent* event) { // This will call CSmartObjectsEditorDialog::OnUpdateProperties if needed. // It must be done here to ensure applying changes on the edited rule! m_Properties->SelectItem(0); QMainWindow::closeEvent(event); } void SmartObjectsEditorDialogLabel::mouseMoveEvent(QMouseEvent* event) { update(); } void SmartObjectsEditorDialogLabel::mousePressEvent(QMouseEvent* event) { if (event->modifiers() == Qt::NoModifier && event->buttons() == Qt::LeftButton && m_rcCloseBtn.contains(event->pos())) { m_closing = true; } event->setAccepted(m_closing); update(); } void SmartObjectsEditorDialogLabel::mouseReleaseEvent(QMouseEvent* event) { if (event->modifiers() == Qt::NoModifier && event->buttons() == Qt::NoButton && m_closing && m_rcCloseBtn.contains(event->pos())) { emit closeRequested(); } if (event->button() == Qt::LeftButton) { m_closing = false; } update(); } ////////////////////////////////////////////////////////////////////////// void SmartObjectsEditorDialogLabel::paintEvent(QPaintEvent* event) { //if (!m_sFilterClasses.IsEmpty()) { QPainter painter(this); QRect rc = rect().adjusted(0, 0, 0, -2); painter.fillRect(rc, palette().toolTipBase()); painter.setPen(palette().toolTipText().color()); QRect rcText(rc); rcText.setRight(max(rcText.left(), rcText.right() - 19)); painter.drawLine(rcText.right() + 3, rcText.top() + 6, rcText.right() + 11, rcText.top() + 14); painter.drawLine(rcText.right() + 4, rcText.top() + 6, rcText.right() + 12, rcText.top() + 14); painter.drawLine(rcText.right() + 3, rcText.top() + 14, rcText.right() + 11, rcText.top() + 6); painter.drawLine(rcText.right() + 4, rcText.top() + 14, rcText.right() + 12, rcText.top() + 6); m_rcCloseBtn = QRect(QPoint(rcText.right() + 1, rcText.top() + 4), QPoint(rcText.right() + 15, rcText.bottom() - 4)); if (text().contains(',')) { painter.drawText(rcText, Qt::AlignVCenter, painter.fontMetrics().elidedText(tr(" Filtered by classes: %1").arg(text()), Qt::ElideRight, rcText.width())); } else { painter.drawText(rcText, Qt::AlignVCenter, painter.fontMetrics().elidedText(tr(" Filtered by class: %1").arg(text()), Qt::ElideRight, rcText.width())); } painter.setPen(palette().shadow().color()); if (m_rcCloseBtn.contains(mapFromGlobal(QCursor::pos()))) { painter.drawRect(m_rcCloseBtn.adjusted(0, 0, -1, -1)); } painter.drawRect(rc.adjusted(0, 0, -1, -1)); rc.setTop(rc.bottom()); rc.setBottom(rc.bottom() + 2); painter.fillRect(rc, palette().button()); } } ////////////////////////////////////////////////////////////////////////// void CSmartObjectsEditorDialog::OnDestroy() { QSettings settings("Amazon", "Lumberyard"); settings.setValue(QStringLiteral("SmartObjectsLayout"), saveState()); } ////////////////////////////////////////////////////////////////////////// #define ID_REMOVE_ITEM 1 #define ID_SORT_ASC 2 #define ID_SORT_DESC 3 #define ID_GROUP_BYTHIS 4 #define ID_SHOW_GROUPBOX 5 #define ID_SHOW_FIELDCHOOSER 6 #define ID_COLUMN_BESTFIT 7 #define ID_COLUMN_ARRANGEBY 100 #define ID_COLUMN_ALIGMENT 200 #define ID_COLUMN_ALIGMENT_LEFT ID_COLUMN_ALIGMENT + 1 #define ID_COLUMN_ALIGMENT_RIGHT ID_COLUMN_ALIGMENT + 2 #define ID_COLUMN_ALIGMENT_CENTER ID_COLUMN_ALIGMENT + 3 void CSmartObjectsEditorDialog::OnReportColumnRClick(const QPoint& point) { QMenu menu; connect(menu.addAction(tr("Name/Description")), &QAction::triggered, m_View, [=]() { m_View->header()->setSectionHidden(SmartObjectsEditorModel::ColumnName, false); m_View->header()->setSectionHidden(SmartObjectsEditorModel::ColumnDescription, false); m_View->header()->setSectionHidden(SmartObjectsEditorModel::ColumnUserClass, true); m_View->header()->setSectionHidden(SmartObjectsEditorModel::ColumnUserState, true); m_View->header()->setSectionHidden(SmartObjectsEditorModel::ColumnObjectClass, true); m_View->header()->setSectionHidden(SmartObjectsEditorModel::ColumnObjectState, true); m_View->header()->setSectionHidden(SmartObjectsEditorModel::ColumnAction, true); }); connect(menu.addAction(tr("Name/User/Object")), &QAction::triggered, m_View, [=]() { m_View->header()->setSectionHidden(SmartObjectsEditorModel::ColumnName, false); m_View->header()->setSectionHidden(SmartObjectsEditorModel::ColumnDescription, true); m_View->header()->setSectionHidden(SmartObjectsEditorModel::ColumnUserClass, false); m_View->header()->setSectionHidden(SmartObjectsEditorModel::ColumnUserState, false); m_View->header()->setSectionHidden(SmartObjectsEditorModel::ColumnObjectClass, false); m_View->header()->setSectionHidden(SmartObjectsEditorModel::ColumnObjectState, false); m_View->header()->setSectionHidden(SmartObjectsEditorModel::ColumnAction, false); }); menu.addSeparator(); // create columns items int nColumnCount = m_View->model()->columnCount(); for (int i = 0; i < nColumnCount; ++i) { const QString sCaption = m_View->model()->headerData(i, Qt::Horizontal).toString(); if (!sCaption.isEmpty()) { QAction* action = menu.addAction(sCaption); action->setCheckable(true); action->setChecked(!m_View->header()->isSectionHidden(i)); connect(action, &QAction::triggered, this, [=]() {m_View->header()->setSectionHidden(i, !action->isChecked()); }); } } // track menu menu.exec(m_View->header()->mapToGlobal(point)); } void CSmartObjectsEditorDialog::OnReportSelChanged() { // This will call CSmartObjectsEditorDialog::OnUpdateProperties if needed. // It must be done here to ensure applying changes on the edited rule! m_Properties->SelectItem(0); SmartObjectCondition* focused = m_View->currentIndex().data(Qt::UserRole).value(); if (!focused) { m_Properties->setEnabled(false); m_Description->clear(); return; } m_Properties->setEnabled(true); QSignalBlocker sb(m_Description); m_Description->setPlainText(focused->sDescription.c_str()); // Update variables. m_Properties->EnableUpdateCallback(false); gSmartObjectsUI.SetConditionToUI(*focused, m_Properties, this); m_Properties->EnableUpdateCallback(true); UpdatePropertyTables(); ///////m_View->setFocus(); EnableIfOneSelected(m_pDuplicate); EnableIfSelected(m_pDelete); } void CSmartObjectsEditorDialog::UpdatePropertyTables() { //bool nav = gSmartObjectsUI.bNavigationRule; //gSmartObjectsUI.entranceHelper.SetFlags( nav ? 0 : IVariable::UI_DISABLED ); //gSmartObjectsUI.exitHelper.SetFlags( nav ? 0 : IVariable::UI_DISABLED ); //m_Properties.Expand( m_Properties.FindItemByVar( &gSmartObjectsUI.navigationTable ), nav ); //gSmartObjectsUI.limitsDistanceFrom.SetFlags( nav ? IVariable::UI_DISABLED : 0 ); //gSmartObjectsUI.limitsDistanceTo.SetFlags( nav ? IVariable::UI_DISABLED : 0 ); //gSmartObjectsUI.limitsOrientation.SetFlags( nav ? IVariable::UI_DISABLED : 0 ); //m_Properties.Expand( m_Properties.FindItemByVar( &gSmartObjectsUI.limitsTable ), !nav ); //gSmartObjectsUI.delayMinimum.SetFlags( nav ? IVariable::UI_DISABLED : 0 ); //gSmartObjectsUI.delayMaximum.SetFlags( nav ? IVariable::UI_DISABLED : 0 ); //gSmartObjectsUI.delayMemory.SetFlags( nav ? IVariable::UI_DISABLED : 0 ); //m_Properties.Expand( m_Properties.FindItemByVar( &gSmartObjectsUI.delayTable ), !nav ); //gSmartObjectsUI.multipliersProximity.SetFlags( nav ? IVariable::UI_DISABLED : 0 ); //gSmartObjectsUI.multipliersOrientation.SetFlags( nav ? IVariable::UI_DISABLED : 0 ); //gSmartObjectsUI.multipliersVisibility.SetFlags( nav ? IVariable::UI_DISABLED : 0 ); //gSmartObjectsUI.multipliersRandomness.SetFlags( nav ? IVariable::UI_DISABLED : 0 ); //m_Properties.Expand( m_Properties.FindItemByVar( &gSmartObjectsUI.multipliersTable ), !nav ); m_Description->setPlainText(gSmartObjectsUI.sDescription); } void CSmartObjectsEditorDialog::OnTreeSelChanged() { m_bSinkNeeded = true; m_pathModel->setPath(GetCurrentFolderPath()); } void SmartObjectsEditorModel::MoveAllRules(const QString& from, const QString& to) { QString temp; QString from1 = from + QLatin1Char('/'); int len = from1.length(); SmartObjectConditions::iterator it, itEnd = CSOLibrary::GetConditions().end(); int i = 0; for (it = CSOLibrary::GetConditions().begin(); it != itEnd; ++it, ++i) { SmartObjectCondition& condition = *it; if (from.compare(condition.sFolder.c_str(), Qt::CaseInsensitive) == 0) { condition.sFolder = to.toUtf8().data(); } else { temp = condition.sFolder.substr(0, len).c_str(); if (from1.compare(temp, Qt::CaseInsensitive) == 0) { condition.sFolder = (to + QLatin1Char('/') + condition.sFolder.substr(len).c_str()).toUtf8().data(); setData(index(i, 0), QVariant::fromValue(&(*it)), Qt::UserRole); } } } } void CSmartObjectsEditorDialog::OnDescriptionEdit() { QModelIndex index = m_View->currentIndex(); index = index.sibling(index.row(), SmartObjectsEditorModel::ColumnDescription); m_View->model()->setData(index, m_Description->toPlainText()); gSmartObjectsUI.sDescription = m_Description->toPlainText().toUtf8().data(); } bool CSmartObjectsEditorDialog::CheckOutLibrary() { // This will call CSmartObjectsEditorDialog::OnUpdateProperties if needed. // It must be done here to ensure applying changes on the edited rule! m_Properties->SelectItem(0); return CSOLibrary::StartEditing(); } bool CSmartObjectsEditorDialog::SaveSOLibrary(bool updatePropertiesFirst) { if (updatePropertiesFirst) { // This will call CSmartObjectsEditorDialog::OnUpdateProperties if needed. // It must be done here to ensure applying changes on the edited rule! m_Properties->SelectItem(0); } return CSOLibrary::Save(); } CSOLibrary::~CSOLibrary() { m_Conditions.clear(); m_vHelpers.clear(); m_vEvents.clear(); m_vStates.clear(); m_vClassTemplates.clear(); m_vClasses.clear(); } bool CSOLibrary::StartEditing() { Load(); AZStd::string fullPath = Path::GetEditingGameDataFolder() + "/" + SMART_OBJECTS_XML; CFileUtil::CreateDirectory(Path::GetPath(fullPath.c_str()).toUtf8().data()); if (!CFileUtil::OverwriteFile(fullPath.c_str())) { return false; } m_bSaveNeeded = true; return true; } bool CSOLibrary::Save() { AZStd::string fullPath = Path::GetEditingGameDataFolder() + "/" + SMART_OBJECTS_XML; if (!CFileUtil::OverwriteFile(fullPath.c_str())) { return false; } // save to SmartObjects.xml QString sSavePath = fullPath.c_str(); if (!SaveToFile(sSavePath.toUtf8().data())) { Warning("CSOLibrary::Save() failed to save in %s", SMART_OBJECTS_XML); return false; } Log("CSOLibrary::Save() saved in %s", SMART_OBJECTS_XML); return true; } struct less_ptr { bool operator() (const SmartObjectCondition* _Left, const SmartObjectCondition* _Right) const { return *_Left < *_Right; } }; bool CSOLibrary::SaveToFile(const char* sFileName) { typedef std::vector< const SmartObjectCondition* > RulesList; RulesList sorted; SmartObjectConditions::iterator itUnsorted, itUnsortedEnd = m_Conditions.end(); for (itUnsorted = m_Conditions.begin(); itUnsorted != itUnsortedEnd; ++itUnsorted) { SmartObjectCondition* pCondition = &*itUnsorted; sorted.push_back(pCondition); } std::sort(sorted.begin(), sorted.end(), less_ptr()); XmlNodeRef root(GetISystem()->CreateXmlNode("SmartObjectsLibrary")); RulesList::iterator itRules = sorted.begin(); for (int i = 0; i < sorted.size(); ++i) { XmlNodeRef node(root->createNode("SmartObject")); const SmartObjectCondition& rule = **itRules; ++itRules; node->setAttr("iTemplateId", rule.iTemplateId); node->setAttr("sName", rule.sName); node->setAttr("sFolder", rule.sFolder); node->setAttr("sDescription", rule.sDescription); //node->setAttr( "iOrder", rule.iOrder ); node->setAttr("sUserClass", rule.sUserClass); node->setAttr("sUserState", rule.sUserState); node->setAttr("sUserHelper", rule.sUserHelper); node->setAttr("sObjectClass", rule.sObjectClass); node->setAttr("sObjectState", rule.sObjectState); node->setAttr("sObjectHelper", rule.sObjectHelper); node->setAttr("iMaxAlertness", rule.iMaxAlertness); node->setAttr("bEnabled", rule.bEnabled); node->setAttr("sEvent", rule.sEvent); node->setAttr("iRuleType", rule.iRuleType); node->setAttr("sEntranceHelper", rule.sEntranceHelper); node->setAttr("sExitHelper", rule.sExitHelper); node->setAttr("fMinDelay", rule.fMinDelay); node->setAttr("fMaxDelay", rule.fMaxDelay); node->setAttr("fMemory", rule.fMemory); node->setAttr("fDistanceFrom", rule.fDistanceFrom); node->setAttr("fDistanceLimit", rule.fDistanceTo); node->setAttr("fOrientationLimit", rule.fOrientationLimit); node->setAttr("bHorizLimitOnly", rule.bHorizLimitOnly); node->setAttr("fOrientToTargetLimit", rule.fOrientationToTargetLimit); node->setAttr("fProximityFactor", rule.fProximityFactor); node->setAttr("fOrientationFactor", rule.fOrientationFactor); node->setAttr("fRandomnessFactor", rule.fRandomnessFactor); node->setAttr("fVisibilityFactor", rule.fVisibilityFactor); node->setAttr("fLookAtOnPerc", rule.fLookAtOnPerc); node->setAttr("sObjectPreActionState", rule.sObjectPreActionState); node->setAttr("sUserPreActionState", rule.sUserPreActionState); node->setAttr("sAction", rule.sAction); node->setAttr("eActionType", rule.eActionType); node->setAttr("sObjectPostActionState", rule.sObjectPostActionState); node->setAttr("sUserPostActionState", rule.sUserPostActionState); node->setAttr("fApproachSpeed", rule.fApproachSpeed); node->setAttr("iApproachStance", rule.iApproachStance); node->setAttr("sAnimationHelper", rule.sAnimationHelper); node->setAttr("sApproachHelper", rule.sApproachHelper); node->setAttr("fStartWidth", rule.fStartWidth); node->setAttr("fStartDirectionTolerance", rule.fDirectionTolerance); node->setAttr("fStartArcAngle", rule.fStartArcAngle); root->addChild(node); } VectorClassData::iterator itClasses, itClassesEnd = m_vClasses.end(); for (itClasses = m_vClasses.begin(); itClasses != itClassesEnd; ++itClasses) { XmlNodeRef node(root->createNode("Class")); const CClassData& classData = *itClasses; node->setAttr("name", classData.name.toUtf8().data()); node->setAttr("description", classData.description.toUtf8().data()); node->setAttr("location", classData.location.toUtf8().data()); node->setAttr("template", classData.templateName.toUtf8().data()); root->addChild(node); } VectorStateData::iterator itStates, itStatesEnd = m_vStates.end(); for (itStates = m_vStates.begin(); itStates != itStatesEnd; ++itStates) { XmlNodeRef node(root->createNode("State")); const CStateData& stateData = *itStates; node->setAttr("name", stateData.name.toUtf8().data()); node->setAttr("description", stateData.description.toUtf8().data()); node->setAttr("location", stateData.location.toUtf8().data()); root->addChild(node); } VectorHelperData::const_iterator itHelpers, itHelpersEnd = m_vHelpers.end(); for (itHelpers = m_vHelpers.begin(); itHelpers != itHelpersEnd; ++itHelpers) { XmlNodeRef node(root->createNode("Helper")); const CHelperData& helper = *itHelpers; node->setAttr("className", helper.className.toUtf8().data()); node->setAttr("name", helper.name.toUtf8().data()); node->setAttr("pos", helper.qt.t); node->setAttr("rot", helper.qt.q); node->setAttr("description", helper.description.toUtf8().data()); root->addChild(node); } VectorEventData::iterator it, itEnd = m_vEvents.end(); for (it = m_vEvents.begin(); it != itEnd; ++it) { XmlNodeRef node(root->createNode("Event")); const CEventData& event = *it; node->setAttr("name", event.name.toUtf8().data()); node->setAttr("description", event.description.toUtf8().data()); root->addChild(node); } bool bSucceed = root->saveToFile(sFileName); if (bSucceed && gEnv->pAISystem) { gEnv->pAISystem->GetSmartObjectManager()->ReloadSmartObjectRules(); } return bSucceed; } void CSOLibrary::AddAllStates(const string& sStates) { if (!sStates.empty()) { SetStrings setStates; String2Classes(sStates, setStates); SetStrings::const_iterator it, itEnd = setStates.end(); for (it = setStates.begin(); it != itEnd; ++it) { AddState(*it, "", ""); } } } bool CSOLibrary::LoadFromFile(const char* sFileName) { XmlNodeRef root = GetISystem()->LoadXmlFromFile(sFileName); if (!root) { return false; } m_Conditions.clear(); m_vHelpers.clear(); m_vEvents.clear(); m_vStates.clear(); m_vClasses.clear(); CHelperData helper; SmartObjectCondition rule; int count = root->getChildCount(); for (int i = 0; i < count; ++i) { XmlNodeRef node = root->getChild(i); if (node->isTag("SmartObject")) { rule.iTemplateId = 0; node->getAttr("iTemplateId", rule.iTemplateId); rule.sName = node->getAttr("sName"); rule.sDescription = node->getAttr("sDescription"); rule.sFolder = node->getAttr("sFolder"); rule.sEvent = node->getAttr("sEvent"); node->getAttr("iMaxAlertness", rule.iMaxAlertness); node->getAttr("bEnabled", rule.bEnabled); rule.iRuleType = 0; node->getAttr("iRuleType", rule.iRuleType); // this was here to convert older data formats - not needed anymore. //float fPassRadius = 0.0f; // default value if not found in XML //node->getAttr( "fPassRadius", fPassRadius ); //if ( fPassRadius > 0 ) // rule.iRuleType = 1; rule.sEntranceHelper = node->getAttr("sEntranceHelper"); rule.sExitHelper = node->getAttr("sExitHelper"); rule.sUserClass = node->getAttr("sUserClass"); if (!rule.sUserClass.empty()) { AddClass(rule.sUserClass, "", "", ""); } rule.sUserState = node->getAttr("sUserState"); AddAllStates(rule.sUserState); rule.sUserHelper = node->getAttr("sUserHelper"); rule.sObjectClass = node->getAttr("sObjectClass"); if (!rule.sObjectClass.empty()) { AddClass(rule.sObjectClass, "", "", ""); } rule.sObjectState = node->getAttr("sObjectState"); AddAllStates(rule.sObjectState); rule.sObjectHelper = node->getAttr("sObjectHelper"); node->getAttr("fMinDelay", rule.fMinDelay); node->getAttr("fMaxDelay", rule.fMaxDelay); node->getAttr("fMemory", rule.fMemory); rule.fDistanceFrom = 0.0f; // default value if not found in XML node->getAttr("fDistanceFrom", rule.fDistanceFrom); node->getAttr("fDistanceLimit", rule.fDistanceTo); node->getAttr("fOrientationLimit", rule.fOrientationLimit); rule.bHorizLimitOnly = false; node->getAttr("bHorizLimitOnly", rule.bHorizLimitOnly); rule.fOrientationToTargetLimit = 360.0f; // default value if not found in XML node->getAttr("fOrientToTargetLimit", rule.fOrientationToTargetLimit); node->getAttr("fProximityFactor", rule.fProximityFactor); node->getAttr("fOrientationFactor", rule.fOrientationFactor); node->getAttr("fVisibilityFactor", rule.fVisibilityFactor); node->getAttr("fRandomnessFactor", rule.fRandomnessFactor); node->getAttr("fLookAtOnPerc", rule.fLookAtOnPerc); rule.sUserPreActionState = node->getAttr("sUserPreActionState"); AddAllStates(rule.sUserPreActionState); rule.sObjectPreActionState = node->getAttr("sObjectPreActionState"); AddAllStates(rule.sObjectPreActionState); rule.sAction = node->getAttr("sAction"); int iActionType; if (node->getAttr("eActionType", iActionType)) { rule.eActionType = (EActionType) iActionType; } else { rule.eActionType = eAT_None; bool bSignal = !rule.sAction.empty() && rule.sAction[0] == '%'; bool bAnimationSignal = !rule.sAction.empty() && rule.sAction[0] == '$'; bool bAnimationAction = !rule.sAction.empty() && rule.sAction[0] == '@'; bool bAction = !rule.sAction.empty() && !bSignal && !bAnimationSignal && !bAnimationAction; bool bHighPriority = rule.iMaxAlertness >= 100; node->getAttr("bHighPriority", bHighPriority); if (bHighPriority && bAction) { rule.eActionType = eAT_PriorityAction; } else if (bAction) { rule.eActionType = eAT_Action; } else if (bSignal) { rule.eActionType = eAT_AISignal; } else if (bAnimationSignal) { rule.eActionType = eAT_AnimationSignal; } else if (bAnimationAction) { rule.eActionType = eAT_AnimationAction; } if (bSignal || bAnimationSignal || bAnimationAction) { rule.sAction.erase(0, 1); } } rule.iMaxAlertness %= 100; rule.sUserPostActionState = node->getAttr("sUserPostActionState"); AddAllStates(rule.sUserPostActionState); rule.sObjectPostActionState = node->getAttr("sObjectPostActionState"); AddAllStates(rule.sObjectPostActionState); rule.fApproachSpeed = 1.0f; // default value if not found in XML node->getAttr("fApproachSpeed", rule.fApproachSpeed); rule.iApproachStance = 4; // default value if not found in XML node->getAttr("iApproachStance", rule.iApproachStance); rule.sAnimationHelper = ""; // default value if not found in XML rule.sAnimationHelper = node->getAttr("sAnimationHelper"); rule.sApproachHelper = ""; // default value if not found in XML rule.sApproachHelper = node->getAttr("sApproachHelper"); rule.fStartWidth = 0.0f; // default value if not found in XML node->getAttr("fStartWidth", rule.fStartWidth); rule.fDirectionTolerance = 0.0f; // default value if not found in XML node->getAttr("fStartDirectionTolerance", rule.fDirectionTolerance); rule.fStartArcAngle = 0.0f; // default value if not found in XML node->getAttr("fStartArcAngle", rule.fStartArcAngle); if (!node->getAttr("iOrder", rule.iOrder)) { rule.iOrder = i; } m_Conditions.push_back(rule); } else if (node->isTag("Helper")) { helper.className = node->getAttr("className"); helper.name = node->getAttr("name"); Vec3 pos; node->getAttr("pos", pos); Quat rot; node->getAttr("rot", rot); helper.qt = QuatT(rot, pos); helper.description = node->getAttr("description"); VectorHelperData::iterator it = HelpersUpperBound(helper.className); m_vHelpers.insert(it, helper); } else if (node->isTag("Event")) { CEventData event; event.name = node->getAttr("name"); event.description = node->getAttr("description"); VectorEventData::iterator it = std::upper_bound(m_vEvents.begin(), m_vEvents.end(), event, less_name< CEventData >()); m_vEvents.insert(it, event); } else if (node->isTag("State")) { QString name = node->getAttr("name"); QString description = node->getAttr("description"); QString location = node->getAttr("location"); AddState(name.toUtf8().data(), description.toUtf8().data(), location.toUtf8().data()); } else if (node->isTag("Class")) { QString name = node->getAttr("name"); QString description = node->getAttr("description"); QString location = node->getAttr("location"); QString templateName = node->getAttr("template"); AddClass(name.toUtf8().data(), description.toUtf8().data(), location.toUtf8().data(), templateName.toUtf8().data()); } } return true; } void CSmartObjectsEditorDialog::SetTemplateDefaults(SmartObjectCondition& condition, const CSOParamBase* param, QString* message /*= NULL*/) const { if (!param) { return; } if (param->bIsGroup) { const CSOParamGroup* pGroup = static_cast< const CSOParamGroup* >(param); SetTemplateDefaults(condition, pGroup->pChildren, message); } else { const CSOParam* pParam = static_cast< const CSOParam* >(param); if (!message || !pParam->bEditable) { if (message) { IVariable* pVar = pParam->pVariable ? pParam->pVariable : gSmartObjectsUI.ResolveVariable(pParam, const_cast(this)); // Report altering on visible properties only if (gSmartObjectsUI.m_vars->IsContainsVariable(pVar)) { QString temp; if (pParam->sName == "bNavigationRule") { temp = condition.iRuleType == 1 ? "1" : "0"; } else if (pParam->sName == "sEvent") { temp = condition.sEvent; } else if (pParam->sName == "sChainedUserEvent") { temp = condition.sChainedUserEvent; } else if (pParam->sName == "sChainedObjectEvent") { temp = condition.sChainedObjectEvent; } else if (pParam->sName == "userClass") { temp = condition.sUserClass; } else if (pParam->sName == "userState") { temp = condition.sUserState; } else if (pParam->sName == "userHelper") { temp = condition.sUserHelper; } else if (pParam->sName == "iMaxAlertness") { temp = QString::number(condition.iMaxAlertness); } else if (pParam->sName == "objectClass") { temp = condition.sObjectClass; } else if (pParam->sName == "objectState") { temp = condition.sObjectState; } else if (pParam->sName == "objectHelper") { temp = condition.sObjectHelper; } else if (pParam->sName == "entranceHelper") { temp = condition.sEntranceHelper; } else if (pParam->sName == "exitHelper") { temp = condition.sExitHelper; } else if (pParam->sName == "limitsDistanceFrom") { temp = QString::number(condition.fDistanceFrom); } else if (pParam->sName == "limitsDistanceTo") { temp = QString::number(condition.fDistanceTo); } else if (pParam->sName == "limitsOrientation") { temp = QString::number(condition.fOrientationLimit); } else if (pParam->sName == "horizLimitOnly") { temp = condition.bHorizLimitOnly ? "1" : "0"; } else if (pParam->sName == "limitsOrientationToTarget") { temp = QString::number(condition.fOrientationToTargetLimit); } else if (pParam->sName == "delayMinimum") { temp = QString::number(condition.fMinDelay); } else if (pParam->sName == "delayMaximum") { temp = QString::number(condition.fMaxDelay); } else if (pParam->sName == "delayMemory") { temp = QString::number(condition.fMemory); } else if (pParam->sName == "multipliersProximity") { temp = QString::number(condition.fProximityFactor); } else if (pParam->sName == "multipliersOrientation") { temp = QString::number(condition.fOrientationFactor); } else if (pParam->sName == "multipliersVisibility") { temp = QString::number(condition.fVisibilityFactor); } else if (pParam->sName == "multipliersRandomness") { temp = QString::number(condition.fRandomnessFactor); } else if (pParam->sName == "fLookAtOnPerc") { temp = QString::number(condition.fLookAtOnPerc); } else if (pParam->sName == "userPreActionState") { temp = condition.sUserPreActionState; } else if (pParam->sName == "objectPreActionState") { temp = condition.sObjectPreActionState; } else if (pParam->sName == "actionType") { temp = QString::number(condition.eActionType); } else if (pParam->sName == "actionName") { temp = condition.sAction; } else if (pParam->sName == "userPostActionState") { temp = condition.sUserPostActionState; } else if (pParam->sName == "objectPostActionState") { temp = condition.sObjectPostActionState; } else if (pParam->sName == "approachSpeed") { temp = QString::number(condition.fApproachSpeed); } else if (pParam->sName == "approachStance") { temp = QString::number(condition.iApproachStance); } else if (pParam->sName == "animationHelper") { temp = condition.sAnimationHelper; } else if (pParam->sName == "approachHelper") { temp = condition.sApproachHelper; } else if (pParam->sName == "fStartRadiusTolerance") // backward compatibility { temp = QString::number(condition.fStartWidth); } else if (pParam->sName == "fStartWidth") { temp = QString::number(condition.fStartWidth); } else if (pParam->sName == "fStartDirectionTolerance") { temp = QString::number(condition.fDirectionTolerance); } else if (pParam->sName == "fTargetRadiusTolerance") // backward compatibility { temp = QString::number(condition.fStartArcAngle); } else if (pParam->sName == "fStartArcAngle") { temp = QString::number(condition.fStartArcAngle); } if (temp != pParam->sValue) { *message += " - "; *message += pVar->GetHumanName(); *message += ";\n"; } } } else { if (pParam->sName == "bNavigationRule") { condition.iRuleType = pParam->sValue == "1"; } else if (pParam->sName == "sEvent") { condition.sEvent = pParam->sValue.toUtf8().data(); } else if (pParam->sName == "sChainedUserEvent") { condition.sChainedUserEvent = pParam->sValue.toUtf8().data(); } else if (pParam->sName == "sChainedObjectEvent") { condition.sChainedObjectEvent = pParam->sValue.toUtf8().data(); } else if (pParam->sName == "userClass") { condition.sUserClass = pParam->sValue.toUtf8().data(); } else if (pParam->sName == "userState") { condition.sUserState = pParam->sValue.toUtf8().data(); } else if (pParam->sName == "userHelper") { condition.sUserHelper = pParam->sValue.toUtf8().data(); } else if (pParam->sName == "iMaxAlertness") { condition.iMaxAlertness = pParam->sValue.toInt(); } else if (pParam->sName == "objectClass") { condition.sObjectClass = pParam->sValue.toUtf8().data(); } else if (pParam->sName == "objectState") { condition.sObjectState = pParam->sValue.toUtf8().data(); } else if (pParam->sName == "objectHelper") { condition.sObjectHelper = pParam->sValue.toUtf8().data(); } else if (pParam->sName == "entranceHelper") { condition.sEntranceHelper = pParam->sValue.toUtf8().data(); } else if (pParam->sName == "exitHelper") { condition.sExitHelper = pParam->sValue.toUtf8().data(); } else if (pParam->sName == "limitsDistanceFrom") { condition.fDistanceFrom = pParam->sValue.toDouble(); } else if (pParam->sName == "limitsDistanceTo") { condition.fDistanceTo = pParam->sValue.toDouble(); } else if (pParam->sName == "limitsOrientation") { condition.fOrientationLimit = pParam->sValue.toDouble(); } else if (pParam->sName == "horizLimitOnly") { condition.bHorizLimitOnly = pParam->sValue == "1"; } else if (pParam->sName == "limitsOrientationToTarget") { condition.fOrientationToTargetLimit = pParam->sValue.toDouble(); } else if (pParam->sName == "delayMinimum") { condition.fMinDelay = pParam->sValue.toDouble(); } else if (pParam->sName == "delayMaximum") { condition.fMaxDelay = pParam->sValue.toDouble(); } else if (pParam->sName == "delayMemory") { condition.fMemory = pParam->sValue.toDouble(); } else if (pParam->sName == "multipliersProximity") { condition.fProximityFactor = pParam->sValue.toDouble(); } else if (pParam->sName == "multipliersOrientation") { condition.fOrientationFactor = pParam->sValue.toDouble(); } else if (pParam->sName == "multipliersVisibility") { condition.fVisibilityFactor = pParam->sValue.toDouble(); } else if (pParam->sName == "multipliersRandomness") { condition.fRandomnessFactor = pParam->sValue.toDouble(); } else if (pParam->sName == "fLookAtOnPerc") { condition.fLookAtOnPerc = pParam->sValue.toDouble(); } else if (pParam->sName == "userPreActionState") { condition.sUserPreActionState = pParam->sValue.toUtf8().data(); } else if (pParam->sName == "objectPreActionState") { condition.sObjectPreActionState = pParam->sValue.toUtf8().data(); } else if (pParam->sName == "actionType") { condition.eActionType = (EActionType) pParam->sValue.toInt(); } else if (pParam->sName == "actionName") { condition.sAction = pParam->sValue.toUtf8().data(); } else if (pParam->sName == "userPostActionState") { condition.sUserPostActionState = pParam->sValue.toUtf8().data(); } else if (pParam->sName == "objectPostActionState") { condition.sObjectPostActionState = pParam->sValue.toUtf8().data(); } else if (pParam->sName == "approachSpeed") { condition.fApproachSpeed = pParam->sValue.toDouble(); } else if (pParam->sName == "approachStance") { condition.iApproachStance = pParam->sValue.toInt(); } else if (pParam->sName == "animationHelper") { condition.sAnimationHelper = pParam->sValue.toUtf8().data(); } else if (pParam->sName == "approachHelper") { condition.sApproachHelper = pParam->sValue.toUtf8().data(); } else if (pParam->sName == "fStartRadiusTolerance") // backward compatibility { condition.fStartArcAngle = pParam->sValue.toDouble(); } else if (pParam->sName == "fStartArcAngle") { condition.fStartArcAngle = pParam->sValue.toDouble(); } else if (pParam->sName == "fStartDirectionTolerance") { condition.fDirectionTolerance = pParam->sValue.toDouble(); } else if (pParam->sName == "fTargetRadiusTolerance") // backward compatibility { condition.fStartArcAngle = pParam->sValue.toDouble(); } else if (pParam->sName == "fStartArcAngle") { condition.fStartArcAngle = pParam->sValue.toDouble(); } } } } SetTemplateDefaults(condition, param->pNext, message); } #include