Portals - Remove node naming restrictions

The use of special prefixes is only actually required during the import phase - the first conversion of rooms, roomgroups, and portals from Spatials and MeshInstances (based on the workflow of importing from blender).

Once converted to the native Godot nodes there is no longer a need for the naming requirements.

This PR removes the requirements except for the import. Manual portal linking after the initial conversion is now done exclusively using the `linked_room` nodepath property of the Portal.
This commit is contained in:
lawnjelly 2021-07-30 11:50:30 +01:00
parent 4542e3382b
commit 8c4c6a93b0
4 changed files with 85 additions and 51 deletions

View File

@ -145,6 +145,7 @@ void Portal::clear() {
_internal = false;
_linkedroom_ID[0] = -1;
_linkedroom_ID[1] = -1;
_importing_portal = false;
}
void Portal::_notification(int p_what) {
@ -279,34 +280,14 @@ bool Portal::try_set_unique_name(const String &p_name) {
void Portal::set_linked_room(const NodePath &link_path) {
_settings_path_linkedroom = link_path;
// change the name of the portal as well, if the link looks legit
// see if the link looks legit
Room *linkedroom = nullptr;
if (has_node(link_path)) {
linkedroom = Object::cast_to<Room>(get_node(link_path));
if (linkedroom) {
if (linkedroom != get_parent()) {
_settings_path_linkedroom = link_path;
// change the portal name
String string_link_room = RoomManager::_find_name_after(linkedroom, "Room");
// we need a unique name for the portal
String string_name_base = "Portal" + GODOT_PORTAL_DELINEATOR + string_link_room;
if (!try_set_unique_name(string_name_base)) {
bool success = false;
for (int n = 0; n < 128; n++) {
String string_name = string_name_base + GODOT_PORTAL_WILDCARD + itos(n);
if (try_set_unique_name(string_name)) {
success = true;
break;
}
}
if (!success) {
WARN_PRINT("Could not set portal name, suggest setting name manually instead.");
}
}
// was ok
} else {
WARN_PRINT("Linked room cannot be the parent room of a portal.");
}

View File

@ -154,6 +154,13 @@ private:
real_t _margin;
bool _use_default_margin;
// during conversion, we need to know
// whether this portal is being imported from a mesh
// and is using an explicitly named link room with prefix.
// If this is not the case, and it is already a Godot Portal node,
// we will either use the assigned nodepath, or autolink.
bool _importing_portal = false;
// for editing
#ifdef TOOLS_ENABLED
ObjectID _room_manager_godot_ID;

View File

@ -776,16 +776,34 @@ void RoomManager::_third_pass_rooms(const LocalVector<Portal *> &p_portals) {
for (int n = 0; n < _rooms.size(); n++) {
Room *room = _rooms[n];
String room_short_name = _find_name_after(room, "ROOM");
convert_log("ROOM\t" + room_short_name);
// no need to do all these string operations if we are not debugging and don't need logs
if (_show_debug) {
String room_short_name = _find_name_after(room, "Room", true);
convert_log("ROOM\t" + room_short_name);
// log output the portals associated with this room
for (int p = 0; p < room->_portals.size(); p++) {
const Portal &portal = *p_portals[room->_portals[p]];
// log output the portals associated with this room
for (int p = 0; p < room->_portals.size(); p++) {
const Portal &portal = *p_portals[room->_portals[p]];
String in_or_out = (portal._linkedroom_ID[0] == room->_room_ID) ? "POUT" : "PIN ";
convert_log("\t\t" + in_or_out + "\t" + portal.get_name());
}
bool portal_links_out = portal._linkedroom_ID[0] == room->_room_ID;
int linked_room_id = (portal_links_out) ? portal._linkedroom_ID[1] : portal._linkedroom_ID[0];
// this shouldn't be out of range, but just in case
if (linked_room_id < _rooms.size()) {
Room *linked_room = _rooms[linked_room_id];
String portal_link_room_name = _find_name_after(linked_room, "Room", true);
String in_or_out = (portal_links_out) ? "POUT" : "PIN ";
// display the name of the room linked to
convert_log("\t\t" + in_or_out + "\t" + portal_link_room_name);
} else {
WARN_PRINT_ONCE("linked_room_id is out of range");
}
}
} // if _show_debug
// do a second pass finding the statics, where they are
// finally added to the rooms in the portal_renderer.
@ -808,21 +826,26 @@ void RoomManager::_third_pass_rooms(const LocalVector<Portal *> &p_portals) {
}
void RoomManager::_second_pass_portals(Spatial *p_roomlist, LocalVector<Portal *> &r_portals) {
convert_log("_second_pass_portals");
for (unsigned int n = 0; n < r_portals.size(); n++) {
Portal *portal = r_portals[n];
String string_link_room_shortname = _find_name_after(portal, "Portal");
String string_link_room = "Room" + GODOT_PORTAL_DELINEATOR + string_link_room_shortname;
if (string_link_room_shortname != "") {
Room *linked_room = Object::cast_to<Room>(p_roomlist->find_node(string_link_room, true, false));
if (linked_room) {
NodePath path = portal->get_path_to(linked_room);
portal->set_linked_room_internal(path);
} else {
WARN_PRINT("Portal link room : " + string_link_room + " not found.");
_warning_portal_link_room_not_found = true;
// we have a choice here.
// If we are importing, we will try linking using the naming convention method.
// We do this by setting the assigned nodepath if we find the link room, then
// the resolving links is done in the usual manner from the nodepath.
if (portal->_importing_portal) {
String string_link_room_shortname = _find_name_after(portal, "Portal");
String string_link_room = "Room" + GODOT_PORTAL_DELINEATOR + string_link_room_shortname;
if (string_link_room_shortname != "") {
Room *linked_room = Object::cast_to<Room>(p_roomlist->find_node(string_link_room, true, false));
if (linked_room) {
NodePath path = portal->get_path_to(linked_room);
portal->set_linked_room_internal(path);
} else {
WARN_PRINT("Portal link room : " + string_link_room + " not found.");
_warning_portal_link_room_not_found = true;
}
}
}
@ -849,8 +872,6 @@ void RoomManager::_second_pass_portals(Spatial *p_roomlist, LocalVector<Portal *
}
void RoomManager::_autolink_portals(Spatial *p_roomlist, LocalVector<Portal *> &r_portals) {
convert_log("_autolink_portals");
for (unsigned int n = 0; n < r_portals.size(); n++) {
Portal *portal = r_portals[n];
@ -958,12 +979,12 @@ bool RoomManager::_check_roomlist_validity(Node *p_node) {
void RoomManager::_convert_rooms_recursive(Spatial *p_node, LocalVector<Portal *> &r_portals, LocalVector<RoomGroup *> &r_roomgroups, int p_roomgroup) {
// is this a room?
if (_name_starts_with(p_node, "Room") || _node_is_type<Room>(p_node)) {
if (_node_is_type<Room>(p_node) || _name_starts_with(p_node, "Room")) {
_convert_room(p_node, r_portals, r_roomgroups, p_roomgroup);
}
// is this a roomgroup?
if (_name_starts_with(p_node, "RoomGroup") || _node_is_type<RoomGroup>(p_node)) {
if (_node_is_type<RoomGroup>(p_node) || _name_starts_with(p_node, "RoomGroup")) {
p_roomgroup = _convert_roomgroup(p_node, r_roomgroups);
}
@ -1570,12 +1591,16 @@ bool RoomManager::_add_plane_if_unique(const Room *p_room, LocalVector<Plane, in
void RoomManager::_convert_portal(Room *p_room, Spatial *p_node, LocalVector<Portal *> &portals) {
Portal *portal = Object::cast_to<Portal>(p_node);
bool importing = false;
// if not a gportal already, convert the node type
if (!portal) {
importing = true;
portal = _change_node_type<Portal>(p_node, "G", false);
portal->create_from_mesh_instance(Object::cast_to<MeshInstance>(p_node));
p_node->queue_delete();
} else {
// only allow converting once
if (portal->_conversion_tick == _conversion_tick) {
@ -1586,6 +1611,10 @@ void RoomManager::_convert_portal(Room *p_room, Spatial *p_node, LocalVector<Por
// make sure to start with fresh internal data each time (for linked rooms etc)
portal->clear();
// mark the portal if we are importing, because we will need to use the naming
// prefix system to look for linked rooms in that case
portal->_importing_portal = importing;
// mark so as only to convert once
portal->_conversion_tick = _conversion_tick;
@ -1976,12 +2005,29 @@ bool RoomManager::_name_starts_with(const Node *p_node, String p_search_string,
return false;
}
String RoomManager::_find_name_after(Node *p_node, String p_string_start) {
p_string_start += GODOT_PORTAL_DELINEATOR;
String RoomManager::_find_name_after(Node *p_node, String p_prefix, bool p_allow_no_prefix) {
ERR_FAIL_NULL_V(p_node, String());
p_prefix += GODOT_PORTAL_DELINEATOR;
p_prefix = p_prefix.to_lower();
int prefix_length = p_prefix.length();
String name = p_node->get_name();
String name_start = name.substr(0, prefix_length).to_lower();
String string_result;
String name = p_node->get_name();
string_result = name.substr(p_string_start.length());
// is the prefix correct?
if (p_prefix == name_start) {
string_result = name.substr(prefix_length);
} else {
if (p_allow_no_prefix) {
// there is no prefix, or the prefix is incorrect...
// we will pass the whole name
string_result = name;
}
// else we will return a null string
}
// because godot doesn't support multiple nodes with the same name, we will strip e.g. a number
// after an @ on the end of the name...

View File

@ -215,7 +215,7 @@ private:
void debug_print_line(String p_string, int p_priority = 0);
public:
static String _find_name_after(Node *p_node, String p_string_start);
static String _find_name_after(Node *p_node, String p_prefix, bool p_allow_no_prefix = false);
static void show_warning(const String &p_string, const String &p_extra_string = "", bool p_alert = true);
static real_t _get_default_portal_margin() { return _default_portal_margin; }