diff --git a/Source/Editors/map_editor.cpp b/Source/Editors/map_editor.cpp index 0641bf46c..e455f01f0 100644 --- a/Source/Editors/map_editor.cpp +++ b/Source/Editors/map_editor.cpp @@ -798,7 +798,6 @@ void MapEditor::Draw() { for (auto obj : scenegraph_->objects_) { if (obj->editor_visible) { const EntityType& type = obj->GetType(); - if (IsTypeEnabled(type) && (( ((type != _group && type != _prefab) || draw_group_and_prefab_boxes) && type != _env_object && @@ -808,7 +807,21 @@ void MapEditor::Draw() { type != _navmesh_hint_object) || obj->Selected())) { // Draw box for selected objects or objects that always have a visible box. - DrawBox(obj->box_, obj, obj->Selected(), obj->box_color); + vec4 box_color; + if (obj->editor_locked) { + box_color = vec4(1.0f, 0.0f, 0.0f, 1.0f); + //So there's still a distinction between playable character boxes and + //non-playable character boxes, even if they're both locked. + if (type == _movement_object) { + MovementObject* mo = (MovementObject*)obj; + if (mo->is_player) { + box_color = vec4(0.75f, 0.25f, 0.0f, 1.0f); + } + } + } else { + box_color = obj->box_color; + } + DrawBox(obj->box_, obj, obj->Selected(), box_color); } } if (always_draw_hotspot_connections) { @@ -1081,6 +1094,13 @@ static Collision lineCheckActiveSelected(SceneGraph* scenegraph, const vec3& sta } static EditorTypes::Tool OmniGetTool(const Collision& c, int permission_flags, const Object* object_, const Box& box_) { + //This just makes it so that while the cursor is over a selected but locked object, that it doesn't show + //any of the sybols for transform/rotate/scale/whatever. + //It'd be pretty cool if it had its own symbol though, like an X or something. + if (object_->editor_locked) { + return EditorTypes::NO_TOOL; + } + const Keyboard& keyboard = Input::Instance()->getKeyboard(); if (KeyCommand::CheckDown(keyboard, KeyCommand::kForceTranslate, KIMF_LEVEL_EDITOR_GENERAL) && permission_flags & Object::CAN_TRANSLATE) { return EditorTypes::TRANSLATE; @@ -3554,7 +3574,7 @@ void MapEditor::UpdateTransformTool(SceneGraph* scenegraph, EditorTypes::Tool ty std::vector moved_objects; PROFILER_ZONE(g_profiler_ctx, "Updating transform"); for (auto obj : scenegraph->objects_) { - if (obj->Selected()) { + if (obj->Selected() && !obj->editor_locked) { obj->SetTranslation(obj->start_transform.translation + curr_tool_transform.translation); obj->SetRotation(curr_tool_transform.rotation * obj->start_transform.rotation); obj->SetScale(curr_tool_transform.scale * obj->start_transform.scale); diff --git a/Source/GUI/dimgui/dimgui.cpp b/Source/GUI/dimgui/dimgui.cpp index 01a63ccf1..5b12d59ce 100644 --- a/Source/GUI/dimgui/dimgui.cpp +++ b/Source/GUI/dimgui/dimgui.cpp @@ -933,6 +933,61 @@ static void DrawLaunchCustomGuiButton(Object* obj, bool& are_script_params_read_ } } +//Not all types are editor-lockable since, for example, being able to editor-lock a dialogue placeholder is +//a pretty useless feature... I think? +// +// In any case, here are the types that are current *not* editor lockable. +/* + _any_type, + _no_type, + _camera_type, + _terrain_type, + _spawn_point, + _rigged_object, + _placeholder_object, + _light_probe_object, + _light_volume_object, +*/ + +static bool EditorLockable(const EntityType& type) { + if (type == _env_object || + type == _decal_object || + type == _movement_object || + type == _hotspot_object || + type == _group || + type == _item_object || + type == _path_point_object || + type == _ambient_sound_object || + type == _dynamic_light_object || + type == _navmesh_hint_object || + type == _navmesh_region_object || + type == _navmesh_connection_object || + type == _reflection_capture_object || + type == _prefab) + { + return true; + } + return false; +} + +//This feature may seem redundant, since you can't really "accidently" select objects in a group unless you're +//usinig the scroll select, but a *potential* use case is allowing one to Ctrl+Shift+A a particular object, +//group them, lock the group, apply the lock to all children, then ungroup and boom now all instances of that +//particular object are locked which I think is pretty neat. And I'm sure there are other use cases too. +static void UpdateChildrenEditorLock(Group* group, bool enabled) { + for (auto& i : group->children) { + Object* obj = i.direct_ptr; + const EntityType& type = obj->GetType(); + if (EditorLockable(type)) { + obj->editor_locked = enabled; + if (type == _group) { + Group* sub_group = (Group*)obj; + UpdateChildrenEditorLock(sub_group, enabled); + } + } + } +} + static void DrawObjectInfoFlat(Object* obj) { ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing()); obj->DrawImGuiEditor(); @@ -942,10 +997,18 @@ static void DrawObjectInfoFlat(Object* obj) { DrawLaunchCustomGuiButton(obj, are_script_params_read_only); bool temp_enabled = obj->enabled_; - if (ImGui::Checkbox("Enabled", &temp_enabled)) { + //Added spaces here so that the "Lock" checkbox is off further to the right. + if (ImGui::Checkbox("Enabled ", &temp_enabled)) { obj->SetEnabled(temp_enabled); } - + ImGui::SameLine(); + if (EditorLockable(obj->GetType())) { + ImGui::Text("Locked"); + ImGui::SameLine(); + ImGui::Checkbox("", &obj->editor_locked); + //The reason that the "Locked" text is its own ImGui::Text thingy as apposed just being part of ImGui::Checkbox + //is because this allows the text to be to the left of the checkbox, which looked better imo. + } const size_t name_len = 64; char name[name_len]; strscpy(name, obj->GetName().c_str(), name_len); @@ -1246,15 +1309,22 @@ static void DrawObjectInfoFlat(Object* obj) { } ImGui::TreePop(); } - ImGui::Separator(); DrawColorPicker(&obj, 1, obj->scenegraph_); - if (obj->GetType() == _env_object) { - EnvObject* eo = (EnvObject*)obj; + if (obj->GetType() == _env_object || obj->GetType() == _group) { ImGui::Separator(); - ImGui::Checkbox("no_navmesh", &eo->no_navmesh); - } + if (obj->GetType() == _env_object) { + EnvObject* eo = (EnvObject*)obj; + ImGui::Checkbox("No navmesh", &eo->no_navmesh); + } + if (obj->GetType() == _group) { + if (ImGui::Button("Apply Lock To Children")) { + Group* group = (Group*)obj; + UpdateChildrenEditorLock(group, obj->editor_locked); + } + } + } ImGui::Separator(); if (obj->GetType() == _movement_object) { MovementObject* mov_obj = (MovementObject*)obj; @@ -1342,10 +1412,15 @@ static void DrawObjectInfo(Object* obj, bool force_expand_script_params) { DrawLaunchCustomGuiButton(obj, are_script_params_read_only); bool temp_enabled = obj->enabled_; - if (ImGui::Checkbox("Enabled", &temp_enabled)) { + if (ImGui::Checkbox("Enabled ", &temp_enabled)) { obj->SetEnabled(temp_enabled); } - + ImGui::SameLine(); + if (EditorLockable(obj->GetType())) { + ImGui::Text("Locked"); + ImGui::SameLine(); + ImGui::Checkbox("", &obj->editor_locked); + } if (ImGui::TreeNode("Name")) { const size_t name_len = 64; char name[name_len]; @@ -1419,11 +1494,23 @@ static void DrawObjectInfo(Object* obj, bool force_expand_script_params) { ImGui::TreePop(); } if (ImGui::TreeNode("Flags")) { - ImGui::Checkbox("no_navmesh", &eo->no_navmesh); + ImGui::Checkbox("No navmesh", &eo->no_navmesh); + //This checkbox ^ used to say "no_navmesh" which is all well and lovely, but I've changed it to + //instead be "No navmesh" on account of the fact that I didn't want the "Lock" checkbox to say + //"editor_locked", so yeah. This has been done for consitency's sake. Though if the previous + //naming convention is preferred then I'm all g with reverting it. ImGui::TreePop(); } } + if (obj->GetType() == _group) { + ImGui::NextColumn(); + if (ImGui::Button("Apply Lock To Children")) { + Group* group = (Group*)obj; + UpdateChildrenEditorLock(group, obj->editor_locked); + } + } + if (obj->GetType() == _movement_object) { MovementObject* mo = (MovementObject*)obj; if (ImGui::TreeNode("Color Palette")) { diff --git a/Source/Game/EntityDescription.cpp b/Source/Game/EntityDescription.cpp index 3faeb23d6..e105255ea 100644 --- a/Source/Game/EntityDescription.cpp +++ b/Source/Game/EntityDescription.cpp @@ -567,6 +567,14 @@ void EntityDescription::SaveToXML(TiXmlElement* parent) const { } break; } + case EDF_EDITOR_LOCKED: { + bool val; + field.ReadBool(&val); + if (val) { + object->SetAttribute("editor_locked", "true"); + } + break; + } case EDF_PREFAB_LOCKED: { bool val; field.ReadBool(&val); @@ -727,6 +735,13 @@ void GetTSRIinfo(EntityDescription& desc, const TiXmlElement* pElement) { desc.AddQuaternion(EDF_ROTATION, rotation); desc.AddVec3(EDF_ROTATION_EULER, rotation_euler); + const char* editor_locked = pElement->Attribute("editor_locked"); + if (editor_locked) { + desc.AddBool(EDF_EDITOR_LOCKED, saysTrue(editor_locked) == 1); + } else { + desc.AddBool(EDF_EDITOR_LOCKED, false); + } + ScriptParamMap spm; const TiXmlElement* params = pElement->FirstChildElement("parameters"); if (params) { diff --git a/Source/Game/EntityDescription.h b/Source/Game/EntityDescription.h index b0a364407..b2cd84c5d 100644 --- a/Source/Game/EntityDescription.h +++ b/Source/Game/EntityDescription.h @@ -71,6 +71,7 @@ enum EntityDescriptionFieldType { EDF_GI_COEFFICIENTS, EDF_NAV_MESH_CONNECTIONS, EDF_NO_NAVMESH, + EDF_EDITOR_LOCKED, EDF_PREFAB_LOCKED, EDF_PREFAB_PATH, EDF_ORIGINAL_SCALE, diff --git a/Source/Objects/object.cpp b/Source/Objects/object.cpp index 11dfbf9d1..28e7ecd09 100644 --- a/Source/Objects/object.cpp +++ b/Source/Objects/object.cpp @@ -239,6 +239,10 @@ bool Object::SetFromDesc(const EntityDescription &desc) { SetScriptParams(spm); break; } + case EDF_EDITOR_LOCKED: { + field.ReadBool(&editor_locked); + break; + } case EDF_HOTSPOT_CONNECTED_TO: { field.ReadIntVec(&unfinalized_connected_to); break; @@ -308,6 +312,7 @@ void Object::GetDesc(EntityDescription &desc) const { desc.AddScriptParams(EDF_SCRIPT_PARAMS, sp.GetParameterMap()); desc.AddIntVec(EDF_HOTSPOT_CONNECTED_TO, connected_to); desc.AddIntVec(EDF_HOTSPOT_CONNECTED_FROM, connected_from); + desc.AddBool(EDF_EDITOR_LOCKED, editor_locked); } void Object::ReceiveObjectMessage(OBJECT_MSG::Type type, ...) { diff --git a/Source/Objects/object.h b/Source/Objects/object.h index ee87b453e..778fc4ae2 100644 --- a/Source/Objects/object.h +++ b/Source/Objects/object.h @@ -119,6 +119,7 @@ class Object { int permission_flags; bool selectable_; bool enabled_; + bool editor_locked; vec4 box_color; Box box_; bool editor_visible; @@ -166,6 +167,7 @@ class Object { exclude_from_save(false), update_list_entry(-1), enabled_(true), + editor_locked(false), parent(NULL), scenegraph_(parent_scenegraph), scale_(1.0f),