mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-09-27 21:56:19 -04:00
Address Patch MPQ Issue (#52)
* Address patch mpq issue Game can now load patch mpq files and will do so instead of a regular mpq version if a patch version exists. Updated level details & char stats loading * deleted screenshots * merge fixes * Fix weapon.cs Code column moved over one in patch mpq * Fixes: armor/misc/weapon column adjustment and mapgen autofac fix * Fix SelectHeroClass to work with None herotype
This commit is contained in:
parent
8699aad41b
commit
be29a0017e
@ -17,9 +17,13 @@ namespace OpenDiablo2.Common.UT
|
||||
energy: 20, health: 50, mana: 40, stamina: 30, manaRegen: 20,
|
||||
perlevelhealth: 3, perlevelmana: 1.5, perlevelstamina: 1,
|
||||
pervitalityhealth: 2, pervitalitystamina: 1, perenergymana: 1.5,
|
||||
perLevelStatPoints: 5,
|
||||
baseatkrating: -30, basedefrating: -30, perdexterityatkrating: 5, perdexteritydefrating: 4,
|
||||
walkVelocity: 6, runVelocity: 9, runDrain: 20, walkFrames: 8, runFrames: 8, swingFrames: 8,
|
||||
spellFrames: 8, getHitFrames: 8, bowFrames: 8, startingSkill: 0, baseWeaponClass: "hth",
|
||||
spellFrames: 8, getHitFrames: 8, bowFrames: 8, startingSkill: "",
|
||||
startingSkills: new string[] { },
|
||||
allSkillsBonusString: "", firstTabBonusString: "", secondTabBonusString: "",
|
||||
thirdTabBonusString: "", classOnlyBonusString: "", baseWeaponClass: "hth",
|
||||
initialEquipment: new List<InitialEquipment>());
|
||||
LevelExperienceConfig expconfig = new LevelExperienceConfig(new List<long>()
|
||||
{
|
||||
|
@ -15,13 +15,14 @@ namespace OpenDiablo2.Common.Interfaces
|
||||
string MapName { get; }
|
||||
Palette CurrentPalette { get; }
|
||||
IEnumerable<PlayerInfo> PlayerInfos { get; }
|
||||
eDifficulty Difficulty { get; }
|
||||
|
||||
ItemInstance SelectedItem { get; }
|
||||
void SelectItem(ItemInstance item);
|
||||
|
||||
int CameraOffset { get; set; }
|
||||
|
||||
void Initialize(string characterName, eHero hero, eSessionType sessionType);
|
||||
void Initialize(string characterName, eHero hero, eSessionType sessionType, eDifficulty difficulty);
|
||||
void Update(long ms);
|
||||
IEnumerable<MapCellInfo> GetMapCellInfo(int cellX, int cellY, eRenderCellType renderCellType);
|
||||
void UpdateMapCellInfo(int cellX, int cellY, eRenderCellType renderCellType, IEnumerable<MapCellInfo> mapCellInfo);
|
||||
|
@ -28,6 +28,8 @@ namespace OpenDiablo2.Common.Interfaces.Mobs
|
||||
double PerVitalityStamina { get; }
|
||||
double PerEnergyMana { get; }
|
||||
|
||||
int PerLevelStatPoints { get; }
|
||||
|
||||
int BaseAttackRating { get; }
|
||||
int PerDexterityAttackRating { get; }
|
||||
|
||||
@ -45,7 +47,14 @@ namespace OpenDiablo2.Common.Interfaces.Mobs
|
||||
int GetHitFrames { get; }
|
||||
int BowFrames { get; }
|
||||
|
||||
int StartingSkill { get; }
|
||||
string StartingSkill { get; }
|
||||
string[] StartingSkills { get; }
|
||||
|
||||
string AllSkillsBonusString { get; }
|
||||
string FirstTabBonusString { get; }
|
||||
string SecondTabBonusString { get; }
|
||||
string ThirdTabBonusString { get; }
|
||||
string ClassOnlyBonusString { get; }
|
||||
string BaseWeaponClass { get; }
|
||||
|
||||
List<InitialEquipment> InitialEquipment { get; }
|
||||
|
12
OpenDiablo2.Common/Interfaces/Mobs/IMissileTypeConfig.cs
Normal file
12
OpenDiablo2.Common/Interfaces/Mobs/IMissileTypeConfig.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenDiablo2.Common.Interfaces.Mobs
|
||||
{
|
||||
public interface IMissileTypeConfig
|
||||
{
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@ namespace OpenDiablo2.Common.Models
|
||||
{
|
||||
Name = row[0],
|
||||
Code = row[17],
|
||||
InvFile = row[33]
|
||||
InvFile = row[34]
|
||||
};
|
||||
|
||||
public static void Write(this BinaryWriter binaryWriter, Armor armor)
|
||||
|
@ -19,8 +19,8 @@ namespace OpenDiablo2.Common.Models
|
||||
=> new Misc
|
||||
{
|
||||
Name = row[0],
|
||||
Code = row[12],
|
||||
InvFile = row[21]
|
||||
Code = row[13],
|
||||
InvFile = row[23]
|
||||
};
|
||||
|
||||
public static void Write(this BinaryWriter binaryWriter, Misc misc)
|
||||
|
@ -19,9 +19,9 @@ namespace OpenDiablo2.Common.Models
|
||||
=> new Weapon
|
||||
{
|
||||
Name = row[0],
|
||||
Code = row[2],
|
||||
WeaponClass = row[34],
|
||||
InvFile = row[45]
|
||||
Code = row[3],
|
||||
WeaponClass = row[37],
|
||||
InvFile = row[48]
|
||||
};
|
||||
|
||||
public static void Write(this BinaryWriter binaryWriter, Weapon weapon)
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@ -6,6 +7,31 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace OpenDiablo2.Common.Models
|
||||
{
|
||||
public sealed class LevelDetailDifficulty
|
||||
{
|
||||
/// <summary>Horizontal size of the level</summary>
|
||||
public int SizeX { get; internal set; }
|
||||
/// <summary>Vertical size of the level</summary>
|
||||
public int SizeY { get; internal set; }
|
||||
|
||||
/// <summary>Level (controls the item level of items that drop from chests etc)</summary>
|
||||
public int MonLevel { get; internal set; }
|
||||
/// <summary> MonLevel, but for expansion games (only use if non-null) </summary>
|
||||
public int? MonLevelEx { get; internal set; }
|
||||
|
||||
/// <summary>The Density of Monsters</summary>
|
||||
public int MonDen { get; internal set; }
|
||||
|
||||
/// <summary>Minimum Unique and Champion Monsters Spawned in this Level</summary>
|
||||
public int MonUMin { get; internal set; }
|
||||
|
||||
/// <summary>Maximum Unique and Champion Monsters Spawned in this Level</summary>
|
||||
public int MonUMax { get; internal set; }
|
||||
|
||||
/// <summary>Monster Species 1-25 (use ID from MonStats.txt)</summary>
|
||||
public string[] M1_25 { get; internal set; }
|
||||
}
|
||||
|
||||
public sealed class LevelDetail
|
||||
{
|
||||
/// <summary>Internal level name</summary>
|
||||
@ -14,20 +40,26 @@ namespace OpenDiablo2.Common.Models
|
||||
/// <summary>Level ID (Used in columns like VIS0-1)</summary>
|
||||
public int Id { get; internal set; }
|
||||
|
||||
public Dictionary<eDifficulty, LevelDetailDifficulty> Difficulties = new Dictionary<eDifficulty, LevelDetailDifficulty>();
|
||||
|
||||
/// <summary>Palette</summary>
|
||||
public int Pal { get; internal set; }
|
||||
|
||||
/// <summary>Act</summary>
|
||||
public int Act { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used in classic (non-expansion) games
|
||||
/// If this is set, the character must have completed the quest with this id to take a portal here
|
||||
/// </summary>
|
||||
public int? QuestFlag { get; internal set; }
|
||||
/// <summary> See above, but for expansion games </summary>
|
||||
public int? QuestFlagEx { get; internal set; }
|
||||
|
||||
/// <summary>What layer the level is on (surface levels are always 0)</summary>
|
||||
public int Layer { get; internal set; }
|
||||
|
||||
/// <summary>Horizontal size of the level</summary>
|
||||
public int SizeX { get; internal set; }
|
||||
|
||||
/// <summary>Vertical size of the level</summary>
|
||||
public int SizeY { get; internal set; }
|
||||
|
||||
/// <summary>Horizontal Placement Offset</summary>
|
||||
public int OffsetX { get; internal set; }
|
||||
@ -38,29 +70,34 @@ namespace OpenDiablo2.Common.Models
|
||||
/// <summary>Special setting for levels that aren't random or preset (like Outer Cloister and Arcane Sancturary)</summary>
|
||||
public int Depend { get; internal set; }
|
||||
|
||||
/// <summary> if false, teleport not allowed in this level </summary>
|
||||
public bool Teleport { get; internal set; }
|
||||
/// <summary> if true, cannot teleport through walls and objects in this level </summary>
|
||||
public bool CantTeleportThroughWallsObjects { get; internal set; }
|
||||
|
||||
/// <summary>If true, it rains (or snows)</summary>
|
||||
public int Rain { get; internal set; }
|
||||
public bool Rain { get; internal set; }
|
||||
|
||||
/// <summary>Unused</summary>
|
||||
public int Mud { get; internal set; }
|
||||
public bool Mud { get; internal set; }
|
||||
|
||||
/// <summary>Perspective mode forced to off if set to 1</summary>
|
||||
public int NoPer { get; internal set; }
|
||||
public bool NoPer { get; internal set; }
|
||||
|
||||
/// <summary>Level of sight drawing</summary>
|
||||
public int LOSDraw { get; internal set; }
|
||||
public bool LOSDraw { get; internal set; }
|
||||
|
||||
/// <summary>Unknown</summary>
|
||||
public int FloorFilter { get; internal set; }
|
||||
public bool FloorFilter { get; internal set; }
|
||||
|
||||
/// <summary>Unknown</summary>
|
||||
public int BlankScreen { get; internal set; }
|
||||
public bool BlankScreen { get; internal set; }
|
||||
|
||||
/// <summary>For levels bordered with mountains or walls</summary>
|
||||
public int DrawEdges { get; internal set; }
|
||||
public bool DrawEdges { get; internal set; }
|
||||
|
||||
/// <summary>Set to 1 if this is underground or inside</summary>
|
||||
public int IsInside { get; internal set; }
|
||||
public bool IsInside { get; internal set; }
|
||||
|
||||
/// <summary> 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)</summary>
|
||||
public int DrlgType { get; internal set; }
|
||||
@ -99,13 +136,13 @@ namespace OpenDiablo2.Common.Models
|
||||
public int Blue { get; internal set; }
|
||||
|
||||
/// <summary>Unknown</summary>
|
||||
public int Portal { get; internal set; }
|
||||
public bool Portal { get; internal set; }
|
||||
|
||||
/// <summary>Settings for preset levels</summary>
|
||||
public int Position { get; internal set; }
|
||||
public bool Position { get; internal set; }
|
||||
|
||||
/// <summary>If true, monster/creatures get saved/loaded instead of despawning.</summary>
|
||||
public int SaveMonsters { get; internal set; }
|
||||
public bool SaveMonsters { get; internal set; }
|
||||
|
||||
/// <summary>Quest flags</summary>
|
||||
public int Quest { get; internal set; }
|
||||
@ -113,23 +150,9 @@ namespace OpenDiablo2.Common.Models
|
||||
/// <summary>Usually 2025, unknown</summary>
|
||||
public int WarpDist { get; internal set; }
|
||||
|
||||
/// <summary>Level on Normal (controls the item level of items that drop from chests etc)</summary>
|
||||
public int MonLvl1 { get; internal set; }
|
||||
|
||||
/// <summary>Level on Nightmare (controls the item level of items that drop from chests etc)</summary>
|
||||
public int MonLvl2 { get; internal set; }
|
||||
|
||||
/// <summary> Level on Hell (controls the item level of items that drop from chests etc)</summary>
|
||||
public int MonLvl3 { get; internal set; }
|
||||
|
||||
/// <summary>The Density of Monsters</summary>
|
||||
public int MonDen { get; internal set; }
|
||||
|
||||
/// <summary>Minimum Unique and Champion Monsters Spawned in this Level</summary>
|
||||
public int MonUMin { get; internal set; }
|
||||
|
||||
/// <summary>Maximum Unique and Champion Monsters Spawned in this Level</summary>
|
||||
public int MonUMax { get; internal set; }
|
||||
|
||||
/// <summary></summary>
|
||||
public int MonWndr { get; internal set; }
|
||||
@ -140,26 +163,20 @@ namespace OpenDiablo2.Common.Models
|
||||
/// <summary>How many different Species of Monsters can occur in this area (example: if you use M1-25 then set Mtot to 25 etc)</summary>
|
||||
public int Mtot { get; internal set; }
|
||||
|
||||
/// <summary>Monster Species 1-25 (use ID from MonStats.txt)</summary>
|
||||
public int[] M1_25 { get; internal set; }
|
||||
|
||||
/// <summary>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)</summary>
|
||||
public int[] S1_25 { get; internal set; }
|
||||
|
||||
/// <summary>How many different Species of Monsters can spawn as Uniques and Champions in this Area (works like Mtot)</summary>
|
||||
public int Utot { get; internal set; }
|
||||
/// <summary> If true, ranged monsters have spawning preference </summary>
|
||||
public bool RangedSpawnPreference { get; internal set; }
|
||||
|
||||
/// <summary> Unique Species 1-25 (same as M1-M25 just for Monsters that you want to appear as Unique/Champions)</summary>
|
||||
public int[] U1_25 { get; internal set; }
|
||||
public string[] U1_25 { get; internal set; }
|
||||
|
||||
/// <summary>Critter Species 1-5 (For monsters set to 1 in the IsCritter Column in MonStats.txt)</summary>
|
||||
public int[] C1_5 { get; internal set; }
|
||||
/// <summary>Critter Species 1-4 (For monsters set to 1 in the IsCritter Column in MonStats.txt)</summary>
|
||||
public string[] C1_4 { get; internal set; }
|
||||
|
||||
/// <summary>Related to C1-5, eg: if you spawn a critter thru C1 then set this column to 30 etc. (function unknown)</summary>
|
||||
public int[] CA1_5 { get; internal set; }
|
||||
/// <summary>Related to C1-5, eg: if you spawn a critter thru C1 then set this column to 30 etc. Controls chance for critter to spawn.</summary>
|
||||
public int[] CA1_4 { get; internal set; }
|
||||
|
||||
/// <summary>Unknown</summary>
|
||||
public int[] CD1_5 { get; internal set; }
|
||||
/// <summary>Unknown and unused</summary>
|
||||
public int[] CD1_4 { get; internal set; }
|
||||
|
||||
/// <summary>unknown</summary>
|
||||
public int Themes { get; internal set; }
|
||||
@ -195,28 +212,60 @@ namespace OpenDiablo2.Common.Models
|
||||
|
||||
public static class LevelDetailHelper
|
||||
{
|
||||
private static int? IntOrEmpty(string s)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(s))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Convert.ToInt32(s);
|
||||
}
|
||||
|
||||
private static int IntOrZero(string s)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(s))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Convert.ToInt32(s);
|
||||
}
|
||||
|
||||
public static LevelDetail ToLevelDetail(this string[] v, IEnumerable<LevelPreset> levelPresets, IEnumerable<LevelType> levelTypes)
|
||||
{
|
||||
var result = new LevelDetail();
|
||||
result.Difficulties.Add(eDifficulty.NORMAL, new LevelDetailDifficulty());
|
||||
result.Difficulties.Add(eDifficulty.NIGHTMARE, new LevelDetailDifficulty());
|
||||
result.Difficulties.Add(eDifficulty.HELL, new LevelDetailDifficulty());
|
||||
int i = 0;
|
||||
result.Name = v[i++];
|
||||
result.Id = Convert.ToInt32(v[i++]);
|
||||
result.Pal = Convert.ToInt32(v[i++]);
|
||||
result.Act = Convert.ToInt32(v[i++]);
|
||||
result.QuestFlag = IntOrEmpty(v[i++]);
|
||||
result.QuestFlagEx = IntOrEmpty(v[i++]);
|
||||
result.Layer = Convert.ToInt32(v[i++]);
|
||||
result.SizeX = Convert.ToInt32(v[i++]);
|
||||
result.SizeY = Convert.ToInt32(v[i++]);
|
||||
result.Difficulties[eDifficulty.NORMAL].SizeX = Convert.ToInt32(v[i++]);
|
||||
result.Difficulties[eDifficulty.NORMAL].SizeY = Convert.ToInt32(v[i++]);
|
||||
result.Difficulties[eDifficulty.NIGHTMARE].SizeX = Convert.ToInt32(v[i++]);
|
||||
result.Difficulties[eDifficulty.NIGHTMARE].SizeY = Convert.ToInt32(v[i++]);
|
||||
result.Difficulties[eDifficulty.HELL].SizeX = Convert.ToInt32(v[i++]);
|
||||
result.Difficulties[eDifficulty.HELL].SizeY = Convert.ToInt32(v[i++]);
|
||||
result.OffsetX = Convert.ToInt32(v[i++]);
|
||||
result.OffsetY = Convert.ToInt32(v[i++]);
|
||||
result.Depend = Convert.ToInt32(v[i++]);
|
||||
result.Rain = Convert.ToInt32(v[i++]);
|
||||
result.Mud = Convert.ToInt32(v[i++]);
|
||||
result.NoPer = Convert.ToInt32(v[i++]);
|
||||
result.LOSDraw = Convert.ToInt32(v[i++]);
|
||||
result.FloorFilter = Convert.ToInt32(v[i++]);
|
||||
result.BlankScreen = Convert.ToInt32(v[i++]);
|
||||
result.DrawEdges = Convert.ToInt32(v[i++]);
|
||||
result.IsInside = Convert.ToInt32(v[i++]);
|
||||
result.Teleport = (v[i] == "1" || v[i] == "2");
|
||||
result.CantTeleportThroughWallsObjects = (v[i] == "0" || v[i] == "2");
|
||||
i++;
|
||||
result.Rain = (v[i++] == "1");
|
||||
result.Mud = (v[i++] == "1");
|
||||
result.NoPer = (v[i++] == "1");
|
||||
result.LOSDraw = (v[i++] == "1");
|
||||
result.FloorFilter = (v[i++] == "1");
|
||||
result.BlankScreen = (v[i++] == "1");
|
||||
result.DrawEdges = (v[i++] == "1");
|
||||
result.IsInside = (v[i++] == "1");
|
||||
result.DrlgType = Convert.ToInt32(v[i++]);
|
||||
result.LevelTypeId = Convert.ToInt32(v[i++]);
|
||||
result.SubType = Convert.ToInt32(v[i++]);
|
||||
@ -231,33 +280,52 @@ namespace OpenDiablo2.Common.Models
|
||||
result.Red = Convert.ToInt32(v[i++]);
|
||||
result.Green = Convert.ToInt32(v[i++]);
|
||||
result.Blue = Convert.ToInt32(v[i++]);
|
||||
result.Portal = Convert.ToInt32(v[i++]);
|
||||
result.Position = Convert.ToInt32(v[i++]);
|
||||
result.SaveMonsters = Convert.ToInt32(v[i++]);
|
||||
result.Portal = (v[i++] == "1");
|
||||
result.Position = (v[i++] == "1");
|
||||
result.SaveMonsters = (v[i++] == "1");
|
||||
result.Quest = Convert.ToInt32(v[i++]);
|
||||
result.WarpDist = Convert.ToInt32(v[i++]);
|
||||
result.MonLvl1 = Convert.ToInt32(v[i++]);
|
||||
result.MonLvl2 = Convert.ToInt32(v[i++]);
|
||||
result.MonLvl3 = Convert.ToInt32(v[i++]);
|
||||
result.MonDen = Convert.ToInt32(v[i++]);
|
||||
result.MonUMin = Convert.ToInt32(v[i++]);
|
||||
result.MonUMax = Convert.ToInt32(v[i++]);
|
||||
result.Difficulties[eDifficulty.NORMAL].MonLevel = Convert.ToInt32(v[i++]);
|
||||
result.Difficulties[eDifficulty.NIGHTMARE].MonLevel = Convert.ToInt32(v[i++]);
|
||||
result.Difficulties[eDifficulty.HELL].MonLevel = Convert.ToInt32(v[i++]);
|
||||
result.Difficulties[eDifficulty.NORMAL].MonLevelEx = IntOrEmpty(v[i++]);
|
||||
result.Difficulties[eDifficulty.NIGHTMARE].MonLevelEx = IntOrEmpty(v[i++]);
|
||||
result.Difficulties[eDifficulty.HELL].MonLevelEx = IntOrEmpty(v[i++]);
|
||||
result.Difficulties[eDifficulty.NORMAL].MonDen = Convert.ToInt32(v[i++]);
|
||||
result.Difficulties[eDifficulty.NIGHTMARE].MonDen = Convert.ToInt32(v[i++]);
|
||||
result.Difficulties[eDifficulty.HELL].MonDen = Convert.ToInt32(v[i++]);
|
||||
result.Difficulties[eDifficulty.NORMAL].MonUMin = IntOrZero(v[i++]);
|
||||
result.Difficulties[eDifficulty.NORMAL].MonUMax = IntOrZero(v[i++]);
|
||||
result.Difficulties[eDifficulty.NIGHTMARE].MonUMin = IntOrZero(v[i++]);
|
||||
result.Difficulties[eDifficulty.NIGHTMARE].MonUMax = IntOrZero(v[i++]);
|
||||
result.Difficulties[eDifficulty.HELL].MonUMin = IntOrZero(v[i++]);
|
||||
result.Difficulties[eDifficulty.HELL].MonUMax = IntOrZero(v[i++]);
|
||||
result.MonWndr = Convert.ToInt32(v[i++]);
|
||||
result.MonSpcWalk = Convert.ToInt32(v[i++]);
|
||||
result.Mtot = Convert.ToInt32(v[i++]);
|
||||
result.M1_25 = new int[25];
|
||||
for (int j = 0; j < 25; j++) result.M1_25[j] = Convert.ToInt32(v[i++]);
|
||||
result.S1_25 = new int[25];
|
||||
for (int j = 0; j < 25; j++) result.S1_25[j] = Convert.ToInt32(v[i++]);
|
||||
result.Utot = Convert.ToInt32(v[i++]);
|
||||
result.U1_25 = new int[25];
|
||||
for (int j = 0; j < 25; j++) result.U1_25[j] = Convert.ToInt32(v[i++]);
|
||||
result.C1_5 = new int[5];
|
||||
for (int j = 0; j < 5; j++) result.C1_5[j] = Convert.ToInt32(v[i++]);
|
||||
result.CA1_5 = new int[5];
|
||||
for (int j = 0; j < 5; j++) result.CA1_5[j] = Convert.ToInt32(v[i++]);
|
||||
result.CD1_5 = new int[5];
|
||||
for (int j = 0; j < 5; j++) result.CD1_5[j] = Convert.ToInt32(v[i++]);
|
||||
result.Mtot = IntOrZero(v[i++]);
|
||||
result.Difficulties[eDifficulty.NORMAL].M1_25 = new string[25]; // NOTE: the game will accept up to 25,
|
||||
// but only the first 10 are present in the mpq provided table
|
||||
// TODO: add check to see if the other 11-25 are present instead of only taking the first 10
|
||||
for (int j = 0; j < 10; j++) result.Difficulties[eDifficulty.NORMAL].M1_25[j] = v[i++];
|
||||
|
||||
result.RangedSpawnPreference = (v[i++] == "1");
|
||||
result.Difficulties[eDifficulty.NIGHTMARE].M1_25 = new string[25];
|
||||
result.Difficulties[eDifficulty.HELL].M1_25 = new string[25];
|
||||
// See above TODO
|
||||
for (int j = 0; j < 10; j++)
|
||||
{
|
||||
result.Difficulties[eDifficulty.NIGHTMARE].M1_25[j] = v[i]; // note: currently these are the same
|
||||
result.Difficulties[eDifficulty.HELL].M1_25[j] = v[i++];
|
||||
}
|
||||
result.U1_25 = new string[25];
|
||||
// See above TODO
|
||||
for (int j = 0; j < 10; j++) result.U1_25[j] = v[i++];
|
||||
result.C1_4 = new string[4];
|
||||
for (int j = 0; j < 4; j++) result.C1_4[j] = v[i++];
|
||||
result.CA1_4 = new int[4];
|
||||
for (int j = 0; j < 4; j++) result.CA1_4[j] = IntOrZero(v[i++]);
|
||||
result.CD1_4 = new int[4];
|
||||
for (int j = 0; j < 4; j++) result.CD1_4[j] = IntOrZero(v[i++]);
|
||||
result.Themes = Convert.ToInt32(v[i++]);
|
||||
result.SoundEnv = Convert.ToInt32(v[i++]);
|
||||
result.Waypoint = Convert.ToInt32(v[i++]);
|
||||
|
@ -53,7 +53,9 @@ namespace OpenDiablo2.Common.Models
|
||||
KeyAdjusted = 0x00020000, // The file's encryption key is adjusted by the block offset and file size (explained in detail in the File Data section). File must be encrypted.
|
||||
IsEncrypted = 0x00010000, // File is encrypted.
|
||||
IsCompressed = 0x00000200, // File is compressed. File cannot be imploded.
|
||||
IsImploded = 0x00000100 // File is imploded. File cannot be compressed.
|
||||
IsImploded = 0x00000100, // File is imploded. File cannot be compressed.
|
||||
IsPatchFile = 0x00100000, // Marks as a mpq patch file
|
||||
IsDeleteFile = 0x02000000, // Marks as a delete request
|
||||
}
|
||||
|
||||
internal struct BlockRecord
|
||||
@ -71,6 +73,8 @@ namespace OpenDiablo2.Common.Models
|
||||
public bool IsEncrypted => (Flags & (UInt32)eBlockRecordBitflag.IsEncrypted) != 0;
|
||||
public bool IsCompressed => (Flags & (UInt32)eBlockRecordBitflag.IsCompressed) != 0;
|
||||
public bool IsImploded => (Flags & (UInt32)eBlockRecordBitflag.IsImploded) != 0;
|
||||
public bool IsPatchFile => (Flags & (UInt32)eBlockRecordBitflag.IsPatchFile) != 0;
|
||||
public bool IsDeleteFile => (Flags & (UInt32)eBlockRecordBitflag.IsDeleteFile) != 0;
|
||||
}
|
||||
|
||||
internal struct HashRecord
|
||||
@ -94,9 +98,15 @@ namespace OpenDiablo2.Common.Models
|
||||
public string Path { get; private set; }
|
||||
public eMPQFormatVersion FormatVersion => (eMPQFormatVersion)Header.FormatVersion;
|
||||
public List<string> Files => GetFilePaths();
|
||||
private List<string> FilesOverride = null;
|
||||
|
||||
private List<string> GetFilePaths()
|
||||
{
|
||||
if(FilesOverride != null)
|
||||
{
|
||||
return FilesOverride; // if we were passed in an existing listfile,
|
||||
// don't query again; use the one we were given instead
|
||||
}
|
||||
using (var stream = OpenFile(LISTFILE_NAME))
|
||||
{
|
||||
if (stream == null)
|
||||
@ -116,9 +126,10 @@ namespace OpenDiablo2.Common.Models
|
||||
InitializeCryptTable();
|
||||
}
|
||||
|
||||
public MPQ(string path)
|
||||
public MPQ(string path, List<string> listfile = null)
|
||||
{
|
||||
this.Path = path;
|
||||
this.FilesOverride = listfile;
|
||||
|
||||
// If you crash here, you may have Diablo2 open... can't do that :)
|
||||
fileStream = new FileStream(path, FileMode.Open);
|
||||
@ -369,6 +380,11 @@ namespace OpenDiablo2.Common.Models
|
||||
return new MPQStream(this, block);
|
||||
}
|
||||
|
||||
public bool HasFile(string filename)
|
||||
{
|
||||
return GetHashRecord(filename, out HashRecord hash);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
fileStream?.Dispose();
|
||||
|
@ -27,7 +27,7 @@ namespace OpenDiablo2.Common.Models
|
||||
this.blockRecord = blockRecord;
|
||||
this.blockSize = 0x200 << mpq.Header.BlockSize;
|
||||
|
||||
if (blockRecord.IsCompressed && !blockRecord.SingleUnit)
|
||||
if ((blockRecord.IsCompressed || blockRecord.IsImploded) && !blockRecord.SingleUnit)
|
||||
LoadBlockOffsets();
|
||||
|
||||
}
|
||||
@ -187,7 +187,7 @@ namespace OpenDiablo2.Common.Models
|
||||
int toread;
|
||||
uint encryptionseed;
|
||||
|
||||
if (blockRecord.IsCompressed)
|
||||
if (blockRecord.IsCompressed || blockRecord.IsImploded)
|
||||
{
|
||||
offset = blockPositions[blockIndex];
|
||||
toread = (int)(blockPositions[blockIndex + 1] - offset);
|
||||
@ -226,6 +226,11 @@ namespace OpenDiablo2.Common.Models
|
||||
data = PKDecompress(new MemoryStream(data), expectedLength);
|
||||
}
|
||||
|
||||
if (blockRecord.IsImploded && (toread != expectedLength))
|
||||
{
|
||||
data = PKDecompress(new MemoryStream(data), expectedLength);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,8 @@ namespace OpenDiablo2.Common.Models.Mobs
|
||||
public double PerVitalityStamina { get; protected set; }
|
||||
public double PerEnergyMana { get; protected set; }
|
||||
|
||||
public int PerLevelStatPoints { get; protected set; }
|
||||
|
||||
public int BaseAttackRating { get; protected set; }
|
||||
public int PerDexterityAttackRating { get; protected set; }
|
||||
|
||||
@ -43,7 +45,15 @@ namespace OpenDiablo2.Common.Models.Mobs
|
||||
public int GetHitFrames { get; protected set; }
|
||||
public int BowFrames { get; protected set; }
|
||||
|
||||
public int StartingSkill { get; protected set; }
|
||||
public string StartingSkill { get; protected set; }
|
||||
public string[] StartingSkills { get; protected set; }
|
||||
|
||||
public string AllSkillsBonusString { get; protected set; }
|
||||
public string FirstTabBonusString { get; protected set; }
|
||||
public string SecondTabBonusString { get; protected set; }
|
||||
public string ThirdTabBonusString { get; protected set; }
|
||||
public string ClassOnlyBonusString { get; protected set; }
|
||||
|
||||
public string BaseWeaponClass { get; protected set; }
|
||||
|
||||
public List<InitialEquipment> InitialEquipment { get; }
|
||||
@ -52,10 +62,13 @@ namespace OpenDiablo2.Common.Models.Mobs
|
||||
int health, int mana, int stamina, int manaRegen,
|
||||
double perlevelhealth, double perlevelmana, double perlevelstamina,
|
||||
double pervitalityhealth, double pervitalitystamina, double perenergymana,
|
||||
int perLevelStatPoints,
|
||||
int baseatkrating, int basedefrating, int perdexterityatkrating, int perdexteritydefrating,
|
||||
int walkVelocity, int runVelocity, int runDrain,
|
||||
int walkFrames, int runFrames, int swingFrames, int spellFrames, int getHitFrames, int bowFrames,
|
||||
int startingSkill,
|
||||
string startingSkill, string[] startingSkills,
|
||||
string allSkillsBonusString, string firstTabBonusString, string secondTabBonusString,
|
||||
string thirdTabBonusString, string classOnlyBonusString,
|
||||
string baseWeaponClass,
|
||||
List<InitialEquipment> initialEquipment)
|
||||
{
|
||||
@ -76,6 +89,8 @@ namespace OpenDiablo2.Common.Models.Mobs
|
||||
PerVitalityStamina = pervitalitystamina;
|
||||
PerEnergyMana = perenergymana;
|
||||
|
||||
PerLevelStatPoints = perLevelStatPoints;
|
||||
|
||||
BaseAttackRating = baseatkrating;
|
||||
BaseDefenseRating = basedefrating;
|
||||
PerDexterityAttackRating = perdexterityatkrating;
|
||||
@ -93,6 +108,13 @@ namespace OpenDiablo2.Common.Models.Mobs
|
||||
BowFrames = bowFrames;
|
||||
|
||||
StartingSkill = startingSkill;
|
||||
StartingSkills = startingSkills;
|
||||
AllSkillsBonusString = allSkillsBonusString;
|
||||
FirstTabBonusString = firstTabBonusString;
|
||||
SecondTabBonusString = secondTabBonusString;
|
||||
ThirdTabBonusString = thirdTabBonusString;
|
||||
ClassOnlyBonusString = classOnlyBonusString;
|
||||
|
||||
BaseWeaponClass = baseWeaponClass;
|
||||
|
||||
InitialEquipment = initialEquipment;
|
||||
@ -122,29 +144,35 @@ namespace OpenDiablo2.Common.Models.Mobs
|
||||
// RunDrain Comment LifePerLevel StaminaPerLevel ManaPerLevel
|
||||
// 21 22 23
|
||||
// LifePerVitality StaminaPerVitality ManaPerMagic
|
||||
// 24 25 26 27 28 29
|
||||
// 24
|
||||
// StatPerLevel
|
||||
// 25 26 27 28 29 30
|
||||
// #walk #run #swing #spell #gethit #bow
|
||||
// 30 31 32
|
||||
// BlockFactor StartSkill baseWClass
|
||||
// 33 34 35 36 37 38
|
||||
// 31 32
|
||||
// BlockFactor StartSkill
|
||||
// 33 ... 42
|
||||
// Skill1 ... Skill10
|
||||
// 43 44 45 46 47
|
||||
// StrAllSkills StrSkillTab1 StrSkillTab2 StrSkillTab3 StrClassOnly
|
||||
// 48
|
||||
// baseWClass
|
||||
// 49 50 51 49+3 50+3 51+3
|
||||
// item1 item1loc item1count item2 item2loc item2count
|
||||
// 39 40 41 42 43 44
|
||||
// item3 item3loc item3count item4 item4loc item4count
|
||||
// 45 46 47 48 49 50
|
||||
// item5 item5loc item5count item6 item6loc item6count
|
||||
// 51 52 53 54 55 56
|
||||
// item7 item7loc item7count item8 item8loc item8count
|
||||
// 57 58 59 60 61 62
|
||||
// item9 item9loc item9count item10 item10loc item10count
|
||||
// itemn = 49 + (n-1)*3, itemnloc = 50 + (n-1)*3, itemncount = 51 + (n-1)*3
|
||||
|
||||
|
||||
// NEW TODO: StatPerLevel Skill1 ... Skill10
|
||||
// StrAllSkills StrSkillTab1 StrSkillTab2 StrSkillTab3 StrClassOnly
|
||||
|
||||
List<string> itemNames = new List<string>();
|
||||
List<string> itemLocs = new List<string>();
|
||||
List<int> itemCounts = new List<int>();
|
||||
List<InitialEquipment> initialEquipment = new List<InitialEquipment>();
|
||||
|
||||
for(int i = 33; i <= 60; i+=3)
|
||||
for(int i = 49; i <= 78; i+=3)
|
||||
{
|
||||
var item = new InitialEquipment();
|
||||
item.name = row[i];
|
||||
@ -169,21 +197,31 @@ namespace OpenDiablo2.Common.Models.Mobs
|
||||
pervitalityhealth: Convert.ToInt32(row[21]) / 4.0,
|
||||
pervitalitystamina: Convert.ToInt32(row[22]) / 4.0,
|
||||
perenergymana: Convert.ToInt32(row[23]) / 4.0,
|
||||
perLevelStatPoints: Convert.ToInt32(row[24]),
|
||||
baseatkrating: Convert.ToInt32(row[13]),
|
||||
basedefrating: Convert.ToInt32(row[30]),
|
||||
basedefrating: Convert.ToInt32(row[31]),
|
||||
perdexterityatkrating: 5,
|
||||
perdexteritydefrating: 4,
|
||||
walkVelocity: Convert.ToInt32(row[14]),
|
||||
runVelocity: Convert.ToInt32(row[15]),
|
||||
runDrain: Convert.ToInt32(row[16]),
|
||||
walkFrames: Convert.ToInt32(row[24]),
|
||||
runFrames: Convert.ToInt32(row[25]),
|
||||
swingFrames: Convert.ToInt32(row[26]),
|
||||
spellFrames: Convert.ToInt32(row[27]),
|
||||
getHitFrames: Convert.ToInt32(row[28]),
|
||||
bowFrames: Convert.ToInt32(row[29]),
|
||||
startingSkill: Convert.ToInt32(row[31]),
|
||||
baseWeaponClass: row[32],
|
||||
walkFrames: Convert.ToInt32(row[25]),
|
||||
runFrames: Convert.ToInt32(row[26]),
|
||||
swingFrames: Convert.ToInt32(row[27]),
|
||||
spellFrames: Convert.ToInt32(row[28]),
|
||||
getHitFrames: Convert.ToInt32(row[29]),
|
||||
bowFrames: Convert.ToInt32(row[30]),
|
||||
startingSkill: row[32],
|
||||
startingSkills: new string[]
|
||||
{
|
||||
row[33], row[34], row[35], row[36], row[37], row[38], row[39], row[40], row[41], row[42]
|
||||
},
|
||||
allSkillsBonusString: row[43],
|
||||
firstTabBonusString: row[44],
|
||||
secondTabBonusString: row[45],
|
||||
thirdTabBonusString: row[46],
|
||||
classOnlyBonusString: row[47],
|
||||
baseWeaponClass: row[48],
|
||||
initialEquipment: initialEquipment);
|
||||
}
|
||||
}
|
||||
|
131
OpenDiablo2.Common/Models/Mobs/MissileTypeConfig.cs
Normal file
131
OpenDiablo2.Common/Models/Mobs/MissileTypeConfig.cs
Normal file
@ -0,0 +1,131 @@
|
||||
using OpenDiablo2.Common.Interfaces.Mobs;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenDiablo2.Common.Models.Mobs
|
||||
{
|
||||
public class MissileTypeConfig : IMissileTypeConfig
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
public int Id { get; private set; }
|
||||
|
||||
public int BaseVelocity { get; private set; }
|
||||
public int MaxVelocity { get; private set; }
|
||||
public int Acceleration { get; private set; }
|
||||
public int Range { get; private set; }
|
||||
public int ExtraRangePerLevel { get; private set; }
|
||||
|
||||
public int LightRadius { get; private set; }
|
||||
public bool LightFlicker { get; private set; }
|
||||
public byte[] LightColor { get; private set; } // rgb
|
||||
|
||||
public int FramesBeforeVisible { get; private set; } // how many anim frames before it becomes visible
|
||||
public int FramesBeforeActive { get; private set; } // anim frames before it can do anything, e.g. collide
|
||||
public bool LoopAnimation { get; private set; } // true: repeat animation until missile hits range or is otherwise destroyed
|
||||
// false: repeat animation once, then vanish (WARNING: only becomes invisible, not deleted)
|
||||
public string CelFilePath { get; private set; }
|
||||
public int AnimationRate { get; private set; } // does not seem to be used
|
||||
public int AnimationLength { get; private set; } // length of animation for one direction
|
||||
public int AnimationSpeed { get; private set; } // actually used, frames per second
|
||||
public int StartingFrame { get; private set; } // 'randstart' is a misnomer, actually just starts at this frame
|
||||
public bool AnimationHasSubLoop { get; private set; } // if true, will repeat in a certain range of frames
|
||||
//instead of repeating the whole animation again
|
||||
public int AnimationSubLoopStart { get; private set; } // what frame to start at if it has a subloop
|
||||
public int AnimationSubLoopEnd { get; private set; } // what frame to end at in subloop
|
||||
// when it hits this, goes back to subloop start or goes to end if missile is set to die (out of range)
|
||||
|
||||
public MissileTypeConfig(string Name, int Id,
|
||||
int BaseVelocity, int MaxVelocity, int Acceleration, int Range, int ExtraRangePerLevel,
|
||||
int LightRadius, bool LightFlicker, byte[] LightColor,
|
||||
int FramesBeforeVisible, int FramesBeforeActive, bool LoopAnimation, string CelFilePath,
|
||||
int AnimationRate, int AnimationLength, int AnimationSpeed, int StartingFrame,
|
||||
bool AnimationHasSubLoop, int AnimationSubLoopStart, int AnimationSubLoopEnd)
|
||||
{
|
||||
this.Name = Name;
|
||||
this.Id = Id;
|
||||
|
||||
this.BaseVelocity = BaseVelocity;
|
||||
this.MaxVelocity = MaxVelocity;
|
||||
this.Acceleration = Acceleration;
|
||||
this.Range = Range;
|
||||
this.ExtraRangePerLevel = ExtraRangePerLevel;
|
||||
|
||||
this.LightRadius = LightRadius;
|
||||
this.LightFlicker = LightFlicker;
|
||||
this.LightColor = LightColor;
|
||||
|
||||
this.FramesBeforeVisible = FramesBeforeVisible;
|
||||
this.FramesBeforeActive = FramesBeforeActive;
|
||||
this.LoopAnimation = LoopAnimation;
|
||||
this.CelFilePath = CelFilePath;
|
||||
this.AnimationRate = AnimationRate;
|
||||
this.AnimationLength = AnimationLength;
|
||||
this.AnimationSpeed = AnimationSpeed;
|
||||
this.StartingFrame = StartingFrame;
|
||||
this.AnimationHasSubLoop = AnimationHasSubLoop;
|
||||
this.AnimationSubLoopStart = AnimationSubLoopStart;
|
||||
this.AnimationSubLoopEnd = AnimationSubLoopEnd;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MissileTypeConfigHelper
|
||||
{
|
||||
//Missile Id Vel MaxVel Accel Range LevRange
|
||||
//0 1 2 3 4 5 6
|
||||
//Light Flicker Red Green Blue
|
||||
//7 8 9 10 11
|
||||
//InitSteps Activate
|
||||
//12 13
|
||||
//LoopAnim CelFile AnimLen RandStart SubLoop SubStart SubStop
|
||||
//14 15 16 17 18 19 20
|
||||
//CollideType CollideKill CollideFriend LastCollide Collision
|
||||
//21 22 23 24 25
|
||||
//ClientSend NextHit NextDelay Size CanDestroy ToHit AlwaysExplode Explosion
|
||||
//26 27 28 29 30 31 32 33
|
||||
//NeverDup ReturnFire GetHit KnockBack Trans Qty Pierce
|
||||
//34 35 36 37 38 39 40
|
||||
//Param1 Param1 Comment Param2 Param2 Comment SpecialSetup
|
||||
//41 42 43 44 45
|
||||
//Open Beta Skill HitShift SrcDamage MinDamage MaxDamage LevDamage
|
||||
//46 47 48 49 50 51 52 53
|
||||
//EType EMin Emax ELevel ELen ELevelLen HitClass NumDirections LocalBlood
|
||||
//54 55 56 57 58 59 60 61 62
|
||||
|
||||
|
||||
public static IMissileTypeConfig ToMissileTypeConfig(this string[] row)
|
||||
{
|
||||
return new MissileTypeConfig(
|
||||
Name: row[0],
|
||||
Id: Convert.ToInt32(row[1]),
|
||||
|
||||
BaseVelocity: Convert.ToInt32(row[2]),
|
||||
MaxVelocity: Convert.ToInt32(row[3]),
|
||||
Acceleration: Convert.ToInt32(row[4]),
|
||||
Range: Convert.ToInt32(row[5]),
|
||||
ExtraRangePerLevel: Convert.ToInt32(row[6]),
|
||||
|
||||
LightRadius: Convert.ToInt32(row[7]),
|
||||
LightFlicker: (row[8] == "1"),
|
||||
LightColor: new byte[] {Convert.ToByte(row[9]), Convert.ToByte(row[10]), Convert.ToByte(row[11])},
|
||||
|
||||
FramesBeforeVisible: Convert.ToInt32(row[12]),
|
||||
FramesBeforeActive: Convert.ToInt32(row[13]),
|
||||
LoopAnimation: (row[14] == "1"),
|
||||
CelFilePath: row[15],
|
||||
|
||||
// TODO: these rows are wrong! research why our missiles.txt has different columns thatn the one in the guide???
|
||||
// TODO: UNFINISHED
|
||||
AnimationRate: Convert.ToInt32(row[16]),
|
||||
AnimationLength: Convert.ToInt32(row[17]),
|
||||
AnimationSpeed: Convert.ToInt32(row[18]),
|
||||
StartingFrame: Convert.ToInt32(row[19]),
|
||||
AnimationHasSubLoop: (row[20] == "1"),
|
||||
AnimationSubLoopStart: Convert.ToInt32(row[16]),
|
||||
AnimationSubLoopEnd: Convert.ToInt32(row[16])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -105,6 +105,7 @@
|
||||
<Compile Include="Interfaces\Mobs\IEnemyTypeCombatConfig.cs" />
|
||||
<Compile Include="Interfaces\Mobs\IEnemyTypeConfig.cs" />
|
||||
<Compile Include="Interfaces\Mobs\IEnemyTypeDifficultyConfig.cs" />
|
||||
<Compile Include="Interfaces\Mobs\IMissileTypeConfig.cs" />
|
||||
<Compile Include="Interfaces\UI\ISkillsPanel.cs" />
|
||||
<Compile Include="Models\Mobs\EnemyTypeAppearanceConfig.cs" />
|
||||
<Compile Include="Models\Mobs\EnemyTypeCombatConfig.cs" />
|
||||
@ -118,6 +119,7 @@
|
||||
<Compile Include="Models\Item\ItemInstance.cs" />
|
||||
<Compile Include="Models\Item\Misc.cs" />
|
||||
<Compile Include="Models\Item\Weapon.cs" />
|
||||
<Compile Include="Models\Mobs\MissileTypeConfig.cs" />
|
||||
<Compile Include="Models\Mobs\PlayerEquipment.cs" />
|
||||
<Compile Include="Models\MPQCOF.cs" />
|
||||
<Compile Include="Models\MPQDCC.cs" />
|
||||
|
@ -244,6 +244,9 @@ namespace OpenDiablo2.Common
|
||||
// --- Enemy Data ---
|
||||
public static string MonStats = "data\\global\\excel\\monstats.txt";
|
||||
|
||||
// --- Skill Data ---
|
||||
public static string Missiles = "data\\global\\excel\\missiles.txt";
|
||||
|
||||
public static string GeneratePathForItem(string spriteName)
|
||||
{
|
||||
return $@"data\global\items\{spriteName}.dc6";
|
||||
|
@ -29,6 +29,7 @@ namespace OpenDiablo2.Core
|
||||
public ImmutableDictionary<eHero, ILevelExperienceConfig> ExperienceConfigs { get; internal set; }
|
||||
public ImmutableDictionary<eHero, IHeroTypeConfig> HeroTypeConfigs { get; internal set; }
|
||||
public ImmutableList<IEnemyTypeConfig> EnemyTypeConfigs { get; internal set; }
|
||||
public ImmutableDictionary<int, IMissileTypeConfig> MissileTypeConfigs { get; internal set; }
|
||||
|
||||
public EngineDataManager(IMPQProvider mpqProvider)
|
||||
{
|
||||
@ -37,6 +38,7 @@ namespace OpenDiablo2.Core
|
||||
LoadLevelDetails();
|
||||
LoadCharacterData();
|
||||
LoadEnemyData();
|
||||
LoadSkillData();
|
||||
|
||||
Items = LoadItemData();
|
||||
}
|
||||
@ -50,7 +52,7 @@ namespace OpenDiablo2.Core
|
||||
.Skip(1)
|
||||
.Where(x => !String.IsNullOrWhiteSpace(x))
|
||||
.Select(x => x.Split('\t'))
|
||||
.Where(x => x.Count() == 36 && x[0] != "Expansion")
|
||||
.Where(x => x.Count() >= 36 && x[0] != "Expansion")
|
||||
.Select(x => x.ToLevelType())
|
||||
.ToImmutableList();
|
||||
|
||||
@ -61,7 +63,7 @@ namespace OpenDiablo2.Core
|
||||
.Skip(1)
|
||||
.Where(x => !String.IsNullOrWhiteSpace(x))
|
||||
.Select(x => x.Split('\t'))
|
||||
.Where(x => x.Count() == 24 && x[0] != "Expansion")
|
||||
.Where(x => x.Count() >= 24 && x[0] != "Expansion")
|
||||
.Select(x => x.ToLevelPreset())
|
||||
.ToImmutableList();
|
||||
|
||||
@ -170,7 +172,8 @@ namespace OpenDiablo2.Core
|
||||
|
||||
private void LoadEnemyData()
|
||||
{
|
||||
EnemyTypeConfigs = LoadEnemyTypeConfig();
|
||||
//TODO: RE-ENABLE THIS once monstats is being loaded properly
|
||||
//EnemyTypeConfigs = LoadEnemyTypeConfig();
|
||||
}
|
||||
|
||||
private ImmutableList<IEnemyTypeConfig> LoadEnemyTypeConfig()
|
||||
@ -183,5 +186,26 @@ namespace OpenDiablo2.Core
|
||||
.ToArray()
|
||||
.Select(x => x.ToEnemyTypeConfig())
|
||||
.ToImmutableList();
|
||||
|
||||
private void LoadSkillData()
|
||||
{
|
||||
MissileTypeConfigs = LoadMissileTypeConfig();
|
||||
}
|
||||
|
||||
private ImmutableDictionary<int, IMissileTypeConfig> LoadMissileTypeConfig()
|
||||
{
|
||||
var data = mpqProvider
|
||||
.GetTextFile(ResourcePaths.Missiles)
|
||||
.Where(x => !String.IsNullOrWhiteSpace(x));
|
||||
|
||||
var splitdata = data
|
||||
.Select(x => x.Split('\t'))
|
||||
.Where(x => x[0] != "Expansion" && x[0] != "unused")
|
||||
.ToArray();
|
||||
|
||||
// TODO: UNFINISHED
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ namespace OpenDiablo2.Core.GameState_
|
||||
public string MapName { get; private set; }
|
||||
public Palette CurrentPalette => paletteProvider.PaletteTable[$"ACT{Act}"];
|
||||
public List<PlayerInfo> PlayerInfos { get; private set; }
|
||||
public eDifficulty Difficulty { get; private set; }
|
||||
|
||||
readonly private IMouseCursor originalMouseCursor;
|
||||
|
||||
@ -78,7 +79,7 @@ namespace OpenDiablo2.Core.GameState_
|
||||
mapDataLookup = new List<MapCellInfo>();
|
||||
}
|
||||
|
||||
public void Initialize(string characterName, eHero hero, eSessionType sessionType)
|
||||
public void Initialize(string characterName, eHero hero, eSessionType sessionType, eDifficulty difficulty)
|
||||
{
|
||||
sessionManager = getSessionManager(sessionType);
|
||||
sessionManager.Initialize();
|
||||
@ -88,6 +89,8 @@ namespace OpenDiablo2.Core.GameState_
|
||||
sessionManager.OnPlayerInfo += OnPlayerInfo;
|
||||
sessionManager.OnFocusOnPlayer += OnFocusOnPlayer;
|
||||
|
||||
Difficulty = difficulty;
|
||||
|
||||
mapInfo = new List<IMapInfo>();
|
||||
|
||||
sceneManager.ChangeScene(eSceneType.Game);
|
||||
|
@ -42,7 +42,7 @@ namespace OpenDiablo2.Core
|
||||
.EnumerateFiles(globalConfiguration.BaseDataPath, "*.mpq")
|
||||
.Where(x => !(Path.GetFileName(x)?.StartsWith("patch") ?? false))
|
||||
.Select(file => new MPQ(file))
|
||||
.ToArray();
|
||||
.ToList();
|
||||
|
||||
|
||||
if (!_mpqs.Any())
|
||||
@ -74,6 +74,32 @@ namespace OpenDiablo2.Core
|
||||
foreach (var file in _mpqs[i].Files)
|
||||
_mpqLookup[file.ToLower()] = i;
|
||||
}
|
||||
|
||||
// Get the combined list file by joining all of the other mpqs
|
||||
List<string> superListFile = _mpqs.SelectMany(x => x.Files).ToList();
|
||||
|
||||
var patchMPQ = Directory
|
||||
.EnumerateFiles(globalConfiguration.BaseDataPath, "*.mpq")
|
||||
.Where(x => Path.GetFileName(x).StartsWith("patch"))
|
||||
.Select(file => new MPQ(file, superListFile))
|
||||
.First();
|
||||
|
||||
_mpqs.Add(patchMPQ);
|
||||
int patchMPQIndex = _mpqs.Count - 1;
|
||||
|
||||
// Replace existing mpqLookups with those from the patch, which take precedence
|
||||
foreach (var file in patchMPQ.Files)
|
||||
{
|
||||
// unlike the other mpqs, we need to ensure that the files actually exist
|
||||
// inside of the patch mpq instead of assuming that they do, because
|
||||
// we can't trust the filelist
|
||||
if (!patchMPQ.HasFile(file))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_mpqLookup[file.ToLower()] = patchMPQIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] GetBytes(string fileName)
|
||||
|
@ -14,10 +14,12 @@ namespace OpenDiablo2.MapGenerators
|
||||
private readonly IGameState gameState;
|
||||
|
||||
private readonly LevelDetail levelDetail;
|
||||
private readonly eDifficulty difficulty;
|
||||
|
||||
public BloodMoor(IGameState gameState, IEngineDataManager dataManager)
|
||||
{
|
||||
this.gameState = gameState;
|
||||
this.difficulty = gameState.Difficulty;
|
||||
|
||||
levelDetail = dataManager.Levels.First(x => x.LevelName == "Blood Moor");
|
||||
}
|
||||
@ -192,8 +194,8 @@ namespace OpenDiablo2.MapGenerators
|
||||
// Generate the cave
|
||||
while (true)
|
||||
{
|
||||
var rx = random.Next(8, levelDetail.SizeX - 16);
|
||||
var ry = random.Next(8, levelDetail.SizeY - 16);
|
||||
var rx = random.Next(8, levelDetail.Difficulties[difficulty].SizeX - 16);
|
||||
var ry = random.Next(8, levelDetail.Difficulties[difficulty].SizeY - 16);
|
||||
rx -= (rx % 8);
|
||||
ry -= (ry % 8);
|
||||
var caveX = rx + location.X;
|
||||
@ -215,8 +217,8 @@ namespace OpenDiablo2.MapGenerators
|
||||
var campsToGenerate = 3;
|
||||
while (campsToGenerate > 0)
|
||||
{
|
||||
var rx = random.Next(8, levelDetail.SizeX - 16);
|
||||
var ry = random.Next(8, levelDetail.SizeY - 16);
|
||||
var rx = random.Next(8, levelDetail.Difficulties[difficulty].SizeX - 16);
|
||||
var ry = random.Next(8, levelDetail.Difficulties[difficulty].SizeY - 16);
|
||||
rx -= (rx % 8);
|
||||
ry -= (ry % 8);
|
||||
var campX = rx + location.X;
|
||||
@ -243,8 +245,8 @@ namespace OpenDiablo2.MapGenerators
|
||||
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;
|
||||
var rightEdge = levelDetail.Difficulties[difficulty].SizeX - 8;
|
||||
var bottomEdge = levelDetail.Difficulties[difficulty].SizeY - 8;
|
||||
|
||||
for (var y = 8; y < bottomEdge; y += 8)
|
||||
{
|
||||
|
@ -271,7 +271,8 @@ namespace OpenDiablo2.Scenes
|
||||
StopSfx();
|
||||
|
||||
// TODO: Support other session types
|
||||
gameState.Initialize(characterNameTextBox.Text, selectedHero.Value, eSessionType.Local);
|
||||
// TODO: support other difficulty types
|
||||
gameState.Initialize(characterNameTextBox.Text, selectedHero.Value, eSessionType.Local, eDifficulty.NORMAL);
|
||||
}
|
||||
|
||||
private void OnExitClicked()
|
||||
@ -321,7 +322,7 @@ namespace OpenDiablo2.Scenes
|
||||
|
||||
private void RenderHeros()
|
||||
{
|
||||
var heros = Enum.GetValues(typeof(eHero)).Cast<eHero>();
|
||||
var heros = Enum.GetValues(typeof(eHero)).Cast<eHero>().Skip(1); // skip NONE
|
||||
foreach (var hero in heros)
|
||||
if (heroRenderInfo[hero].Stance == eHeroStance.Idle || heroRenderInfo[hero].Stance == eHeroStance.IdleSelected)
|
||||
RenderHero(hero);
|
||||
@ -419,6 +420,10 @@ namespace OpenDiablo2.Scenes
|
||||
|
||||
private void UpdateHeroSelectionHover(eHero hero, long ms, bool canSelect)
|
||||
{
|
||||
if(hero == eHero.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var renderInfo = heroRenderInfo[hero];
|
||||
if (renderInfo.Stance == eHeroStance.Approaching)
|
||||
{
|
||||
|
BIN
Screenshot.png
BIN
Screenshot.png
Binary file not shown.
Before Width: | Height: | Size: 521 KiB |
BIN
Screenshot2.png
BIN
Screenshot2.png
Binary file not shown.
Before Width: | Height: | Size: 728 KiB |
Loading…
Reference in New Issue
Block a user