Beispiel #1
0
        void PlaceEntities(ISceneBroker scene, OverloadLevelEditor.Level overloadLevelData, bool isNewLevel, IGameObjectBroker sceneLevelContainerObject, Dictionary <int, int> doorNumToPortalMap)
        {
            List <LevelData.SpawnPoint> item_spawn_points   = new List <LevelData.SpawnPoint>();
            List <LevelData.SpawnPoint> player_spawn_points = new List <LevelData.SpawnPoint>();
            List <LevelData.SpawnPoint> mp_camera_points    = new List <LevelData.SpawnPoint>();

            bool found_player_start = false;

            // Create a container object to reduce clutter
            IGameObjectBroker entity_container_object = null;

            {
                if (!isNewLevel)
                {
                    // Look for the existing container object
                    for (int child_idx = 0, num_children = sceneLevelContainerObject.Transform.ChildCount; child_idx < num_children; ++child_idx)
                    {
                        var test_object = sceneLevelContainerObject.Transform.GetChild(child_idx).ownerGameObject;
                        if (test_object.Name == s_specialEntityContainerObjectName)
                        {
                            entity_container_object = test_object;
                            break;
                        }
                    }

                    if (entity_container_object != null)
                    {
                        // Clean out all the old entities
                        while (entity_container_object.Transform.ChildCount > 0)
                        {
                            scene.DestroyGameObject(entity_container_object.Transform.GetChild(0).ownerGameObject);
                        }
                    }
                }

                if (entity_container_object == null)
                {
                    entity_container_object = scene.CreateRootGameObject(s_specialEntityContainerObjectName);
                    entity_container_object.Transform.Parent = sceneLevelContainerObject.Transform;
                }
            }

            // Get the valid entity container types
            string[] entity_category_names = Enumerable.Range(0, (int)OverloadLevelEditor.EntityType.NUM)
                                             .Select(idx => Enum.GetName(typeof(OverloadLevelEditor.EntityType), (OverloadLevelEditor.EntityType)idx))
                                             .ToArray();

            // Create the container categories
            IGameObjectBroker[] entity_category_objects = new IGameObjectBroker[entity_category_names.Length];
            for (int i = 0, count = entity_category_names.Length; i < count; ++i)
            {
                var entity_category = entity_category_names[i];
                entity_category_objects[i] = scene.CreateRootGameObject(entity_category);
                entity_category_objects[i].Transform.Parent = entity_container_object.Transform;
            }

            var  cached_prefab_map = new Dictionary <OverloadLevelEditor.EntityType, Dictionary <int, IGameObjectBroker> >();
            bool added_player_ship = false;

#if OVERLOAD_LEVEL_EDITOR
            var map_guid_to_gameobject = new Dictionary <System.Guid, IGameObjectBroker>();
#else
            var map_guid_to_gameobject = new Dictionary <string, IGameObjectBroker>();
#endif
            var deferred_properties_list = new List <KeyValuePair <IGameObjectBroker, OverloadLevelEditor.Entity> >();

            // Called when a door/portal link is found
            List <LevelData.PortalDoorConnection> portal_door_references = new List <LevelData.PortalDoorConnection>();
            Action <int, IComponentBroker>        link_portal_to_door    = (int portal_index, IComponentBroker door) => {
                portal_door_references.Add(new LevelData.PortalDoorConnection()
                {
                    PortalIndex = portal_index,
#if OVERLOAD_LEVEL_EDITOR
                    ReferenceDoor = door,
#else
                    ReferenceDoor = door.InternalObject as DoorBase,
#endif
                });
            };

            // Called to resolve a DropType to a prefab

            /*Func<Overload.DropTypes, GameObject> resolve_droptype_prefab = (Overload.DropTypes drop_type) => {
             *      string prefab_name = ItemPrefabs[drop_type];
             *
             *      // Construct a prefab name: "entity_item_<enum_name>"
             *      string enum_as_string = drop_type.ToString();
             *      string enum_as_snake_case_string = enum_as_string.ToSnakeCase();
             *      string enum_as_lower_string = enum_as_string.ToLowerInvariant();
             *      string[] search_prefab_names = new string[] {
             *              prefab_name,
             *              "entity_item_" + enum_as_lower_string,
             *              "entity_item_" + enum_as_snake_case_string,
             *      };
             *
             *      foreach (var search_prefab_name in search_prefab_names) {
             *              var prefab_match_guids = AssetDatabase.FindAssets(search_prefab_name + " t:GameObject");
             *              if (prefab_match_guids != null && prefab_match_guids.Length != 0) {
             *                      // TODO(Jeff): If there are multiple options - should we pick the one that matches the name the closest?
             *                      var entity_prefab_path = AssetDatabase.GUIDToAssetPath(prefab_match_guids[0]);
             *                      return AssetDatabase.LoadAssetAtPath(entity_prefab_path, typeof(GameObject)) as GameObject;
             *              }
             *      }
             *
             *      return null;
             * };*/

            foreach (int entity_idx in overloadLevelData.EnumerateAliveEntityIndices())
            {
                var entity_src    = overloadLevelData.entity[entity_idx];
                var entity_orient = OpenTKExtensions.OpenTKQuaternion.ExtractRotation(entity_src.rotation).ToUnity();

                //Look for robot spawn points
                if ((entity_src.Type == OverloadLevelEditor.EntityType.SPECIAL) && (entity_src.SubType == (int)OverloadLevelEditor.SpecialSubType.ROBOT_SPAWN_POINT))
                {
                    // Robot spawn points -- handled separately
                    continue;
                }
                else if ((entity_src.Type == OverloadLevelEditor.EntityType.SPECIAL) && (entity_src.SubType == (int)OverloadLevelEditor.SpecialSubType.PLAYER_START))
                {
                    if (!found_player_start)
                    {
                        // First player spawn point (still added to list)
                        player_spawn_points.Add(new LevelData.SpawnPoint(entity_src.position.ToUnity(), entity_orient, entity_src.m_multiplayer_team_association_mask));
                        found_player_start = true;
                    }
                    else
                    {
                        // (Alternative) Player spawn points
                        player_spawn_points.Add(new LevelData.SpawnPoint(entity_src.position.ToUnity(), entity_orient, entity_src.m_multiplayer_team_association_mask));
                        continue;
                    }
                }
                else if ((entity_src.Type == OverloadLevelEditor.EntityType.ITEM) && (entity_src.SubType == (int)OverloadLevelEditor.ItemSubType.CM_SPAWN))
                {
                    // Item spawn points (uses "super" flag for team bit = 1, otherwise team bit = 0)
                    item_spawn_points.Add(new LevelData.SpawnPoint(entity_src.position.ToUnity(), entity_orient, ((Overload.EntityPropsItem)entity_src.entity_props).super ? 1 : 0));
                    continue;
                }
                else if ((entity_src.Type == OverloadLevelEditor.EntityType.PROP) && (entity_src.SubType == (int)OverloadLevelEditor.PropSubType.MP_CAMERA))
                {
                    // Item spawn points
                    mp_camera_points.Add(new LevelData.SpawnPoint(entity_src.position.ToUnity(), entity_orient, 0));
                    continue;
                }
                if (entity_src.Type == OverloadLevelEditor.EntityType.TRIGGER && entity_src.SubType == (int)OverloadLevelEditor.TriggerSubType.REFLECTION_PROBE)
                {
                    // reflection probe -- handled separately
                    continue;
                }

                // Resolve the prefab for the entity
                Dictionary <int, IGameObjectBroker> cache_subtype_map;
                if (!cached_prefab_map.TryGetValue(entity_src.Type, out cache_subtype_map))
                {
                    cache_subtype_map = new Dictionary <int, IGameObjectBroker>();
                    cached_prefab_map.Add(entity_src.Type, cache_subtype_map);
                }

                // SPECIAL CASE: If the subtype is set to 'NUM' (max value) then assume that it is being set internally
                // for level conversion purposes. In that case, the name of the prefab will be specially named and will
                // have been defined before the conversion process.
                bool   cacheIt = true;
                string entity_prefab_name;
                if (entity_src.NumSubTypes() == entity_src.SubType)
                {
                    // This is the special path
                    entity_prefab_name = string.Format("$INTERNAL$:{0}", entity_src.reference_guid.ToString());
                    cacheIt            = false;
                }
                else
                {
                    // This is the normal path
                    entity_prefab_name = string.Format("entity_{0}_{1}", entity_src.Type.ToString(), entity_src.SubTypeName());
                }

                IGameObjectBroker cached_game_object_prefab;
                if (!cache_subtype_map.TryGetValue(entity_src.SubType, out cached_game_object_prefab))
                {
                    cached_game_object_prefab = scene.FindAndLoadPrefabAsset(entity_prefab_name);
                    if (cached_game_object_prefab == null)
                    {
                        Debug.LogError(string.Format("Unable to resolve prefab named '{0}'", entity_prefab_name));
                    }

                    if (cacheIt)
                    {
                        cache_subtype_map.Add(entity_src.SubType, cached_game_object_prefab);
                    }
                }

                if (cached_game_object_prefab == null)
                {
                    // Failed to find the Prefab
                    continue;
                }

                var entity_position = entity_src.position.ToUnity();
                var entity_instance = scene.InstantiatePrefab(cached_game_object_prefab);
                if (entity_instance == null)
                {
                    // Failed to instantiate the Prefab
                    continue;
                }
                entity_instance.Transform.Position = entity_position;
                entity_instance.Transform.Rotation = entity_orient;

                if ((int)entity_src.Type < entity_category_objects.Length && entity_category_objects[(int)entity_src.Type] != null)
                {
                    // put under the correct category
                    entity_instance.Transform.Parent = entity_category_objects[(int)entity_src.Type].Transform;
                }
                else
                {
                    // fallback to the container
                    entity_instance.Transform.Parent = entity_container_object.Transform;
                }

                string desired_entity_name;
                if (entity_src.Type == OverloadLevelEditor.EntityType.SPECIAL && entity_src.SubType == (int)OverloadLevelEditor.SpecialSubType.PLAYER_START && !added_player_ship)
                {
                    // Name the player ship to the name scripts are looking for
                    desired_entity_name = "PlayerShip";
                    added_player_ship   = true;
                }
                else
                {
#if OVERLOAD_LEVEL_EDITOR
                    desired_entity_name = entity_prefab_name.ToLowerInvariant();
#else
                    desired_entity_name = cached_game_object_prefab.Name;
#endif
                }
                entity_instance.Name = desired_entity_name;

                // Remember this object by its Guid for when we apply properties
                var entity_guid = new Overload.EntityGuid(entity_src.guid);
                map_guid_to_gameobject.Add(entity_guid.m_guid, entity_instance);

                // Add this for later
                deferred_properties_list.Add(new KeyValuePair <IGameObjectBroker, OverloadLevelEditor.Entity>(entity_instance, entity_src));
            }

            Action <IEnumerable <Overload.EntityGuid>, IComponentBroker, string> resolve_entity_links = (IEnumerable <Overload.EntityGuid> src_links, IComponentBroker comp, string propertyName) => {
                List <IGameObjectBroker> dst_links = new List <IGameObjectBroker>();
                foreach (var src_link in src_links)
                {
                    if (src_link == Overload.EntityGuid.Empty)
                    {
                        // Skip over the unknowns
                        continue;
                    }

                    // Resolve
                    IGameObjectBroker go;
                    if (!map_guid_to_gameobject.TryGetValue(src_link.m_guid, out go))
                    {
                        Debug.LogError(string.Format("Unable to resolve GUID: {0}", src_link.m_guid));
                    }
                    else
                    {
                        dst_links.Add(go);
                    }
                }
#if OVERLOAD_LEVEL_EDITOR
                comp.SetProperty(propertyName, dst_links);
#else
                List <GameObject> dst_links_as_go = dst_links.Select(gob => gob.InternalObject).ToList();
                comp.SetProperty(propertyName, dst_links_as_go);
#endif
            };

            // Apply all of the properties now that we know all of the entities and their GUIDs
            foreach (var kvo in deferred_properties_list)
            {
                IGameObjectBroker          entity_instance = kvo.Key;
                OverloadLevelEditor.Entity entity_src      = kvo.Value;

                //string category_name = entity_category_names[(int)entity_src.Type];
                Overload.EntityProps     entity_props      = entity_src.entity_props;
                Overload.EntityPropsType entity_props_type = entity_props.GetPropsType();

                // Apply the properties now
                switch (entity_props_type)
                {
                case Overload.EntityPropsType.Door: {
                    var e_door = entity_instance.GetComponentInChildren("DoorBase");
                    var p_door = (Overload.EntityPropsDoor)entity_props;

                    int portal_index;
                    if (!doorNumToPortalMap.TryGetValue(entity_src.num, out portal_index))
                    {
                        Debug.LogError(string.Format("Unable to resolve portal from door (Entity {0})", entity_src.num));
                        portal_index = -1;
                    }

                    e_door.SetProperty("LockType", p_door.m_door_lock);
                    e_door.SetProperty("Portal", portal_index);
                    e_door.SetProperty("NoChunk", p_door.m_no_chunk);
                    link_portal_to_door(portal_index, e_door);
                } break;

                case Overload.EntityPropsType.Robot: {
                    var e_robot = entity_instance.GetComponentInChildren("Robot");
                    var p_robot = (Overload.EntityPropsRobot)entity_props;
                    e_robot.SetProperty("AI_robot_station", p_robot.station);
                    e_robot.SetProperty("m_headlight_on", p_robot.headlight);
                    e_robot.SetProperty("m_init_super", p_robot.super);
                    e_robot.SetProperty("m_init_variant", p_robot.variant);
                    e_robot.SetProperty("m_init_ng_plus", p_robot.ng_plus);
                    e_robot.SetProperty("m_init_hidden", p_robot.hidden);
                    e_robot.SetProperty("m_init_stasis", p_robot.stasis);

                    e_robot.SetProperty("m_bonus_drop1", p_robot.bonus_drop1);
                    e_robot.SetProperty("m_bonus_drop2", p_robot.bonus_drop2);
                    e_robot.SetProperty("m_replace_default_drop", p_robot.replace_default_drop);
                } break;

                case Overload.EntityPropsType.Item: {
                    var e_item = entity_instance.GetComponentInChildren("Item");
                    var p_item = (Overload.EntityPropsItem)entity_props;
                    e_item.SetProperty("m_respawning", p_item.respawns);
                    e_item.SetProperty("m_super", p_item.super);
                    e_item.SetProperty("m_secret", p_item.secret);
                    e_item.SetProperty("m_index", p_item.index);
                } break;

                case Overload.EntityPropsType.Light: {
                    var e_light = entity_instance.GetComponentInChildren("Light");
                    var p_light = (Overload.EntityPropsLight)entity_props;
                    e_light.SetProperty("spotAngle", p_light.angle);
                    e_light.SetProperty("range", p_light.range);
                    e_light.SetProperty("intensity", p_light.intensity);
                    e_light.SetProperty("color", HSBColor.ConvertToColor(p_light.c_hue, p_light.c_sat, p_light.c_bri));
                } break;

                case Overload.EntityPropsType.Prop: {
                    var e_prop = entity_instance.GetComponentInChildren("PropBase");
                    var p_prop = (Overload.EntityPropsProp)entity_props;

                    e_prop.SetProperty("m_invulnerable", p_prop.invulnerable);
                    e_prop.SetProperty("m_index", p_prop.index);
                    e_prop.SetProperty("NoChunk", p_prop.m_no_chunk);
                } break;

                case Overload.EntityPropsType.Script: {
                    var e_script = entity_instance.GetComponentInChildren("ScriptBase");
                    var p_script = (Overload.EntityPropsScript)entity_props;
                    e_script.SetProperty("delay", p_script.delay);
                    e_script.SetProperty("show_message", p_script.show_message);
                    e_script.SetProperty("special_index", p_script.special_index);
                    resolve_entity_links(p_script.entity_link, e_script, "c_go_link");
                } break;

                case Overload.EntityPropsType.Special: {
                    var   e_special   = entity_instance.GetComponentInChildren("RobotMatcen");
                    var   p_special   = (Overload.EntityPropsSpecial)entity_props;
                    float spawn_prob1 = (float)p_special.matcen_spawn_probability_1;
                    float spawn_prob2 = (float)p_special.matcen_spawn_probability_2;
                    if (spawn_prob1 + spawn_prob2 == 0f)                                         // Don't allow them both to be zero, likely only happen to matcens that were placed before probability support.
                    {
                        spawn_prob1 = 1f;
                        spawn_prob2 = 1f;
                    }

                    e_special.SetProperty("m_special_props", p_special.special_props);
                    e_special.SetProperty("m_spawnable_robot_1", (int)p_special.matcen_spawn_type_1);
                    e_special.SetProperty("m_spawnable_probability_1", spawn_prob1);
                    e_special.SetProperty("m_spawnable_robot_2", (int)p_special.matcen_spawn_type_2);
                    e_special.SetProperty("m_spawnable_probability_2", spawn_prob2);
                    e_special.SetProperty("m_matcen_max_robots_alive", p_special.m_max_alive);
                    e_special.SetProperty("m_matcen_spawn_wait", (float)p_special.m_spawn_wait);
                    e_special.SetProperty("m_invulnerable", (bool)p_special.ed_invulnerable);
                } break;

                case Overload.EntityPropsType.Trigger: {
                    // NOTE: On Export a Trigger is only going to have one of
                    // these components, but we don't want to do if/else here
                    // because that would mess up LevelEditor export. When doing
                    // LevelEditor export, an IGameObjectBroker will have all components
                    // queried, so it will set all of these properties. At runtime, when
                    // loading the exported level, it will see the component missing and
                    // will just skip over setting those properties. If you have an
                    // if/else that would mess up this process.
                    var p_trigger = (Overload.EntityPropsTrigger)entity_props;
                    var e_trigger = entity_instance.GetComponentInChildren("TriggerBase");
                    e_trigger.SetProperty("m_one_time", p_trigger.one_time);
                    e_trigger.SetProperty("m_repeat_delay", p_trigger.repeat_delay);
                    e_trigger.SetProperty("m_player_weapons", p_trigger.player_weapons);
                    e_trigger.SetProperty("m_size", p_trigger.size);
                    resolve_entity_links(p_trigger.entity_link, e_trigger, "c_go_link");

                    // Energy center special case
                    var e_ec = entity_instance.GetComponentInChildren("TriggerEnergy");
                    e_ec.SetProperty("m_size", p_trigger.size);

                    // Ambient particle special case
                    if (entity_src.SubType == (int)OverloadLevelEditor.TriggerSubType.BOX_LAVA_NORMAL ||
                        entity_src.SubType == (int)OverloadLevelEditor.TriggerSubType.BOX_LAVA_ALIEN)
                    {
                        // Ambient particle entities should have this component but don't
                        var e_ap = entity_instance.AddComponent("TriggerAmbientParticles");
                        e_ap.SetProperty("m_repeat_delay", p_trigger.repeat_delay);
                        e_ap.SetProperty("m_size", p_trigger.size);
                    }

                    // Wind tunnel special case
                    var e_wt = entity_instance.GetComponentInChildren("TriggerWindTunnel");
                    e_wt.SetProperty("m_one_time", p_trigger.one_time);
                    e_wt.SetProperty("m_repeat_delay", p_trigger.repeat_delay);
                    e_wt.SetProperty("m_player_weapons", p_trigger.player_weapons);
                    e_wt.SetProperty("m_size", p_trigger.size);
                    resolve_entity_links(p_trigger.entity_link, e_wt, "c_go_link");
                } break;
                }
            }

            // Certain data can't be stored in the level meta data - in particular anything that has
            // references to GameObjects and such that are in the scene (an asset can't reference objects
            // in a Unity scene)
            IComponentBroker level_object_initializer = sceneLevelContainerObject.GetComponent("LevelData");
            level_object_initializer.SetProperty("m_robot_spawn_points", overloadLevelData.ExtractRobotSpawnPoints().Select(loc => new LevelData.SpawnPoint(loc.Pos.ToUnity(), loc.Rotation.ToUnity(), 0)).ToArray());
            level_object_initializer.SetProperty("m_player_spawn_points", player_spawn_points.ToArray());
            level_object_initializer.SetProperty("m_item_spawn_points", item_spawn_points.ToArray());
            level_object_initializer.SetProperty("m_mp_camera_points", mp_camera_points.ToArray());
            level_object_initializer.SetProperty("m_portal_to_door_references", portal_door_references.ToArray());
        }