diff --git a/OpenDiablo2.Common/Attributes/RandomizedMapAttribute.cs b/OpenDiablo2.Common/Attributes/RandomizedMapAttribute.cs
new file mode 100644
index 00000000..80e1b4e5
--- /dev/null
+++ b/OpenDiablo2.Common/Attributes/RandomizedMapAttribute.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace OpenDiablo2.Common.Attributes
+{
+ [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
+ public sealed class RandomizedMapAttribute : Attribute
+ {
+ readonly string mapName;
+ public string MapName => mapName;
+
+ public RandomizedMapAttribute(string mapName)
+ {
+ this.mapName = mapName;
+ }
+
+
+
+ }
+}
diff --git a/OpenDiablo2.Common/Enums/eRenderCellType.cs b/OpenDiablo2.Common/Enums/eRenderCellType.cs
index 9849af64..7989b0db 100644
--- a/OpenDiablo2.Common/Enums/eRenderCellType.cs
+++ b/OpenDiablo2.Common/Enums/eRenderCellType.cs
@@ -11,6 +11,7 @@ namespace OpenDiablo2.Common.Enums
Floor,
WallNormal,
WallLower,
+ Shadow,
Roof,
MAX
}
diff --git a/OpenDiablo2.Common/Interfaces/Data/IResourceManager.cs b/OpenDiablo2.Common/Interfaces/Data/IResourceManager.cs
index 56e68ab9..e5b5a5c6 100644
--- a/OpenDiablo2.Common/Interfaces/Data/IResourceManager.cs
+++ b/OpenDiablo2.Common/Interfaces/Data/IResourceManager.cs
@@ -16,7 +16,7 @@ namespace OpenDiablo2.Common.Interfaces
/// The that was requested. Throw an exception if not found.
ImageSet GetImageSet(string resourcePath);
MPQFont GetMPQFont(string resourcePath);
- MPQDS1 GetMPQDS1(string resourcePath, LevelPreset level, LevelDetail levelDetail, LevelType levelType);
+ MPQDS1 GetMPQDS1(string resourcePath, LevelPreset level, LevelType levelType);
MPQDT1 GetMPQDT1(string resourcePath);
Palette GetPalette(string paletteFile);
MPQCOF GetPlayerAnimation(eHero hero, eWeaponClass weaponClass, eMobMode mobMode);
diff --git a/OpenDiablo2.Common/Interfaces/IEngineDataManager.cs b/OpenDiablo2.Common/Interfaces/IEngineDataManager.cs
index 028f97a2..20cb52bd 100644
--- a/OpenDiablo2.Common/Interfaces/IEngineDataManager.cs
+++ b/OpenDiablo2.Common/Interfaces/IEngineDataManager.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.Collections.Immutable;
using OpenDiablo2.Common.Enums;
using OpenDiablo2.Common.Interfaces.Mobs;
using OpenDiablo2.Common.Models;
@@ -7,11 +8,11 @@ namespace OpenDiablo2.Common.Interfaces
{
public interface IEngineDataManager
{
- List LevelPresets { get; }
- List LevelTypes { get; }
- List LevelDetails { get; }
- List- Items { get; }
- Dictionary ExperienceConfigs { get; }
- Dictionary HeroTypeConfigs { get; }
+ ImmutableList Levels { get; }
+ ImmutableList LevelPresets { get; }
+ ImmutableList LevelTypes { get; }
+ ImmutableList
- Items { get; }
+ ImmutableDictionary ExperienceConfigs { get; }
+ ImmutableDictionary HeroTypeConfigs { get; }
}
}
diff --git a/OpenDiablo2.Common/Interfaces/IGameState.cs b/OpenDiablo2.Common/Interfaces/IGameState.cs
index d58143ea..84a87221 100644
--- a/OpenDiablo2.Common/Interfaces/IGameState.cs
+++ b/OpenDiablo2.Common/Interfaces/IGameState.cs
@@ -25,7 +25,13 @@ namespace OpenDiablo2.Common.Interfaces
void Update(long ms);
IEnumerable GetMapCellInfo(int cellX, int cellY, eRenderCellType renderCellType);
void UpdateMapCellInfo(int cellX, int cellY, eRenderCellType renderCellType, IEnumerable mapCellInfo);
- MapInfo LoadMap(eLevelId levelId, Point origin);
- MapInfo LoadSubMap(int levelDefId, Point origin, MapInfo primaryMap, int subTile = -1);
+ IMapInfo InsertMap(eLevelId levelId, IMapInfo parentMap = null);
+ IMapInfo InsertMap(int levelId, Point origin, IMapInfo parentMap = null);
+ IMapInfo InsertSubMap(int levelPresetId, int levelTypeId, Point origin, IMapInfo primaryMap, int subTile = -1);
+ IMapInfo GetSubMapInfo(int levelPresetId, int levelTypeId, IMapInfo primaryMap, Point origin, int subTile = -1);
+ void AddMap(IMapInfo map);
+ int HasMap(int cellX, int cellY);
+ IEnumerable GetMapSizes(int cellX, int cellY);
+ void RemoveEverythingAt(int cellX, int cellY);
}
}
diff --git a/OpenDiablo2.Common/Interfaces/IRandomizedMapGenerator.cs b/OpenDiablo2.Common/Interfaces/IRandomizedMapGenerator.cs
new file mode 100644
index 00000000..6d6f85b6
--- /dev/null
+++ b/OpenDiablo2.Common/Interfaces/IRandomizedMapGenerator.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+using System.Drawing;
+using OpenDiablo2.Common.Models;
+
+namespace OpenDiablo2.Common.Interfaces
+{
+ public interface IRandomizedMapGenerator
+ {
+ void Generate(IMapInfo parentMap, Point location);
+ }
+}
diff --git a/OpenDiablo2.Common/Models/LevelDetail.cs b/OpenDiablo2.Common/Models/LevelDetail.cs
index 28d9dbbd..e47402ea 100644
--- a/OpenDiablo2.Common/Models/LevelDetail.cs
+++ b/OpenDiablo2.Common/Models/LevelDetail.cs
@@ -9,189 +9,193 @@ namespace OpenDiablo2.Common.Models
public sealed class LevelDetail
{
/// Internal level name
- public string Name { get; set; }
+ public string Name { get; internal set; }
/// Level ID (Used in columns like VIS0-1)
- public int Id { get; set; }
+ public int Id { get; internal set; }
/// Palette
- public int Pal { get; set; }
+ public int Pal { get; internal set; }
/// Act
- public int Act { get; set; }
+ public int Act { get; internal set; }
/// What layer the level is on (surface levels are always 0)
- public int Layer { get; set; }
+ public int Layer { get; internal set; }
/// Horizontal size of the level
- public int SizeX { get; set; }
+ public int SizeX { get; internal set; }
/// Vertical size of the level
- public int SizeY { get; set; }
+ public int SizeY { get; internal set; }
/// Horizontal Placement Offset
- public int OffsetX { get; set; }
+ public int OffsetX { get; internal set; }
/// Vertical placement offset
- public int OffsetY { get; set; }
+ public int OffsetY { get; internal set; }
/// Special setting for levels that aren't random or preset (like Outer Cloister and Arcane Sancturary)
- public int Depend { get; set; }
+ public int Depend { get; internal set; }
/// If true, it rains (or snows)
- public int Rain { get; set; }
+ public int Rain { get; internal set; }
/// Unused
- public int Mud { get; set; }
+ public int Mud { get; internal set; }
/// Perspective mode forced to off if set to 1
- public int NoPer { get; set; }
+ public int NoPer { get; internal set; }
/// Level of sight drawing
- public int LOSDraw { get; set; }
+ public int LOSDraw { get; internal set; }
/// Unknown
- public int FloorFilter { get; set; }
+ public int FloorFilter { get; internal set; }
/// Unknown
- public int BlankScreen { get; set; }
+ public int BlankScreen { get; internal set; }
/// For levels bordered with mountains or walls
- public int DrawEdges { get; set; }
+ public int DrawEdges { get; internal set; }
/// Set to 1 if this is underground or inside
- public int IsInside { get; set; }
+ public int IsInside { get; internal set; }
/// Setting for Level Generation: 1=Random Size, amount of rooms defined by LVLMAZE.TXT, 2=pre set map (example: catacombs lvl4) and 3=Random Area with preset size (wildernesses)
- public int DrlgType { get; set; }
+ public int DrlgType { get; internal set; }
/// The level id to reference in lvltypes.txt
- public int LevelType { get; set; }
+ public int LevelTypeId { get; internal set; }
/// Setting Regarding Level Type for lvlsub.txt (6=wilderness, 9=desert etc, -1=no subtype)
- public int SubType { get; set; }
+ public int SubType { get; internal set; }
///
- public int SubTheme { get; set; }
+ public int SubTheme { get; internal set; }
///
- public int SubWaypoint { get; set; }
+ public int SubWaypoint { get; internal set; }
///
- public int SubShrine { get; set; }
+ public int SubShrine { get; internal set; }
/// Entry/Exit to level #1-#8
- public int[] Vis0_7 { get; set; }
+ public int[] Vis0_7 { get; internal set; }
/// ID into lvlwarp.txt
- public int[] Warp0_7 { get; set; }
+ public int[] Warp0_7 { get; internal set; }
/// Light intensity (0-255)
- public int Intensity { get; set; }
+ public int Intensity { get; internal set; }
///
- public int Red { get; set; }
+ public int Red { get; internal set; }
///
- public int Green { get; set; }
+ public int Green { get; internal set; }
///
- public int Blue { get; set; }
+ public int Blue { get; internal set; }
/// Unknown
- public int Portal { get; set; }
+ public int Portal { get; internal set; }
/// Settings for preset levels
- public int Position { get; set; }
+ public int Position { get; internal set; }
/// If true, monster/creatures get saved/loaded instead of despawning.
- public int SaveMonsters { get; set; }
+ public int SaveMonsters { get; internal set; }
/// Quest flags
- public int Quest { get; set; }
+ public int Quest { get; internal set; }
/// Usually 2025, unknown
- public int WarpDist { get; set; }
+ public int WarpDist { get; internal set; }
/// Level on Normal (controls the item level of items that drop from chests etc)
- public int MonLvl1 { get; set; }
+ public int MonLvl1 { get; internal set; }
/// Level on Nightmare (controls the item level of items that drop from chests etc)
- public int MonLvl2 { get; set; }
+ public int MonLvl2 { get; internal set; }
/// Level on Hell (controls the item level of items that drop from chests etc)
- public int MonLvl3 { get; set; }
+ public int MonLvl3 { get; internal set; }
/// The Density of Monsters
- public int MonDen { get; set; }
+ public int MonDen { get; internal set; }
/// Minimum Unique and Champion Monsters Spawned in this Level
- public int MonUMin { get; set; }
+ public int MonUMin { get; internal set; }
/// Maximum Unique and Champion Monsters Spawned in this Level
- public int MonUMax { get; set; }
+ public int MonUMax { get; internal set; }
///
- public int MonWndr { get; set; }
+ public int MonWndr { get; internal set; }
///
- public int MonSpcWalk { get; set; }
+ public int MonSpcWalk { get; internal set; }
/// How many different Species of Monsters can occur in this area (example: if you use M1-25 then set Mtot to 25 etc)
- public int Mtot { get; set; }
+ public int Mtot { get; internal set; }
/// Monster Species 1-25 (use ID from MonStats.txt)
- public int[] M1_25 { get; set; }
+ public int[] M1_25 { get; internal set; }
/// Spawned Species 1-25 (use ID from MonStats for Monsters which have eSpawnCol set to 2, related to M1-25, eg: if M1 Spawns S1 will Spawn)
- public int[] S1_25 { get; set; }
+ public int[] S1_25 { get; internal set; }
/// How many different Species of Monsters can spawn as Uniques and Champions in this Area (works like Mtot)
- public int Utot { get; set; }
+ public int Utot { get; internal set; }
/// Unique Species 1-25 (same as M1-M25 just for Monsters that you want to appear as Unique/Champions)
- public int[] U1_25 { get; set; }
+ public int[] U1_25 { get; internal set; }
/// Critter Species 1-5 (For monsters set to 1 in the IsCritter Column in MonStats.txt)
- public int[] C1_5 { get; set; }
+ public int[] C1_5 { get; internal set; }
/// Related to C1-5, eg: if you spawn a critter thru C1 then set this column to 30 etc. (function unknown)
- public int[] CA1_5 { get; set; }
+ public int[] CA1_5 { get; internal set; }
/// Unknown
- public int[] CD1_5 { get; set; }
+ public int[] CD1_5 { get; internal set; }
/// unknown
- public int Themes { get; set; }
+ public int Themes { get; internal set; }
/// Referes to a entry in SoundEnviron.txt (for the Levels Music)
- public int SoundEnv { get; set; }
+ public int SoundEnv { get; internal set; }
/// 255=No way Point, other #'s Waypoint ID
- public int Waypoint { get; set; }
+ public int Waypoint { get; internal set; }
/// String Code for the Display name of the Level
- public string LevelName { get; set; }
+ public string LevelName { get; internal set; }
/// String Code for the Display name of a entrance to this Level
- public string LevelWarp { get; set; }
+ public string LevelWarp { get; internal set; }
/// Which *.DC6 Title Image is loaded when you enter this area
- public string EntryFile { get; set; }
+ public string EntryFile { get; internal set; }
/// Use the ID of the ObjectGroup you want to Spawn in this Area (from ObjectGroups.txt)
- public int[] ObjGrp0_7 { get; set; }
+ public int[] ObjGrp0_7 { get; internal set; }
/// Object Spawn Possibility: the Chance for this object to occur (if you use ObjGrp0 then set ObjPrb0 to a value below 100)
- public int[] ObjPrb0_7 { get; set; }
+ public int[] ObjPrb0_7 { get; internal set; }
/// Unused
- public bool Beta { get; set; }
+ public bool Beta { get; internal set; }
+
+ public LevelPreset LevelPreset { get; internal set; }
+
+ public LevelType LevelType { get; internal set; }
}
public static class LevelDetailHelper
{
- public static LevelDetail ToLevelDetail(this string[] v)
+ public static LevelDetail ToLevelDetail(this string[] v, IEnumerable levelPresets, IEnumerable levelTypes)
{
var result = new LevelDetail();
int i = 0;
@@ -214,7 +218,7 @@ namespace OpenDiablo2.Common.Models
result.DrawEdges = Convert.ToInt32(v[i++]);
result.IsInside = Convert.ToInt32(v[i++]);
result.DrlgType = Convert.ToInt32(v[i++]);
- result.LevelType = Convert.ToInt32(v[i++]);
+ result.LevelTypeId = Convert.ToInt32(v[i++]);
result.SubType = Convert.ToInt32(v[i++]);
result.SubTheme = Convert.ToInt32(v[i++]);
result.SubWaypoint = Convert.ToInt32(v[i++]);
@@ -265,8 +269,8 @@ namespace OpenDiablo2.Common.Models
result.ObjPrb0_7 = new int[8];
for (int j = 0; j < 5; j++) result.ObjPrb0_7[j] = Convert.ToInt32(v[i++]);
result.Beta = Convert.ToInt32(v[i]) == 1;
-
-
+ result.LevelPreset = levelPresets.FirstOrDefault(x => x.LevelId == result.Id);
+ result.LevelType = levelTypes.FirstOrDefault(x => x.Id == result.LevelTypeId);
return result;
}
}
diff --git a/OpenDiablo2.Common/Models/LevelPreset.cs b/OpenDiablo2.Common/Models/LevelPreset.cs
index b79f1641..866dc5b8 100644
--- a/OpenDiablo2.Common/Models/LevelPreset.cs
+++ b/OpenDiablo2.Common/Models/LevelPreset.cs
@@ -9,30 +9,30 @@ namespace OpenDiablo2.Common.Models
//
public sealed class LevelPreset
{
- public string Name { get; set; }
- public int Def { get; set; }
- public int LevelId { get; set; }
- public bool Populate { get; set; }
- public int Logicals { get; set; }
- public int Outdoors { get; set; }
- public int Animate { get; set; }
- public int KillEdge { get; set; }
- public int FillBlanks { get; set; }
- public int SizeX { get; set; }
- public int SizeY { get; set; }
- public int AutoMap { get; set; }
- public int Scan { get; set; }
- public int Pops { get; set; }
- public int PopPad { get; set; }
- public int Files { get; set; }
- public string File1 { get; set; }
- public string File2 { get; set; }
- public string File3 { get; set; }
- public string File4 { get; set; }
- public string File5 { get; set; }
- public string File6 { get; set; }
- public UInt32 Dt1Mask { get; set; }
- public bool Beta { get; set; }
+ public string Name { get; internal set; }
+ public int Def { get; internal set; }
+ public int LevelId { get; internal set; }
+ public bool Populate { get; internal set; }
+ public int Logicals { get; internal set; }
+ public int Outdoors { get; internal set; }
+ public int Animate { get; internal set; }
+ public int KillEdge { get; internal set; }
+ public int FillBlanks { get; internal set; }
+ public int SizeX { get; internal set; }
+ public int SizeY { get; internal set; }
+ public int AutoMap { get; internal set; }
+ public int Scan { get; internal set; }
+ public int Pops { get; internal set; }
+ public int PopPad { get; internal set; }
+ public int Files { get; internal set; }
+ public string File1 { get; internal set; }
+ public string File2 { get; internal set; }
+ public string File3 { get; internal set; }
+ public string File4 { get; internal set; }
+ public string File5 { get; internal set; }
+ public string File6 { get; internal set; }
+ public UInt32 Dt1Mask { get; internal set; }
+ public bool Beta { get; internal set; }
}
public static class LevelPresetHelper
@@ -63,7 +63,7 @@ namespace OpenDiablo2.Common.Models
File5 = row[20],
File6 = row[21],
Dt1Mask = Convert.ToUInt32(row[22]),
- Beta = Convert.ToInt32(row[23]) == 1,
+ Beta = Convert.ToInt32(row[23]) == 1
};
- }
+ }
}
diff --git a/OpenDiablo2.Common/Models/MPQDS1.cs b/OpenDiablo2.Common/Models/MPQDS1.cs
index 93535e2c..1c23f911 100644
--- a/OpenDiablo2.Common/Models/MPQDS1.cs
+++ b/OpenDiablo2.Common/Models/MPQDS1.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
-using System.Threading.Tasks;
using OpenDiablo2.Common.Interfaces;
namespace OpenDiablo2.Common.Models
@@ -96,7 +95,7 @@ namespace OpenDiablo2.Common.Models
public MPQDS1Object[] Objects { get; internal set; }
public MPQDS1Group[] Groups { get; internal set; }
- public MPQDS1(Stream stream, LevelPreset level, LevelDetail levelDetail, LevelType levelType, IEngineDataManager engineDataManager, IResourceManager resourceManager)
+ public MPQDS1(Stream stream, LevelPreset level, LevelType levelType, IEngineDataManager engineDataManager, IResourceManager resourceManager)
{
var br = new BinaryReader(stream);
Version = br.ReadInt32();
@@ -295,6 +294,7 @@ namespace OpenDiablo2.Common.Models
DT1s[i] = resourceManager.GetMPQDT1("data\\global\\tiles\\" + levelType.File[i].Replace("/", "\\"));
}
+
LookupTable = new List();
foreach(var dt1 in DT1s.Where(x => x != null))
{
diff --git a/OpenDiablo2.Common/Models/MapInfo.cs b/OpenDiablo2.Common/Models/MapInfo.cs
index 80c680c8..42bb4eb1 100644
--- a/OpenDiablo2.Common/Models/MapInfo.cs
+++ b/OpenDiablo2.Common/Models/MapInfo.cs
@@ -4,15 +4,26 @@ using OpenDiablo2.Common.Enums;
namespace OpenDiablo2.Common.Models
{
- public sealed class MapInfo
+ public interface IMapInfo
{
- public eLevelId LevelId { get; set; } = eLevelId.None;
- public MapInfo PrimaryMap { get; set; } = null;
+ Dictionary CellInfo { get; set; }
+ MPQDS1 FileData { get; set; }
+ Rectangle TileLocation { get; set; }
+ }
+
+ public sealed class MapInfo : IMapInfo
+ {
+ public int LevelId { get; set; } = (int)eLevelId.None;
public MPQDS1 FileData { get; set; }
- public LevelPreset LevelPreset { get; set; }
- public LevelDetail LevelDetail { get; set; }
- public LevelType LevelType { get; set; }
public Dictionary CellInfo { get; set; }
public Rectangle TileLocation { get; set; } = new Rectangle();
}
+
+ public sealed class SubMapInfo : IMapInfo
+ {
+ public IMapInfo PrimaryMap { get; set; }
+ public Dictionary CellInfo { get; set; }
+ public MPQDS1 FileData { get; set; }
+ public Rectangle TileLocation { get; set; } = new Rectangle();
+ }
}
diff --git a/OpenDiablo2.Common/Models/Mobs/LevelExperienceConfig.cs b/OpenDiablo2.Common/Models/Mobs/LevelExperienceConfig.cs
index 3eb6dd2d..c7f55b9c 100644
--- a/OpenDiablo2.Common/Models/Mobs/LevelExperienceConfig.cs
+++ b/OpenDiablo2.Common/Models/Mobs/LevelExperienceConfig.cs
@@ -3,6 +3,7 @@ using OpenDiablo2.Common.Exceptions;
using OpenDiablo2.Common.Interfaces.Mobs;
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -35,7 +36,7 @@ namespace OpenDiablo2.Common.Models.Mobs
public static class LevelExperienceConfigHelper
{
- public static Dictionary ToLevelExperienceConfigs(this string[][] data)
+ public static ImmutableDictionary ToLevelExperienceConfigs(this string[][] data)
{
Dictionary result = new Dictionary();
for (int i = 1; i < data[0].Length; i++)
@@ -65,7 +66,7 @@ namespace OpenDiablo2.Common.Models.Mobs
result.Add(herotype, new LevelExperienceConfig(expperlevel));
}
- return result;
+ return result.ToImmutableDictionary();
}
}
}
diff --git a/OpenDiablo2.Common/OpenDiablo2.Common.csproj b/OpenDiablo2.Common/OpenDiablo2.Common.csproj
index 205cb339..c5a5cd50 100644
--- a/OpenDiablo2.Common/OpenDiablo2.Common.csproj
+++ b/OpenDiablo2.Common/OpenDiablo2.Common.csproj
@@ -58,6 +58,7 @@
+
@@ -90,6 +91,7 @@
+
diff --git a/OpenDiablo2.Core/EngineDataManager.cs b/OpenDiablo2.Core/EngineDataManager.cs
index bb08e468..bd503415 100644
--- a/OpenDiablo2.Core/EngineDataManager.cs
+++ b/OpenDiablo2.Core/EngineDataManager.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -18,78 +19,65 @@ namespace OpenDiablo2.Core
private readonly IMPQProvider mpqProvider;
- public List LevelPresets { get; internal set; }
- public List LevelTypes { get; internal set; }
- public List LevelDetails { get; internal set; }
- public List
- Items { get; internal set; } = new List
- ();
- public Dictionary ExperienceConfigs { get; internal set; } = new Dictionary();
- public Dictionary HeroTypeConfigs { get; internal set; } = new Dictionary();
+ public ImmutableList Levels { get; internal set; }
+ public ImmutableList LevelPresets { get; internal set; }
+ public ImmutableList LevelTypes { get; internal set; }
+
+ public ImmutableList
- Items { get; internal set; }
+ public ImmutableDictionary ExperienceConfigs { get; internal set; }
+ public ImmutableDictionary HeroTypeConfigs { get; internal set; }
public EngineDataManager(IMPQProvider mpqProvider)
{
this.mpqProvider = mpqProvider;
- LoadLevelPresets();
- LoadLevelTypes();
LoadLevelDetails();
-
- LoadItemData();
-
LoadCharacterData();
+
+ Items = LoadItemData();
}
- private void LoadLevelTypes()
+
+ private void LoadLevelDetails()
{
log.Info("Loading level types");
- var data = mpqProvider
+ LevelTypes = mpqProvider
.GetTextFile(ResourcePaths.LevelType)
.Skip(1)
.Where(x => !String.IsNullOrWhiteSpace(x))
.Select(x => x.Split('\t'))
.Where(x => x.Count() == 36 && x[0] != "Expansion")
- .Select(x => x.ToLevelType());
+ .Select(x => x.ToLevelType())
+ .ToImmutableList();
- LevelTypes = new List(data);
- }
-
- private void LoadLevelPresets()
- {
+
log.Info("Loading level presets");
- var data = mpqProvider
+ LevelPresets = mpqProvider
.GetTextFile(ResourcePaths.LevelPreset)
.Skip(1)
.Where(x => !String.IsNullOrWhiteSpace(x))
.Select(x => x.Split('\t'))
.Where(x => x.Count() == 24 && x[0] != "Expansion")
- .Select(x => x.ToLevelPreset());
-
- LevelPresets = new List(data);
- }
-
- private void LoadLevelDetails()
- {
+ .Select(x => x.ToLevelPreset())
+ .ToImmutableList();
+
log.Info("Loading level details");
- var data = mpqProvider
+ Levels = mpqProvider
.GetTextFile(ResourcePaths.LevelDetails)
.Skip(1)
.Where(x => !String.IsNullOrWhiteSpace(x))
.Select(x => x.Split('\t'))
.Where(x => x.Count() > 80 && x[0] != "Expansion")
- .Select(x => x.ToLevelDetail());
-
- LevelDetails = new List(data);
+ .Select(x => x.ToLevelDetail(LevelPresets, LevelTypes))
+ .ToImmutableList();
}
- private void LoadItemData()
- {
- var weaponData = LoadWeaponData();
- var armorData = LoadArmorData();
- var miscData = LoadMiscData();
-
- Items.AddRange(weaponData);
- Items.AddRange(armorData);
- Items.AddRange(miscData);
- }
+ private ImmutableList
- LoadItemData()
+ => new List
- ()
+ .Concat(LoadWeaponData())
+ .Concat(LoadArmorData())
+ .Concat(LoadMiscData())
+ .ToImmutableList();
private IEnumerable LoadWeaponData()
{
@@ -101,7 +89,7 @@ namespace OpenDiablo2.Core
//.Where(x => !String.IsNullOrWhiteSpace(x[27]))
.Select(x => x.ToWeapon());
- return data;
+ return data;
}
private IEnumerable LoadArmorData()
@@ -114,7 +102,7 @@ namespace OpenDiablo2.Core
//.Where(x => !String.IsNullOrWhiteSpace(x[27]))
.Select(x => x.ToArmor());
- return data;
+ return data;
}
private IEnumerable LoadMiscData()
@@ -127,38 +115,30 @@ namespace OpenDiablo2.Core
//.Where(x => !String.IsNullOrWhiteSpace(x[27]))
.Select(x => x.ToMisc());
- return data;
+ return data;
}
private void LoadCharacterData()
{
- LoadExperienceConfig();
- LoadHeroTypeConfig();
+ ExperienceConfigs = LoadExperienceConfig();
+ HeroTypeConfigs = LoadHeroTypeConfig();
}
- private void LoadExperienceConfig()
- {
- var data = mpqProvider
+ private ImmutableDictionary LoadExperienceConfig()
+ => mpqProvider
.GetTextFile(ResourcePaths.Experience)
.Where(x => !String.IsNullOrWhiteSpace(x))
.Select(x => x.Split('\t'))
.ToArray()
.ToLevelExperienceConfigs();
-
- ExperienceConfigs = data;
- }
- private void LoadHeroTypeConfig()
- {
- var data = mpqProvider
+ private ImmutableDictionary LoadHeroTypeConfig()
+ => mpqProvider
.GetTextFile(ResourcePaths.CharStats)
.Skip(1)
.Where(x => !String.IsNullOrWhiteSpace(x))
.Select(x => x.Split('\t'))
.Where(x => x[0] != "Expansion")
- .ToDictionary(x => (eHero)Enum.Parse(typeof(eHero),x[0]), x => x.ToHeroTypeConfig());
-
- HeroTypeConfigs = data;
- }
+ .ToImmutableDictionary(x => (eHero)Enum.Parse(typeof(eHero), x[0]), x => x.ToHeroTypeConfig());
}
}
diff --git a/OpenDiablo2.Core/GameState/GameState.cs b/OpenDiablo2.Core/GameState/GameState.cs
index a1fa2460..f0fa7309 100644
--- a/OpenDiablo2.Core/GameState/GameState.cs
+++ b/OpenDiablo2.Core/GameState/GameState.cs
@@ -24,9 +24,10 @@ namespace OpenDiablo2.Core.GameState_
private readonly IMPQProvider mpqProvider;
private readonly Func getMapEngine;
private readonly Func getSessionManager;
+ private readonly Func getRandomizedMapGenerator;
private float animationTime = 0f;
- private List mapInfo;
+ private List mapInfo;
private readonly List mapDataLookup = new List();
private ISessionManager sessionManager;
@@ -57,7 +58,8 @@ namespace OpenDiablo2.Core.GameState_
ISoundProvider soundProvider,
IMPQProvider mpqProvider,
Func getMapEngine,
- Func getSessionManager
+ Func getSessionManager,
+ Func getRandomizedMapGenerator
)
{
this.sceneManager = sceneManager;
@@ -69,6 +71,7 @@ namespace OpenDiablo2.Core.GameState_
this.renderWindow = renderWindow;
this.soundProvider = soundProvider;
this.mpqProvider = mpqProvider;
+ this.getRandomizedMapGenerator = getRandomizedMapGenerator;
originalMouseCursor = renderWindow.MouseCursor;
@@ -84,7 +87,7 @@ namespace OpenDiablo2.Core.GameState_
sessionManager.OnPlayerInfo += OnPlayerInfo;
sessionManager.OnFocusOnPlayer += OnFocusOnPlayer;
- mapInfo = new List();
+ mapInfo = new List();
sceneManager.ChangeScene(eSceneType.Game);
sessionManager.JoinGame(characterName, hero);
@@ -115,76 +118,102 @@ namespace OpenDiablo2.Core.GameState_
new MapGenerator(this).Generate();
}
+ public int HasMap(int cellX, int cellY)
+ => mapInfo.Count(z => (cellX >= z.TileLocation.Left) && (cellX < z.TileLocation.Right)
+ && (cellY >= z.TileLocation.Top) && (cellY < z.TileLocation.Bottom));
+ public IEnumerable GetMapSizes(int cellX, int cellY)
+ => mapInfo
+ .Where(z => (cellX >= z.TileLocation.Left) && (cellX < z.TileLocation.Right) && (cellY >= z.TileLocation.Top) && (cellY < z.TileLocation.Bottom))
+ .Select(x => x.TileLocation.Size);
- public MapInfo LoadSubMap(int levelDefId, Point origin, MapInfo primaryMap, int subTile = -1)
+ public void RemoveEverythingAt(int cellX, int cellY)
+ => mapInfo.RemoveAll(z => (cellX >= z.TileLocation.Left) && (cellX < z.TileLocation.Right) && (cellY >= z.TileLocation.Top) && (cellY < z.TileLocation.Bottom));
+
+ public IMapInfo GetSubMapInfo(int levelPresetId, int levelTypeId, IMapInfo primaryMap, Point origin, int subTile = -1)
{
- var level = engineDataManager.LevelPresets.First(x => x.Def == levelDefId);
- var levelDetails = engineDataManager.LevelDetails.First(x => x.Id == level.LevelId);
- var levelType = engineDataManager.LevelTypes.First(x => x.Id == levelDetails.LevelType);
+ var levelPreset = engineDataManager.LevelPresets.First(x => x.Def == levelPresetId);
+ var levelType = engineDataManager.LevelTypes.First(x => x.Id == levelTypeId);
// Some maps have variations, so lets pick a random one
var mapNames = new List();
- if (level.File1 != "0") mapNames.Add(level.File1);
- if (level.File2 != "0") mapNames.Add(level.File2);
- if (level.File3 != "0") mapNames.Add(level.File3);
- if (level.File4 != "0") mapNames.Add(level.File4);
- if (level.File5 != "0") mapNames.Add(level.File5);
- if (level.File6 != "0") mapNames.Add(level.File6);
+ if (levelPreset.File1 != "0") mapNames.Add(levelPreset.File1);
+ if (levelPreset.File2 != "0") mapNames.Add(levelPreset.File2);
+ if (levelPreset.File3 != "0") mapNames.Add(levelPreset.File3);
+ if (levelPreset.File4 != "0") mapNames.Add(levelPreset.File4);
+ if (levelPreset.File5 != "0") mapNames.Add(levelPreset.File5);
+ if (levelPreset.File6 != "0") mapNames.Add(levelPreset.File6);
var random = new Random(Seed + origin.X + origin.Y);
var mapName = "data\\global\\tiles\\" + mapNames[subTile == -1 ? random.Next(mapNames.Count) : subTile].Replace("/", "\\");
- var fileData = resourceManager.GetMPQDS1(mapName, level, levelDetails, levelType);
+ var fileData = resourceManager.GetMPQDS1(mapName, levelPreset, levelType);
- var result = new MapInfo
+ var result = new SubMapInfo
{
- LevelId = eLevelId.None,
- LevelPreset = level,
- LevelDetail = levelDetails,
- LevelType = levelType,
FileData = fileData,
PrimaryMap = primaryMap,
CellInfo = new Dictionary(),
TileLocation = new Rectangle(origin, new Size(fileData.Width - 1, fileData.Height - 1))
};
+
+ return result;
+ }
+ public void AddMap(IMapInfo map) => mapInfo.Add(map);
+
+ public IMapInfo InsertSubMap(int levelPresetId, int levelTypeId, Point origin, IMapInfo primaryMap, int subTile = -1)
+ {
+ var result = GetSubMapInfo(levelPresetId, levelTypeId, primaryMap, origin, subTile);
mapInfo.Add(result);
return result;
}
- public MapInfo LoadMap(eLevelId levelId, Point origin)
+ public IMapInfo InsertMap(eLevelId levelId, IMapInfo parentMap = null) => InsertMap((int)levelId, new Point(0, 0), parentMap);
+
+ public IMapInfo InsertMap(int levelId, Point origin, IMapInfo parentMap = null)
{
- var level = engineDataManager.LevelPresets.First(x => x.LevelId == (int)levelId);
- var levelDetails = engineDataManager.LevelDetails.First(x => x.Id == level.LevelId);
- var levelType = engineDataManager.LevelTypes.First(x => x.Id == levelDetails.LevelType);
+
+ var levelDetails = engineDataManager.Levels.First(x => x.Id == levelId);
+
+ if (levelDetails.LevelPreset == null)
+ {
+ // There is no preset level, so we must generate one
+ var generator = getRandomizedMapGenerator(levelDetails.LevelName);
+
+ if (generator == null)
+ throw new OpenDiablo2Exception($"Could not locate a map generator for '{levelDetails.LevelName}'.");
+
+ generator.Generate(parentMap, origin);
+
+ // There is no core map so we cannot return a value here. If anyone actually uses
+ // this value on a generated map they are making a terrible mistake anyway...
+ return null;
+ }
// Some maps have variations, so lets pick a random one
var mapNames = new List();
- if (level.File1 != "0") mapNames.Add(level.File1);
- if (level.File2 != "0") mapNames.Add(level.File2);
- if (level.File3 != "0") mapNames.Add(level.File3);
- if (level.File4 != "0") mapNames.Add(level.File4);
- if (level.File5 != "0") mapNames.Add(level.File5);
- if (level.File6 != "0") mapNames.Add(level.File6);
+ if (levelDetails.LevelPreset.File1 != "0") mapNames.Add(levelDetails.LevelPreset.File1);
+ if (levelDetails.LevelPreset.File2 != "0") mapNames.Add(levelDetails.LevelPreset.File2);
+ if (levelDetails.LevelPreset.File3 != "0") mapNames.Add(levelDetails.LevelPreset.File3);
+ if (levelDetails.LevelPreset.File4 != "0") mapNames.Add(levelDetails.LevelPreset.File4);
+ if (levelDetails.LevelPreset.File5 != "0") mapNames.Add(levelDetails.LevelPreset.File5);
+ if (levelDetails.LevelPreset.File6 != "0") mapNames.Add(levelDetails.LevelPreset.File6);
var random = new Random(Seed);
//var mapName = "data\\global\\tiles\\" + mapNames[random.Next(mapNames.Count)].Replace("/", "\\");
- // TEMP FOR TESTING
+ // TODO: ***TEMP FOR TESTING
var mapName = "data\\global\\tiles\\" + mapNames[0].Replace("/", "\\");
- MapName = level.Name;
- Act = levelType.Act;
+ MapName = levelDetails.LevelPreset.Name;
+ Act = levelDetails.LevelType.Act;
+
+ var fileData = resourceManager.GetMPQDS1(mapName, levelDetails.LevelPreset, levelDetails.LevelType);
- var fileData = resourceManager.GetMPQDS1(mapName, level, levelDetails, levelType);
-
var result = new MapInfo
{
LevelId = levelId,
- LevelPreset = level,
- LevelDetail = levelDetails,
- LevelType = levelType,
FileData = fileData,
CellInfo = new Dictionary(),
TileLocation = new Rectangle(origin, new Size(fileData.Width - 1, fileData.Height - 1))
@@ -192,9 +221,13 @@ namespace OpenDiablo2.Core.GameState_
mapInfo.Add(result);
- soundProvider.StopSong();
- soundProvider.LoadSong(mpqProvider.GetStream(ResourcePaths.GetMusicPathForLevel(levelId)));
- soundProvider.PlaySong();
+ // Only change music if loading a 'core' map
+ if (Enum.IsDefined(typeof(eLevelId), levelId))
+ {
+ soundProvider.StopSong();
+ soundProvider.LoadSong(mpqProvider.GetStream(ResourcePaths.GetMusicPathForLevel((eLevelId)levelId)));
+ soundProvider.PlaySong();
+ }
return result;
@@ -218,7 +251,10 @@ namespace OpenDiablo2.Core.GameState_
return map.FileData.FloorLayers
.Select(floorLayer => GetMapCellInfo(map, cellX, cellY, floorLayer.Props[idx], eRenderCellType.Floor, 0))
.Where(x => x != null);
-
+ case eRenderCellType.Shadow:
+ return map.FileData.ShadowLayers
+ .Select(shadowLayer => GetMapCellInfo(map, cellX, cellY, shadowLayer.Props[idx], eRenderCellType.Shadow, 0))
+ .Where(x => x != null);
case eRenderCellType.WallNormal:
case eRenderCellType.WallLower:
case eRenderCellType.Roof:
@@ -231,11 +267,11 @@ namespace OpenDiablo2.Core.GameState_
}
}
- private MapInfo GetMap(ref int cellX, ref int cellY)
+ private IMapInfo GetMap(ref int cellX, ref int cellY)
{
var x = cellX;
var y = cellY;
- var map = mapInfo.FirstOrDefault(z => (x >= z.TileLocation.X) && (y >= z.TileLocation.Y)
+ var map = mapInfo.LastOrDefault(z => (x >= z.TileLocation.X) && (y >= z.TileLocation.Y)
&& (x < z.TileLocation.Right) && (y < z.TileLocation.Bottom));
if (map == null)
{
@@ -268,7 +304,7 @@ namespace OpenDiablo2.Core.GameState_
this.SelectedItem = item;
}
- private MapCellInfo GetMapCellInfo(MapInfo map, int cellX, int cellY, MPQDS1TileProps props, eRenderCellType cellType, byte orientation)
+ private MapCellInfo GetMapCellInfo(IMapInfo map, int cellX, int cellY, MPQDS1TileProps props, eRenderCellType cellType, byte orientation)
{
if (props.Prop1 == 0)
return null;
@@ -288,8 +324,8 @@ namespace OpenDiablo2.Core.GameState_
if (orientation == 0)
{
- // Floor
- if (cellType != eRenderCellType.Floor)
+ // Floor or Shadow
+ if (cellType != eRenderCellType.Floor && cellType != eRenderCellType.Shadow)
{
map.CellInfo[cellType][cellX + (cellY * map.FileData.Width)] = new MapCellInfo { Ignore = true };
return null;
@@ -297,9 +333,12 @@ namespace OpenDiablo2.Core.GameState_
}
else if (orientation == 10 || orientation == 11)
{
- // Special tile
- map.CellInfo[cellType][cellX + (cellY * map.FileData.Width)] = new MapCellInfo { Ignore = true };
- return null;
+ if (cellType != eRenderCellType.WallNormal)
+ {
+ // Special tile
+ map.CellInfo[cellType][cellX + (cellY * map.FileData.Width)] = new MapCellInfo { Ignore = true };
+ return null;
+ }
}
else if (orientation == 14)
{
@@ -339,12 +378,14 @@ namespace OpenDiablo2.Core.GameState_
}
int frame = 0;
- var tiles = (map.PrimaryMap ?? map).FileData.LookupTable
+ IEnumerable tiles = Enumerable.Empty();
+ tiles = map.FileData.LookupTable
.Where(x => x.MainIndex == main_index && x.SubIndex == sub_index && x.Orientation == orientation)
.Select(x => x.TileRef);
- if (!tiles.Any())
+ if (tiles == null || !tiles.Any())
{
+ log.Error($"Could not find tile [{main_index}:{sub_index}:{orientation}]!");
map.CellInfo[cellType][cellX + (cellY * map.FileData.Width)] = new MapCellInfo { Ignore = true };
return null;
}
diff --git a/OpenDiablo2.Core/Map Engine/MapGenerator.cs b/OpenDiablo2.Core/Map Engine/MapGenerator.cs
index bb704789..82160e05 100644
--- a/OpenDiablo2.Core/Map Engine/MapGenerator.cs
+++ b/OpenDiablo2.Core/Map Engine/MapGenerator.cs
@@ -2,6 +2,7 @@
using System.Drawing;
using OpenDiablo2.Common.Enums;
using OpenDiablo2.Common.Interfaces;
+using OpenDiablo2.Common.Models;
namespace OpenDiablo2.Core.Map_Engine
{
@@ -21,137 +22,36 @@ namespace OpenDiablo2.Core.Map_Engine
private void GenerateAct1Town()
{
- var townMap = gameState.LoadMap(eLevelId.Act1_Town1, new Point(0, 0));
- //var wildBorder = 5; // (4-15)
-
- Rectangle bloodMooreRect;
-
- // 32-37 is grassy field?
- bool westExit = false;
- bool eastExit = false;
- bool southExit = false;
- bool northExit = false;
+ var townMap = gameState.InsertMap((int)eLevelId.Act1_Town1, new Point(0, 0));
+ Point bloodMoorOrigin;
if (townMap.FileData.MapFile.Contains("S1"))
{
var defId = 3; // Act 1 - Town 1 Transition S
- var borderMap = gameState.LoadSubMap(defId, new Point(0, townMap.FileData.Height - 2), townMap);
- borderMap.PrimaryMap = townMap;
+ var borderMap = gameState.InsertSubMap(defId, 1, new Point(0, townMap.FileData.Height - 2), townMap);
- var wilderness = gameState.LoadSubMap(defId, new Point(26, townMap.FileData.Height + borderMap.FileData.Height - 2), townMap);
- wilderness.PrimaryMap = townMap;
+ gameState.InsertSubMap(defId, 1, new Point(26, townMap.FileData.Height + borderMap.FileData.Height - 2), townMap);
- bloodMooreRect = new Rectangle(-40, townMap.FileData.Height + borderMap.FileData.Height + 4, 120, 80);
- southExit = true;
+ bloodMoorOrigin = new Point(0, townMap.FileData.Height + borderMap.FileData.Height + 4);
}
else if (townMap.FileData.MapFile.Contains("E1"))
{
var defId = 2; // Act 1 - Town 1 Transition E
- var borderMap = gameState.LoadSubMap(defId, new Point(townMap.FileData.Width - 2, 0), townMap);
- borderMap.PrimaryMap = townMap;
+ var borderMap = gameState.InsertSubMap(defId, 1, new Point(townMap.FileData.Width - 2, 0), townMap);
- bloodMooreRect = new Rectangle(townMap.FileData.Width + borderMap.FileData.Width - 4, -40, 80, 120);
- eastExit = true;
+ bloodMoorOrigin = new Point(townMap.FileData.Width + borderMap.FileData.Width - 4, 0);
}
else if (townMap.FileData.MapFile.Contains("W1"))
{
// West
- bloodMooreRect = new Rectangle(-120, 0, 120, townMap.FileData.Height);
- westExit = true;
+ bloodMoorOrigin = new Point(-80, 0);
}
else // North
{
- bloodMooreRect = new Rectangle(0, -120, townMap.FileData.Width - 8, 120);
- northExit = true;
+ bloodMoorOrigin = new Point(-22, -80); // Align along the eastern edge
}
- // Generate the Blood Moore?
- for (var y = 0; y < bloodMooreRect.Height; y += 8)
- {
- for (var x = 0; x < bloodMooreRect.Width; x += 8)
- {
- var px = bloodMooreRect.Left + x;
- var py = bloodMooreRect.Top + y;
-
- if ((x == 0) && (y == 0)) // North West
- {
- gameState.LoadSubMap((int)eWildBorder.NorthWest, new Point(px, py), townMap, 0);
- }
- else if ((x == bloodMooreRect.Width - 9) && (y == 0)) // North East
- {
- gameState.LoadSubMap((int)eWildBorder.NorthEast, new Point(px, py), townMap, 0);
- }
- else if ((x == bloodMooreRect.Width - 9) && (y == bloodMooreRect.Height - 9)) // South East
- {
- if (northExit)
- {
- gameState.LoadSubMap((int)eWildBorder.RiverUpper, new Point(bloodMooreRect.Left + x, bloodMooreRect.Top + y), townMap, 0);
- }
- else gameState.LoadSubMap((int)eWildBorder.SouthEast, new Point(px, py), townMap, 0);
- }
- else if ((x == 0) && (y == bloodMooreRect.Height - 9)) // South West
- {
- if (northExit)
- {
- gameState.LoadSubMap((int)eWildBorder.West, new Point(px, py), townMap, 0);
- }
- else gameState.LoadSubMap((int)eWildBorder.SouthWest, new Point(px, py), townMap, 0);
- }
- else if ((x == 0) && ((y % 8) == 0)) // West
- {
- if (westExit)
- {
- gameState.LoadSubMap((int)eWildBorder.RiverUpper, new Point(px, py), townMap, 3);
- }
- else if (eastExit)
- {
- // TODO: Transition to town
- }
- else gameState.LoadSubMap((int)eWildBorder.West, new Point(px, py), townMap, 0);
- }
- else if ((x == bloodMooreRect.Width - 9) && ((y % 8) == 0)) // East
- {
- if (westExit)
- {
- // TODO: Transition to town
- }
- if (northExit || eastExit)
- {
- gameState.LoadSubMap((int)eWildBorder.RiverUpper, new Point(px, py), townMap, 3);
- }
- else gameState.LoadSubMap((int)eWildBorder.East, new Point(px, py), townMap, 0);
- }
- else if (((x % 8) == 0) && (y == 0)) // North
- {
- if (southExit)
- {
-
- }
- else gameState.LoadSubMap((int)eWildBorder.North, new Point(px, py), townMap, 0);
- }
- else if (((x % 8) == 0) && (y == (bloodMooreRect.Height - 9))) // South
- {
- if (northExit)
- {
- //var tileIdx = 31; // 8x8 fill
- //gameState.LoadSubMap(tileIdx, new Point(bloodMooreRect.Left + x, bloodMooreRect.Top + y), townMap);
- }
- else gameState.LoadSubMap((int)eWildBorder.South, new Point(px, py), townMap, 0);
- }
- else
- {
- if (((x % 8) == 0) && ((y % 8)) == 0)
- {
- var tileIdx = 31; // 8x8 fill
- gameState.LoadSubMap(tileIdx, new Point(bloodMooreRect.Left + x, bloodMooreRect.Top + y), townMap, 0);
- }
-
- }
- }
- }
-
-
-
+ gameState.InsertMap(2 /*Wilderness 1*/, bloodMoorOrigin, townMap);
}
}
diff --git a/OpenDiablo2.Core/OpenDiablo2.Core.csproj b/OpenDiablo2.Core/OpenDiablo2.Core.csproj
index a19e3c60..04175d91 100644
--- a/OpenDiablo2.Core/OpenDiablo2.Core.csproj
+++ b/OpenDiablo2.Core/OpenDiablo2.Core.csproj
@@ -42,6 +42,9 @@
..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll
+
+ ..\packages\System.Collections.Immutable.1.5.0\lib\netstandard2.0\System.Collections.Immutable.dll
+
diff --git a/OpenDiablo2.Core/ResourceManager.cs b/OpenDiablo2.Core/ResourceManager.cs
index 3e079b10..a26ba708 100644
--- a/OpenDiablo2.Core/ResourceManager.cs
+++ b/OpenDiablo2.Core/ResourceManager.cs
@@ -48,9 +48,9 @@ namespace OpenDiablo2.Core
public MPQFont GetMPQFont(string resourcePath)
=> cache.AddOrGetExisting($"Font::{resourcePath}", () => MPQFont.LoadFromStream(mpqProvider.GetStream($"{resourcePath}.DC6"), mpqProvider.GetStream($"{resourcePath}.tbl")));
- public MPQDS1 GetMPQDS1(string resourcePath, LevelPreset level, LevelDetail levelDetail, LevelType levelType)
- => cache.AddOrGetExisting($"DS1::{resourcePath}::{level}::{levelDetail}::{levelType}", ()
- => new MPQDS1(mpqProvider.GetStream(resourcePath), level, levelDetail, levelType, engineDataManager, this) { MapFile = resourcePath });
+ public MPQDS1 GetMPQDS1(string resourcePath, LevelPreset level, LevelType levelType)
+ => cache.AddOrGetExisting($"DS1::{resourcePath}::{level}::{levelType}", ()
+ => new MPQDS1(mpqProvider.GetStream(resourcePath), level, levelType, engineDataManager, this) { MapFile = resourcePath });
public Palette GetPalette(string paletteFile)
=> cache.AddOrGetExisting($"Palette::{paletteFile}", () =>
diff --git a/OpenDiablo2.Core/packages.config b/OpenDiablo2.Core/packages.config
index d0b0cbed..28f3824c 100644
--- a/OpenDiablo2.Core/packages.config
+++ b/OpenDiablo2.Core/packages.config
@@ -3,5 +3,6 @@
+
\ No newline at end of file
diff --git a/OpenDiablo2.GameServer/OpenDiablo2.GameServer.csproj b/OpenDiablo2.GameServer/OpenDiablo2.GameServer.csproj
index 6973a36d..afba024c 100644
--- a/OpenDiablo2.GameServer/OpenDiablo2.GameServer.csproj
+++ b/OpenDiablo2.GameServer/OpenDiablo2.GameServer.csproj
@@ -39,6 +39,9 @@
..\packages\log4net.2.0.8\lib\net45-full\log4net.dll
+
+ ..\packages\System.Collections.Immutable.1.5.0\lib\netstandard2.0\System.Collections.Immutable.dll
+
diff --git a/OpenDiablo2.GameServer/packages.config b/OpenDiablo2.GameServer/packages.config
index 6764f843..470040d4 100644
--- a/OpenDiablo2.GameServer/packages.config
+++ b/OpenDiablo2.GameServer/packages.config
@@ -2,4 +2,5 @@
+
\ No newline at end of file
diff --git a/OpenDiablo2.MapGenerators/AutofacModule.cs b/OpenDiablo2.MapGenerators/AutofacModule.cs
new file mode 100644
index 00000000..e0f5c737
--- /dev/null
+++ b/OpenDiablo2.MapGenerators/AutofacModule.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Autofac;
+using OpenDiablo2.Common.Attributes;
+using OpenDiablo2.Common.Interfaces;
+
+namespace OpenDiablo2.MapGenerators
+{
+ public sealed class AutofacModule : Module
+ {
+ private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
+
+ protected override void Load(ContainerBuilder builder)
+ {
+ log.Info("Configuring OpenDiablo2.MapGenerators service implementations.");
+
+ var types = ThisAssembly.GetTypes().Where(x => typeof(IRandomizedMapGenerator).IsAssignableFrom(x) && x.IsClass);
+ foreach (var type in types)
+ {
+ var att = type.GetCustomAttributes(true).First(x => (x is RandomizedMapAttribute)) as RandomizedMapAttribute;
+ builder
+ .RegisterType(type)
+ .Keyed(att.MapName)
+ .InstancePerDependency();
+ }
+ }
+ }
+}
diff --git a/OpenDiablo2.MapGenerators/BloodMoor.cs b/OpenDiablo2.MapGenerators/BloodMoor.cs
new file mode 100644
index 00000000..cb607877
--- /dev/null
+++ b/OpenDiablo2.MapGenerators/BloodMoor.cs
@@ -0,0 +1,288 @@
+using OpenDiablo2.Common.Attributes;
+using OpenDiablo2.Common.Enums;
+using OpenDiablo2.Common.Interfaces;
+using OpenDiablo2.Common.Models;
+using System;
+using System.Drawing;
+using System.Linq;
+
+namespace OpenDiablo2.MapGenerators
+{
+ [RandomizedMap("Blood Moor")]
+ public sealed class BloodMoor : IRandomizedMapGenerator
+ {
+ private readonly IGameState gameState;
+
+ private readonly LevelDetail levelDetail;
+
+ public BloodMoor(IGameState gameState, IEngineDataManager dataManager)
+ {
+ this.gameState = gameState;
+
+ levelDetail = dataManager.Levels.First(x => x.LevelName == "Blood Moor");
+ }
+
+ public void Generate(IMapInfo parentMap, Point location)
+ {
+
+ // Generate the area inside of the borders
+ GenerateGeneralContents(location, parentMap);
+ }
+
+ private void GenerateGeneralContents(Point location, IMapInfo parentMap)
+ {
+ bool northExit = location.Y < 0;
+ bool southExit = location.Y > 0;
+ bool westExit = location.X < 0;
+ bool eastExit = location.X > 0;
+
+
+ if (northExit)
+ {
+ GenerateSouthernTownEntrance(location, parentMap);
+ GenerateEasternRiver(location, parentMap);
+ GenerateWesternFence(location, parentMap);
+ GenerateNorthernFence(location, parentMap);
+ }
+ FillCenterArea(location, parentMap);
+ GeneratePointsOfInterests(location, parentMap);
+
+ #region old code
+ //for (var y = 0; y < levelDetail.SizeY; y += 8)
+ //{
+ // for (var x = 0; x < levelDetail.SizeX; x += 8)
+ // {
+ // var px = location.X + x;
+ // var py = location.Y + y;
+
+ // if ((x == 0) && (y == 0)) // North West
+ // {
+ // gameState.InsertSubMap((int)eWildBorder.NorthWest, 2, new Point(px, py), parentMap, 0);
+ // }
+ // else if ((x == levelDetail.SizeX - 8) && (y == 0)) // North East
+ // {
+ // gameState.InsertSubMap((int)eWildBorder.NorthEast, 2, new Point(px, py), parentMap, 0);
+ // }
+ // else if ((x == levelDetail.SizeX - 8) && (y == levelDetail.SizeY - 8)) // South East
+ // {
+ // if (northExit)
+ // {
+ // gameState.InsertSubMap((int)eWildBorder.RiverUpper, 2, new Point(location.X + x, location.Y + y), parentMap, 0);
+ // }
+ // else gameState.InsertSubMap((int)eWildBorder.SouthEast, 2, new Point(px, py), parentMap, 0);
+ // }
+ // else if ((x == 0) && (y == levelDetail.SizeY - 8)) // South West
+ // {
+ // if (northExit)
+ // {
+ // gameState.InsertSubMap((int)eWildBorder.West, 2, new Point(px, py), parentMap, 0);
+ // }
+ // else gameState.InsertSubMap((int)eWildBorder.SouthWest, 2, new Point(px, py), parentMap, 0);
+ // }
+ // else if ((x == 0) && ((y % 8) == 0)) // West
+ // {
+ // if (westExit)
+ // {
+ // gameState.InsertSubMap((int)eWildBorder.RiverUpper, 2, new Point(px, py), parentMap, 3);
+ // }
+ // else if (eastExit)
+ // {
+ // // TODO: Transition to town
+ // }
+ // else gameState.InsertSubMap((int)eWildBorder.West, 2, new Point(px, py), parentMap, 0);
+ // }
+ // else if ((x == levelDetail.SizeX - 8) && ((y % 8) == 0)) // East
+ // {
+ // if (westExit)
+ // {
+ // // TODO: Transition to town
+ // }
+ // if (northExit || eastExit)
+ // {
+ // gameState.InsertSubMap((int)eWildBorder.RiverUpper, 2, new Point(px, py), parentMap, 3);
+ // }
+ // else gameState.InsertSubMap((int)eWildBorder.East, 2, new Point(px, py), parentMap, 0);
+ // }
+ // else if (((x % 8) == 0) && (y == 0)) // North
+ // {
+ // if (southExit)
+ // {
+
+ // }
+ // else gameState.InsertSubMap((int)eWildBorder.North, 2, new Point(px, py), parentMap, 0);
+ // }
+ // else if (((x % 8) == 0) && (y == (levelDetail.SizeY - 8))) // South
+ // {
+ // if (northExit)
+ // {
+ // //var tileIdx = 31; // 8x8 fill
+ // //gameState.InsertSubMap(tileIdx, new Point(bloodMooreRect.Left + x, bloodMooreRect.Top + y), townMap);
+ // }
+ // else gameState.InsertSubMap((int)eWildBorder.South, 2, new Point(px, py), parentMap, 0);
+ // }
+ // else
+ // {
+
+ // //if (((x % 8) == 0) && ((y % 8)) == 0)
+ // //{
+ // // var tileIdx = 31; // 8x8 fill
+ // // gameState.InsertSubMap(tileIdx, 2, new Point(px, py), parentMap, 0);
+ // //}
+
+ // }
+ // }
+ //}
+ #endregion
+ }
+
+ private void GenerateNorthernFence(Point location, IMapInfo parentMap)
+ {
+
+ for (var x = location.X + 8; x < location.X + 64; x += 8)
+ {
+ gameState.InsertSubMap((int)eWildBorder.North, 2, new Point(x, location.Y), parentMap, 0);
+ }
+ }
+
+ private void GenerateWesternFence(Point location, IMapInfo parentMap)
+ {
+ gameState.InsertSubMap((int)eWildBorder.NorthWest, 2, new Point(location.X, location.Y), parentMap, 0);
+
+ for (var y = location.Y + 8; y < location.Y + 72; y += 8)
+ {
+ gameState.InsertSubMap((int)eWildBorder.West, 2, new Point(location.X, y), parentMap, 0);
+ }
+ }
+
+ private void GenerateEasternRiver(Point location, IMapInfo parentMap)
+ {
+ for (var y = location.Y + 8; y < location.Y + 72; y += 8)
+ {
+ gameState.InsertSubMap(26 /* River Upper */, 2, new Point(location.X + 62, y), parentMap, 3);
+ gameState.InsertSubMap(27 /* River Lower */, 2, new Point(location.X + 70, y), parentMap, 3);
+ }
+
+ // River near town
+ gameState.InsertSubMap(26 /* River Upper */, 2, new Point(location.X + 62, location.Y + 72), parentMap, 2);
+ gameState.InsertSubMap(27 /* River Lower */, 2, new Point(location.X + 70, location.Y + 72), parentMap, 2);
+
+ // River near the back
+ gameState.InsertSubMap(26 /* River Upper */, 2, new Point(location.X + 62, location.Y), parentMap, 1);
+ gameState.InsertSubMap(27 /* River Lower */, 2, new Point(location.X + 70, location.Y), parentMap, 1);
+ }
+
+ private void GenerateSouthernTownEntrance(Point location, IMapInfo parentMap)
+ {
+ var random = new Random(gameState.Seed + location.X + location.Y);
+ for (var x = location.X; x < location.X + 64; x += 8)
+ {
+ if (x == location.X) // Bottom left of the map
+ gameState.InsertSubMap((int)eWildBorder.ClosedBoxBottomLeft, 2, new Point(x, location.Y + 72), parentMap);
+ else if (x == location.X + 48) // Town exit
+ gameState.InsertSubMap((int)eWildBorder.South, 2, new Point(x, location.Y + 72), parentMap, 3);
+ else // Everything else
+ gameState.InsertSubMap((int)eWildBorder.South, 2, new Point(x, location.Y + 72), parentMap, 0);
+ }
+ }
+
+ private void GeneratePointsOfInterests(Point location, IMapInfo parentMap)
+ {
+ var random = new Random(gameState.Seed + location.X + location.Y);
+
+ // Generate the cave
+ while (true)
+ {
+ var rx = random.Next(8, levelDetail.SizeX - 16);
+ var ry = random.Next(8, levelDetail.SizeY - 16);
+ rx -= (rx % 8);
+ ry -= (ry % 8);
+ var caveX = rx + location.X;
+ var caveY = ry + location.Y;
+ /*
+ // Don't generate a camp on something that's already generated
+ var loc = gameState.GetMapSizes(caveX, caveY).First();
+
+ if (loc.Width != 8 || loc.Height != 8)
+ continue;
+
+ gameState.RemoveEverythingAt(caveX, caveY);
+ */
+ gameState.InsertSubMap(52/*cave entrance*/, 2, new Point(caveX, caveY), parentMap);
+ break;
+ }
+
+ // Generate camps
+ var campsToGenerate = 3;
+ while (campsToGenerate > 0)
+ {
+ var rx = random.Next(8, levelDetail.SizeX - 16);
+ var ry = random.Next(8, levelDetail.SizeY - 16);
+ rx -= (rx % 8);
+ ry -= (ry % 8);
+ var campX = rx + location.X;
+ var campY = ry + location.Y;
+ /*
+ // Don't generate a camp on something that's already generated
+ var loc = gameState.GetMapSizes(campX, campY).First();
+
+ if (loc.Width != 8 || loc.Height != 8)
+ continue;
+
+ gameState.RemoveEverythingAt(campX, campY);
+ */
+
+ if (gameState.HasMap(campX, campY) > 1)
+ continue;
+
+ gameState.InsertSubMap(random.Next(42, 43 + 1), 2, new Point(campX, campY), parentMap);
+ campsToGenerate--;
+ }
+
+ }
+
+ static int[] tileFillIds = new int[] { 29, 30, 31, 38, 39, 40, 41 };
+ private void FillCenterArea(Point location, IMapInfo parentMap)
+ {
+ var rightEdge = levelDetail.SizeX - 8;
+ var bottomEdge = levelDetail.SizeY - 8;
+
+ for (var y = 8; y < bottomEdge; y += 8)
+ {
+ for (var x = 8; x < rightEdge; x += 8)
+ {
+ var px = location.X + x;
+ var py = location.Y + y;
+
+ // If this space is already filled, move on
+ if (gameState.HasMap(px, py) > 0)
+ continue;
+
+ // Generate filler
+ var random = new Random(gameState.Seed + location.X + location.Y + x + y);
+
+ while (true)
+ {
+ var tileIdx = tileFillIds[random.Next(0, tileFillIds.Count())];
+ var info = gameState.GetSubMapInfo(tileIdx, 2, parentMap, new Point(location.X + x, location.Y + y));
+
+ // Make sure this tile actually fits here
+ if (info.TileLocation.Right > (location.X + rightEdge) || info.TileLocation.Bottom > (location.Y + bottomEdge))
+ continue;
+
+ if (info.TileLocation.Width == 16 && gameState.HasMap(px + 8, py) > 0)
+ continue;
+
+ if (info.TileLocation.Height == 16 && gameState.HasMap(px, py + 8) > 0)
+ continue;
+
+ // We found a tile that fits, so add it
+ gameState.AddMap(info);
+ break;
+ }
+
+
+ }
+ }
+ }
+ }
+}
diff --git a/OpenDiablo2.MapGenerators/OpenDiablo2.MapGenerators.csproj b/OpenDiablo2.MapGenerators/OpenDiablo2.MapGenerators.csproj
new file mode 100644
index 00000000..3e65ec19
--- /dev/null
+++ b/OpenDiablo2.MapGenerators/OpenDiablo2.MapGenerators.csproj
@@ -0,0 +1,69 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {A3370223-A28B-45B8-AD79-C17EAD99AF36}
+ Library
+ Properties
+ OpenDiablo2.MapGenerators
+ OpenDiablo2.MapGenerators
+ v4.6.1
+ 512
+ true
+
+
+ true
+ bin\x64\Debug\
+ DEBUG;TRACE
+ full
+ x64
+ prompt
+ MinimumRecommendedRules.ruleset
+
+
+ bin\x64\Release\
+ TRACE
+ true
+ pdbonly
+ x64
+ prompt
+ MinimumRecommendedRules.ruleset
+
+
+
+ ..\packages\Autofac.4.8.1\lib\net45\Autofac.dll
+
+
+ ..\packages\log4net.2.0.8\lib\net45-full\log4net.dll
+
+
+
+ ..\packages\System.Collections.Immutable.1.5.0\lib\netstandard2.0\System.Collections.Immutable.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {b743160e-a0bb-45dc-9998-967a85e50562}
+ OpenDiablo2.Common
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenDiablo2.MapGenerators/Properties/AssemblyInfo.cs b/OpenDiablo2.MapGenerators/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..1ed523ff
--- /dev/null
+++ b/OpenDiablo2.MapGenerators/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("OpenDiablo2.MapGenerators")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("OpenDiablo2.MapGenerators")]
+[assembly: AssemblyCopyright("Copyright © 2018")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("a3370223-a28b-45b8-ad79-c17ead99af36")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/OpenDiablo2.MapGenerators/packages.config b/OpenDiablo2.MapGenerators/packages.config
new file mode 100644
index 00000000..470040d4
--- /dev/null
+++ b/OpenDiablo2.MapGenerators/packages.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenDiablo2.SDL2/OpenDiablo2.SDL2.csproj b/OpenDiablo2.SDL2/OpenDiablo2.SDL2.csproj
index ba59f093..84d97ce9 100644
--- a/OpenDiablo2.SDL2/OpenDiablo2.SDL2.csproj
+++ b/OpenDiablo2.SDL2/OpenDiablo2.SDL2.csproj
@@ -62,7 +62,7 @@
-
+
diff --git a/OpenDiablo2.SDL2/SDL2MusicProvider.cs b/OpenDiablo2.SDL2/SDL2SoundProvider.cs
similarity index 91%
rename from OpenDiablo2.SDL2/SDL2MusicProvider.cs
rename to OpenDiablo2.SDL2/SDL2SoundProvider.cs
index 360271ac..5e2c6e51 100644
--- a/OpenDiablo2.SDL2/SDL2MusicProvider.cs
+++ b/OpenDiablo2.SDL2/SDL2SoundProvider.cs
@@ -41,6 +41,8 @@ namespace OpenDiablo2.SDL2_
return;
musicChannel = SDL_mixer.Mix_PlayChannel(-1, music, 1);
+ //SDL_mixer.Mix_Volume(musicChannel, 64); // TODO: Customizable volume
+
}
public void LoadSong(Stream data)
@@ -79,7 +81,9 @@ namespace OpenDiablo2.SDL2_
return -1;
var sound = SDL_mixer.Mix_QuickLoad_WAV(data);
- return SDL_mixer.Mix_PlayChannel(-1, sound, 0);
+ var channel = SDL_mixer.Mix_PlayChannel(-1, sound, 0);
+ //SDL_mixer.Mix_Volume(channel, 64); // TODO: Customizable volume
+ return channel;
}
public void StopSfx(int channel)
diff --git a/OpenDiablo2.TestConsole/OpenDiablo2.TestConsole.csproj b/OpenDiablo2.TestConsole/OpenDiablo2.TestConsole.csproj
index a47a15e0..c38b6cb1 100644
--- a/OpenDiablo2.TestConsole/OpenDiablo2.TestConsole.csproj
+++ b/OpenDiablo2.TestConsole/OpenDiablo2.TestConsole.csproj
@@ -34,6 +34,9 @@
+
+ ..\packages\System.Collections.Immutable.1.5.0\lib\netstandard2.0\System.Collections.Immutable.dll
+
@@ -48,6 +51,7 @@
+
diff --git a/OpenDiablo2.TestConsole/Program.cs b/OpenDiablo2.TestConsole/Program.cs
index bace4f4c..da6719d1 100644
--- a/OpenDiablo2.TestConsole/Program.cs
+++ b/OpenDiablo2.TestConsole/Program.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using OpenDiablo2.Common.Enums;
using OpenDiablo2.Common.Models;
using OpenDiablo2.Core;
@@ -152,7 +153,7 @@ namespace OpenDiablo2.TestConsole
}
string path = words[0];
string output = "public enum eLevelId\r\n{\r\nNone,\r\n";
- output += ELevelIdHelper.GenerateEnum(EngineDataMan.LevelPresets);
+ output += ELevelIdHelper.GenerateEnum(EngineDataMan.Levels.Select(x => x.LevelPreset).Distinct().ToList());
output += "}";
File.WriteAllText(path, output);
Console.WriteLine("Wrote eLevelIds enum to " + path + ".");
diff --git a/OpenDiablo2.TestConsole/packages.config b/OpenDiablo2.TestConsole/packages.config
new file mode 100644
index 00000000..f1e32e9e
--- /dev/null
+++ b/OpenDiablo2.TestConsole/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/OpenDiablo2.sln b/OpenDiablo2.sln
index b8cc6238..837c54ea 100644
--- a/OpenDiablo2.sln
+++ b/OpenDiablo2.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.27130.2036
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.28315.86
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenDiablo2", "OpenDiablo2\OpenDiablo2.csproj", "{2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD}"
EndProject
@@ -27,6 +27,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenDiablo2.ServiceBus", "O
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenDiablo2.GameServer", "OpenDiablo2.GameServer\OpenDiablo2.GameServer.csproj", "{C29A84E8-E708-4BE2-9946-202899B68E19}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenDiablo2.MapGenerators", "OpenDiablo2.MapGenerators\OpenDiablo2.MapGenerators.csproj", "{A3370223-A28B-45B8-AD79-C17EAD99AF36}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -73,6 +75,10 @@ Global
{C29A84E8-E708-4BE2-9946-202899B68E19}.Debug|x64.Build.0 = Debug|x64
{C29A84E8-E708-4BE2-9946-202899B68E19}.Release|x64.ActiveCfg = Release|x64
{C29A84E8-E708-4BE2-9946-202899B68E19}.Release|x64.Build.0 = Release|x64
+ {A3370223-A28B-45B8-AD79-C17EAD99AF36}.Debug|x64.ActiveCfg = Debug|x64
+ {A3370223-A28B-45B8-AD79-C17EAD99AF36}.Debug|x64.Build.0 = Debug|x64
+ {A3370223-A28B-45B8-AD79-C17EAD99AF36}.Release|x64.ActiveCfg = Release|x64
+ {A3370223-A28B-45B8-AD79-C17EAD99AF36}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/OpenDiablo2/OpenDiablo2.csproj b/OpenDiablo2/OpenDiablo2.csproj
index d845e60b..a8fdcf74 100644
--- a/OpenDiablo2/OpenDiablo2.csproj
+++ b/OpenDiablo2/OpenDiablo2.csproj
@@ -106,6 +106,10 @@
{c29a84e8-e708-4be2-9946-202899b68e19}
OpenDiablo2.GameServer
+
+ {a3370223-a28b-45b8-ad79-c17ead99af36}
+ OpenDiablo2.MapGenerators
+
{05224fe7-293f-4184-b1d6-89f5171b60e0}
OpenDiablo2.Scenes
diff --git a/OpenDiablo2/Program.cs b/OpenDiablo2/Program.cs
index a62cb7d7..0533c583 100644
--- a/OpenDiablo2/Program.cs
+++ b/OpenDiablo2/Program.cs
@@ -118,6 +118,12 @@ namespace OpenDiablo2
return (itemContainerType) => componentContext.Resolve(new NamedParameter("itemContainerLayout", ItemContainerLayout.Values[itemContainerType]));
});
+ containerBuilder.Register>(c =>
+ {
+ var componentContext = c.Resolve();
+ return (levelName) => componentContext.ResolveKeyed(levelName);
+ });
+
/* Uncomment the below if we support multiple textbox types
containerBuilder.Register>(c =>
{