1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-06-20 14:15:23 +00: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:
nicholasdechiara 2018-12-15 12:18:32 -05:00 committed by Tim Sarbin
parent 8699aad41b
commit be29a0017e
25 changed files with 1305 additions and 956 deletions

View File

@ -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>()
{

View File

@ -1,26 +1,26 @@
namespace OpenDiablo2.Common.Enums
{
public enum eButtonType
{
Wide,
Medium,
Narrow,
Cancel,
Tall,
Short,
// Game UI
Skill,
Run,
Menu,
GoldCoin,
Close,
SecondaryInvHand,
MinipanelCharacter,
MinipanelInventory,
MinipanelSkill,
MinipanelAutomap,
MinipanelMessage,
MinipanelQuest,
MinipanelMenu
}
}
namespace OpenDiablo2.Common.Enums
{
public enum eButtonType
{
Wide,
Medium,
Narrow,
Cancel,
Tall,
Short,
// Game UI
Skill,
Run,
Menu,
GoldCoin,
Close,
SecondaryInvHand,
MinipanelCharacter,
MinipanelInventory,
MinipanelSkill,
MinipanelAutomap,
MinipanelMessage,
MinipanelQuest,
MinipanelMenu
}
}

View File

@ -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);

View File

@ -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; }

View 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
{
}
}

View File

@ -1,66 +1,66 @@
/* OpenDiablo 2 - An open source re-implementation of Diablo 2 in C#
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using OpenDiablo2.Common.Enums;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Drawing;
namespace OpenDiablo2.Common.Models
{
public class ButtonLayout
{
public int XSegments { get; set; } = 1;
public int YSegments { get; set; } = 1;
public string ResourceName { get; set; }
public string PaletteName { get; set; }
public bool Toggleable { get; set; } = false;
public int BaseFrame { get; set; } = 0;
public int DisabledFrame { get; set; } = -1;
public string FontPath { get; set; } = ResourcePaths.FontExocet10;
public Rectangle ClickableRect { get; set; }
public bool AllowFrameChange { get; set; } = true;
public bool IsDarkenedWhenDisabled => DisabledFrame == -1;
public static ImmutableDictionary<eButtonType, ButtonLayout> Values { get; } = new Dictionary<eButtonType, ButtonLayout>
{
{eButtonType.Wide, new ButtonLayout { XSegments = 2, ResourceName = ResourcePaths.WideButtonBlank, PaletteName = Palettes.Units } },
{eButtonType.Medium, new ButtonLayout{ ResourceName = ResourcePaths.MediumButtonBlank, PaletteName = Palettes.Units } },
{eButtonType.Narrow, new ButtonLayout { ResourceName = ResourcePaths.NarrowButtonBlank, PaletteName = Palettes.Units } },
{eButtonType.Tall, new ButtonLayout { ResourceName = ResourcePaths.TallButtonBlank, PaletteName = Palettes.Units } },
{eButtonType.Short, new ButtonLayout { ResourceName = ResourcePaths.ShortButtonBlank, PaletteName = Palettes.Units, FontPath = ResourcePaths.FontExocet10 } },
{eButtonType.Cancel, new ButtonLayout { ResourceName = ResourcePaths.CancelButton, PaletteName = Palettes.Units } },
// Minipanel
{eButtonType.MinipanelCharacter, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = Palettes.Units, BaseFrame = 0 } },
{eButtonType.MinipanelInventory, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = Palettes.Units, BaseFrame = 2 } },
{eButtonType.MinipanelSkill, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = Palettes.Units, BaseFrame = 4 } },
{eButtonType.MinipanelAutomap, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = Palettes.Units, BaseFrame = 8 } },
{eButtonType.MinipanelMessage, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = Palettes.Units, BaseFrame = 10 } },
{eButtonType.MinipanelQuest, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = Palettes.Units, BaseFrame = 12 } },
{eButtonType.MinipanelMenu, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = Palettes.Units, BaseFrame = 14 } },
{eButtonType.SecondaryInvHand, new ButtonLayout { ResourceName = ResourcePaths.InventoryWeaponsTab, PaletteName = Palettes.Units,
ClickableRect = new Rectangle(0, 0, 0, 20), AllowFrameChange = false } },
{eButtonType.Run, new ButtonLayout { ResourceName = ResourcePaths.RunButton, PaletteName = Palettes.Units, Toggleable = true } },
{eButtonType.Menu, new ButtonLayout { ResourceName = ResourcePaths.MenuButton, PaletteName = Palettes.Units, Toggleable = true } },
{eButtonType.GoldCoin, new ButtonLayout { ResourceName = ResourcePaths.GoldCoinButton, PaletteName = Palettes.Units } },
{eButtonType.Close, new ButtonLayout { ResourceName = ResourcePaths.SquareButton, PaletteName = Palettes.Units, BaseFrame = 10 } },
{eButtonType.Skill, new ButtonLayout { ResourceName = ResourcePaths.AddSkillButton, PaletteName = Palettes.Units, DisabledFrame = 2 } },
}.ToImmutableDictionary();
}
}
/* OpenDiablo 2 - An open source re-implementation of Diablo 2 in C#
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using OpenDiablo2.Common.Enums;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Drawing;
namespace OpenDiablo2.Common.Models
{
public class ButtonLayout
{
public int XSegments { get; set; } = 1;
public int YSegments { get; set; } = 1;
public string ResourceName { get; set; }
public string PaletteName { get; set; }
public bool Toggleable { get; set; } = false;
public int BaseFrame { get; set; } = 0;
public int DisabledFrame { get; set; } = -1;
public string FontPath { get; set; } = ResourcePaths.FontExocet10;
public Rectangle ClickableRect { get; set; }
public bool AllowFrameChange { get; set; } = true;
public bool IsDarkenedWhenDisabled => DisabledFrame == -1;
public static ImmutableDictionary<eButtonType, ButtonLayout> Values { get; } = new Dictionary<eButtonType, ButtonLayout>
{
{eButtonType.Wide, new ButtonLayout { XSegments = 2, ResourceName = ResourcePaths.WideButtonBlank, PaletteName = Palettes.Units } },
{eButtonType.Medium, new ButtonLayout{ ResourceName = ResourcePaths.MediumButtonBlank, PaletteName = Palettes.Units } },
{eButtonType.Narrow, new ButtonLayout { ResourceName = ResourcePaths.NarrowButtonBlank, PaletteName = Palettes.Units } },
{eButtonType.Tall, new ButtonLayout { ResourceName = ResourcePaths.TallButtonBlank, PaletteName = Palettes.Units } },
{eButtonType.Short, new ButtonLayout { ResourceName = ResourcePaths.ShortButtonBlank, PaletteName = Palettes.Units, FontPath = ResourcePaths.FontExocet10 } },
{eButtonType.Cancel, new ButtonLayout { ResourceName = ResourcePaths.CancelButton, PaletteName = Palettes.Units } },
// Minipanel
{eButtonType.MinipanelCharacter, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = Palettes.Units, BaseFrame = 0 } },
{eButtonType.MinipanelInventory, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = Palettes.Units, BaseFrame = 2 } },
{eButtonType.MinipanelSkill, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = Palettes.Units, BaseFrame = 4 } },
{eButtonType.MinipanelAutomap, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = Palettes.Units, BaseFrame = 8 } },
{eButtonType.MinipanelMessage, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = Palettes.Units, BaseFrame = 10 } },
{eButtonType.MinipanelQuest, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = Palettes.Units, BaseFrame = 12 } },
{eButtonType.MinipanelMenu, new ButtonLayout { ResourceName = ResourcePaths.MinipanelButton, PaletteName = Palettes.Units, BaseFrame = 14 } },
{eButtonType.SecondaryInvHand, new ButtonLayout { ResourceName = ResourcePaths.InventoryWeaponsTab, PaletteName = Palettes.Units,
ClickableRect = new Rectangle(0, 0, 0, 20), AllowFrameChange = false } },
{eButtonType.Run, new ButtonLayout { ResourceName = ResourcePaths.RunButton, PaletteName = Palettes.Units, Toggleable = true } },
{eButtonType.Menu, new ButtonLayout { ResourceName = ResourcePaths.MenuButton, PaletteName = Palettes.Units, Toggleable = true } },
{eButtonType.GoldCoin, new ButtonLayout { ResourceName = ResourcePaths.GoldCoinButton, PaletteName = Palettes.Units } },
{eButtonType.Close, new ButtonLayout { ResourceName = ResourcePaths.SquareButton, PaletteName = Palettes.Units, BaseFrame = 10 } },
{eButtonType.Skill, new ButtonLayout { ResourceName = ResourcePaths.AddSkillButton, PaletteName = Palettes.Units, DisabledFrame = 2 } },
}.ToImmutableDictionary();
}
}

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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++]);

View File

@ -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();

View File

@ -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;
}

View File

@ -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);
}
}

View 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])
);
}
}
}

View File

@ -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" />

View File

@ -1,380 +1,383 @@
/* OpenDiablo 2 - An open source re-implementation of Diablo 2 in C#
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using OpenDiablo2.Common.Enums;
using System;
namespace OpenDiablo2.Common
{
public static class ResourcePaths
{
// --- Loading Screen ---
public const string LoadingScreen = @"data\global\ui\Loading\loadingscreen.dc6";
// --- Main Menu ---
public const string GameSelectScreen = @"data\global\ui\FrontEnd\gameselectscreenEXP.dc6";
public const string Diablo2LogoFireLeft = @"data\global\ui\FrontEnd\D2logoFireLeft.DC6";
public const string Diablo2LogoFireRight = @"data\global\ui\FrontEnd\D2logoFireRight.DC6";
public const string Diablo2LogoBlackLeft = @"data\global\ui\FrontEnd\D2logoBlackLeft.DC6";
public const string Diablo2LogoBlackRight = @"data\global\ui\FrontEnd\D2logoBlackRight.DC6";
// --- Credits ---
public const string CreditsBackground = @"data\global\ui\CharSelect\creditsbckgexpand.dc6";
public const string CreditsText = @"data\local\ui\eng\ExpansionCredits.txt";
// --- Character Select Screen ---
public const string CharacterSelectBackground = @"data\global\ui\FrontEnd\charactercreationscreenEXP.dc6";
public const string CharacterSelectCampfire = @"data\global\ui\FrontEnd\fire.DC6";
public const string CharacterSelectBarbarianUnselected = @"data\global\ui\FrontEnd\barbarian\banu1.DC6";
public const string CharacterSelectBarbarianUnselectedH = @"data\global\ui\FrontEnd\barbarian\banu2.DC6";
public const string CharacterSelectBarbarianSelected = @"data\global\ui\FrontEnd\barbarian\banu3.DC6";
public const string CharacterSelectBarbarianForwardWalk = @"data\global\ui\FrontEnd\barbarian\bafw.DC6";
public const string CharacterSelectBarbarianForwardWalkOverlay = @"data\global\ui\FrontEnd\barbarian\BAFWs.DC6";
public const string CharacterSelectBarbarianBackWalk = @"data\global\ui\FrontEnd\barbarian\babw.DC6";
public const string CharacterSelecSorceressUnselected = @"data\global\ui\FrontEnd\sorceress\SONU1.DC6";
public const string CharacterSelecSorceressUnselectedH = @"data\global\ui\FrontEnd\sorceress\SONU2.DC6";
public const string CharacterSelecSorceressSelected = @"data\global\ui\FrontEnd\sorceress\SONU3.DC6";
public const string CharacterSelecSorceressSelectedOverlay = @"data\global\ui\FrontEnd\sorceress\SONU3s.DC6";
public const string CharacterSelecSorceressForwardWalk = @"data\global\ui\FrontEnd\sorceress\SOFW.DC6";
public const string CharacterSelecSorceressForwardWalkOverlay = @"data\global\ui\FrontEnd\sorceress\SOFWs.DC6";
public const string CharacterSelecSorceressBackWalk = @"data\global\ui\FrontEnd\sorceress\SOBW.DC6";
public const string CharacterSelecSorceressBackWalkOverlay = @"data\global\ui\FrontEnd\sorceress\SOBWs.DC6";
public const string CharacterSelectNecromancerUnselected = @"data\global\ui\FrontEnd\necromancer\NENU1.DC6";
public const string CharacterSelectNecromancerUnselectedH = @"data\global\ui\FrontEnd\necromancer\NENU2.DC6";
public const string CharacterSelecNecromancerSelected = @"data\global\ui\FrontEnd\necromancer\NENU3.DC6";
public const string CharacterSelecNecromancerSelectedOverlay = @"data\global\ui\FrontEnd\necromancer\NENU3s.DC6";
public const string CharacterSelecNecromancerForwardWalk = @"data\global\ui\FrontEnd\necromancer\NEFW.DC6";
public const string CharacterSelecNecromancerForwardWalkOverlay = @"data\global\ui\FrontEnd\necromancer\NEFWs.DC6";
public const string CharacterSelecNecromancerBackWalk = @"data\global\ui\FrontEnd\necromancer\NEBW.DC6";
public const string CharacterSelecNecromancerBackWalkOverlay = @"data\global\ui\FrontEnd\necromancer\NEBWs.DC6";
public const string CharacterSelectPaladinUnselected = @"data\global\ui\FrontEnd\paladin\PANU1.DC6";
public const string CharacterSelectPaladinUnselectedH = @"data\global\ui\FrontEnd\paladin\PANU2.DC6";
public const string CharacterSelecPaladinSelected = @"data\global\ui\FrontEnd\paladin\PANU3.DC6";
public const string CharacterSelecPaladinForwardWalk = @"data\global\ui\FrontEnd\paladin\PAFW.DC6";
public const string CharacterSelecPaladinForwardWalkOverlay = @"data\global\ui\FrontEnd\paladin\PAFWs.DC6";
public const string CharacterSelecPaladinBackWalk = @"data\global\ui\FrontEnd\paladin\PABW.DC6";
public const string CharacterSelectAmazonUnselected = @"data\global\ui\FrontEnd\amazon\AMNU1.DC6";
public const string CharacterSelectAmazonUnselectedH = @"data\global\ui\FrontEnd\amazon\AMNU2.DC6";
public const string CharacterSelecAmazonSelected = @"data\global\ui\FrontEnd\amazon\AMNU3.DC6";
public const string CharacterSelecAmazonForwardWalk = @"data\global\ui\FrontEnd\amazon\AMFW.DC6";
public const string CharacterSelecAmazonForwardWalkOverlay = @"data\global\ui\FrontEnd\amazon\AMFWs.DC6";
public const string CharacterSelecAmazonBackWalk = @"data\global\ui\FrontEnd\amazon\AMBW.DC6";
public const string CharacterSelectAssassinUnselected = @"data\global\ui\FrontEnd\assassin\ASNU1.DC6";
public const string CharacterSelectAssassinUnselectedH = @"data\global\ui\FrontEnd\assassin\ASNU2.DC6";
public const string CharacterSelectAssassinSelected = @"data\global\ui\FrontEnd\assassin\ASNU3.DC6";
public const string CharacterSelectAssassinForwardWalk = @"data\global\ui\FrontEnd\assassin\ASFW.DC6";
public const string CharacterSelectAssassinBackWalk = @"data\global\ui\FrontEnd\assassin\ASBW.DC6";
public const string CharacterSelectDruidUnselected = @"data\global\ui\FrontEnd\druid\DZNU1.dc6";
public const string CharacterSelectDruidUnselectedH = @"data\global\ui\FrontEnd\druid\DZNU2.dc6";
public const string CharacterSelectDruidSelected = @"data\global\ui\FrontEnd\druid\DZNU3.DC6";
public const string CharacterSelectDruidForwardWalk = @"data\global\ui\FrontEnd\druid\DZFW.DC6";
public const string CharacterSelectDruidBackWalk = @"data\global\ui\FrontEnd\druid\DZBW.DC6";
// -- Character Selection
public const string CharacterSelectionBackground = @"data\global\ui\CharSelect\characterselectscreenEXP.dc6";
// --- Game ---
public const string GamePanels = @"data\global\ui\PANEL\800ctrlpnl7.dc6";
public const string GameGlobeOverlap = @"data\global\ui\PANEL\overlap.DC6";
public const string HealthMana = @"data\global\ui\PANEL\hlthmana.DC6";
public const string GameSmallMenuButton = @"data\global\ui\PANEL\menubutton.DC6"; // TODO: Used for inventory popout
public const string SkillIcon = @"data\global\ui\PANEL\Skillicon.DC6"; // TODO: Used for skill icon button
public const string AddSkillButton = @"data\global\ui\PANEL\level.DC6";
// --- Mouse Pointers ---
public const string CursorDefault = @"data\global\ui\CURSOR\ohand.DC6";
// --- Fonts ---
public const string Font6 = @"data\local\font\latin\font6";
public const string Font8 = @"data\local\font\latin\font8";
public const string Font16 = @"data\local\font\latin\font16";
public const string Font24 = @"data\local\font\latin\font24";
public const string Font30 = @"data\local\font\latin\font30";
public const string FontFormal12 = @"data\local\font\latin\fontformal12";
public const string FontFormal11 = @"data\local\font\latin\fontformal11";
public const string FontFormal10 = @"data\local\font\latin\fontformal10";
public const string FontExocet10 = @"data\local\font\latin\fontexocet10";
public const string FontExocet8 = @"data\local\font\latin\fontexocet8";
// --- UI ---
public const string WideButtonBlank = @"data\global\ui\FrontEnd\WideButtonBlank.dc6";
public const string MediumButtonBlank = @"data\global\ui\FrontEnd\MediumButtonBlank.dc6";
public const string CancelButton = @"data\global\ui\FrontEnd\CancelButtonBlank.dc6";
public const string NarrowButtonBlank = @"data\global\ui\FrontEnd\NarrowButtonBlank.dc6";
public const string ShortButtonBlank = @"data\global\ui\CharSelect\ShortButtonBlank.dc6";
public const string TextBox2 = @"data\global\ui\FrontEnd\textbox2.dc6";
public const string TallButtonBlank = @"data\global\ui\CharSelect\TallButtonBlank.dc6";
// --- GAME UI ---
public const string MinipanelSmall = @"data\global\ui\PANEL\minipanel_s.dc6";
public const string MinipanelButton = @"data\global\ui\PANEL\minipanelbtn.DC6";
public const string Frame = @"data\global\ui\PANEL\800borderframe.dc6";
public const string InventoryCharacterPanel = @"data\global\ui\PANEL\invchar6.DC6";
public const string InventoryWeaponsTab = @"data\global\ui\PANEL\invchar6Tab.DC6";
public const string SkillsPanelAmazon = @"data\global\ui\SPELLS\skltree_a_back.DC6";
public const string SkillsPanelBarbarian = @"data\global\ui\SPELLS\skltree_b_back.DC6";
public const string SkillsPanelDruid = @"data\global\ui\SPELLS\skltree_d_back.DC6";
public const string SkillsPanelAssassin = @"data\global\ui\SPELLS\skltree_i_back.DC6";
public const string SkillsPanelNecromancer = @"data\global\ui\SPELLS\skltree_n_back.DC6";
public const string SkillsPanelPaladin = @"data\global\ui\SPELLS\skltree_p_back.DC6";
public const string SkillsPanelSorcerer = @"data\global\ui\SPELLS\skltree_s_back.DC6";
public const string GenericSkills = @"data\global\ui\SPELLS\Skillicon.DC6";
public const string AmazonSkills = @"data\global\ui\SPELLS\AmSkillicon.DC6";
public const string BarbarianSkills = @"data\global\ui\SPELLS\BaSkillicon.DC6";
public const string DruidSkills = @"data\global\ui\SPELLS\DrSkillicon.DC6";
public const string AssassinSkills = @"data\global\ui\SPELLS\AsSkillicon.DC6";
public const string NecromancerSkills = @"data\global\ui\SPELLS\NeSkillicon.DC6";
public const string PaladinSkills = @"data\global\ui\SPELLS\PaSkillicon.DC6";
public const string SorcererSkills = @"data\global\ui\SPELLS\SoSkillicon.DC6";
public const string RunButton = @"data\global\ui\PANEL\runbutton.dc6";
public const string MenuButton = @"data\global\ui\PANEL\menubutton.DC6";
public const string GoldCoinButton = @"data\global\ui\panel\goldcoinbtn.dc6";
public const string SquareButton = @"data\global\ui\panel\buysellbtn.dc6";
public const string ArmorPlaceholder = @"data\global\ui\PANEL\inv_armor.DC6";
public const string BeltPlaceholder = @"data\global\ui\PANEL\inv_belt.DC6";
public const string BootsPlaceholder = @"data\global\ui\PANEL\inv_boots.DC6";
public const string HelmGlovePlaceholder = @"data\global\ui\PANEL\inv_helm_glove.DC6";
public const string RingAmuletPlaceholder = @"data\global\ui\PANEL\inv_ring_amulet.DC6";
public const string WeaponsPlaceholder = @"data\global\ui\PANEL\inv_weapons.DC6";
// --- Data ---
public const string EnglishTable = @"data\local\lng\eng\English.txt";
public const string ExpansionStringTable = @"data\local\lng\eng\expansionstring.tbl";
public const string LevelPreset = @"data\global\excel\LvlPrest.txt";
public const string LevelType = @"data\global\excel\LvlTypes.txt";
public const string LevelDetails = @"data\global\excel\Levels.txt";
public const string ObjectDetails = @"data\global\excel\Objects.txt";
// --- Animations ---
public const string ObjectData = @"data\global\objects";
public const string AnimationData = @"data\global\animdata.d2";
public const string PlayerAnimationBase = @"data\global\CHARS";
// --- Inventory Data ---
public const string Weapons = @"data\global\excel\weapons.txt";
public const string Armor = @"data\global\excel\armor.txt";
public const string Misc = @"data\global\excel\misc.txt";
// --- Character Data ---
public const string Experience = @"data\global\excel\experience.txt";
public const string CharStats = @"data\global\excel\charstats.txt";
// --- Music ---
public const string BGMTitle = @"data\global\music\introedit.wav";
public const string BGMOptions = @"data\global\music\Common\options.wav";
public const string BGMAct1AndarielAction = @"data\global\music\Act1\andarielaction.wav";
public const string BGMAct1BloodRavenResolution = @"data\global\music\Act1\bloodravenresolution.wav";
public const string BGMAct1Caves = @"data\global\music\Act1\caves.wav";
public const string BGMAct1Crypt = @"data\global\music\Act1\crypt.wav";
public const string BGMAct1DenOfEvilAction = @"data\global\music\Act1\denofevilaction.wav";
public const string BGMAct1Monastery = @"data\global\music\Act1\monastery.wav";
public const string BGMAct1Town1 = @"data\global\music\Act1\town1.wav";
public const string BGMAct1Tristram = @"data\global\music\Act1\tristram.wav";
public const string BGMAct1Wild = @"data\global\music\Act1\wild.wav";
public const string BGMAct2Desert = @"data\global\music\Act2\desert.wav";
public const string BGMAct2Harem = @"data\global\music\Act2\harem.wav";
public const string BGMAct2HoradricAction = @"data\global\music\Act2\horadricaction.wav";
public const string BGMAct2Lair = @"data\global\music\Act2\lair.wav";
public const string BGMAct2RadamentResolution = @"data\global\music\Act2\radamentresolution.wav";
public const string BGMAct2Sanctuary = @"data\global\music\Act2\sanctuary.wav";
public const string BGMAct2Sewer = @"data\global\music\Act2\sewer.wav";
public const string BGMAct2TaintedSunAction = @"data\global\music\Act2\taintedsunaction.wav";
public const string BGMAct2Tombs = @"data\global\music\Act2\tombs.wav";
public const string BGMAct2Town2 = @"data\global\music\Act2\town2.wav";
public const string BGMAct2Valley = @"data\global\music\Act2\valley.wav";
public const string BGMAct3Jungle = @"data\global\music\Act3\jungle.wav";
public const string BGMAct3Kurast = @"data\global\music\Act3\kurast.wav";
public const string BGMAct3KurastSewer = @"data\global\music\Act3\kurastsewer.wav";
public const string BGMAct3MefDeathAction = @"data\global\music\Act3\mefdeathaction.wav";
public const string BGMAct3OrbAction = @"data\global\music\Act3\orbaction.wav";
public const string BGMAct3Spider = @"data\global\music\Act3\spider.wav";
public const string BGMAct3Town3 = @"data\global\music\Act3\town3.wav";
public const string BGMAct4Diablo = @"data\global\music\Act4\diablo.wav";
public const string BGMAct4DiabloAction = @"data\global\music\Act4\diabloaction.wav";
public const string BGMAct4ForgeAction = @"data\global\music\Act4\forgeaction.wav";
public const string BGMAct4IzualAction = @"data\global\music\Act4\izualaction.wav";
public const string BGMAct4Mesa = @"data\global\music\Act4\mesa.wav";
public const string BGMAct4Town4 = @"data\global\music\Act4\town4.wav";
public const string BGMAct5Baal = @"data\global\music\Act5\baal.wav";
public const string BGMAct5XTown = @"data\global\music\Act5\xtown.wav";
// --- Sound Effects ---
public const string SFXButtonClick = @"data\global\sfx\Cursor\button.wav";
public const string SFXAmazonDeselect = @"data\global\sfx\Cursor\intro\amazon deselect.wav";
public const string SFXAmazonSelect = @"data\global\sfx\Cursor\intro\amazon select.wav";
public const string SFXAssassinDeselect = @"data\global\sfx\Cursor\intro\assassin deselect.wav";
public const string SFXAssassinSelect = @"data\global\sfx\Cursor\intro\assassin select.wav";
public const string SFXBarbarianDeselect = @"data\global\sfx\Cursor\intro\barbarian deselect.wav";
public const string SFXBarbarianSelect = @"data\global\sfx\Cursor\intro\barbarian select.wav";
public const string SFXDruidDeselect = @"data\global\sfx\Cursor\intro\druid deselect.wav";
public const string SFXDruidSelect = @"data\global\sfx\Cursor\intro\druid select.wav";
public const string SFXNecromancerDeselect = @"data\global\sfx\Cursor\intro\necromancer deselect.wav";
public const string SFXNecromancerSelect = @"data\global\sfx\Cursor\intro\necromancer select.wav";
public const string SFXPaladinDeselect = @"data\global\sfx\Cursor\intro\paladin deselect.wav";
public const string SFXPaladinSelect = @"data\global\sfx\Cursor\intro\paladin select.wav";
public const string SFXSorceressDeselect = @"data\global\sfx\Cursor\intro\sorceress deselect.wav";
public const string SFXSorceressSelect = @"data\global\sfx\Cursor\intro\sorceress select.wav";
// --- Enemy Data ---
public static string MonStats = "data\\global\\excel\\monstats.txt";
public static string GeneratePathForItem(string spriteName)
{
return $@"data\global\items\{spriteName}.dc6";
}
public static string GetMusicPathForLevel(eLevelId levelId)
{
switch (levelId)
{
case eLevelId.None:
return string.Empty;
case eLevelId.Act1_Town1:
return BGMAct1Town1;
case eLevelId.Act1_CaveTreasure2:
return BGMAct1Caves;
case eLevelId.Act1_CaveTreasure3:
return BGMAct1Caves;
case eLevelId.Act1_CaveTreasure4:
return BGMAct1Caves;
case eLevelId.Act1_CaveTreasure5:
return BGMAct1Caves;
case eLevelId.Act1_CryptCountessX:
return BGMAct1BloodRavenResolution; // TODO: Verify
case eLevelId.Act1_Tower2:
return BGMAct1Caves; // TODO: Verify
case eLevelId.Act1_MonFront:
return BGMAct1DenOfEvilAction; // TODO: Verify
case eLevelId.Act1_Courtyard1:
return BGMAct1Monastery; // TODO: Verify
case eLevelId.Act1_Courtyard2:
return BGMAct1Monastery; // TODO: Verify
case eLevelId.Act1_Cathedral:
return BGMAct1Monastery; // TODO: Verify
case eLevelId.Act1_Andariel:
return BGMAct1AndarielAction;
case eLevelId.Act1_Tristram:
return BGMAct1Tristram;
case eLevelId.Act2_Town:
return BGMAct2Town2;
case eLevelId.Act2_Harem:
return BGMAct2Harem;
case eLevelId.Act2_DurielsLair:
return BGMAct2Lair;
case eLevelId.Act3_Town:
return BGMAct3Town3;
case eLevelId.Act3_DungeonTreasure1:
return BGMAct3Kurast; // TODO: Verify
case eLevelId.Act3_DungeonTreasure2:
return BGMAct3Kurast; // TODO: Verify
case eLevelId.Act3_SewerTreasureX:
return BGMAct3KurastSewer; // TODO: Verify
case eLevelId.Act3_Temple1:
return BGMAct3Kurast; // TODO: Verify
case eLevelId.Act3_Temple2:
return BGMAct3Kurast; // TODO: Verify
case eLevelId.Act3_Temple3:
return BGMAct3Kurast; // TODO: Verify
case eLevelId.Act3_Temple4:
return BGMAct3Kurast; // TODO: Verify
case eLevelId.Act3_Temple5:
return BGMAct3Kurast; // TODO: Verify
case eLevelId.Act3_Temple6:
return BGMAct3Kurast; // TODO: Verify
case eLevelId.Act3_MephistoComplex:
return BGMAct3MefDeathAction; // TODO: Verify
case eLevelId.Act4_Fortress:
return BGMAct4Mesa; // TODO: Verify
case eLevelId.Act5_Town:
return BGMAct5XTown;
case eLevelId.Act5_TempleFinalRoom:
return BGMAct2Sanctuary; // TODO: Verify
case eLevelId.Act5_ThroneRoom:
return BGMAct2Sanctuary; // TODO: Verify
case eLevelId.Act5_WorldStone:
return BGMAct4ForgeAction; // TODO: Verify
case eLevelId.Act5_TempleEntrance:
return BGMAct5Baal; // TODO: Verify
case eLevelId.Act5_BaalEntrance:
return BGMAct5Baal; // TODO: Verify
default:
return string.Empty;
}
}
public static string GetHeroSkillPanel(eHero hero)
{
switch (hero)
{
case eHero.Amazon:
return SkillsPanelAmazon;
case eHero.Assassin:
return SkillsPanelAssassin;
case eHero.Barbarian:
return SkillsPanelBarbarian;
case eHero.Druid:
return SkillsPanelDruid;
case eHero.Necromancer:
return SkillsPanelNecromancer;
case eHero.Paladin:
return SkillsPanelPaladin;
case eHero.Sorceress:
return SkillsPanelSorcerer;
default:
throw new ArgumentException($"Unknown hero type: {hero}");
}
}
public static string GetHeroSkillsIcons(eHero hero)
{
switch (hero)
{
case eHero.None:
return GenericSkills;
case eHero.Amazon:
return AmazonSkills;
case eHero.Assassin:
return AssassinSkills;
case eHero.Barbarian:
return BarbarianSkills;
case eHero.Druid:
return DruidSkills;
case eHero.Necromancer:
return NecromancerSkills;
case eHero.Paladin:
return PaladinSkills;
case eHero.Sorceress:
return SorcererSkills;
default:
throw new ArgumentException($"Unknown hero type: {hero}");
}
}
}
}
/* OpenDiablo 2 - An open source re-implementation of Diablo 2 in C#
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using OpenDiablo2.Common.Enums;
using System;
namespace OpenDiablo2.Common
{
public static class ResourcePaths
{
// --- Loading Screen ---
public const string LoadingScreen = @"data\global\ui\Loading\loadingscreen.dc6";
// --- Main Menu ---
public const string GameSelectScreen = @"data\global\ui\FrontEnd\gameselectscreenEXP.dc6";
public const string Diablo2LogoFireLeft = @"data\global\ui\FrontEnd\D2logoFireLeft.DC6";
public const string Diablo2LogoFireRight = @"data\global\ui\FrontEnd\D2logoFireRight.DC6";
public const string Diablo2LogoBlackLeft = @"data\global\ui\FrontEnd\D2logoBlackLeft.DC6";
public const string Diablo2LogoBlackRight = @"data\global\ui\FrontEnd\D2logoBlackRight.DC6";
// --- Credits ---
public const string CreditsBackground = @"data\global\ui\CharSelect\creditsbckgexpand.dc6";
public const string CreditsText = @"data\local\ui\eng\ExpansionCredits.txt";
// --- Character Select Screen ---
public const string CharacterSelectBackground = @"data\global\ui\FrontEnd\charactercreationscreenEXP.dc6";
public const string CharacterSelectCampfire = @"data\global\ui\FrontEnd\fire.DC6";
public const string CharacterSelectBarbarianUnselected = @"data\global\ui\FrontEnd\barbarian\banu1.DC6";
public const string CharacterSelectBarbarianUnselectedH = @"data\global\ui\FrontEnd\barbarian\banu2.DC6";
public const string CharacterSelectBarbarianSelected = @"data\global\ui\FrontEnd\barbarian\banu3.DC6";
public const string CharacterSelectBarbarianForwardWalk = @"data\global\ui\FrontEnd\barbarian\bafw.DC6";
public const string CharacterSelectBarbarianForwardWalkOverlay = @"data\global\ui\FrontEnd\barbarian\BAFWs.DC6";
public const string CharacterSelectBarbarianBackWalk = @"data\global\ui\FrontEnd\barbarian\babw.DC6";
public const string CharacterSelecSorceressUnselected = @"data\global\ui\FrontEnd\sorceress\SONU1.DC6";
public const string CharacterSelecSorceressUnselectedH = @"data\global\ui\FrontEnd\sorceress\SONU2.DC6";
public const string CharacterSelecSorceressSelected = @"data\global\ui\FrontEnd\sorceress\SONU3.DC6";
public const string CharacterSelecSorceressSelectedOverlay = @"data\global\ui\FrontEnd\sorceress\SONU3s.DC6";
public const string CharacterSelecSorceressForwardWalk = @"data\global\ui\FrontEnd\sorceress\SOFW.DC6";
public const string CharacterSelecSorceressForwardWalkOverlay = @"data\global\ui\FrontEnd\sorceress\SOFWs.DC6";
public const string CharacterSelecSorceressBackWalk = @"data\global\ui\FrontEnd\sorceress\SOBW.DC6";
public const string CharacterSelecSorceressBackWalkOverlay = @"data\global\ui\FrontEnd\sorceress\SOBWs.DC6";
public const string CharacterSelectNecromancerUnselected = @"data\global\ui\FrontEnd\necromancer\NENU1.DC6";
public const string CharacterSelectNecromancerUnselectedH = @"data\global\ui\FrontEnd\necromancer\NENU2.DC6";
public const string CharacterSelecNecromancerSelected = @"data\global\ui\FrontEnd\necromancer\NENU3.DC6";
public const string CharacterSelecNecromancerSelectedOverlay = @"data\global\ui\FrontEnd\necromancer\NENU3s.DC6";
public const string CharacterSelecNecromancerForwardWalk = @"data\global\ui\FrontEnd\necromancer\NEFW.DC6";
public const string CharacterSelecNecromancerForwardWalkOverlay = @"data\global\ui\FrontEnd\necromancer\NEFWs.DC6";
public const string CharacterSelecNecromancerBackWalk = @"data\global\ui\FrontEnd\necromancer\NEBW.DC6";
public const string CharacterSelecNecromancerBackWalkOverlay = @"data\global\ui\FrontEnd\necromancer\NEBWs.DC6";
public const string CharacterSelectPaladinUnselected = @"data\global\ui\FrontEnd\paladin\PANU1.DC6";
public const string CharacterSelectPaladinUnselectedH = @"data\global\ui\FrontEnd\paladin\PANU2.DC6";
public const string CharacterSelecPaladinSelected = @"data\global\ui\FrontEnd\paladin\PANU3.DC6";
public const string CharacterSelecPaladinForwardWalk = @"data\global\ui\FrontEnd\paladin\PAFW.DC6";
public const string CharacterSelecPaladinForwardWalkOverlay = @"data\global\ui\FrontEnd\paladin\PAFWs.DC6";
public const string CharacterSelecPaladinBackWalk = @"data\global\ui\FrontEnd\paladin\PABW.DC6";
public const string CharacterSelectAmazonUnselected = @"data\global\ui\FrontEnd\amazon\AMNU1.DC6";
public const string CharacterSelectAmazonUnselectedH = @"data\global\ui\FrontEnd\amazon\AMNU2.DC6";
public const string CharacterSelecAmazonSelected = @"data\global\ui\FrontEnd\amazon\AMNU3.DC6";
public const string CharacterSelecAmazonForwardWalk = @"data\global\ui\FrontEnd\amazon\AMFW.DC6";
public const string CharacterSelecAmazonForwardWalkOverlay = @"data\global\ui\FrontEnd\amazon\AMFWs.DC6";
public const string CharacterSelecAmazonBackWalk = @"data\global\ui\FrontEnd\amazon\AMBW.DC6";
public const string CharacterSelectAssassinUnselected = @"data\global\ui\FrontEnd\assassin\ASNU1.DC6";
public const string CharacterSelectAssassinUnselectedH = @"data\global\ui\FrontEnd\assassin\ASNU2.DC6";
public const string CharacterSelectAssassinSelected = @"data\global\ui\FrontEnd\assassin\ASNU3.DC6";
public const string CharacterSelectAssassinForwardWalk = @"data\global\ui\FrontEnd\assassin\ASFW.DC6";
public const string CharacterSelectAssassinBackWalk = @"data\global\ui\FrontEnd\assassin\ASBW.DC6";
public const string CharacterSelectDruidUnselected = @"data\global\ui\FrontEnd\druid\DZNU1.dc6";
public const string CharacterSelectDruidUnselectedH = @"data\global\ui\FrontEnd\druid\DZNU2.dc6";
public const string CharacterSelectDruidSelected = @"data\global\ui\FrontEnd\druid\DZNU3.DC6";
public const string CharacterSelectDruidForwardWalk = @"data\global\ui\FrontEnd\druid\DZFW.DC6";
public const string CharacterSelectDruidBackWalk = @"data\global\ui\FrontEnd\druid\DZBW.DC6";
// -- Character Selection
public const string CharacterSelectionBackground = @"data\global\ui\CharSelect\characterselectscreenEXP.dc6";
// --- Game ---
public const string GamePanels = @"data\global\ui\PANEL\800ctrlpnl7.dc6";
public const string GameGlobeOverlap = @"data\global\ui\PANEL\overlap.DC6";
public const string HealthMana = @"data\global\ui\PANEL\hlthmana.DC6";
public const string GameSmallMenuButton = @"data\global\ui\PANEL\menubutton.DC6"; // TODO: Used for inventory popout
public const string SkillIcon = @"data\global\ui\PANEL\Skillicon.DC6"; // TODO: Used for skill icon button
public const string AddSkillButton = @"data\global\ui\PANEL\level.DC6";
// --- Mouse Pointers ---
public const string CursorDefault = @"data\global\ui\CURSOR\ohand.DC6";
// --- Fonts ---
public const string Font6 = @"data\local\font\latin\font6";
public const string Font8 = @"data\local\font\latin\font8";
public const string Font16 = @"data\local\font\latin\font16";
public const string Font24 = @"data\local\font\latin\font24";
public const string Font30 = @"data\local\font\latin\font30";
public const string FontFormal12 = @"data\local\font\latin\fontformal12";
public const string FontFormal11 = @"data\local\font\latin\fontformal11";
public const string FontFormal10 = @"data\local\font\latin\fontformal10";
public const string FontExocet10 = @"data\local\font\latin\fontexocet10";
public const string FontExocet8 = @"data\local\font\latin\fontexocet8";
// --- UI ---
public const string WideButtonBlank = @"data\global\ui\FrontEnd\WideButtonBlank.dc6";
public const string MediumButtonBlank = @"data\global\ui\FrontEnd\MediumButtonBlank.dc6";
public const string CancelButton = @"data\global\ui\FrontEnd\CancelButtonBlank.dc6";
public const string NarrowButtonBlank = @"data\global\ui\FrontEnd\NarrowButtonBlank.dc6";
public const string ShortButtonBlank = @"data\global\ui\CharSelect\ShortButtonBlank.dc6";
public const string TextBox2 = @"data\global\ui\FrontEnd\textbox2.dc6";
public const string TallButtonBlank = @"data\global\ui\CharSelect\TallButtonBlank.dc6";
// --- GAME UI ---
public const string MinipanelSmall = @"data\global\ui\PANEL\minipanel_s.dc6";
public const string MinipanelButton = @"data\global\ui\PANEL\minipanelbtn.DC6";
public const string Frame = @"data\global\ui\PANEL\800borderframe.dc6";
public const string InventoryCharacterPanel = @"data\global\ui\PANEL\invchar6.DC6";
public const string InventoryWeaponsTab = @"data\global\ui\PANEL\invchar6Tab.DC6";
public const string SkillsPanelAmazon = @"data\global\ui\SPELLS\skltree_a_back.DC6";
public const string SkillsPanelBarbarian = @"data\global\ui\SPELLS\skltree_b_back.DC6";
public const string SkillsPanelDruid = @"data\global\ui\SPELLS\skltree_d_back.DC6";
public const string SkillsPanelAssassin = @"data\global\ui\SPELLS\skltree_i_back.DC6";
public const string SkillsPanelNecromancer = @"data\global\ui\SPELLS\skltree_n_back.DC6";
public const string SkillsPanelPaladin = @"data\global\ui\SPELLS\skltree_p_back.DC6";
public const string SkillsPanelSorcerer = @"data\global\ui\SPELLS\skltree_s_back.DC6";
public const string GenericSkills = @"data\global\ui\SPELLS\Skillicon.DC6";
public const string AmazonSkills = @"data\global\ui\SPELLS\AmSkillicon.DC6";
public const string BarbarianSkills = @"data\global\ui\SPELLS\BaSkillicon.DC6";
public const string DruidSkills = @"data\global\ui\SPELLS\DrSkillicon.DC6";
public const string AssassinSkills = @"data\global\ui\SPELLS\AsSkillicon.DC6";
public const string NecromancerSkills = @"data\global\ui\SPELLS\NeSkillicon.DC6";
public const string PaladinSkills = @"data\global\ui\SPELLS\PaSkillicon.DC6";
public const string SorcererSkills = @"data\global\ui\SPELLS\SoSkillicon.DC6";
public const string RunButton = @"data\global\ui\PANEL\runbutton.dc6";
public const string MenuButton = @"data\global\ui\PANEL\menubutton.DC6";
public const string GoldCoinButton = @"data\global\ui\panel\goldcoinbtn.dc6";
public const string SquareButton = @"data\global\ui\panel\buysellbtn.dc6";
public const string ArmorPlaceholder = @"data\global\ui\PANEL\inv_armor.DC6";
public const string BeltPlaceholder = @"data\global\ui\PANEL\inv_belt.DC6";
public const string BootsPlaceholder = @"data\global\ui\PANEL\inv_boots.DC6";
public const string HelmGlovePlaceholder = @"data\global\ui\PANEL\inv_helm_glove.DC6";
public const string RingAmuletPlaceholder = @"data\global\ui\PANEL\inv_ring_amulet.DC6";
public const string WeaponsPlaceholder = @"data\global\ui\PANEL\inv_weapons.DC6";
// --- Data ---
public const string EnglishTable = @"data\local\lng\eng\English.txt";
public const string ExpansionStringTable = @"data\local\lng\eng\expansionstring.tbl";
public const string LevelPreset = @"data\global\excel\LvlPrest.txt";
public const string LevelType = @"data\global\excel\LvlTypes.txt";
public const string LevelDetails = @"data\global\excel\Levels.txt";
public const string ObjectDetails = @"data\global\excel\Objects.txt";
// --- Animations ---
public const string ObjectData = @"data\global\objects";
public const string AnimationData = @"data\global\animdata.d2";
public const string PlayerAnimationBase = @"data\global\CHARS";
// --- Inventory Data ---
public const string Weapons = @"data\global\excel\weapons.txt";
public const string Armor = @"data\global\excel\armor.txt";
public const string Misc = @"data\global\excel\misc.txt";
// --- Character Data ---
public const string Experience = @"data\global\excel\experience.txt";
public const string CharStats = @"data\global\excel\charstats.txt";
// --- Music ---
public const string BGMTitle = @"data\global\music\introedit.wav";
public const string BGMOptions = @"data\global\music\Common\options.wav";
public const string BGMAct1AndarielAction = @"data\global\music\Act1\andarielaction.wav";
public const string BGMAct1BloodRavenResolution = @"data\global\music\Act1\bloodravenresolution.wav";
public const string BGMAct1Caves = @"data\global\music\Act1\caves.wav";
public const string BGMAct1Crypt = @"data\global\music\Act1\crypt.wav";
public const string BGMAct1DenOfEvilAction = @"data\global\music\Act1\denofevilaction.wav";
public const string BGMAct1Monastery = @"data\global\music\Act1\monastery.wav";
public const string BGMAct1Town1 = @"data\global\music\Act1\town1.wav";
public const string BGMAct1Tristram = @"data\global\music\Act1\tristram.wav";
public const string BGMAct1Wild = @"data\global\music\Act1\wild.wav";
public const string BGMAct2Desert = @"data\global\music\Act2\desert.wav";
public const string BGMAct2Harem = @"data\global\music\Act2\harem.wav";
public const string BGMAct2HoradricAction = @"data\global\music\Act2\horadricaction.wav";
public const string BGMAct2Lair = @"data\global\music\Act2\lair.wav";
public const string BGMAct2RadamentResolution = @"data\global\music\Act2\radamentresolution.wav";
public const string BGMAct2Sanctuary = @"data\global\music\Act2\sanctuary.wav";
public const string BGMAct2Sewer = @"data\global\music\Act2\sewer.wav";
public const string BGMAct2TaintedSunAction = @"data\global\music\Act2\taintedsunaction.wav";
public const string BGMAct2Tombs = @"data\global\music\Act2\tombs.wav";
public const string BGMAct2Town2 = @"data\global\music\Act2\town2.wav";
public const string BGMAct2Valley = @"data\global\music\Act2\valley.wav";
public const string BGMAct3Jungle = @"data\global\music\Act3\jungle.wav";
public const string BGMAct3Kurast = @"data\global\music\Act3\kurast.wav";
public const string BGMAct3KurastSewer = @"data\global\music\Act3\kurastsewer.wav";
public const string BGMAct3MefDeathAction = @"data\global\music\Act3\mefdeathaction.wav";
public const string BGMAct3OrbAction = @"data\global\music\Act3\orbaction.wav";
public const string BGMAct3Spider = @"data\global\music\Act3\spider.wav";
public const string BGMAct3Town3 = @"data\global\music\Act3\town3.wav";
public const string BGMAct4Diablo = @"data\global\music\Act4\diablo.wav";
public const string BGMAct4DiabloAction = @"data\global\music\Act4\diabloaction.wav";
public const string BGMAct4ForgeAction = @"data\global\music\Act4\forgeaction.wav";
public const string BGMAct4IzualAction = @"data\global\music\Act4\izualaction.wav";
public const string BGMAct4Mesa = @"data\global\music\Act4\mesa.wav";
public const string BGMAct4Town4 = @"data\global\music\Act4\town4.wav";
public const string BGMAct5Baal = @"data\global\music\Act5\baal.wav";
public const string BGMAct5XTown = @"data\global\music\Act5\xtown.wav";
// --- Sound Effects ---
public const string SFXButtonClick = @"data\global\sfx\Cursor\button.wav";
public const string SFXAmazonDeselect = @"data\global\sfx\Cursor\intro\amazon deselect.wav";
public const string SFXAmazonSelect = @"data\global\sfx\Cursor\intro\amazon select.wav";
public const string SFXAssassinDeselect = @"data\global\sfx\Cursor\intro\assassin deselect.wav";
public const string SFXAssassinSelect = @"data\global\sfx\Cursor\intro\assassin select.wav";
public const string SFXBarbarianDeselect = @"data\global\sfx\Cursor\intro\barbarian deselect.wav";
public const string SFXBarbarianSelect = @"data\global\sfx\Cursor\intro\barbarian select.wav";
public const string SFXDruidDeselect = @"data\global\sfx\Cursor\intro\druid deselect.wav";
public const string SFXDruidSelect = @"data\global\sfx\Cursor\intro\druid select.wav";
public const string SFXNecromancerDeselect = @"data\global\sfx\Cursor\intro\necromancer deselect.wav";
public const string SFXNecromancerSelect = @"data\global\sfx\Cursor\intro\necromancer select.wav";
public const string SFXPaladinDeselect = @"data\global\sfx\Cursor\intro\paladin deselect.wav";
public const string SFXPaladinSelect = @"data\global\sfx\Cursor\intro\paladin select.wav";
public const string SFXSorceressDeselect = @"data\global\sfx\Cursor\intro\sorceress deselect.wav";
public const string SFXSorceressSelect = @"data\global\sfx\Cursor\intro\sorceress select.wav";
// --- 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";
}
public static string GetMusicPathForLevel(eLevelId levelId)
{
switch (levelId)
{
case eLevelId.None:
return string.Empty;
case eLevelId.Act1_Town1:
return BGMAct1Town1;
case eLevelId.Act1_CaveTreasure2:
return BGMAct1Caves;
case eLevelId.Act1_CaveTreasure3:
return BGMAct1Caves;
case eLevelId.Act1_CaveTreasure4:
return BGMAct1Caves;
case eLevelId.Act1_CaveTreasure5:
return BGMAct1Caves;
case eLevelId.Act1_CryptCountessX:
return BGMAct1BloodRavenResolution; // TODO: Verify
case eLevelId.Act1_Tower2:
return BGMAct1Caves; // TODO: Verify
case eLevelId.Act1_MonFront:
return BGMAct1DenOfEvilAction; // TODO: Verify
case eLevelId.Act1_Courtyard1:
return BGMAct1Monastery; // TODO: Verify
case eLevelId.Act1_Courtyard2:
return BGMAct1Monastery; // TODO: Verify
case eLevelId.Act1_Cathedral:
return BGMAct1Monastery; // TODO: Verify
case eLevelId.Act1_Andariel:
return BGMAct1AndarielAction;
case eLevelId.Act1_Tristram:
return BGMAct1Tristram;
case eLevelId.Act2_Town:
return BGMAct2Town2;
case eLevelId.Act2_Harem:
return BGMAct2Harem;
case eLevelId.Act2_DurielsLair:
return BGMAct2Lair;
case eLevelId.Act3_Town:
return BGMAct3Town3;
case eLevelId.Act3_DungeonTreasure1:
return BGMAct3Kurast; // TODO: Verify
case eLevelId.Act3_DungeonTreasure2:
return BGMAct3Kurast; // TODO: Verify
case eLevelId.Act3_SewerTreasureX:
return BGMAct3KurastSewer; // TODO: Verify
case eLevelId.Act3_Temple1:
return BGMAct3Kurast; // TODO: Verify
case eLevelId.Act3_Temple2:
return BGMAct3Kurast; // TODO: Verify
case eLevelId.Act3_Temple3:
return BGMAct3Kurast; // TODO: Verify
case eLevelId.Act3_Temple4:
return BGMAct3Kurast; // TODO: Verify
case eLevelId.Act3_Temple5:
return BGMAct3Kurast; // TODO: Verify
case eLevelId.Act3_Temple6:
return BGMAct3Kurast; // TODO: Verify
case eLevelId.Act3_MephistoComplex:
return BGMAct3MefDeathAction; // TODO: Verify
case eLevelId.Act4_Fortress:
return BGMAct4Mesa; // TODO: Verify
case eLevelId.Act5_Town:
return BGMAct5XTown;
case eLevelId.Act5_TempleFinalRoom:
return BGMAct2Sanctuary; // TODO: Verify
case eLevelId.Act5_ThroneRoom:
return BGMAct2Sanctuary; // TODO: Verify
case eLevelId.Act5_WorldStone:
return BGMAct4ForgeAction; // TODO: Verify
case eLevelId.Act5_TempleEntrance:
return BGMAct5Baal; // TODO: Verify
case eLevelId.Act5_BaalEntrance:
return BGMAct5Baal; // TODO: Verify
default:
return string.Empty;
}
}
public static string GetHeroSkillPanel(eHero hero)
{
switch (hero)
{
case eHero.Amazon:
return SkillsPanelAmazon;
case eHero.Assassin:
return SkillsPanelAssassin;
case eHero.Barbarian:
return SkillsPanelBarbarian;
case eHero.Druid:
return SkillsPanelDruid;
case eHero.Necromancer:
return SkillsPanelNecromancer;
case eHero.Paladin:
return SkillsPanelPaladin;
case eHero.Sorceress:
return SkillsPanelSorcerer;
default:
throw new ArgumentException($"Unknown hero type: {hero}");
}
}
public static string GetHeroSkillsIcons(eHero hero)
{
switch (hero)
{
case eHero.None:
return GenericSkills;
case eHero.Amazon:
return AmazonSkills;
case eHero.Assassin:
return AssassinSkills;
case eHero.Barbarian:
return BarbarianSkills;
case eHero.Druid:
return DruidSkills;
case eHero.Necromancer:
return NecromancerSkills;
case eHero.Paladin:
return PaladinSkills;
case eHero.Sorceress:
return SorcererSkills;
default:
throw new ArgumentException($"Unknown hero type: {hero}");
}
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);

View File

@ -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)

View File

@ -1,234 +1,234 @@
using OpenDiablo2.Common;
using OpenDiablo2.Common.Interfaces;
using OpenDiablo2.Common.Models;
using System.Drawing;
namespace OpenDiablo2.Core.UI
{
public sealed class Button : IButton
{
private readonly IMouseInfoProvider mouseInfoProvider;
private readonly IRenderWindow renderWindow;
private readonly ISoundProvider musicProvider;
private readonly ButtonLayout buttonLayout;
private readonly byte[] sfxButtonClick;
public OnActivateDelegate OnActivate { get; set; }
public OnToggleDelegate OnToggle { get; set; }
private Point location = new Point();
public Point Location
{
get => location;
set
{
location = value;
sprite.Location = value;
}
}
private readonly int buttonWidth;
private readonly int buttonHeight;
private readonly ISprite sprite;
private readonly IFont font;
private readonly ILabel label;
private bool pressed = false;
private bool active = false; // When true, button is actively being focus pressed
private bool activeLock = false; // When true, we have locked the mouse from everything else
public bool Toggled { get; private set; } = false;
private Point labelOffset = new Point();
private bool enabled = true;
public bool Enabled
{
get => enabled;
set
{
if (value == enabled)
return;
enabled = value;
if(buttonLayout.IsDarkenedWhenDisabled)
sprite.Darken = !enabled;
}
}
private string text;
public string Text
{
get => text;
set
{
text = value;
UpdateText();
}
}
public Button(
ButtonLayout buttonLayout,
IRenderWindow renderWindow,
IMouseInfoProvider mouseInfoProvider,
ISoundProvider soundProvider,
IMPQProvider mpqProvider
)
{
this.buttonLayout = buttonLayout;
this.renderWindow = renderWindow;
this.mouseInfoProvider = mouseInfoProvider;
this.musicProvider = soundProvider;
font = renderWindow.LoadFont(buttonLayout.FontPath, Palettes.Units);
label = renderWindow.CreateLabel(font);
sprite = renderWindow.LoadSprite(buttonLayout.ResourceName, buttonLayout.PaletteName, true);
// TODO: Less stupid way of doing this would be super nice
buttonWidth = 0;
buttonHeight = 0;
for (int i = 0; i < buttonLayout.XSegments; i++)
{
sprite.Frame = i;
buttonWidth += sprite.LocalFrameSize.Width;
}
for(int i = 0; i < buttonLayout.YSegments; i++)
{
sprite.Frame = i * buttonLayout.YSegments;
buttonHeight += sprite.LocalFrameSize.Height;
}
label.MaxWidth = buttonWidth - 8;
label.Alignment = Common.Enums.eTextAlign.Centered;
sfxButtonClick = mpqProvider.GetBytes(ResourcePaths.SFXButtonClick);
}
public bool Toggle()
{
Toggled = !Toggled;
OnToggle?.Invoke(Toggled);
return Toggled;
}
public bool Toggle(bool isToggled)
{
if(Toggled != isToggled)
{
OnToggle?.Invoke(isToggled);
Toggled = isToggled;
}
return isToggled;
}
public void Update()
{
if (!enabled)
{
// Prevent sticky locks
if (activeLock && mouseInfoProvider.ReserveMouse)
{
activeLock = false;
mouseInfoProvider.ReserveMouse = false;
}
active = false;
return;
}
int clickWidth = buttonLayout.ClickableRect.Width > 0 ? buttonLayout.ClickableRect.Width : buttonWidth;
int clickHeight = buttonLayout.ClickableRect.Height > 0 ? buttonLayout.ClickableRect.Height : buttonHeight;
var hovered = mouseInfoProvider.MouseX >= location.X + buttonLayout.ClickableRect.X
&& mouseInfoProvider.MouseY >= location.Y + buttonLayout.ClickableRect.Y
&& mouseInfoProvider.MouseX < location.X + clickWidth + buttonLayout.ClickableRect.X
&& mouseInfoProvider.MouseY < location.Y + clickHeight + buttonLayout.ClickableRect.Y;
if (!activeLock && hovered && mouseInfoProvider.LeftMouseDown && !mouseInfoProvider.ReserveMouse)
{
// The button is being pressed down
mouseInfoProvider.ReserveMouse = true;
active = true;
musicProvider.PlaySfx(sfxButtonClick);
}
else if (active && !mouseInfoProvider.LeftMouseDown)
{
mouseInfoProvider.ReserveMouse = false;
active = false;
if (hovered)
{
OnActivate?.Invoke();
if (buttonLayout.Toggleable)
{
Toggle();
}
}
}
else if (!active && mouseInfoProvider.LeftMouseDown)
{
activeLock = true;
}
else if (activeLock && !mouseInfoProvider.LeftMouseDown)
{
activeLock = false;
}
pressed = hovered && mouseInfoProvider.LeftMouseDown && active;
}
public void Render()
{
var frame = buttonLayout.BaseFrame;
if (buttonLayout.AllowFrameChange)
{
if(!Enabled && buttonLayout.DisabledFrame >= 0)
{
frame = buttonLayout.DisabledFrame;
}
else if (Toggled && pressed)
{
frame = buttonLayout.BaseFrame + 3;
}
else if (pressed)
{
frame = buttonLayout.BaseFrame + 1;
}
else if (Toggled)
{
frame = buttonLayout.BaseFrame + 2;
}
}
renderWindow.Draw(sprite, buttonLayout.XSegments, buttonLayout.YSegments, frame);
var offset = pressed ? -2 : 0;
label.Location = new Point(location.X + offset + labelOffset.X, location.Y - offset + labelOffset.Y);
renderWindow.Draw(label);
}
private void UpdateText()
{
label.Text = text;
label.TextColor = Color.FromArgb(75, 75, 75);
var offsetX = (buttonWidth / 2) - (label.TextArea.Width / 2);
var offsetY = (buttonHeight / 2) - (label.TextArea.Height / 2);
labelOffset = new Point(offsetX, offsetY - 5);
}
public void Dispose()
{
sprite.Dispose();
font.Dispose();
label.Dispose();
}
}
}
using OpenDiablo2.Common;
using OpenDiablo2.Common.Interfaces;
using OpenDiablo2.Common.Models;
using System.Drawing;
namespace OpenDiablo2.Core.UI
{
public sealed class Button : IButton
{
private readonly IMouseInfoProvider mouseInfoProvider;
private readonly IRenderWindow renderWindow;
private readonly ISoundProvider musicProvider;
private readonly ButtonLayout buttonLayout;
private readonly byte[] sfxButtonClick;
public OnActivateDelegate OnActivate { get; set; }
public OnToggleDelegate OnToggle { get; set; }
private Point location = new Point();
public Point Location
{
get => location;
set
{
location = value;
sprite.Location = value;
}
}
private readonly int buttonWidth;
private readonly int buttonHeight;
private readonly ISprite sprite;
private readonly IFont font;
private readonly ILabel label;
private bool pressed = false;
private bool active = false; // When true, button is actively being focus pressed
private bool activeLock = false; // When true, we have locked the mouse from everything else
public bool Toggled { get; private set; } = false;
private Point labelOffset = new Point();
private bool enabled = true;
public bool Enabled
{
get => enabled;
set
{
if (value == enabled)
return;
enabled = value;
if(buttonLayout.IsDarkenedWhenDisabled)
sprite.Darken = !enabled;
}
}
private string text;
public string Text
{
get => text;
set
{
text = value;
UpdateText();
}
}
public Button(
ButtonLayout buttonLayout,
IRenderWindow renderWindow,
IMouseInfoProvider mouseInfoProvider,
ISoundProvider soundProvider,
IMPQProvider mpqProvider
)
{
this.buttonLayout = buttonLayout;
this.renderWindow = renderWindow;
this.mouseInfoProvider = mouseInfoProvider;
this.musicProvider = soundProvider;
font = renderWindow.LoadFont(buttonLayout.FontPath, Palettes.Units);
label = renderWindow.CreateLabel(font);
sprite = renderWindow.LoadSprite(buttonLayout.ResourceName, buttonLayout.PaletteName, true);
// TODO: Less stupid way of doing this would be super nice
buttonWidth = 0;
buttonHeight = 0;
for (int i = 0; i < buttonLayout.XSegments; i++)
{
sprite.Frame = i;
buttonWidth += sprite.LocalFrameSize.Width;
}
for(int i = 0; i < buttonLayout.YSegments; i++)
{
sprite.Frame = i * buttonLayout.YSegments;
buttonHeight += sprite.LocalFrameSize.Height;
}
label.MaxWidth = buttonWidth - 8;
label.Alignment = Common.Enums.eTextAlign.Centered;
sfxButtonClick = mpqProvider.GetBytes(ResourcePaths.SFXButtonClick);
}
public bool Toggle()
{
Toggled = !Toggled;
OnToggle?.Invoke(Toggled);
return Toggled;
}
public bool Toggle(bool isToggled)
{
if(Toggled != isToggled)
{
OnToggle?.Invoke(isToggled);
Toggled = isToggled;
}
return isToggled;
}
public void Update()
{
if (!enabled)
{
// Prevent sticky locks
if (activeLock && mouseInfoProvider.ReserveMouse)
{
activeLock = false;
mouseInfoProvider.ReserveMouse = false;
}
active = false;
return;
}
int clickWidth = buttonLayout.ClickableRect.Width > 0 ? buttonLayout.ClickableRect.Width : buttonWidth;
int clickHeight = buttonLayout.ClickableRect.Height > 0 ? buttonLayout.ClickableRect.Height : buttonHeight;
var hovered = mouseInfoProvider.MouseX >= location.X + buttonLayout.ClickableRect.X
&& mouseInfoProvider.MouseY >= location.Y + buttonLayout.ClickableRect.Y
&& mouseInfoProvider.MouseX < location.X + clickWidth + buttonLayout.ClickableRect.X
&& mouseInfoProvider.MouseY < location.Y + clickHeight + buttonLayout.ClickableRect.Y;
if (!activeLock && hovered && mouseInfoProvider.LeftMouseDown && !mouseInfoProvider.ReserveMouse)
{
// The button is being pressed down
mouseInfoProvider.ReserveMouse = true;
active = true;
musicProvider.PlaySfx(sfxButtonClick);
}
else if (active && !mouseInfoProvider.LeftMouseDown)
{
mouseInfoProvider.ReserveMouse = false;
active = false;
if (hovered)
{
OnActivate?.Invoke();
if (buttonLayout.Toggleable)
{
Toggle();
}
}
}
else if (!active && mouseInfoProvider.LeftMouseDown)
{
activeLock = true;
}
else if (activeLock && !mouseInfoProvider.LeftMouseDown)
{
activeLock = false;
}
pressed = hovered && mouseInfoProvider.LeftMouseDown && active;
}
public void Render()
{
var frame = buttonLayout.BaseFrame;
if (buttonLayout.AllowFrameChange)
{
if(!Enabled && buttonLayout.DisabledFrame >= 0)
{
frame = buttonLayout.DisabledFrame;
}
else if (Toggled && pressed)
{
frame = buttonLayout.BaseFrame + 3;
}
else if (pressed)
{
frame = buttonLayout.BaseFrame + 1;
}
else if (Toggled)
{
frame = buttonLayout.BaseFrame + 2;
}
}
renderWindow.Draw(sprite, buttonLayout.XSegments, buttonLayout.YSegments, frame);
var offset = pressed ? -2 : 0;
label.Location = new Point(location.X + offset + labelOffset.X, location.Y - offset + labelOffset.Y);
renderWindow.Draw(label);
}
private void UpdateText()
{
label.Text = text;
label.TextColor = Color.FromArgb(75, 75, 75);
var offsetX = (buttonWidth / 2) - (label.TextArea.Width / 2);
var offsetY = (buttonHeight / 2) - (label.TextArea.Height / 2);
labelOffset = new Point(offsetX, offsetY - 5);
}
public void Dispose()
{
sprite.Dispose();
font.Dispose();
label.Dispose();
}
}
}

View File

@ -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)
{

View File

@ -1,122 +1,122 @@
/* OpenDiablo 2 - An open source re-implementation of Diablo 2 in C#
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using OpenDiablo2.Common;
using OpenDiablo2.Common.Attributes;
using OpenDiablo2.Common.Enums;
using OpenDiablo2.Common.Interfaces;
using System;
namespace OpenDiablo2.Scenes
{
[Scene(eSceneType.Game)]
public sealed class Game : IScene
{
private readonly IRenderWindow renderWindow;
private readonly IMapEngine mapEngine;
private readonly IMouseInfoProvider mouseInfoProvider;
private readonly IGameState gameState;
private readonly ISessionManager sessionManager;
private readonly IGameHUD gameHUD;
private eMovementType lastMovementType = eMovementType.Stopped;
private byte lastDirection = 255;
private bool clickedOnHud = false;
const double Rad2Deg = 180.0 / Math.PI;
public Game(
IRenderWindow renderWindow,
IMapEngine mapEngine,
IGameState gameState,
IMouseInfoProvider mouseInfoProvider,
IItemManager itemManager,
ISessionManager sessionManager,
ISoundProvider soundProvider,
IMPQProvider mpqProvider,
IGameHUD gameHUD
)
{
this.renderWindow = renderWindow;
this.mapEngine = mapEngine;
this.gameState = gameState;
this.mouseInfoProvider = mouseInfoProvider;
this.sessionManager = sessionManager;
this.gameHUD = gameHUD;
//var item = itemManager.getItem("hdm");
}
public void Render()
{
// TODO: Maybe show some sort of connecting/loading message?
if (mapEngine.FocusedPlayerId == 0)
return;
mapEngine.Render();
gameHUD.Render();
}
public void Update(long ms)
{
HandleMovement();
mapEngine.Update(ms);
gameHUD.Update();
}
private void HandleMovement()
{
if(gameHUD.IsMouseOver() && lastMovementType == eMovementType.Stopped)
clickedOnHud = true;
else if (!mouseInfoProvider.LeftMouseDown)
clickedOnHud = false;
if (clickedOnHud)
return;
var mx = mouseInfoProvider.MouseX - 400 - gameState.CameraOffset;
var my = mouseInfoProvider.MouseY - 300;
var tx = (mx / 60f + my / 40f) / 2f;
var ty = (my / 40f - (mx / 60f)) / 2f;
var cursorDirection = (int)Math.Round(Math.Atan2(ty, tx) * Rad2Deg);
if (cursorDirection < 0)
cursorDirection += 360;
var actualDirection = (byte)(cursorDirection / 22);
if (actualDirection >= 16)
actualDirection -= 16;
if (mouseInfoProvider.LeftMouseDown && (lastMovementType == eMovementType.Stopped || lastDirection != actualDirection))
{
lastDirection = actualDirection;
lastMovementType = gameHUD.IsRunningEnabled ? eMovementType.Running : eMovementType.Walking;
sessionManager.MoveRequest(actualDirection, lastMovementType);
}
else if (!mouseInfoProvider.LeftMouseDown && lastMovementType != eMovementType.Stopped)
{
lastDirection = actualDirection;
lastMovementType = eMovementType.Stopped;
sessionManager.MoveRequest(actualDirection, lastMovementType);
}
}
public void Dispose()
{
}
}
}
/* OpenDiablo 2 - An open source re-implementation of Diablo 2 in C#
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using OpenDiablo2.Common;
using OpenDiablo2.Common.Attributes;
using OpenDiablo2.Common.Enums;
using OpenDiablo2.Common.Interfaces;
using System;
namespace OpenDiablo2.Scenes
{
[Scene(eSceneType.Game)]
public sealed class Game : IScene
{
private readonly IRenderWindow renderWindow;
private readonly IMapEngine mapEngine;
private readonly IMouseInfoProvider mouseInfoProvider;
private readonly IGameState gameState;
private readonly ISessionManager sessionManager;
private readonly IGameHUD gameHUD;
private eMovementType lastMovementType = eMovementType.Stopped;
private byte lastDirection = 255;
private bool clickedOnHud = false;
const double Rad2Deg = 180.0 / Math.PI;
public Game(
IRenderWindow renderWindow,
IMapEngine mapEngine,
IGameState gameState,
IMouseInfoProvider mouseInfoProvider,
IItemManager itemManager,
ISessionManager sessionManager,
ISoundProvider soundProvider,
IMPQProvider mpqProvider,
IGameHUD gameHUD
)
{
this.renderWindow = renderWindow;
this.mapEngine = mapEngine;
this.gameState = gameState;
this.mouseInfoProvider = mouseInfoProvider;
this.sessionManager = sessionManager;
this.gameHUD = gameHUD;
//var item = itemManager.getItem("hdm");
}
public void Render()
{
// TODO: Maybe show some sort of connecting/loading message?
if (mapEngine.FocusedPlayerId == 0)
return;
mapEngine.Render();
gameHUD.Render();
}
public void Update(long ms)
{
HandleMovement();
mapEngine.Update(ms);
gameHUD.Update();
}
private void HandleMovement()
{
if(gameHUD.IsMouseOver() && lastMovementType == eMovementType.Stopped)
clickedOnHud = true;
else if (!mouseInfoProvider.LeftMouseDown)
clickedOnHud = false;
if (clickedOnHud)
return;
var mx = mouseInfoProvider.MouseX - 400 - gameState.CameraOffset;
var my = mouseInfoProvider.MouseY - 300;
var tx = (mx / 60f + my / 40f) / 2f;
var ty = (my / 40f - (mx / 60f)) / 2f;
var cursorDirection = (int)Math.Round(Math.Atan2(ty, tx) * Rad2Deg);
if (cursorDirection < 0)
cursorDirection += 360;
var actualDirection = (byte)(cursorDirection / 22);
if (actualDirection >= 16)
actualDirection -= 16;
if (mouseInfoProvider.LeftMouseDown && (lastMovementType == eMovementType.Stopped || lastDirection != actualDirection))
{
lastDirection = actualDirection;
lastMovementType = gameHUD.IsRunningEnabled ? eMovementType.Running : eMovementType.Walking;
sessionManager.MoveRequest(actualDirection, lastMovementType);
}
else if (!mouseInfoProvider.LeftMouseDown && lastMovementType != eMovementType.Stopped)
{
lastDirection = actualDirection;
lastMovementType = eMovementType.Stopped;
sessionManager.MoveRequest(actualDirection, lastMovementType);
}
}
public void Dispose()
{
}
}
}

View File

@ -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)
{

Binary file not shown.

Before

Width:  |  Height:  |  Size: 521 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 728 KiB