public ServerProp(World world, int snoId, TagMap tags) : base(world, snoId, tags) { this.Field2 = 16; this.Field7 = 0x00000001; this.CollFlags = 0; // a hack for passing through blockers /fasbat }
// get nearest target of targetType public static Actor GetNearestTarget(World world, Actor attacker, Vector3D centerPosition, float range, ActorType targetType = ActorType.Monster) { Actor result = null; List<Actor> actors = world.QuadTree.Query<Actor>(new Circle(centerPosition.X, centerPosition.Y, range)); if (actors.Count > 1) { float distanceNearest = range; // max. range float distance = 0f; foreach (var target in actors.Where(target => ((target.ActorType == targetType) && (target != attacker) && !target.Attributes[GameAttribute.Is_NPC]))) { if ((target.World == null) || (world.GetActorByDynamicId(target.DynamicID) == null)) { // leaving world continue; } distance = ActorUtils.GetDistance(centerPosition, target.Position); if ((result == null) || (distance < distanceNearest)) { result = target; distanceNearest = distance; } } } return result; }
public InteractiveNPC(World world, int actorSNO, Vector3D position) : base(world, actorSNO, position) { this.Attributes[GameAttribute.NPC_Has_Interact_Options, 0] = true; this.Attributes[GameAttribute.NPC_Is_Operatable] = true; this.Attributes[GameAttribute.Buff_Visual_Effect, 0x00FFFFF] = true; }
public Minion(World world, int snoId, TagMap tags) : base(world, snoId, tags) { // The following two seems to be shared with monsters. One wonders why there isn't a specific actortype for minions. this.Field2 = 0x8; this.GBHandle.Type = (int)GBHandleType.Monster; this.GBHandle.GBID = 1; }
public TreasurePygmy(World world, int snoId, TagMap tags) : base(world, snoId, tags) { (Brain as MonsterBrain).AddPresetPower(105371); (Brain as MonsterBrain).AddPresetPower(54836); (Brain as MonsterBrain).AddPresetPower(105665); }
public Artisan(World world, int snoId, TagMap tags) : base(world, snoId, tags) { this.Attributes[GameAttribute.MinimapActive] = true; Interactions.Add(new CraftInteraction()); }
public Portal(World world, int snoId, TagMap tags) : base(world, snoId, tags) { this.Destination = new ResolvedPortalDestination { WorldSNO = tags[MarkerKeys.DestinationWorld].Id, }; if (tags.ContainsKey(MarkerKeys.DestinationLevelArea)) this.Destination.DestLevelAreaSNO = tags[MarkerKeys.DestinationLevelArea].Id; if (tags.ContainsKey(MarkerKeys.DestinationActorTag)) this.Destination.StartingPointActorTag = tags[MarkerKeys.DestinationActorTag]; else Logger.Warn("Found portal {0}without target location actor", this.ActorSNO.Id); this.Field2 = 16; // FIXME: Hardcoded crap; probably don't need to set most of these. /komiga this.Attributes[GameAttribute.MinimapActive] = true; this.Attributes[GameAttribute.Hitpoints_Max_Total] = 1f; this.Attributes[GameAttribute.Hitpoints_Max] = 0.0009994507f; this.Attributes[GameAttribute.Hitpoints_Total_From_Level] = 3.051758E-05f; this.Attributes[GameAttribute.Hitpoints_Cur] = 0.0009994507f; this.Attributes[GameAttribute.Level] = 1; }
private static Actor CreateGizmo(World world, int snoId, Dictionary<int,TagMapEntry> tags) { if (tags.ContainsKey((int)MarkerTagTypes.DestinationWorld)) return new Portal(world, snoId, tags); return new Gizmo(world, snoId, tags); }
public Portal(World world, int actorSNO, Vector3D position, Dictionary<int, TagMapEntry> tags) : base(world, world.NewActorID, position, tags) { this.SNOId = actorSNO; this.Destination = new ResolvedPortalDestination { WorldSNO = tags[(int)MarkerTagTypes.DestinationWorld].Int2, }; if (tags.ContainsKey((int)MarkerTagTypes.DestinationLevelArea)) this.Destination.DestLevelAreaSNO = tags[(int)MarkerTagTypes.DestinationLevelArea].Int2; if (tags.ContainsKey((int)MarkerTagTypes.DestinationActorTag)) this.Destination.StartingPointActorTag = tags[(int)MarkerTagTypes.DestinationActorTag].Int2; else Logger.Warn("Found portal {0}without target location actor", this.SNOId); this.Field8 = this.SNOId; this.Field2 = 16; this.Field3 = 0; this.CollFlags = 0x00000001; // FIXME: Hardcoded crap; probably don't need to set most of these. /komiga this.Attributes[GameAttribute.MinimapActive] = true; this.Attributes[GameAttribute.Hitpoints_Max_Total] = 1f; this.Attributes[GameAttribute.Hitpoints_Max] = 0.0009994507f; this.Attributes[GameAttribute.Hitpoints_Total_From_Level] = 3.051758E-05f; this.Attributes[GameAttribute.Hitpoints_Cur] = 0.0009994507f; this.Attributes[GameAttribute.TeamID] = 1; this.Attributes[GameAttribute.Level] = 1; }
private static Actor CreateGizmo(World world, int snoId, TagMap tags) { if (tags.ContainsKey(MarkerKeys.DestinationWorld)) return new Portal(world, snoId, tags); return new Gizmo(world, snoId, tags); }
public Living(World world, int snoId, Dictionary<int, TagMapEntry> tags) : base(world, snoId, tags) { this.SNOId = snoId; this.SNOMonsterId = this.ActorData.MonsterSNO; // FIXME: This is hardcoded crap this.Field3 = 0x0; this.RotationAmount = (float)(RandomHelper.NextDouble() * 2.0f * Math.PI); this.RotationAxis.X = 0f; this.RotationAxis.Y = 0f; this.RotationAxis.Z = 1f; this.GBHandle.Type = -1; this.GBHandle.GBID = -1; this.Field7 = 0x00000001; this.Field8 = this.SNOId; this.Field10 = 0x0; this.Field11 = 0x0; this.Field12 = 0x0; this.Field13 = 0x0; this.CollFlags = 1; this.Attributes[GameAttribute.Hitpoints_Max_Total] = 4.546875f; this.Attributes[GameAttribute.Hitpoints_Max] = 4.546875f; this.Attributes[GameAttribute.Hitpoints_Total_From_Level] = 0f; this.Attributes[GameAttribute.Hitpoints_Cur] = 4.546875f; this.Attributes[GameAttribute.Level] = 1; }
public Hireling(World world, int snoId, TagMap tags) : base(world, snoId, tags) { //this.Attributes[GameAttribute.TeamID] = 2; Interactions.Add(new HireInteraction()); Interactions.Add(new InventoryInteraction()); }
public SpellRune(World world, Mooege.Common.MPQ.FileFormats.ItemTable definition) : base(world, definition) { if (!definition.Name.Contains("X")) { // attuned rune, randomize power int classRnd = RandomHelper.Next(0, 5); int PowerSNOId = -1; switch (classRnd) { case 0: PowerSNOId = Skills.Skills.Barbarian.AllActiveSkillsList.ElementAt(RandomHelper.Next(0, Mooege.Core.GS.Skills.Skills.Barbarian.AllActiveSkillsList.Count)); break; case 1: PowerSNOId = Skills.Skills.DemonHunter.AllActiveSkillsList.ElementAt(RandomHelper.Next(0, Mooege.Core.GS.Skills.Skills.DemonHunter.AllActiveSkillsList.Count)); break; case 2: PowerSNOId = Skills.Skills.Monk.AllActiveSkillsList.ElementAt(RandomHelper.Next(0, Mooege.Core.GS.Skills.Skills.Monk.AllActiveSkillsList.Count)); break; case 3: PowerSNOId = Skills.Skills.WitchDoctor.AllActiveSkillsList.ElementAt(RandomHelper.Next(0, Mooege.Core.GS.Skills.Skills.WitchDoctor.AllActiveSkillsList.Count)); break; case 4: PowerSNOId = Skills.Skills.Wizard.AllActiveSkillsList.ElementAt(RandomHelper.Next(0, Mooege.Core.GS.Skills.Skills.Wizard.AllActiveSkillsList.Count)); break; } //this.Attributes[GameAttribute.Rune_Attuned_Power] = PowerSNOId; } }
public Barricade(World world, int snoId, TagMap tags) : base(world, snoId, tags) { base.Attributes[GameAttribute.Experience_Granted] = 0; base.Attributes[GameAttribute.DropsNoLoot] = true; this.Attributes[GameAttribute.Hitpoints_Cur] = 1; }
public Living(World world, int snoId, Dictionary<int, TagMapEntry> tags) : base(world, snoId, tags) { this.SNOId = snoId; var actor = (Mooege.Common.MPQ.FileFormats.Actor)Mooege.Common.MPQ.MPQStorage.Data.Assets[Common.Types.SNO.SNOGroup.Actor][snoId].Data; this.snoMonster = actor.MonsterSNO; if (actor.AnimSetSNO != -1) { this.Animset = (Mooege.Common.MPQ.FileFormats.AnimSet)Mooege.Common.MPQ.MPQStorage.Data.Assets[Common.Types.SNO.SNOGroup.AnimSet][actor.AnimSetSNO].Data; } // FIXME: This is hardcoded crap this.Field3 = 0x0; this.RotationAmount = (float)(RandomHelper.NextDouble() * 2.0f * Math.PI); this.RotationAxis.X = 0f; this.RotationAxis.Y = 0f; this.RotationAxis.Z = 1f; this.GBHandle.Type = -1; this.GBHandle.GBID = -1; this.Field7 = 0x00000001; this.Field8 = this.SNOId; this.Field10 = 0x0; this.Field11 = 0x0; this.Field12 = 0x0; this.Field13 = 0x0; //this.AnimationSNO = 0x11150; this.CollFlags = 1; this.Attributes[GameAttribute.Hitpoints_Max_Total] = 4.546875f; this.Attributes[GameAttribute.Hitpoints_Max] = 4.546875f; this.Attributes[GameAttribute.Hitpoints_Total_From_Level] = 0f; this.Attributes[GameAttribute.Hitpoints_Cur] = 4.546875f; // !!!! This line below caused Hirelings to crash! WTF? /fasbat //this.Attributes[GameAttribute.Level] = 1; }
public NPC(World world, int snoId, TagMap tags) : base(world, snoId, tags) { this.Field2 = 0x9; this.Field7 = 2; this.Attributes[GameAttribute.Is_NPC] = true; }
public static Actor Create(World world, int snoId, TagMap tagMap) { if (!MPQStorage.Data.Assets[SNOGroup.Actor].ContainsKey(snoId)) return null; var actorAsset = MPQStorage.Data.Assets[SNOGroup.Actor][snoId]; var actorData = actorAsset.Data as Mooege.Common.MPQ.FileFormats.Actor; if (actorData == null) return null; if (actorData.Type == ActorType.Invalid) return null; // read tagMapEntries and put them into a dictionary var tags = tagMap.TagMapEntries.ToDictionary(entry => entry.TagID); // see if we have an implementation for actor. if (SNOHandlers.ContainsKey(snoId)) return (Actor) Activator.CreateInstance(SNOHandlers[snoId], new object[] {world, snoId, tags}); switch (actorData.Type) { case ActorType.Monster: if(tags.ContainsKey((int)MarkerTagTypes.ConversationList)) return new InteractiveNPC(world, snoId, tags); else return new Monster(world, snoId, tags); case ActorType.Gizmo: return CreateGizmo(world, snoId, tags); } return null; }
public Vendor(World world, int snoId, Dictionary<int, TagMapEntry> tags) : base(world, snoId, tags) { this.Attributes[GameAttribute.MinimapActive] = true; _vendorGrid = new InventoryGrid(this, 1, 20, (int) EquipmentSlotId.Vendor); PopulateItems(); }
protected WorldObject(World world, uint dynamicID) : base(dynamicID) { this._world = world; // Specifically avoid calling the potentially overridden setter for this.World /komiga. this._world.Game.StartTracking(this); this._rotationAxis = new Vector3D(); this._position = new Vector3D(); }
public Monster(World world, int actorSNO, Vector3D position) : base(world, actorSNO, position) { this.Field2 = 0x8; this.GBHandle.Type = (int)GBHandleType.Monster; this.GBHandle.GBID = 1; this.Attributes[GameAttribute.TeamID] = 10; this.Attributes[GameAttribute.Experience_Granted] = 125; }
public Environment(World world, int snoId, Dictionary<int, TagMapEntry> tags) : base(world, snoId, tags) { this.Field2 = 16; this.Field3 = 0x0; this.Field7 = 0x00000001; this.Field8 = this.SNOId; }
/// <summary> /// Creates a new world object. /// </summary> /// <param name="world">The world object belongs to.</param> /// <param name="dynamicID">The dynamicId of the object.</param> protected WorldObject(World world, uint dynamicID) : base(dynamicID) { this.World = world; this.World.Game.StartTracking(this); // track the object. this.RotationAxis = new Vector3D(); this._position = new Vector3D(); }
public Gizmo(World world, int snoId, TagMap tags) : base(world, snoId, tags) { this.Field2 = 16; this.Field7 = 0x00000001; this.Attributes[Mooege.Net.GS.Message.GameAttribute.Hitpoints_Cur] = 1; }
public NPC(World world, int snoId, Dictionary<int, TagMapEntry> tags) : base(world, snoId, tags) { this.Field2 = 0x9; this.Field7 = 2; this.Attributes[GameAttribute.TeamID] = 1; this.Attributes[GameAttribute.Is_NPC] = true; }
public Monster(World world, int snoId, Dictionary<int, TagMapEntry> tags) : base(world, snoId, tags) { this.Field2 = 0x8; this.GBHandle.Type = (int)GBHandleType.Monster; this.GBHandle.GBID = 1; this.Attributes[GameAttribute.TeamID] = 10; this.Attributes[GameAttribute.Experience_Granted] = 125; }
public DesctructibleLootContainer(World world, int snoId, TagMap tags) : base(world, snoId, tags) { if (ActorData.TagMap.ContainsKey(ActorKeys.LootTreasureClass)) TreasureClass = (TreasureClass)ActorData.TagMap[ActorKeys.LootTreasureClass].Target; base.Attributes[GameAttribute.Experience_Granted] = 0; this.Attributes[GameAttribute.Hitpoints_Cur] = 1; base.Attributes[GameAttribute.DropsNoLoot] = true; }
public NPC(World world, int actorSNO, Vector3D position, Dictionary<int, TagMapEntry> tags) : base(world, actorSNO, position, tags) { this.Field2 = 0x9; this.Field7 = 1; this.Field8 = actorSNO; //TODO check if this is not true for every actor / living? /fasbat this.Attributes[GameAttribute.TeamID] = 1; this.Attributes[GameAttribute.Is_NPC] = true; }
public Cain(World world, int snoId, Dictionary<int, TagMapEntry> tags) : base(world, snoId, tags) { this.Attributes[GameAttribute.MinimapActive] = true; Conversations.Add(new ConversationInteraction(72416)); Conversations.Add(new ConversationInteraction(198588)); Conversations.Add(new ConversationInteraction(73171)); Interactions.Add(new IdentifyAllInteraction()); }
public Gizmo(World world, int actorSNO, Vector3D position, Dictionary<int, TagMapEntry> tags) : base(world, world.NewActorID, position, tags) { this.SNOId = actorSNO; this.Field2 = 16; this.Field3 = 0x0; this.Field7 = 0x00000001; this.Field8 = this.SNOId; }
public InteractiveNPC(World world, int snoId, Dictionary<int, TagMapEntry> tags) : base(world, snoId, tags) { this.Attributes[GameAttribute.NPC_Has_Interact_Options, 0] = true; this.Attributes[GameAttribute.NPC_Is_Operatable] = true; this.Attributes[GameAttribute.Buff_Visual_Effect, 0x00FFFFF] = true; Interactions = new List<IInteraction>(); Conversations = new List<ConversationInteraction>(); }
private static void loadActor(SNOHandle actorHandle, PRTransform location, World world, TagMap tagMap) { var actor = Mooege.Core.GS.Actors.ActorFactory.Create(world, actorHandle.Id, tagMap); if (actor == null) { if (actorHandle.Id != -1) { Logger.Warn("ActorFactory did not load actor {0}", actorHandle); } return; } actor.RotationW = location.Quaternion.W; actor.RotationAxis = location.Quaternion.Vector3D; actor.EnterWorld(location.Vector3D); }
/// <summary> /// Loads content for level areas. Call this after scenes have been generated and after scenes have their GizmoLocations /// set (this is done in Scene.LoadActors right now) /// </summary> /// <param name="levelAreas">Dictionary that for every level area has the scenes it consists of</param> /// <param name="world">The world to which to add loaded actors</param> private static void loadLevelAreas(Dictionary <int, List <Scene> > levelAreas, World world) { /// Each Scene has one to four level areas assigned to it. I dont know if that means /// the scene belongs to both level areas or if the scene is split /// Scenes marker tags have generic GizmoLocationA to Z that are used /// to provide random spawning possibilities. /// For each of these 26 LocationGroups, the LevelArea has a entry in its SpawnType array that defines /// what type of actor/encounter/adventure could spawn there /// /// It could for example define, that for a level area X, out of the four spawning options /// two are randomly picked and have barrels placed there foreach (int la in levelAreas.Keys) { SNOHandle levelAreaHandle = new SNOHandle(SNOGroup.LevelArea, la); if (!levelAreaHandle.IsValid) { Logger.Warn("Level area {0} does not exist", la); continue; } var levelArea = levelAreaHandle.Target as LevelArea; for (int i = 0; i < 26; i++) { // Merge the gizmo starting locations from all scenes and // their subscenes into a single list for the whole level area List <PRTransform> gizmoLocations = new List <PRTransform>(); foreach (var scene in levelAreas[la]) { if (scene.GizmoSpawningLocations[i] != null) { gizmoLocations.AddRange(scene.GizmoSpawningLocations[i]); } foreach (Scene subScene in scene.Subscenes) { if (subScene.GizmoSpawningLocations[i] != null) { gizmoLocations.AddRange(subScene.GizmoSpawningLocations[i]); } } } // Load all spawns that are defined for that location group foreach (GizmoLocSpawnEntry spawnEntry in levelArea.LocSet.SpawnType[i].SpawnEntry) { // Get a random amount of spawns ... int amount = RandomHelper.Next(spawnEntry.Max, spawnEntry.Max); if (amount > gizmoLocations.Count) { Logger.Warn("Breaking after spawnEntry {0} for LevelArea {1} because there are less locations ({2}) than spawn amount ({3}, {4} min)", spawnEntry.SNOHandle, levelAreaHandle, gizmoLocations.Count, amount, spawnEntry.Min); break; } Logger.Trace("Spawning {0} ({3} - {4} {1} in {2}", amount, spawnEntry.SNOHandle, levelAreaHandle, spawnEntry.Min, spawnEntry.Max); // ...and place each one on a random position within the location group for (; amount > 0; amount--) { int location = RandomHelper.Next(gizmoLocations.Count - 1); switch (spawnEntry.SNOHandle.Group) { case SNOGroup.Actor: loadActor(spawnEntry.SNOHandle, gizmoLocations[location], world, new TagMap()); break; case SNOGroup.Encounter: var encounter = spawnEntry.SNOHandle.Target as Encounter; var actor = RandomHelper.RandomItem(encounter.Spawnoptions, x => x.Probability); loadActor(new SNOHandle(actor.SNOSpawn), gizmoLocations[location], world, new TagMap()); break; case SNOGroup.Adventure: // Adventure are basically made up of a markerSet that has relative PRTransforms // it has some other fields that are always 0 and a reference to a symbol actor // no idea what they are used for - farmy var adventure = spawnEntry.SNOHandle.Target as Adventure; var markerSet = new SNOHandle(adventure.SNOMarkerSet).Target as MarkerSet; foreach (var marker in markerSet.Markers) { // relative marker set coordinates to absolute world coordinates var absolutePRTransform = new PRTransform { Vector3D = marker.PRTransform.Vector3D + gizmoLocations[location].Vector3D, Quaternion = new Quaternion { Vector3D = new Vector3D(marker.PRTransform.Quaternion.Vector3D.X, marker.PRTransform.Quaternion.Vector3D.Y, marker.PRTransform.Quaternion.Vector3D.Z), W = marker.PRTransform.Quaternion.W } }; switch (marker.Type) { case MarkerType.Actor: loadActor(marker.SNOHandle, absolutePRTransform, world, marker.TagMap); break; case MarkerType.Encounter: var encounter2 = marker.SNOHandle.Target as Encounter; var actor2 = RandomHelper.RandomItem(encounter2.Spawnoptions, x => x.Probability); loadActor(new SNOHandle(actor2.SNOSpawn), absolutePRTransform, world, marker.TagMap); break; default: Logger.Warn("Unhandled marker type {0} in actor loading", marker.Type); break; } } break; default: if (spawnEntry.SNOHandle.Id != -1) { Logger.Warn("Unknown sno handle in LevelArea spawn entries: {0}", spawnEntry.SNOHandle); } break; } // dont use that location again gizmoLocations.RemoveAt(location); } } } // Load monsters for level area foreach (var scene in levelAreas[la]) { // HACK: don't spawn monsters in tristram town scenes /mdz if (MPQStorage.Data.Assets[SNOGroup.Scene][scene.SceneSNO.Id].Name.StartsWith("trOut_Tristram_")) { continue; } for (int i = 0; i < 100; i++) { if (RandomHelper.NextDouble() > 0.8) { // TODO Load correct spawn population // 2.5 is units per square, TODO: Find out how to calculate units per square. Is it F1 * V0.I1 / SquareCount? int x = RandomHelper.Next(scene.NavMesh.SquaresCountX); int y = RandomHelper.Next(scene.NavMesh.SquaresCountY); if ((scene.NavMesh.Squares[y * scene.NavMesh.SquaresCountX + x].Flags & Mooege.Common.MPQ.FileFormats.Scene.NavCellFlags.NoSpawn) == 0) { loadActor( new SNOHandle(6652), new PRTransform { Vector3D = new Vector3D { X = (float)(x * 2.5 + scene.Position.X), Y = (float)(y * 2.5 + scene.Position.Y), Z = scene.NavMesh.Squares[y * scene.NavMesh.SquaresCountX + x].Z + scene.Position.Z }, Quaternion = new Quaternion { W = (float)(RandomHelper.NextDouble() * System.Math.PI * 2), Vector3D = new Vector3D(0, 0, 1) } }, world, new TagMap() ); } } } } } }
public static World Generate(Game game, int worldSNO) { if (!MPQStorage.Data.Assets[SNOGroup.Worlds].ContainsKey(worldSNO)) { Logger.Error("Can't find a valid world definition for sno: {0}", worldSNO); return(null); } var worldAsset = MPQStorage.Data.Assets[SNOGroup.Worlds][worldSNO]; var worldData = (Mooege.Common.MPQ.FileFormats.World)worldAsset.Data; if (worldData.IsGenerated) { Logger.Error("World {0} [{1}] is a dynamic world! Can't generate dynamic worlds yet!", worldAsset.Name, worldAsset.SNOId); return(null); } var world = new World(game, worldSNO); var levelAreas = new Dictionary <int, List <Scene> >(); // Create a clusterID => Cluster Dictionary var clusters = new Dictionary <int, Mooege.Common.MPQ.FileFormats.SceneCluster>(); foreach (var cluster in worldData.SceneClusterSet.SceneClusters) { clusters[cluster.ClusterId] = cluster; } // Scenes are not aligned to (0, 0) but apparently need to be -farmy float minX = worldData.SceneParams.SceneChunks.Min(x => x.PRTransform.Vector3D.X); float minY = worldData.SceneParams.SceneChunks.Min(x => x.PRTransform.Vector3D.Y); // Count all occurences of each cluster /fasbat var clusterCount = new Dictionary <int, int>(); foreach (var sceneChunk in worldData.SceneParams.SceneChunks) { var cID = sceneChunk.SceneSpecification.ClusterID; if (cID != -1 && clusters.ContainsKey(cID)) // Check for wrong clusters /fasbat { if (!clusterCount.ContainsKey(cID)) { clusterCount[cID] = 0; } clusterCount[cID]++; } } // For each cluster generate a list of randomly selected subcenes /fasbat var clusterSelected = new Dictionary <int, List <Mooege.Common.MPQ.FileFormats.SubSceneEntry> >(); foreach (var cID in clusterCount.Keys) { var selected = new List <Mooege.Common.MPQ.FileFormats.SubSceneEntry>(); clusterSelected[cID] = selected; var count = clusterCount[cID]; foreach (var group in clusters[cID].SubSceneGroups) // First select from each subscene group /fasbat { for (int i = 0; i < group.I0 && count > 0; i++, count--) //TODO Rename I0 to requiredCount? /fasbat { var subSceneEntry = RandomHelper.RandomItem(group.Entries, entry => entry.Probability); selected.Add(subSceneEntry); } if (count == 0) { break; } } while (count > 0) // Fill the rest with defaults /fasbat { var subSceneEntry = RandomHelper.RandomItem(clusters[cID].Default.Entries, entry => entry.Probability); selected.Add(subSceneEntry); count--; } } foreach (var sceneChunk in worldData.SceneParams.SceneChunks) { var position = sceneChunk.PRTransform.Vector3D - new Vector3D(minX, minY, 0); var scene = new Scene(world, position, sceneChunk.SNOHandle.Id, null) { MiniMapVisibility = SceneMiniMapVisibility.Revealed, RotationW = sceneChunk.PRTransform.Quaternion.W, RotationAxis = sceneChunk.PRTransform.Quaternion.Vector3D, SceneGroupSNO = -1 }; // If the scene has a subscene (cluster ID is set), choose a random subscenes from the cluster load it and attach it to parent scene /farmy if (sceneChunk.SceneSpecification.ClusterID != -1) { if (!clusters.ContainsKey(sceneChunk.SceneSpecification.ClusterID)) { Logger.Warn("Referenced clusterID {0} not found for chunk {1} in world {2}", sceneChunk.SceneSpecification.ClusterID, sceneChunk.SNOHandle.Id, worldSNO); } else { var entries = clusterSelected[sceneChunk.SceneSpecification.ClusterID]; // Select from our generated list /fasbat Mooege.Common.MPQ.FileFormats.SubSceneEntry subSceneEntry = null; if (entries.Count > 0) { //subSceneEntry = entries[RandomHelper.Next(entries.Count - 1)]; subSceneEntry = RandomHelper.RandomItem <Mooege.Common.MPQ.FileFormats.SubSceneEntry>(entries, entry => 1); // TODO Just shuffle the list, dont random every time. /fasbat entries.Remove(subSceneEntry); } else { Logger.Error("No SubScenes defined for cluster {0} in world {1}", sceneChunk.SceneSpecification.ClusterID, world.DynamicID); } Vector3D pos = FindSubScenePosition(sceneChunk); // TODO According to BoyC, scenes can have more than one subscene, so better enumerate over all subscenepositions /farmy if (pos == null) { Logger.Error("No scene position marker for SubScenes of Scene {0} found", sceneChunk.SNOHandle.Id); } else { var subScenePosition = scene.Position + pos; var subscene = new Scene(world, subScenePosition, subSceneEntry.SNOScene, scene) { MiniMapVisibility = SceneMiniMapVisibility.Revealed, RotationW = sceneChunk.PRTransform.Quaternion.W, RotationAxis = sceneChunk.PRTransform.Quaternion.Vector3D, Specification = sceneChunk.SceneSpecification }; scene.Subscenes.Add(subscene); subscene.LoadMarkers(); } } } scene.Specification = sceneChunk.SceneSpecification; scene.LoadMarkers(); // add scene to level area dictionary foreach (var levelArea in scene.Specification.SNOLevelAreas) { if (levelArea != -1) { if (!levelAreas.ContainsKey(levelArea)) { levelAreas.Add(levelArea, new List <Scene>()); } levelAreas[levelArea].Add(scene); } } } loadLevelAreas(levelAreas, world); return(world); }