1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-12-25 19:46:50 -05:00

Added hardware mouse support with scailing

This commit is contained in:
Tim Sarbin 2018-11-27 20:02:18 -05:00
parent 3b543131a5
commit 22f75b7e39
14 changed files with 147 additions and 33 deletions

View File

@ -0,0 +1,5 @@
namespace OpenDiablo2.Common.Interfaces
{
/// <summary>A cursor that can be displayed on the screen to indicate the location of the mouse.</summary>
public interface IMouseCursor { }
}

View File

@ -22,6 +22,8 @@ namespace OpenDiablo2.Common.Interfaces
void Draw(ISprite sprite, int frame, Point location);
void Draw(ISprite sprite, int frame);
void Draw(ISprite sprite, int xSegments, int ySegments, int offset);
IMouseCursor LoadCursor(ISprite sprite, int frame, Point hotspot);
void SetCursor(IMouseCursor mouseCursor);
void Draw(ILabel label);
void DrawMapCell(int xCell, int yCell, int xPixel, int yPixel, MPQDS1 mapData, int main_index, int sub_index, Palette palette, int orientation = -1);
}

View File

@ -1,7 +1,16 @@
namespace OpenDiablo2.Common.Models
{
public enum eMouseMode
{
Software,
Hardware
}
public sealed class GlobalConfiguration
{
public string BaseDataPath { get; set; }
public eMouseMode MouseMode { get; set; }
public int HardwareMouseScale { get; set; }
}
}

View File

@ -28,7 +28,11 @@ namespace OpenDiablo2.Common.Models
public UInt32 GetColor(int x, int y, Palette palette)
{
var index = ImageData[x + (y * Width)];
var i = x + (y * Width);
if (i >= ImageData.Length)
return 0;
var index = ImageData[i];
if (index == -1)
return 0;

View File

@ -85,6 +85,7 @@
<Compile Include="Interfaces\ILabel.cs" />
<Compile Include="Interfaces\IMapEngine.cs" />
<Compile Include="Interfaces\IMiniPanel.cs" />
<Compile Include="Interfaces\IMouseCursor.cs" />
<Compile Include="Interfaces\IMPQProvider.cs" />
<Compile Include="Interfaces\IMusicProvider.cs" />
<Compile Include="Interfaces\IPaletteProvider.cs" />

View File

@ -16,6 +16,7 @@ namespace OpenDiablo2.Core
{
static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private readonly GlobalConfiguration globalConfig;
private readonly IMPQProvider mpqProvider;
private readonly Func<IRenderWindow> getRenderWindow;
private readonly Func<IMouseInfoProvider> getMouseInfoProvider;
@ -34,6 +35,7 @@ namespace OpenDiablo2.Core
public GameEngine(
GlobalConfiguration globalConfig,
IMPQProvider mpqProvider,
Func<IRenderWindow> getRenderWindow,
Func<IMouseInfoProvider> getMouseInfoProvider,
@ -41,6 +43,7 @@ namespace OpenDiablo2.Core
Func<IResourceManager> getResourceManager
)
{
this.globalConfig = globalConfig;
this.mpqProvider = mpqProvider;
this.getRenderWindow = getRenderWindow;
this.getMouseInfoProvider = getMouseInfoProvider;
@ -84,14 +87,19 @@ namespace OpenDiablo2.Core
LoadSoundData();
mouseSprite = renderWindow.LoadSprite(ResourcePaths.CursorDefault, Palettes.Units);
IMouseCursor cursor;
if (globalConfig.MouseMode == eMouseMode.Hardware)
{
cursor = renderWindow.LoadCursor(mouseSprite, 0, new Point(0, 0));
renderWindow.SetCursor(cursor);
}
currentScene = getScene("Main Menu");
sw.Start();
while (getRenderWindow().IsRunning)
{
while (sw.ElapsedMilliseconds < 33)
Thread.Sleep((int)Math.Min(1, 33 -sw.ElapsedMilliseconds)); // Lock to 30fps
Thread.Sleep(1);
var ms = sw.ElapsedMilliseconds;
sw.Restart();
@ -114,8 +122,8 @@ namespace OpenDiablo2.Core
renderWindow.Clear();
currentScene.Render();
// Draw the mouse
renderWindow.Draw(mouseSprite, new Point(mouseInfoProvider.MouseX, mouseInfoProvider.MouseY + 3));
if (globalConfig.MouseMode == eMouseMode.Software)
renderWindow.Draw(mouseSprite, new Point(mouseInfoProvider.MouseX, mouseInfoProvider.MouseY + 3));
renderWindow.Sync();
}

View File

@ -78,6 +78,7 @@
<Compile Include="SDL2Extensions.cs" />
<Compile Include="SDL2Font.cs" />
<Compile Include="SDL2Label.cs" />
<Compile Include="SDL2MouseCursor.cs" />
<Compile Include="SDL2MusicPlayer.cs" />
<Compile Include="SDL2RenderWindow.cs" />
<Compile Include="SDL2Sprite.cs" />

View File

@ -0,0 +1,10 @@
using System;
using OpenDiablo2.Common.Interfaces;
namespace OpenDiablo2.SDL2_
{
public sealed class SDL2MouseCursor : IMouseCursor
{
public IntPtr Surface { get; set; }
}
}

View File

@ -32,10 +32,12 @@ namespace OpenDiablo2.SDL2_
private readonly IMPQProvider mpqProvider;
private readonly IPaletteProvider paletteProvider;
private readonly IResourceManager resourceManager;
private readonly GlobalConfiguration globalConfig;
private readonly IGameState gameState;
private readonly Func<IMapEngine> getMapEngine;
public SDL2RenderWindow(
GlobalConfiguration globalConfig,
IMPQProvider mpqProvider,
IPaletteProvider paletteProvider,
IResourceManager resourceManager,
@ -43,6 +45,7 @@ namespace OpenDiablo2.SDL2_
Func<IMapEngine> getMapEngine
)
{
this.globalConfig = globalConfig;
this.mpqProvider = mpqProvider;
this.paletteProvider = paletteProvider;
this.resourceManager = resourceManager;
@ -53,7 +56,7 @@ namespace OpenDiablo2.SDL2_
if (SDL.SDL_SetHint(SDL.SDL_HINT_RENDER_SCALE_QUALITY, "0") == SDL.SDL_bool.SDL_FALSE)
throw new ApplicationException($"Unable to Init hinting: {SDL.SDL_GetError()}");
window = SDL.SDL_CreateWindow("OpenDiablo2", SDL.SDL_WINDOWPOS_UNDEFINED, SDL.SDL_WINDOWPOS_UNDEFINED, 800, 600, SDL.SDL_WindowFlags.SDL_WINDOW_SHOWN);
window = SDL.SDL_CreateWindow("OpenDiablo2", SDL.SDL_WINDOWPOS_UNDEFINED, SDL.SDL_WINDOWPOS_UNDEFINED, 800, 600, SDL.SDL_WindowFlags.SDL_WINDOW_SHOWN | SDL.SDL_WindowFlags.SDL_WINDOW_ALLOW_HIGHDPI);
if (window == IntPtr.Zero)
throw new ApplicationException($"Unable to create SDL Window: {SDL.SDL_GetError()}");
@ -63,7 +66,8 @@ namespace OpenDiablo2.SDL2_
SDL.SDL_SetRenderDrawBlendMode(renderer, SDL.SDL_BlendMode.SDL_BLENDMODE_BLEND);
SDL.SDL_ShowCursor(0);
SDL.SDL_ShowCursor(globalConfig.MouseMode == eMouseMode.Hardware ? 1 : 0);
IsRunning = true;
@ -411,5 +415,34 @@ namespace OpenDiablo2.SDL2_
SDL.SDL_RenderCopy(renderer, texId, ref srcRect, ref dr);
}
public unsafe IMouseCursor LoadCursor(ISprite sprite, int frame, Point hotspot)
{
if (globalConfig.MouseMode != eMouseMode.Hardware)
throw new ApplicationException("Tried to set a hardware cursor, but we are using software cursors!");
var multiple = globalConfig.HardwareMouseScale;
var spr = sprite as SDL2Sprite;
var surface = SDL.SDL_CreateRGBSurface(0, spr.FrameSize.Width * multiple, spr.FrameSize.Height * multiple, 32, 0xFF0000, 0xFF00, 0xFF, 0xFF000000);
var pixels = (UInt32*)((SDL.SDL_Surface*)surface)->pixels;
for (var y = 0; y < (spr.FrameSize.Height * multiple) - 1; y ++)
for (var x = 0; x < (spr.FrameSize.Width * multiple) - 1; x ++)
{
pixels[x + (y * spr.FrameSize.Width * multiple)] = spr.source.Frames[frame].GetColor(x / multiple, y / multiple, sprite.CurrentPalette);
}
var cursor = SDL.SDL_CreateColorCursor(surface, hotspot.X, hotspot.Y);
if (cursor == IntPtr.Zero)
throw new ApplicationException($"Unable to set the cursor cursor: {SDL.SDL_GetError()}"); // TODO: Is this supported everywhere? May need to still support software cursors.
return new SDL2MouseCursor { Surface = cursor };
}
public void SetCursor(IMouseCursor mouseCursor)
{
if (globalConfig.MouseMode != eMouseMode.Hardware)
throw new ApplicationException("Tried to set a hardware cursor, but we are using software cursors!");
SDL.SDL_SetCursor((mouseCursor as SDL2MouseCursor).Surface);
}
}
}

View File

@ -22,7 +22,7 @@ namespace OpenDiablo2.Scenes
private readonly IPaletteProvider paletteProvider;
private readonly IMPQProvider mpqProvider;
private readonly IMouseInfoProvider mouseInfoProvider;
private readonly IMusicProvider musicProvider;
//private readonly IMusicProvider musicProvider;
private readonly ISceneManager sceneManager;
private float logoFrame;
@ -36,7 +36,7 @@ namespace OpenDiablo2.Scenes
IPaletteProvider paletteProvider,
IMPQProvider mpqProvider,
IMouseInfoProvider mouseInfoProvider,
IMusicProvider musicProvider,
//IMusicProvider musicProvider,
ISceneManager sceneManager,
IResourceManager resourceManager,
Func<eButtonType, IButton> createButton,

View File

@ -15,40 +15,69 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenDiablo2.Scenes", "OpenD
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenDiablo2.TestConsole", "OpenDiablo2.TestConsole\OpenDiablo2.TestConsole.csproj", "{40BD2DDE-DC6F-4F6D-9050-9B423C631192}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utilities", "Utilities", "{A1AAF640-AEFB-48E7-8BCE-E01D287B5286}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD}.Debug|x64.ActiveCfg = Debug|x64
{2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD}.Debug|x64.Build.0 = Debug|x64
{2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD}.Release|Any CPU.Build.0 = Release|Any CPU
{2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD}.Release|x64.ActiveCfg = Release|x64
{2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD}.Release|x64.Build.0 = Release|x64
{B743160E-A0BB-45DC-9998-967A85E50562}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B743160E-A0BB-45DC-9998-967A85E50562}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B743160E-A0BB-45DC-9998-967A85E50562}.Debug|x64.ActiveCfg = Debug|x64
{B743160E-A0BB-45DC-9998-967A85E50562}.Debug|x64.Build.0 = Debug|x64
{B743160E-A0BB-45DC-9998-967A85E50562}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B743160E-A0BB-45DC-9998-967A85E50562}.Release|Any CPU.Build.0 = Release|Any CPU
{B743160E-A0BB-45DC-9998-967A85E50562}.Release|x64.ActiveCfg = Release|x64
{B743160E-A0BB-45DC-9998-967A85E50562}.Release|x64.Build.0 = Release|x64
{8FC6BF7D-835A-47C1-A6B2-125495FA0900}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8FC6BF7D-835A-47C1-A6B2-125495FA0900}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8FC6BF7D-835A-47C1-A6B2-125495FA0900}.Debug|x64.ActiveCfg = Debug|x64
{8FC6BF7D-835A-47C1-A6B2-125495FA0900}.Debug|x64.Build.0 = Debug|x64
{8FC6BF7D-835A-47C1-A6B2-125495FA0900}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8FC6BF7D-835A-47C1-A6B2-125495FA0900}.Release|Any CPU.Build.0 = Release|Any CPU
{8FC6BF7D-835A-47C1-A6B2-125495FA0900}.Release|x64.ActiveCfg = Release|x64
{8FC6BF7D-835A-47C1-A6B2-125495FA0900}.Release|x64.Build.0 = Release|x64
{1F8731D5-393B-4561-9CEA-887A2F466576}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1F8731D5-393B-4561-9CEA-887A2F466576}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1F8731D5-393B-4561-9CEA-887A2F466576}.Debug|x64.ActiveCfg = Debug|x64
{1F8731D5-393B-4561-9CEA-887A2F466576}.Debug|x64.Build.0 = Debug|x64
{1F8731D5-393B-4561-9CEA-887A2F466576}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1F8731D5-393B-4561-9CEA-887A2F466576}.Release|Any CPU.Build.0 = Release|Any CPU
{1F8731D5-393B-4561-9CEA-887A2F466576}.Release|x64.ActiveCfg = Release|x64
{1F8731D5-393B-4561-9CEA-887A2F466576}.Release|x64.Build.0 = Release|x64
{05224FE7-293F-4184-B1D6-89F5171B60E0}.Debug|Any CPU.ActiveCfg = Debug|x64
{05224FE7-293F-4184-B1D6-89F5171B60E0}.Debug|x64.ActiveCfg = Debug|x64
{05224FE7-293F-4184-B1D6-89F5171B60E0}.Debug|x64.Build.0 = Debug|x64
{05224FE7-293F-4184-B1D6-89F5171B60E0}.Release|Any CPU.ActiveCfg = Release|x64
{05224FE7-293F-4184-B1D6-89F5171B60E0}.Release|x64.ActiveCfg = Release|x64
{05224FE7-293F-4184-B1D6-89F5171B60E0}.Release|x64.Build.0 = Release|x64
{40BD2DDE-DC6F-4F6D-9050-9B423C631192}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{40BD2DDE-DC6F-4F6D-9050-9B423C631192}.Debug|Any CPU.Build.0 = Debug|Any CPU
{40BD2DDE-DC6F-4F6D-9050-9B423C631192}.Debug|x64.ActiveCfg = Debug|x64
{40BD2DDE-DC6F-4F6D-9050-9B423C631192}.Debug|x64.Build.0 = Debug|x64
{40BD2DDE-DC6F-4F6D-9050-9B423C631192}.Release|Any CPU.ActiveCfg = Release|Any CPU
{40BD2DDE-DC6F-4F6D-9050-9B423C631192}.Release|Any CPU.Build.0 = Release|Any CPU
{40BD2DDE-DC6F-4F6D-9050-9B423C631192}.Release|x64.ActiveCfg = Release|Any CPU
{40BD2DDE-DC6F-4F6D-9050-9B423C631192}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{40BD2DDE-DC6F-4F6D-9050-9B423C631192} = {A1AAF640-AEFB-48E7-8BCE-E01D287B5286}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {782826E1-7E8E-4878-88FB-1B564D82C621}
EndGlobalSection

View File

@ -6,5 +6,12 @@ namespace OpenDiablo2
{
[Option('p', "datapath", Required = false, HelpText = "Specifies the root data path")]
public string DataPath { get; set; }
[Option("hwmouse", Default = false, Required = false, HelpText = "Use the hardware mouse instead of software")]
public bool HardwareMouse { get; set; }
[Option("mousescale", Default = 1, Required = false, HelpText = "When hardware mouse is enabled, this defines the pixel scale of the mouse. No effect for software mode")]
public int MouseScale { get; set; }
}
}

View File

@ -1,17 +1,12 @@
using CommandLine;
using System;
using System.Collections.Generic;
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autofac;
using OpenDiablo2.Common.Models;
using System.Reflection;
using OpenDiablo2.Common.Interfaces;
using System.Diagnostics;
using OpenDiablo2.Core.UI;
using Autofac;
using CommandLine;
using OpenDiablo2.Common.Enums;
using OpenDiablo2.Common.Interfaces;
using OpenDiablo2.Common.Models;
namespace OpenDiablo2
{
@ -23,13 +18,16 @@ namespace OpenDiablo2
{
log.Info("OpenDiablo 2: The Free and Open Source Diablo 2 clone!");
Parser.Default.ParseArguments<CommandLineOptions>(args).WithParsed<CommandLineOptions>(o =>
Parser.Default.ParseArguments<CommandLineOptions>(args).WithParsed(o => globalConfiguration = new GlobalConfiguration
{
globalConfiguration = new GlobalConfiguration
{
BaseDataPath = Path.GetFullPath(o.DataPath ?? Directory.GetCurrentDirectory())
};
});
BaseDataPath = Path.GetFullPath(o.DataPath ?? Directory.GetCurrentDirectory()),
MouseMode = o.HardwareMouse == true ? eMouseMode.Hardware : eMouseMode.Software,
HardwareMouseScale = o.MouseScale
}).WithNotParsed(o =>
{
log.Warn($"Could not parse command line options.");
globalConfiguration = new GlobalConfiguration { BaseDataPath = Directory.GetCurrentDirectory(), MouseMode = eMouseMode.Software };
}); ;
#if !DEBUG
try
@ -55,10 +53,7 @@ namespace OpenDiablo2
static ContainerBuilder RegisterLocalTypes(this ContainerBuilder containerBuilder)
{
containerBuilder.Register<GlobalConfiguration>(x =>
{
return globalConfiguration;
}).AsSelf().SingleInstance();
containerBuilder.Register(x => globalConfiguration).AsSelf().SingleInstance();
containerBuilder.Register<Func<string, IScene>>(c =>
{

View File

@ -10,7 +10,7 @@ An open source re-implementation of Diablo 2 in C#
## About this project
This is an attempt to re-create Diablo 2's game engine in C#, and potentially make it cross platform as well. This project does not ship with the assets or content required to work. You must have a legally purchased copy of [Diablo 2](https://us.shop.battle.net/en-us/product/diablo-ii) and its expansion [Lord of Destruction](https://us.shop.battle.net/en-us/product/diablo-ii-lord-of-destruction) installed on your computer in order to run this engine. If you have an original copy of the disks, those files should work fine as well.
This is an attempt to re-create Diablo 2's game engine in C#, and make it cross platform as well. This project does not ship with the assets or content required to work. You must have a legally purchased copy of [Diablo 2](https://us.shop.battle.net/en-us/product/diablo-ii) and its expansion [Lord of Destruction](https://us.shop.battle.net/en-us/product/diablo-ii-lord-of-destruction) installed on your computer in order to run this engine. If you have an original copy of the disks, those files should work fine as well.
Please note that **this game is neither developed by, nor endorsed by Blizzard or its parent company Activision**.
@ -26,13 +26,23 @@ To build this engine, you simply need to have [Microsoft Visual Studio 2017](htt
## Building On Linux
You need to have MonoDevelop installed, as well as any depenencies for that. You also need LibSDL2 installed (installing via your favorite package manager should be fine).
## Running
When running via VisualStudio, go to the debug tab and specify the following command line options:
## Command Line Parameters
| Long Name | Description |
| ------------ | ------------------------------------------------------------ |
| --datapath | Defines the path where the data files can be found |
| --hwmouse | Use the hardware mouse instead of software |
| --mousescale | When hardware mouse is enabled, this defines the pixel scale of the mouse. No effect for software mode |
`-p "C:\Program Files (x86)\Diablo II"`
By default OpenDiablo2 assumes the media files are in the same folder as itself. At minimum you'll most likely want to specify this option:
```-p "C:\Program Files (x86)\Diablo II"```
Substitute the path with wherever you have installed Diablo 2 and its expansions.
When running via VisualStudio, go to the debug section of the OpenDiablo2 project and specify the command line options there. At minimum you'll want to specify the path to the data files.
## Contributing
If you find something you'd like to fix thats obviously broken, create a branch, commit your code, and submit a pull request. If it's a new or missing feature you'd like to see, add an issue, and be descriptive!
If you'd like to help out and are not quite sure how, you can look through any open issues and tasks.