""" 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. """ # Built-in Imports from __future__ import annotations from typing import List, Tuple, Union # Helper file Imports import utils # Lumberyard Imports import azlmbr import azlmbr.bus as bus import azlmbr.editor as editor import azlmbr.math as math import azlmbr.legacy.general as general class EditorComponent: """ EditorComponent class used to set and get the component property value using path EditorComponent object is returned from either of EditorEntity.add_component() or Entity.add_components() or EditorEntity.get_component_objects() which also assigns self.id and self.type_id to the EditorComponent object. """ # Methods def get_component_name(self) -> str: """ Used to get name of component :return: name of component """ type_names = editor.EditorComponentAPIBus(bus.Broadcast, "FindComponentTypeNames", [self.type_id]) assert len(type_names) != 0, "Component object does not have type id" return type_names[0] def get_property_tree(self): """ Used to get the property tree object of component that has following functions associated with it: 1. prop_tree.is_container(path) 2. prop_tree.get_container_count(path) 3. prop_tree.reset_container(path) 4. prop_tree.add_container_item(path, key, item) 5. prop_tree.remove_container_item(path, key) 6. prop_tree.update_container_item(path, key, value) 7. prop_tree.get_container_item(path, key) :return: Property tree object of a component """ build_prop_tree_outcome = editor.EditorComponentAPIBus( bus.Broadcast, "BuildComponentPropertyTreeEditor", self.id ) assert ( build_prop_tree_outcome.IsSuccess() ), f"Failure: Could not build property tree of component: '{self.get_component_name()}'" prop_tree = build_prop_tree_outcome.GetValue() utils.Report.info(prop_tree.build_paths_list()) return prop_tree def get_component_property_value(self, component_property_path: str): """ Given a component property path, outputs the property's value :param component_property_path: String of component property. (e.g. 'Settings|Visible') :return: Value set in given component_property_path. Type is dependent on component property """ get_component_property_outcome = editor.EditorComponentAPIBus( bus.Broadcast, "GetComponentProperty", self.id, component_property_path ) assert ( get_component_property_outcome.IsSuccess() ), f"Failure: Could not get value from {self.get_component_name()} : {component_property_path}" return get_component_property_outcome.GetValue() def set_component_property_value(self, component_property_path: str, value: object): """ Used to set component property value :param component_property_path: Path of property in the component to act on :param value: new value for the variable being changed in the component """ outcome = editor.EditorComponentAPIBus( bus.Broadcast, "SetComponentProperty", self.id, component_property_path, value ) assert ( outcome.IsSuccess() ), f"Failure: Could not set value to '{self.get_component_name()}' : '{component_property_path}'" @staticmethod def get_type_ids(component_names: list) -> list: """ Used to get type ids of given components list :param: component_names: List of components to get type ids :return: List of type ids of given components. """ type_ids = editor.EditorComponentAPIBus( bus.Broadcast, "FindComponentTypeIdsByEntityType", component_names, azlmbr.entity.EntityType().Game ) return type_ids class EditorEntity: """ Entity class is used to create and interact with Editor Entities. Example: To create Editor Entity, Use the code: test_entity = Entity.create_editor_entity("TestEntity") # This creates a python object with 'test_entity' linked to entity name "TestEntity" in Editor. # To add component, use: test_entity.add_component() """ def __init__(self, id: azlmbr.entity.EntityId): self.id: azlmbr.entity.EntityId = id # Creation functions @classmethod def find_editor_entity(cls, entity_name: str) -> EditorEntity: """ Given Entity name, outputs entity object :param entity_name: Name of entity to find :return: EditorEntity class object """ entity_id = general.find_editor_entity(entity_name) assert entity_id.IsValid(), f"Failure: Couldn't find entity with name: '{entity_name}'" entity = cls(entity_id) return entity @classmethod def create_editor_entity(cls, name: str = None, parent_id=None) -> EditorEntity: """ Used to create entity at default position using 'CreateNewEntity' Bus :param name: Name of the Entity to be created :param parent_id: (optional) Used to create child entity under parent_id if specified :return: EditorEntity class object """ if parent_id is None: parent_id = azlmbr.entity.EntityId() new_id = azlmbr.editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", parent_id) assert new_id.IsValid(), "Failure: Could not create Editor Entity" entity = cls(new_id) if name: entity.set_name(name) return entity @classmethod def create_editor_entity_at( cls, entity_position: Union[List, Tuple, math.Vector3], name: str = None, parent_id: azlmbr.entity.EntityId = None, ) -> EditorEntity: """ Used to create entity at position using 'CreateNewEntityAtPosition' Bus. :param entity_position: World Position(X, Y, Z) of entity in viewport. Example: [512.0, 512.0, 32.0] :param name: Name of the Entity to be created :parent_id: (optional) Used to create child entity under parent_id if specified :Example: test_entity = EditorEntity.create_editor_entity_at([512.0, 512.0, 32.0], "TestEntity") :return: EditorEntity class object """ def convert_to_azvector3(xyz) -> math.Vector3: if isinstance(xyz, Tuple) or isinstance(xyz, List): assert len(xyz) == 3, ValueError("vector must be a 3 element list/tuple or azlmbr.math.Vector3") return math.Vector3(*xyz) elif isinstance(xyz, type(math.Vector3())): return xyz else: raise ValueError("vector must be a 3 element list/tuple or azlmbr.math.Vector3") if parent_id is None: parent_id = azlmbr.entity.EntityId() new_id = azlmbr.editor.ToolsApplicationRequestBus( bus.Broadcast, "CreateNewEntityAtPosition", convert_to_azvector3(entity_position), parent_id ) assert new_id.IsValid(), "Failure: Could not create Editor Entity" entity = cls(new_id) if name: entity.set_name(name) return entity # Methods def set_name(self, entity_name: str): """ Given entity_name, sets name to Entity :param: entity_name: Name of the entity to set """ editor.EditorEntityAPIBus(bus.Event, "SetName", self.id, entity_name) def get_name(self) -> str: """ Used to get the name of entity """ return editor.EditorEntityInfoRequestBus(bus.Event, "GetName", self.id) def set_parent_entity(self, parent_entity_id): """ Used to set this entity to be child of parent entity passed in :param: parent_entity_id: Entity Id of parent to set """ assert ( parent_entity_id.IsValid() ), f"Failure: Could not set parent to entity: {self.get_name()}, Invalid parent id" editor.EditorEntityAPIBus(bus.Event, "SetParent", self.id, parent_entity_id) def get_parent_id(self) -> azlmbr.entity.EntityId: """ :return: Entity id of parent. Type: entity.EntityId() """ return editor.EditorEntityInfoRequestBus(bus.Event, "GetParent", self.id) def add_component(self, component_name: str) -> EditorComponent: """ Used to add new component to Entity. :param component_name: String of component name to add. :return: Component object of newly added component. """ component = self.add_components([component_name])[0] return component def add_components(self, component_names: list) -> List[EditorComponent]: """ Used to add multiple components :param: component_names: List of components to add to entity :return: List of newly added components to the entity """ components = [] type_ids = EditorComponent.get_type_ids(component_names) for type_id in type_ids: new_comp = EditorComponent() new_comp.type_id = type_id add_component_outcome = editor.EditorComponentAPIBus( bus.Broadcast, "AddComponentsOfType", self.id, [type_id] ) assert ( add_component_outcome.IsSuccess() ), f"Failure: Could not add component: '{new_comp.get_component_name()}' to entity: '{self.get_name()}'" new_comp.id = add_component_outcome.GetValue()[0] components.append(new_comp) return components def get_components_of_type(self, component_names: list) -> List[EditorComponent]: """ Used to get components of type component_name that already exists on Entity :param component_name: Name to component to check :return: List of Entity Component objects of given component name """ component_list = [] type_ids = EditorComponent.get_type_ids(component_names) for type_id in type_ids: component = EditorComponent() component.type_id = type_id get_component_of_type_outcome = editor.EditorComponentAPIBus( bus.Broadcast, "GetComponentOfType", self.id, type_id ) assert ( get_component_of_type_outcome.IsSuccess() ), f"Failure: Entity: '{self.get_name()}' does not have component:'{component.get_component_name()}'" component.id = get_component_of_type_outcome.GetValue() component_list.append(component) return component_list def has_component(self, component_name: str) -> bool: """ Used to verify if the entity has the specified component :param component_name: Name of component to check for :return: True, if entity has specified component. Else, False """ type_ids = EditorComponent.get_type_ids([component_name]) return editor.EditorComponentAPIBus(bus.Broadcast, "HasComponentOfType", self.id, type_ids[0]) def get_start_status(self) -> int: """ This will return a value for an entity's starting status (active, inactive, or editor) in the form of azlmbr.globals.property.EditorEntityStartStatus_. For comparisons using this value, should compare against the azlmbr property and not the int value """ status = editor.EditorEntityInfoRequestBus(bus.Event, "GetStartStatus", self.id) if status == azlmbr.globals.property.EditorEntityStartStatus_StartActive: status_text = "active" elif status == azlmbr.globals.property.EditorEntityStartStatus_StartInactive: status_text = "inactive" elif status == azlmbr.globals.property.EditorEntityStartStatus_EditorOnly: status_text = "editor" utils.Report.info(f"The start status for {self.get_name} is {status_text}") self.start_status = status return status def set_start_status(self, desired_start_status: str): """ Set an entity as active/inactive at beginning of runtime or it is editor-only, given its entity id and the start status then return set success :param desired_start_status: must be one of three choices: active, inactive, or editor """ if desired_start_status == "active": status_to_set = azlmbr.globals.property.EditorEntityStartStatus_StartActive elif desired_start_status == "inactive": status_to_set = azlmbr.globals.property.EditorEntityStartStatus_StartInactive elif desired_start_status == "editor": status_to_set = azlmbr.globals.property.EditorEntityStartStatus_EditorOnly else: utils.Report.info( f"Invalid desired_start_status argument for {self.get_name} set_start_status command;\ Use editor, active, or inactive" ) editor.EditorEntityAPIBus(bus.Event, "SetStartStatus", self.id, status_to_set) set_status = self.get_start_status() assert set_status == status_to_set, f"Failed to set start status of {desired_start_status} to {self.get_name}"