/* * 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 "PythonEditorFuncs.h" #include "IEditor.h" #include "GameEngine.h" #include "ViewManager.h" #include "StringDlg.h" #include "GenericSelectItemDialog.h" #include "Util/Ruler.h" #include "DisplaySettings.h" #include #include #include #include #include #include #include namespace { ////////////////////////////////////////////////////////////////////////// const char* PyGetCVar(const char* pName) { ICVar* pCVar = GetIEditor()->GetSystem()->GetIConsole()->GetCVar(pName); if (!pCVar) { Warning("PyGetCVar: Attempt to access non-existent CVar '%s'", pName ? pName : "(null)"); throw std::logic_error((QString("\"") + pName + "\" is an invalid cvar.").toUtf8().data()); } return pCVar->GetString(); } ////////////////////////////////////////////////////////////////////////// const char* PyGetCVarAsString(const char* pName) { ICVar* pCVar = GetIEditor()->GetSystem()->GetIConsole()->GetCVar(pName); if (!pCVar) { AZ_Warning("editor", false, "PyGetCVar: Attempt to access non-existent CVar '%s'", pName ? pName : "(null)"); return "(missing)"; } return pCVar->GetString(); } ////////////////////////////////////////////////////////////////////////// // forward declarations void PySetCVarFromInt(const char* pName, int pValue); void PySetCVarFromFloat(const char* pName, float pValue); ////////////////////////////////////////////////////////////////////////// void PySetCVarFromString(const char* pName, const char* pValue) { ICVar* pCVar = GetIEditor()->GetSystem()->GetIConsole()->GetCVar(pName); if (!pCVar) { AZ_Warning("editor", false, "Attempt to set non-existent string CVar '%s'", pName ? pName : "(null)"); } else if (pCVar->GetType() == CVAR_INT) { PySetCVarFromInt(pName, std::stol(pValue)); } else if (pCVar->GetType() == CVAR_FLOAT) { PySetCVarFromFloat(pName, std::stod(pValue)); } else if (pCVar->GetType() != CVAR_STRING) { AZ_Warning("editor", false, "Type mismatch while assigning CVar '%s' as a string.", pName ? pName : "(null)"); } else { pCVar->Set(pValue); } } ////////////////////////////////////////////////////////////////////////// void PySetCVarFromInt(const char* pName, int pValue) { ICVar* pCVar = GetIEditor()->GetSystem()->GetIConsole()->GetCVar(pName); if (!pCVar) { AZ_Warning("editor", false, "Attempt to set non-existent integer CVar '%s'", pName ? pName : "(null)"); } else if (pCVar->GetType() == CVAR_FLOAT) { PySetCVarFromFloat(pName, float(pValue)); } else if (pCVar->GetType() == CVAR_STRING) { auto stringValue = AZStd::to_string(pValue); PySetCVarFromString(pName, stringValue.c_str()); } else if (pCVar->GetType() != CVAR_INT) { AZ_Warning("editor", false, "Type mismatch while assigning CVar '%s' as an integer.", pName ? pName : "(null)"); } else { pCVar->Set(pValue); } } ////////////////////////////////////////////////////////////////////////// void PySetCVarFromFloat(const char* pName, float pValue) { ICVar* pCVar = GetIEditor()->GetSystem()->GetIConsole()->GetCVar(pName); if (!pCVar) { AZ_Warning("editor", false, "Attempt to set non-existent float CVar '%s'", pName ? pName : "(null)"); } else if (pCVar->GetType() == CVAR_INT) { PySetCVarFromInt(pName, static_cast(pValue)); } else if (pCVar->GetType() == CVAR_STRING) { auto stringValue = AZStd::to_string(pValue); PySetCVarFromString(pName, stringValue.c_str()); } else if (pCVar->GetType() != CVAR_FLOAT) { AZ_Warning("editor", false, "Type mismatch while assigning CVar '%s' as a float.", pName ? pName : "(null)"); } else { pCVar->Set(pValue); } } ////////////////////////////////////////////////////////////////////////// void PySetCVarFromAny(const char* pName, const AZStd::any& value) { ICVar* pCVar = GetIEditor()->GetSystem()->GetIConsole()->GetCVar(pName); if (!pCVar) { AZ_Warning("editor", false, "Attempt to set non-existent float CVar '%s'", pName ? pName : "(null)"); } else if (pCVar->GetType() == CVAR_INT) { PySetCVarFromInt(pName, AZStd::any_cast(value)); } else if (pCVar->GetType() == CVAR_FLOAT) { PySetCVarFromFloat(pName, AZStd::any_cast(value)); } else if (pCVar->GetType() == CVAR_STRING) { PySetCVarFromString(pName, AZStd::any_cast(value).data()); } else { AZ_Warning("editor", false, "Type mismatch while assigning CVar '%s' as a float.", pName ? pName : "(null)"); } } ////////////////////////////////////////////////////////////////////////// void PyEnterGameMode() { if (GetIEditor()->GetGameEngine()) { GetIEditor()->GetGameEngine()->RequestSetGameMode(true); } } void PyExitGameMode() { if (GetIEditor()->GetGameEngine()) { GetIEditor()->GetGameEngine()->RequestSetGameMode(false); } } bool PyIsInGameMode() { return GetIEditor()->IsInGameMode(); } ////////////////////////////////////////////////////////////////////////// void PyEnterSimulationMode() { if (!GetIEditor()->IsInSimulationMode()) { CCryEditApp::instance()->OnSwitchPhysics(); } } void PyExitSimulationMode() { if (GetIEditor()->IsInSimulationMode()) { CCryEditApp::instance()->OnSwitchPhysics(); } } bool PyIsInSimulationMode() { return GetIEditor()->IsInSimulationMode(); } ////////////////////////////////////////////////////////////////////////// QString PyNewObject(const char* typeName, const char* fileName, const char* name, float x, float y, float z) { CBaseObject* object = GetIEditor()->NewObject(typeName, fileName, name, x, y, z); if (object) { return object->GetName(); } else { return ""; } } ////////////////////////////////////////////////////////////////////////// QString PyNewObjectAtCursor(const char* typeName, const char* fileName, const char* name) { CUndo undo("Create new object"); Vec3 pos(0, 0, 0); QPoint p = QCursor::pos(); CViewport* viewport = GetIEditor()->GetViewManager()->GetViewportAtPoint(p); if (viewport) { viewport->ScreenToClient(p); if (GetIEditor()->GetAxisConstrains() != AXIS_TERRAIN) { pos = viewport->MapViewToCP(p); } else { // Snap to terrain. bool hitTerrain; pos = viewport->ViewToWorld(p, &hitTerrain); if (hitTerrain) { pos.z = GetIEditor()->GetTerrainElevation(pos.x, pos.y) + 1.0f; } pos = viewport->SnapToGrid(pos); } } return PyNewObject(typeName, fileName, name, pos.x, pos.y, pos.z); } ////////////////////////////////////////////////////////////////////////// void PyStartObjectCreation(const char* typeName, const char* fileName) { CUndo undo("Create new object"); GetIEditor()->StartObjectCreation(typeName, fileName); } ////////////////////////////////////////////////////////////////////////// void PyRunConsole(const char* text) { GetIEditor()->GetSystem()->GetIConsole()->ExecuteString(text); } ////////////////////////////////////////////////////////////////////////// void PyRunLua(const char* text) { GetIEditor()->GetSystem()->GetIScriptSystem()->ExecuteBuffer(text, strlen(text)); } ////////////////////////////////////////////////////////////////////////// bool GetPythonScriptPath(const char* pFile, QString& path) { bool bRelativePath = true; char drive[_MAX_DRIVE]; drive[0] = '\0'; _splitpath(pFile, drive, 0, 0, 0); if (strlen(drive) != 0) { bRelativePath = false; } if (bRelativePath) { // Try to open from user folder QString userSandboxFolder = Path::GetResolvedUserSandboxFolder(); Path::ConvertBackSlashToSlash(userSandboxFolder); path = userSandboxFolder + pFile; // If not found try editor folder if (!CFileUtil::FileExists(path)) { const char* engineRoot = nullptr; AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(engineRoot, &AzToolsFramework::ToolsApplicationRequests::GetEngineRootPath); QDir engineDir = engineRoot ? QDir(engineRoot) : QDir::current(); QString scriptFolder = engineDir.absoluteFilePath("Editor/Scripts/"); Path::ConvertBackSlashToSlash(scriptFolder); path = scriptFolder + pFile; if (!CFileUtil::FileExists(path)) { QString error = QString("Could not find '%1'\n in '%2'\n or '%3'\n").arg(pFile).arg(userSandboxFolder).arg(scriptFolder); AZ_Warning("python", false, error.toUtf8().data()); return false; } } } else { path = pFile; if (!CFileUtil::FileExists(path)) { QString error = QString("Could not find '") + pFile + "'\n"; AZ_Warning("python", false, error.toUtf8().data()); return false; } } Path::ConvertBackSlashToSlash(path); return true; } ////////////////////////////////////////////////////////////////////////// void GetPythonArgumentsVector(const char* pArguments, QStringList& inputArguments) { if (pArguments == NULL) { return; } inputArguments = QString(pArguments).split(" ", Qt::SkipEmptyParts); } ////////////////////////////////////////////////////////////////////////// void PyRunFileWithParameters(const char* pFile, const char* pArguments) { QString path; QStringList inputArguments; GetPythonArgumentsVector(pArguments, inputArguments); if (GetPythonScriptPath(pFile, path)) { AZStd::vector argList; argList.reserve(inputArguments.size() + 1); QByteArray p = path.toUtf8(); QVector argData; argData.reserve(inputArguments.count()); for (auto iter = inputArguments.begin(); iter != inputArguments.end(); ++iter) { argData.push_back(iter->toLatin1()); argList.push_back(argData.last().data()); } using namespace AzToolsFramework; EditorPythonRunnerRequestBus::Broadcast(&AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByFilenameWithArgs, p.data(), argList); } } void PyRunFile(const char* pFile) { PyRunFileWithParameters(pFile, nullptr); } ////////////////////////////////////////////////////////////////////////// void PyExecuteCommand(const char* cmdline) { GetIEditor()->GetCommandManager()->Execute(cmdline); } ////////////////////////////////////////////////////////////////////////// void PyLog(const char* pMessage) { if (strcmp(pMessage, "") != 0) { CryLogAlways(pMessage); } } ////////////////////////////////////////////////////////////////////////// bool PyMessageBox(const char* pMessage) { return QMessageBox::information(QApplication::activeWindow(), QString(), pMessage, QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok; } bool PyMessageBoxYesNo(const char* pMessage) { return QMessageBox::question(QApplication::activeWindow(), QString(), pMessage) == QMessageBox::Yes; } bool PyMessageBoxOK(const char* pMessage) { return QMessageBox::information(QApplication::activeWindow(), QString(), pMessage) == QMessageBox::Ok; } AZStd::string PyEditBox(AZStd::string_view pTitle) { StringDlg stringDialog(pTitle.data()); if (stringDialog.exec() == QDialog::Accepted) { return stringDialog.GetString().toUtf8().constData(); } return ""; } AZStd::any PyEditBoxAndCheckProperty(const char* pTitle) { StringDlg stringDialog(pTitle); stringDialog.SetString(QStringLiteral("")); if (stringDialog.exec() == QDialog::Accepted) { const QString stringValue = stringDialog.GetString(); // detect data type QString tempString = stringValue; int countComa = 0; int countOpenRoundBraket = 0; int countCloseRoundBraket = 0; int countDots = 0; int posDots = 0; int posComa = 0; int posOpenRoundBraket = 0; int posCloseRoundBraket = 0; for (int i = 0; i < 3; i++) { if (tempString.indexOf(".", posDots) > -1) { posDots = tempString.indexOf(".", posDots) + 1; countDots++; } if (tempString.indexOf(",", posComa) > -1) { posComa = tempString.indexOf(",", posComa) + 1; countComa++; } if (tempString.indexOf("(", posOpenRoundBraket) > -1) { posOpenRoundBraket = tempString.indexOf("(", posOpenRoundBraket) + 1; countOpenRoundBraket++; } if (tempString.indexOf(")", posCloseRoundBraket) > -1) { posCloseRoundBraket = tempString.indexOf(")", posCloseRoundBraket) + 1; countCloseRoundBraket++; } } if (countDots == 3 && countComa == 2 && countOpenRoundBraket == 1 && countCloseRoundBraket == 1) { // for example: (1.95, 2.75, 3.36) QString valueRed = stringValue; int iStart = valueRed.indexOf("("); valueRed.remove(0, iStart + 1); int iEnd = valueRed.indexOf(","); valueRed.remove(iEnd, valueRed.length()); float fValueRed = valueRed.toFloat(); QString valueGreen = stringValue; iStart = valueGreen.indexOf(","); valueGreen.remove(0, iStart + 1); iEnd = valueGreen.indexOf(","); valueGreen.remove(iEnd, valueGreen.length()); float fValueGreen = valueGreen.toFloat(); QString valueBlue = stringValue; valueBlue.remove(0, valueBlue.indexOf(",") + 1); valueBlue.remove(0, valueBlue.indexOf(",") + 1); valueBlue.remove(valueBlue.indexOf(")"), valueBlue.length()); float fValueBlue = valueBlue.toFloat(); return AZStd::make_any(fValueRed, fValueGreen, fValueBlue); } else if (countDots == 0 && countComa == 2 && countOpenRoundBraket == 1 && countCloseRoundBraket == 1) { // for example: (128, 32, 240) const AZ::u8 lowColorValue { 0 }; const AZ::u8 highColorValue { 255 }; QString valueRed = stringValue; int iStart = valueRed.indexOf("("); valueRed.remove(0, iStart + 1); int iEnd = valueRed.indexOf(","); valueRed.remove(iEnd, valueRed.length()); AZ::u8 iValueRed = AZStd::clamp(aznumeric_cast(valueRed.toInt()), lowColorValue, highColorValue); QString valueGreen = stringValue; iStart = valueGreen.indexOf(","); valueGreen.remove(0, iStart + 1); iEnd = valueGreen.indexOf(","); valueGreen.remove(iEnd, valueGreen.length()); AZ::u8 iValueGreen = AZStd::clamp(aznumeric_cast(valueGreen.toInt()), lowColorValue, highColorValue); QString valueBlue = stringValue; valueBlue.remove(0, valueBlue.indexOf(",") + 1); valueBlue.remove(0, valueBlue.indexOf(",") + 1); valueBlue.remove(valueBlue.indexOf(")"), valueBlue.length()); AZ::u8 iValueBlue = AZStd::clamp(aznumeric_cast(valueBlue.toInt()), lowColorValue, highColorValue); return AZStd::make_any(iValueRed, iValueGreen, iValueBlue, highColorValue); } else if (countDots == 1 && countComa == 0 && countOpenRoundBraket == 0 && countCloseRoundBraket == 0) { // for example: 2.56 return AZStd::make_any(stringValue.toDouble()); } else if (countDots == 0 && countComa == 0 && countOpenRoundBraket == 0 && countCloseRoundBraket == 0) { if (stringValue == "False" || stringValue == "True") { // for example: True return AZStd::make_any(stringValue == "True"); } else { const bool anyNotDigits = AZStd::any_of(stringValue.begin(), stringValue.end(), [](const QChar& ch) { return !ch.isDigit(); }); // looks like a string value? if (stringValue.isEmpty() || anyNotDigits) { // for example: Hello return AZStd::make_any(stringValue.toUtf8().data()); } else // then it looks like an integer { // for example: 456 return AZStd::make_any(stringValue.toInt()); } } } } QMessageBox::critical(AzToolsFramework::GetActiveWindow(), QObject::tr("Invalid Data"), QObject::tr("Invalid data type.")); return {}; } AZStd::string PyOpenFileBox() { QString path = QFileDialog::getOpenFileName(); if (!path.isEmpty()) { Path::ConvertBackSlashToSlash(path); } return path.toUtf8().constData(); } AZStd::string PyComboBox(AZStd::string title, AZStd::vector values, int selectedIdx = 0) { AZStd::string result; if (title.empty()) { throw std::runtime_error("Incorrect title argument passed in. "); return result; } if (values.size() == 0) { throw std::runtime_error("Empty value list passed in. "); return result; } QStringList list; for (const AZStd::string& str : values) { list.push_back(str.c_str()); } CGenericSelectItemDialog pyDlg; pyDlg.setWindowTitle(title.c_str()); pyDlg.SetMode(CGenericSelectItemDialog::eMODE_LIST); pyDlg.SetItems(list); pyDlg.PreSelectItem(list[selectedIdx]); if (pyDlg.exec() == QDialog::Accepted) { result = pyDlg.GetSelectedItem().toUtf8().constData(); } return result; } static void PyDrawLabel(int x, int y, float size, float r, float g, float b, float a, const char* pLabel) { if (!pLabel) { throw std::logic_error("No label given."); return; } if (!r || !g || !b || !a) { throw std::logic_error("Invalid color parameters given."); return; } if (!x || !y || !size) { throw std::logic_error("Invalid position or size parameters given."); } else { float color[] = {r, g, b, a}; gEnv->pRenderer->Draw2dLabel(x, y, size, color, false, pLabel); } } ////////////////////////////////////////////////////////////////////////// // Constrain ////////////////////////////////////////////////////////////////////////// const char* PyGetAxisConstraint() { AxisConstrains actualConstrain = GetIEditor()->GetAxisConstrains(); switch (actualConstrain) { case AXIS_X: return "X"; case AXIS_Y: return "Y"; case AXIS_Z: return "Z"; case AXIS_XY: return "XY"; case AXIS_XZ: return "XZ"; case AXIS_YZ: return "YZ"; case AXIS_XYZ: return "XYZ"; case AXIS_TERRAIN: return (GetIEditor()->IsTerrainAxisIgnoreObjects()) ? "TERRAIN" : "TERRAINSNAP"; default: throw std::logic_error("Invalid axes."); } } void PySetAxisConstraint(AZStd::string_view pConstrain) { if (pConstrain == "X") { GetIEditor()->SetAxisConstraints(AXIS_X); } else if (pConstrain == "Y") { GetIEditor()->SetAxisConstraints(AXIS_Y); } else if (pConstrain == "Z") { GetIEditor()->SetAxisConstraints(AXIS_Z); } else if (pConstrain == "XY") { GetIEditor()->SetAxisConstraints(AXIS_XY); } else if (pConstrain == "YZ") { GetIEditor()->SetAxisConstraints(AXIS_YZ); } else if (pConstrain == "XZ") { GetIEditor()->SetAxisConstraints(AXIS_XZ); } else if (pConstrain == "XYZ") { GetIEditor()->SetAxisConstraints(AXIS_XYZ); } else if (pConstrain == "TERRAIN") { GetIEditor()->SetAxisConstraints(AXIS_TERRAIN); GetIEditor()->SetTerrainAxisIgnoreObjects(true); } else if (pConstrain == "TERRAINSNAP") { GetIEditor()->SetAxisConstraints(AXIS_TERRAIN); GetIEditor()->SetTerrainAxisIgnoreObjects(false); } else { throw std::logic_error("Invalid axes."); } } ////////////////////////////////////////////////////////////////////////// // Edit Mode ////////////////////////////////////////////////////////////////////////// const char* PyGetEditMode() { int actualEditMode = GetIEditor()->GetEditMode(); switch (actualEditMode) { case eEditModeSelect: return "SELECT"; case eEditModeSelectArea: return "SELECTAREA"; case eEditModeMove: return "MOVE"; case eEditModeRotate: return "ROTATE"; case eEditModeScale: return "SCALE"; case eEditModeTool: return "TOOL"; default: throw std::logic_error("Invalid edit mode."); } } void PySetEditMode(AZStd::string_view pEditMode) { if (pEditMode == "MOVE") { GetIEditor()->SetEditMode(eEditModeMove); } else if (pEditMode == "ROTATE") { GetIEditor()->SetEditMode(eEditModeRotate); } else if (pEditMode == "SCALE") { GetIEditor()->SetEditMode(eEditModeScale); } else if (pEditMode == "SELECT") { GetIEditor()->SetEditMode(eEditModeSelect); } else if (pEditMode == "SELECTAREA") { GetIEditor()->SetEditMode(eEditModeSelectArea); } else if (pEditMode == "TOOL") { GetIEditor()->SetEditMode(eEditModeTool); } else if (pEditMode == "RULER") { CRuler* pRuler = GetIEditor()->GetRuler(); pRuler->SetActive(!pRuler->IsActive()); } else { throw std::logic_error("Invalid edit mode."); } } ////////////////////////////////////////////////////////////////////////// const char* PyGetPakFromFile(const char* filename) { ICryPak* pIPak = GetIEditor()->GetSystem()->GetIPak(); AZ::IO::HandleType fileHandle = pIPak->FOpen(filename, "rb"); if (fileHandle == AZ::IO::InvalidHandle) { throw std::logic_error("Invalid file name."); } const char* pArchPath = pIPak->GetFileArchivePath(fileHandle); pIPak->FClose(fileHandle); return pArchPath; } ////////////////////////////////////////////////////////////////////////// void PyUndo() { GetIEditor()->Undo(); } ////////////////////////////////////////////////////////////////////////// void PyRedo() { GetIEditor()->Redo(); } ////////////////////////////////////////////////////////////////////////// // HideMask controls ////////////////////////////////////////////////////////////////////////// void PySetHideMaskAll() { GetIEditor()->GetDisplaySettings()->SetObjectHideMask(OBJTYPE_ANY); } ////////////////////////////////////////////////////////////////////////// void PySetHideMaskNone() { GetIEditor()->GetDisplaySettings()->SetObjectHideMask(0); } ////////////////////////////////////////////////////////////////////////// void PySetHideMaskInvert() { uint32 hideMask = GetIEditor()->GetDisplaySettings()->GetObjectHideMask(); GetIEditor()->GetDisplaySettings()->SetObjectHideMask(~hideMask); } void PySetHideMask(const char* pName, bool bAdd) { uint32 hideMask = GetIEditor()->GetDisplaySettings()->GetObjectHideMask(); int hideType = 0; if(!azstricmp(pName, "aipoints")) { hideType = OBJTYPE_AIPOINT; } else if (!azstricmp(pName, "brushes")) { hideType = OBJTYPE_BRUSH; } else if (!azstricmp(pName, "decals")) { hideType = OBJTYPE_DECAL; } else if (!azstricmp(pName, "entities")) { hideType = OBJTYPE_AZENTITY; } else if (!azstricmp(pName, "groups")) { hideType = OBJTYPE_GROUP; } else if (!azstricmp(pName, "prefabs")) { hideType = OBJTYPE_PREFAB; } else if (!azstricmp(pName, "other")) { hideType = OBJTYPE_OTHER; } else if (!azstricmp(pName, "shapes")) { hideType = OBJTYPE_SHAPE; } else if (!azstricmp(pName, "solids")) { hideType = OBJTYPE_SOLID; } else if (!azstricmp(pName, "tagpoints")) { hideType = OBJTYPE_TAGPOINT; } else if (!azstricmp(pName, "volumes")) { hideType = OBJTYPE_VOLUME; } else if (!azstricmp(pName, "geomcaches")) { hideType = OBJTYPE_GEOMCACHE; } else if (!azstricmp(pName, "roads")) { hideType = OBJTYPE_ROAD; } else if (!azstricmp(pName, "rivers")) { hideType = OBJTYPE_ROAD; } if (bAdd) { hideMask |= hideType; } else { hideMask &= ~hideType; } GetIEditor()->GetDisplaySettings()->SetObjectHideMask(hideMask); } bool PyGetHideMask(const char* pName) { uint32 hideMask = GetIEditor()->GetDisplaySettings()->GetObjectHideMask(); if ((!azstricmp(pName, "aipoints") && (hideMask & OBJTYPE_AIPOINT)) || (!azstricmp(pName, "brushes") && (hideMask & OBJTYPE_BRUSH)) || (!azstricmp(pName, "decals") && (hideMask & OBJTYPE_DECAL)) || (!azstricmp(pName, "entities") && (hideMask & OBJTYPE_AZENTITY)) || (!azstricmp(pName, "groups") && (hideMask & OBJTYPE_GROUP)) || (!azstricmp(pName, "prefabs") && (hideMask & OBJTYPE_PREFAB)) || (!azstricmp(pName, "other") && (hideMask & OBJTYPE_OTHER)) || (!azstricmp(pName, "shapes") && (hideMask & OBJTYPE_SHAPE)) || (!azstricmp(pName, "solids") && (hideMask & OBJTYPE_SOLID)) || (!azstricmp(pName, "tagpoints") && (hideMask & OBJTYPE_TAGPOINT)) || (!azstricmp(pName, "volumes") && (hideMask & OBJTYPE_VOLUME)) || (!azstricmp(pName, "geomcaches") && (hideMask & OBJTYPE_GEOMCACHE)) || (!azstricmp(pName, "roads") && (hideMask & OBJTYPE_ROAD)) || (!azstricmp(pName, "rivers") && (hideMask & OBJTYPE_ROAD))) { return true; } return false; } } ////////////////////////////////////////////////////////////////////////// // Temporal, to be removed by LY-101149 AZ::EntityId PyFindEditorEntity(const char* name) { AZ::EntityId foundEntityId; auto searchFunc = [name, &foundEntityId](AZ::Entity* e) { if (!foundEntityId.IsValid() && e->GetName() == name) { bool isEditorEntity = false; AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(isEditorEntity, &AzToolsFramework::EditorEntityContextRequests::IsEditorEntity, e->GetId()); if (isEditorEntity) { foundEntityId = e->GetId(); } } }; AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationRequests::EnumerateEntities, searchFunc); return foundEntityId; } AZ::EntityId PyFindGameEntity(const char* name) { AZ::EntityId foundEntityId; auto searchFunc = [name, &foundEntityId](AZ::Entity* e) { if (!foundEntityId.IsValid() && e->GetName() == name) { bool isEditorEntity = true; AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(isEditorEntity, &AzToolsFramework::EditorEntityContextRequests::IsEditorEntity, e->GetId()); if (!isEditorEntity) { foundEntityId = e->GetId(); } } }; AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationRequests::EnumerateEntities, searchFunc); return foundEntityId; } struct PyDumpBindings { static AZ_INLINE bool IsBehaviorFlaggedForEditor(const AZ::AttributeArray& attributes) { // defaults to Launcher AZ::Script::Attributes::ScopeFlags scopeType = AZ::Script::Attributes::ScopeFlags::Launcher; AZ::Attribute* scopeAttribute = AZ::FindAttribute(AZ::Script::Attributes::Scope, attributes); if (scopeAttribute) { AZ::AttributeReader scopeAttributeReader(nullptr, scopeAttribute); scopeAttributeReader.Read(scopeType); } return (scopeType == AZ::Script::Attributes::ScopeFlags::Automation || scopeType == AZ::Script::Attributes::ScopeFlags::Common); } static AZ_INLINE AZStd::string GetModuleName(const AZ::AttributeArray& attributes) { AZStd::string moduleName; AZ::Attribute* moduleAttribute = AZ::FindAttribute(AZ::Script::Attributes::Module, attributes); if (moduleAttribute) { AZ::AttributeReader scopeAttributeReader(nullptr, moduleAttribute); scopeAttributeReader.Read(moduleName); } if (!moduleName.empty()) { moduleName = "azlmbr." + moduleName; } else { moduleName = "azlmbr"; } return moduleName; } static AZStd::string ParameterToString(const AZ::BehaviorMethod* method, size_t index) { const AZStd::string* argNameStr = method->GetArgumentName(index); const char* argName = (argNameStr && !argNameStr->empty()) ? argNameStr->c_str() : nullptr; if (argName) { return AZStd::string::format("%s %s", method->GetArgument(index)->m_name, argName); } else { return method->GetArgument(index)->m_name; } } static AZStd::string MethodArgumentsToString(const AZ::BehaviorMethod* method) { AZStd::string ret; AZStd::string argumentStr; for (size_t i = 0; i < method->GetNumArguments(); ++i) { argumentStr = ParameterToString(method, i); ret += argumentStr; if (i < method->GetNumArguments() - 1) { ret += ", "; } } return ret; } static AZStd::string MethodToString(const AZStd::string& methodName, const AZ::BehaviorMethod* method) { AZStd::string methodNameStrip = methodName.data() + methodName.rfind(':') + 1; // remove ClassName:: part as it is redundant return AZStd::string::format("%s %s(%s)%s", method->GetResult()->m_name, methodNameStrip.c_str(), MethodArgumentsToString(method).c_str(), method->m_isConst ? " const" : ""); } static AZStd::string GetExposedPythonClasses() { AZ::BehaviorContext* behaviorContext(nullptr); AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext); AZStd::string output = ""; output += "// Classes\n\n"; for (auto elem : behaviorContext->m_classes) { AZ::BehaviorClass* cls = elem.second; bool exposedToPython = IsBehaviorFlaggedForEditor(cls->m_attributes); if (!exposedToPython) continue; output += AZStd::string::format("// Module: %s\n", GetModuleName(cls->m_attributes).c_str()); output += AZStd::string::format("class %s\n", cls->m_name.c_str()); output += "{\n"; if (cls->m_methods.size() > 0) { output += " // Methods\n"; for (auto method_elem : cls->m_methods) { output += AZStd::string::format(" %s;\n", MethodToString(method_elem.first, method_elem.second).c_str()); } } if (cls->m_properties.size() > 0) { output += " // Properties\n"; for (auto property_elem : cls->m_properties) { AZ::BehaviorProperty* bproperty = property_elem.second; output += AZStd::string::format(" %s %s;\n", bproperty->m_getter->GetResult()->m_name, bproperty->m_name.c_str()); } } output += "}\n"; } output += "\n\n// Ebuses\n\n"; for (auto elem : behaviorContext->m_ebuses) { AZ::BehaviorEBus* ebus = elem.second; bool exposedToPython = IsBehaviorFlaggedForEditor(ebus->m_attributes); if (!exposedToPython) continue; output += AZStd::string::format("// Module: %s\n", GetModuleName(ebus->m_attributes).c_str()); output += AZStd::string::format("ebus %s\n", ebus->m_name.c_str()); output += "{\n"; for (auto event_elem : ebus->m_events) { auto method = event_elem.second.m_event ? event_elem.second.m_event : event_elem.second.m_broadcast; if (method) { const char* comment = event_elem.second.m_event ? "/* event */" : "/* broadcast */"; output += AZStd::string::format(" %s %s\n", comment, MethodToString(event_elem.first, method).c_str()); } else { output += AZStd::string::format(" %s %s\n", "/* unknown */", event_elem.first.c_str()); } } if (ebus->m_createHandler) { AZ::BehaviorEBusHandler* handler = nullptr; ebus->m_createHandler->InvokeResult(handler); if (handler) { const auto& notifications = handler->GetEvents(); for (const auto& notification : notifications) { AZStd::string argsStr; const size_t paramCount = notification.m_parameters.size(); for (size_t i = 0; i < notification.m_parameters.size(); ++i) { AZStd::string argName = notification.m_parameters[i].m_name; argsStr += argName; if (i != paramCount - 1) { argsStr += ", "; } } AZStd::string funcName = notification.m_name; output += AZStd::string::format(" /* notification */ %s(%s);\n", funcName.c_str(), argsStr.c_str()); } ebus->m_destroyHandler->Invoke(handler); } } output += "}\n"; } AzFramework::StringFunc::Replace(output, "AZStd::basic_string, allocator>", "AZStd::string"); return output; } }; ////////////////////////////////////////////////////////////////////////// namespace AzToolsFramework { void PythonEditorComponent::Reflect(AZ::ReflectContext* context) { if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) { behaviorContext->EBus("PythonEditorBus") ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) ->Attribute(AZ::Script::Attributes::Module, "python_editor_funcs") ->Event("GetCVar", &EditorLayerPythonRequestBus::Events::GetCVar) ->Event("SetCVar", &EditorLayerPythonRequestBus::Events::SetCVar) ->Event("SetCVarFromString", &EditorLayerPythonRequestBus::Events::SetCVarFromString) ->Event("SetCVarFromInteger", &EditorLayerPythonRequestBus::Events::SetCVarFromInteger) ->Event("SetCVarFromFloat", &EditorLayerPythonRequestBus::Events::SetCVarFromFloat) ->Event("RunConsole", &EditorLayerPythonRequestBus::Events::PyRunConsole) ->Event("EnterGameMode", &EditorLayerPythonRequestBus::Events::EnterGameMode) ->Event("IsInGameMode", &EditorLayerPythonRequestBus::Events::IsInGameMode) ->Event("ExitGameMode", &EditorLayerPythonRequestBus::Events::ExitGameMode) ->Event("EnterSimulationMode", &EditorLayerPythonRequestBus::Events::EnterSimulationMode) ->Event("IsInSimulationMode", &EditorLayerPythonRequestBus::Events::IsInSimulationMode) ->Event("ExitSimulationMode", &EditorLayerPythonRequestBus::Events::ExitSimulationMode) ->Event("RunLua", &EditorLayerPythonRequestBus::Events::RunLua) ->Event("RunFile", &EditorLayerPythonRequestBus::Events::RunFile) ->Event("RunFileParameters", &EditorLayerPythonRequestBus::Events::RunFileParameters) ->Event("ExecuteCommand", &EditorLayerPythonRequestBus::Events::ExecuteCommand) ->Event("MessageBoxOkCancel", &EditorLayerPythonRequestBus::Events::MessageBoxOkCancel) ->Event("MessageBoxYesNo", &EditorLayerPythonRequestBus::Events::MessageBoxYesNo) ->Event("MessageBoxOk", &EditorLayerPythonRequestBus::Events::MessageBoxOk) ->Event("EditBox", &EditorLayerPythonRequestBus::Events::EditBox) ->Event("EditBoxCheckDataType", &EditorLayerPythonRequestBus::Events::EditBoxCheckDataType) ->Event("OpenFileBox", &EditorLayerPythonRequestBus::Events::OpenFileBox) ->Event("GetAxisConstraint", &EditorLayerPythonRequestBus::Events::GetAxisConstraint) ->Event("SetAxisConstraint", &EditorLayerPythonRequestBus::Events::SetAxisConstraint) ->Event("GetEditMode", &EditorLayerPythonRequestBus::Events::GetEditMode) ->Event("SetEditMode", &EditorLayerPythonRequestBus::Events::SetEditMode) ->Event("GetPakFromFile", &EditorLayerPythonRequestBus::Events::GetPakFromFile) ->Event("Log", &EditorLayerPythonRequestBus::Events::Log) ->Event("Undo", &EditorLayerPythonRequestBus::Events::Undo) ->Event("Redo", &EditorLayerPythonRequestBus::Events::Redo) ->Event("DrawLabel", &EditorLayerPythonRequestBus::Events::DrawLabel) ->Event("ComboBox", &EditorLayerPythonRequestBus::Events::ComboBox) ->Event("SetHidemaskAll", &EditorLayerPythonRequestBus::Events::SetHidemaskAll) ->Event("SetHidemaskNone", &EditorLayerPythonRequestBus::Events::SetHidemaskNone) ->Event("SetHidemaskInvert", &EditorLayerPythonRequestBus::Events::SetHidemaskInvert) ->Event("SetHidemask", &EditorLayerPythonRequestBus::Events::SetHidemask) ->Event("GetHidemask", &EditorLayerPythonRequestBus::Events::GetHidemask) ; } } void PythonEditorComponent::Activate() { EditorLayerPythonRequestBus::Handler::BusConnect(GetEntityId()); } void PythonEditorComponent::Deactivate() { EditorLayerPythonRequestBus::Handler::BusDisconnect(); } const char* PythonEditorComponent::GetCVar(const char* pName) { return PyGetCVarAsString(pName); } void PythonEditorComponent::SetCVar(const char* pName, const AZStd::any& value) { return PySetCVarFromAny(pName, value); } void PythonEditorComponent::SetCVarFromString(const char* pName, const char* pValue) { return PySetCVarFromString(pName, pValue); } void PythonEditorComponent::SetCVarFromInteger(const char* pName, int pValue) { return PySetCVarFromInt(pName, pValue); } void PythonEditorComponent::SetCVarFromFloat(const char* pName, float pValue) { return PySetCVarFromFloat(pName, pValue); } void PythonEditorComponent::PyRunConsole(const char* text) { return ::PyRunConsole(text); } void PythonEditorComponent::EnterGameMode() { return PyEnterGameMode(); } bool PythonEditorComponent::IsInGameMode() { return PyIsInGameMode(); } void PythonEditorComponent::ExitGameMode() { return PyExitGameMode(); } void PythonEditorComponent::EnterSimulationMode() { return PyEnterSimulationMode(); } bool PythonEditorComponent::IsInSimulationMode() { return PyIsInSimulationMode(); } void PythonEditorComponent::ExitSimulationMode() { return PyExitSimulationMode(); } void PythonEditorComponent::RunLua(const char *text) { return PyRunLua(text); } void PythonEditorComponent::RunFile(const char *pFile) { return PyRunFile(pFile); } void PythonEditorComponent::RunFileParameters(const char* pFile, const char* pArguments) { return PyRunFileWithParameters(pFile, pArguments); } void PythonEditorComponent::ExecuteCommand(const char* cmdline) { return PyExecuteCommand(cmdline); } bool PythonEditorComponent::MessageBoxOkCancel(const char* pMessage) { return PyMessageBox(pMessage); } bool PythonEditorComponent::MessageBoxYesNo(const char* pMessage) { return PyMessageBoxYesNo(pMessage); } bool PythonEditorComponent::MessageBoxOk(const char* pMessage) { return PyMessageBoxOK(pMessage); } AZStd::string PythonEditorComponent::EditBox(AZStd::string_view pTitle) { return PyEditBox(pTitle); } AZStd::any PythonEditorComponent::EditBoxCheckDataType(const char* pTitle) { return PyEditBoxAndCheckProperty(pTitle); } AZStd::string PythonEditorComponent::OpenFileBox() { return PyOpenFileBox(); } const char* PythonEditorComponent::GetAxisConstraint() { return PyGetAxisConstraint(); } void PythonEditorComponent::SetAxisConstraint(AZStd::string_view pConstrain) { return PySetAxisConstraint(pConstrain); } const char* PythonEditorComponent::GetEditMode() { return PyGetEditMode(); } void PythonEditorComponent::SetEditMode(AZStd::string_view pEditMode) { return PySetEditMode(pEditMode); } const char* PythonEditorComponent::GetPakFromFile(const char* filename) { return PyGetPakFromFile(filename); } void PythonEditorComponent::Log(const char* pMessage) { return PyLog(pMessage); } void PythonEditorComponent::Undo() { return PyUndo(); } void PythonEditorComponent::Redo() { return PyRedo(); } void PythonEditorComponent::DrawLabel(int x, int y, float size, float r, float g, float b, float a, const char* pLabel) { return PyDrawLabel(x, y, size, r, g, b, a, pLabel); } AZStd::string PythonEditorComponent::ComboBox(AZStd::string title, AZStd::vector values, int selectedIdx) { return PyComboBox(title, values, selectedIdx); } void PythonEditorComponent::SetHidemaskAll() { return PySetHideMaskAll(); } void PythonEditorComponent::SetHidemaskNone() { return PySetHideMaskNone(); } void PythonEditorComponent::SetHidemaskInvert() { return PySetHideMaskInvert(); } void PythonEditorComponent::SetHidemask(const char* pName, bool bAdd) { return PySetHideMask(pName, bAdd); } bool PythonEditorComponent::GetHidemask(const char* pName) { return PyGetHideMask(pName); } } namespace AzToolsFramework { void PythonEditorFuncsHandler::Reflect(AZ::ReflectContext* context) { if (auto behaviorContext = azrtti_cast(context)) { // this will put these methods into the 'azlmbr.legacy.general' module auto addLegacyGeneral = [](AZ::BehaviorContext::GlobalMethodBuilder methodBuilder) { methodBuilder->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) ->Attribute(AZ::Script::Attributes::Category, "Legacy/General") ->Attribute(AZ::Script::Attributes::Module, "legacy.general"); }; addLegacyGeneral(behaviorContext->Method("get_cvar", PyGetCVarAsString, nullptr, "Gets a CVar value as a string.")); addLegacyGeneral(behaviorContext->Method("set_cvar", PySetCVarFromAny, nullptr, "Sets a CVar value from any simple value.")); addLegacyGeneral(behaviorContext->Method("set_cvar_string", PySetCVarFromString, nullptr, "Sets a CVar value from a string.")); addLegacyGeneral(behaviorContext->Method("set_cvar_integer", PySetCVarFromInt, nullptr, "Sets a CVar value from an integer.")); addLegacyGeneral(behaviorContext->Method("set_cvar_float", PySetCVarFromFloat, nullptr, "Sets a CVar value from a float.")); addLegacyGeneral(behaviorContext->Method("run_console", PyRunConsole, nullptr, "Runs a console command.")); addLegacyGeneral(behaviorContext->Method("enter_game_mode", PyEnterGameMode, nullptr, "Enters the editor game mode.")); addLegacyGeneral(behaviorContext->Method("is_in_game_mode", PyIsInGameMode, nullptr, "Queries if it's in the game mode or not.")); addLegacyGeneral(behaviorContext->Method("exit_game_mode", PyExitGameMode, nullptr, "Exits the editor game mode.")); addLegacyGeneral(behaviorContext->Method("enter_simulation_mode", PyEnterSimulationMode, nullptr, "Enters the editor AI/Physics simulation mode.")); addLegacyGeneral(behaviorContext->Method("is_in_simulation_mode", PyIsInSimulationMode, nullptr, "Queries if the editor is currently in the AI/Physics simulation mode or not.")); addLegacyGeneral(behaviorContext->Method("exit_simulation_mode", PyExitSimulationMode, nullptr, "Exits the editor AI/Physics simulation mode.")); addLegacyGeneral(behaviorContext->Method("run_lua", PyRunLua, nullptr, "Runs a lua script.")); addLegacyGeneral(behaviorContext->Method("run_file", PyRunFile, nullptr, "Runs a script file. A relative path from the editor user folder or an absolute path should be given as an argument.")); addLegacyGeneral(behaviorContext->Method("run_file_parameters", PyRunFileWithParameters, nullptr, "Runs a script file with parameters. A relative path from the editor user folder or an absolute path should be given as an argument. The arguments should be separated by whitespace.")); addLegacyGeneral(behaviorContext->Method("execute_command", PyExecuteCommand, nullptr, "Executes a given string as an editor command.")); addLegacyGeneral(behaviorContext->Method("message_box", PyMessageBox, nullptr, "Shows a confirmation message box with ok|cancel and shows a custom message.")); addLegacyGeneral(behaviorContext->Method("message_box_yes_no", PyMessageBoxYesNo, nullptr, "Shows a confirmation message box with yes|no and shows a custom message.")); addLegacyGeneral(behaviorContext->Method("message_box_ok", PyMessageBoxOK, nullptr, "Shows a confirmation message box with only ok and shows a custom message.")); addLegacyGeneral(behaviorContext->Method("edit_box", PyEditBox, nullptr, "Shows an edit box and returns the value as string.")); addLegacyGeneral(behaviorContext->Method("edit_box_check_data_type", PyEditBoxAndCheckProperty, nullptr, "Shows an edit box and checks the custom value to use the return value with other functions correctly.")); addLegacyGeneral(behaviorContext->Method("open_file_box", PyOpenFileBox, nullptr, "Shows an open file box and returns the selected file path and name.")); addLegacyGeneral(behaviorContext->Method("get_axis_constraint", PyGetAxisConstraint, nullptr, "Gets axis.")); addLegacyGeneral(behaviorContext->Method("set_axis_constraint", PySetAxisConstraint, nullptr, "Sets axis.")); addLegacyGeneral(behaviorContext->Method("get_edit_mode", PyGetEditMode, nullptr, "Gets edit mode.")); addLegacyGeneral(behaviorContext->Method("set_edit_mode", PySetEditMode, nullptr, "Sets edit mode.")); addLegacyGeneral(behaviorContext->Method("get_pak_from_file", PyGetPakFromFile, nullptr, "Finds a pak file name for a given file.")); addLegacyGeneral(behaviorContext->Method("log", PyLog, nullptr, "Prints the message to the editor console window.")); addLegacyGeneral(behaviorContext->Method("undo", PyUndo, nullptr, "Undoes the last operation.")); addLegacyGeneral(behaviorContext->Method("redo", PyRedo, nullptr, "Redoes the last undone operation.")); addLegacyGeneral(behaviorContext->Method("draw_label", PyDrawLabel, nullptr, "Shows a 2d label on the screen at the given position and given color.")); addLegacyGeneral(behaviorContext->Method("combo_box", PyComboBox, nullptr, "Shows a combo box listing each value passed in, returns string value selected by the user.")); addLegacyGeneral(behaviorContext->Method("set_hidemask_all", PySetHideMaskAll, nullptr, "Sets the current hidemask to 'all'.")); addLegacyGeneral(behaviorContext->Method("set_hidemask_none", PySetHideMaskNone, nullptr, "Sets the current hidemask to 'none'.")); addLegacyGeneral(behaviorContext->Method("set_hidemask_invert", PySetHideMaskInvert, nullptr, "Inverts the current hidemask.")); addLegacyGeneral(behaviorContext->Method("set_hidemask", PySetHideMask, nullptr, "Assigns a specified value to a specific object type in the current hidemask.")); addLegacyGeneral(behaviorContext->Method("get_hidemask", PyGetHideMask, nullptr, "Gets the value of a specific object type in the current hidemask.")); ///////////////////////////////////////////////////////////////////////// // Temporal, to be removed by LY-101149 addLegacyGeneral(behaviorContext->Method("find_editor_entity", PyFindEditorEntity, nullptr, "Retrieves a editor entity id by name")); addLegacyGeneral(behaviorContext->Method("find_game_entity", PyFindGameEntity, nullptr, "Retrieves a game entity id by name")); ////////////////////////////////////////////////////////////////////////// addLegacyGeneral(behaviorContext->Method("dump_exposed_classes", PyDumpBindings::GetExposedPythonClasses, nullptr, "Retrieves exposed classes")); } } }