Updated to v0.3.1

This commit is contained in:
Robert 2019-08-03 05:06:17 +02:00
parent e01233c205
commit 7d702b0f8d
14 changed files with 371 additions and 124 deletions

View file

@ -1,41 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace CrosshairMod
{
// Button Wrapper class, utilizing GUI.Button()
class GUIButton
{
public Vector2 position = new Vector2(0, 0);
public Vector2 dimensions = new Vector2(0, 0);
public string label = "";
public event EventHandler OnClick;
public GUIButton(uint x, uint y, uint width, uint height, string label)
{
Logging.Log("Button Constructor");
this.position = new Vector2(x, y);
this.dimensions = new Vector2(width, height);
this.label = label;
}
public GUIButton()
{
// Empty
}
public void Update()
{
bool buttonPressed = GUI.Button(new Rect(position, dimensions), label);
if (buttonPressed)
OnClick?.Invoke(this, EventArgs.Empty);
}
}
}

View file

@ -2,16 +2,23 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace CrosshairMod
{
/* The class responsible for drawing/creating/administrating the crosshair.
*
* This is where settings are applied to the crosshair.
*/
static class Crosshair
{
// Crosshair Texture / Style
private static Texture2D m_texture = new Texture2D(0, 0);
private static GUIStyle m_style;
// If crosshair is visible or hidden
private static bool m_enabled = true;
private static bool m_validState = true;
@ -22,6 +29,12 @@ namespace CrosshairMod
Settings.SetSetting("crosshairVisible", 1, true);
}
// Returns wether the crosshair is enabled
public static bool Enabled()
{
return m_enabled;
}
// Change Color
public static void SetColor(int r, int g, int b, int a)
{
@ -112,8 +125,11 @@ namespace CrosshairMod
m_style.normal.background = m_texture;
}
// Render the Crosshair
public static void Render()
{
// If the crosshair is faulty, then don't execute this code
// This is here to stop the Logger from spamming the console.
if (m_validState)
{
if (InvalidCrosshair())
@ -122,13 +138,14 @@ namespace CrosshairMod
return;
}
// Don't draw a hidden crosshair. Duh.
if (m_enabled)
GUI.Label(new Rect(Screen.width / 2 - m_texture.width / 2, Screen.height / 2 - m_texture.height / 2, m_texture.width, m_texture.height),
m_texture, m_style);
}
}
// Check Crosshair State
private static bool InvalidCrosshair()
{
// Check if the texture is bigger than (0, 0) to see if it was initialized.

View file

@ -9,9 +9,10 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>CrosshairMod</RootNamespace>
<AssemblyName>CrosshairMod</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -44,16 +45,15 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Button.cs" />
<Compile Include="Interface\Button.cs" />
<Compile Include="Crosshair.cs" />
<Compile Include="Interface.cs" />
<Compile Include="Interface\InputObject.cs" />
<Compile Include="Interface\Slider.cs" />
<Compile Include="Logging.cs" />
<Compile Include="Main.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Settings.cs" />
</ItemGroup>
<ItemGroup>
<None Include="chSettings.sett" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -2,55 +2,86 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace CrosshairMod
{
/* A class that handles the Crosshair GUI.
*
* Contains all Buttons, Sliders etc. that are able to modify the crosshair.
*/
// TODO: Create GUILayout.Window to make a less crappy version of the settings window
static class Interface
{
// Saves wether the interface is visible or not
private static bool m_visible = false;
private static Dictionary<string, GUIButton> m_buttons = new Dictionary<string, GUIButton>();
// Stores all Buttons used in the interface.
private static List<InputObject> m_inputs = new List<InputObject>();
// Values of the RGBA Sliders
private static int rSliderValue, gSliderValue, bSliderValue, aSliderValue;
private static Texture2D m_background = new Texture2D(1, 1);
private static GUIStyle m_style = new GUIStyle();
// Position ind dimension of the GUI background
private static Vector2 m_position;
private static Vector2 m_dimension;
// Creates a new button object and adds it to the List
private static void AddButton(float x, float y, float width, float height, string label, string ID, params EventHandler[] onClickEvent)
{
GUIButton buttonObj = new GUIButton(x, y, width, height, label, ID, onClickEvent);
m_inputs.Add(buttonObj);
}
// Creates a new slider object and adds it to the List
// Returns the index of the button
private static void AddSlider(float x, float y, float width, float height, float min, float max, float init, string ID)
{
GUISlider sliderObj = new GUISlider(x, y, width, height, min, max, init, ID);
m_inputs.Add(sliderObj);
}
// Initializes all Buttons, gives them their function etc
public static void Init()
{
// Set dimension to 0.25 of the screen width/height
m_dimension = new Vector2(Screen.width / 4, Screen.height / 4);
// Center the interface
m_position = new Vector2((Screen.width - m_dimension.x) / 2, (Screen.height - m_dimension.y) / 2);
m_background.SetPixel(0, 0, new Color(0.4f, 0.4f, 0.4f, 0.4f));
m_background.wrapMode = TextureWrapMode.Repeat;
m_background.Apply();
m_style.normal.background = m_background;
// Create Crosshair Visibilty Button
AddButton(20, 20, 200, 30,
(Crosshair.Enabled() ? "Hide Crosshair" : "Show Crosshair"), "Toggle", (object sender, EventArgs e) => { Crosshair.Toggle(); },
(object sender, EventArgs e) => { GUIButton btn = (GUIButton)sender; btn.label = (Crosshair.Enabled() ? "Hide Crosshair" : "Show Crosshair"); });
m_buttons.Add("visibility", new GUIButton((uint)m_position.x + 20, (uint)m_position.y + 20, 200, 30, "Toggle Crosshair"));
m_buttons["visibility"].OnClick += (object sender, EventArgs e) => { Crosshair.Toggle(); };
// Create Crosshair Size +/- Buttons
AddButton(20, 60, 30, 30,
"-", "sizedown", (object sender, EventArgs e) => { Crosshair.ChangeSize(-1); });
AddButton(190, 60, 30, 30,
"+", "sizeup", (object sender, EventArgs e) => { Crosshair.ChangeSize(+1); });
m_buttons.Add("size-", new GUIButton((uint)m_position.x + 20, (uint)m_position.y + 60, 30, 30, "-"));
m_buttons.Add("size+", new GUIButton((uint)m_position.x + 190, (uint)m_position.y + 60, 30, 30, "+"));
m_buttons["size-"].OnClick += (object sender, EventArgs e) => { Crosshair.ChangeSize(-1); };
m_buttons["size+"].OnClick += (object sender, EventArgs e) => { Crosshair.ChangeSize(+1); };
m_buttons.Add("thick-", new GUIButton((uint)m_position.x + 20, (uint)m_position.y + 100, 30, 30, "-"));
m_buttons.Add("thick+", new GUIButton((uint)m_position.x + 190, (uint)m_position.y + 100, 30, 30, "+"));
m_buttons["thick-"].OnClick += (object sender, EventArgs e) => { Crosshair.ChangeThickness(-1); };
m_buttons["thick+"].OnClick += (object sender, EventArgs e) => { Crosshair.ChangeThickness(+1); };
// Create Crosshair Thickness +/- Buttons
AddButton(20, 100, 30, 30,
"-", "thickdown", (object sender, EventArgs e) => { Crosshair.ChangeThickness(-1); });
AddButton(190, 100, 30, 30,
"+", "thickup", (object sender, EventArgs e) => { Crosshair.ChangeThickness(+1); });
rSliderValue = Settings.GetValue("crosshairColorRed");
gSliderValue = Settings.GetValue("crosshairColorGreen");
bSliderValue = Settings.GetValue("crosshairColorBlue");
aSliderValue = Settings.GetValue("crosshairColorAlpha");
// Create RGBA Sliders
AddSlider(m_dimension.x / 2 + 60, 30, 200, 10, 0, 255, rSliderValue, "red");
AddSlider(m_dimension.x / 2 + 60, 70, 200, 30, 0, 255, gSliderValue, "green");
AddSlider(m_dimension.x / 2 + 60, 110, 200, 30, 0, 255, bSliderValue, "blue");
AddSlider(m_dimension.x / 2 + 60, 150, 200, 30, 0, 255, aSliderValue, "alpha");
}
// Displays / Hides the menu
@ -59,40 +90,47 @@ namespace CrosshairMod
m_visible = !m_visible;
}
// Renders the Panel, but also handles Updating the buttons
// Renders the window
public static void Render()
{
if(m_visible)
{
GUI.Label(new Rect(m_position, m_dimension), m_background, m_style);
GUI.Label(new Rect(m_position.x + 60, m_position.y + 70, 120, 30), "Length: " + Settings.GetValue("crosshairLength"));
GUI.Label(new Rect(m_position.x + 60, m_position.y + 110, 120, 30), "Thickness: " + Settings.GetValue("crosshairThickness"));
GUI.Label(new Rect(m_position.x + m_dimension.x / 2 + 20, m_position.y + 30, 200, 30), "R: " + rSliderValue);
rSliderValue = (int)GUI.HorizontalSlider(new Rect(m_position.x + m_dimension.x / 2 + 60, m_position.y + 20, 200, 30), (int)rSliderValue, 0f, 255f);
GUI.Label(new Rect(m_position.x + m_dimension.x / 2 + 20, m_position.y + 70, 200, 30), "G: " + gSliderValue);
gSliderValue = (int)GUI.HorizontalSlider(new Rect(m_position.x + m_dimension.x / 2 + 60, m_position.y + 60, 200, 30), (int)gSliderValue, 0f, 255f);
GUI.Label(new Rect(m_position.x + m_dimension.x / 2 + 20, m_position.y + 110, 200, 30), "B: " + bSliderValue);
bSliderValue = (int)GUI.HorizontalSlider(new Rect(m_position.x + m_dimension.x / 2 + 60, m_position.y + 100, 200, 30), (int)bSliderValue, 0f, 255f);
GUI.Label(new Rect(m_position.x + m_dimension.x / 2 + 20, m_position.y + 150, 200, 30), "A: " + aSliderValue);
aSliderValue = (int)GUI.HorizontalSlider(new Rect(m_position.x + m_dimension.x / 2 + 60, m_position.y + 140, 200, 30), (int)aSliderValue, 0f, 255f);
Crosshair.SetColor(rSliderValue, gSliderValue, bSliderValue, aSliderValue);
// Update Buttons
HandleButtons();
}
if (m_visible)
GUI.Window(420, new Rect(m_position, m_dimension), RenderFunc, "Crosshair Settings");
}
// Renders the Panel, but also handles Updating the buttons
private static void RenderFunc(int windowID)
{
// Draw the Length and Thickness Labels
GUI.Label(new Rect(60, 70, 120, 30), "Length: " + Settings.GetValue("crosshairLength"));
GUI.Label(new Rect(60, 110, 120, 30), "Thickness: " + Settings.GetValue("crosshairThickness"));
// Draw the RGBA Labels and Sliders
GUI.Label(new Rect(m_dimension.x / 2 + 20, 30, 200, 30), "R: " + rSliderValue);
GUI.Label(new Rect(m_dimension.x / 2 + 20, 70, 200, 30), "G: " + gSliderValue);
GUI.Label(new Rect(m_dimension.x / 2 + 20, 110, 200, 30), "B: " + bSliderValue);
GUI.Label(new Rect(m_dimension.x / 2 + 20, 150, 200, 30), "A: " + aSliderValue);
// Set crosshair Colour after getting slider values
IEnumerable<GUISlider> it = m_inputs.OfType<GUISlider>();
rSliderValue = (int)it.First(slider => slider.ID == "red").Value;
gSliderValue = (int)it.First(slider => slider.ID == "green").Value;
bSliderValue = (int)it.First(slider => slider.ID == "blue").Value;
aSliderValue = (int)it.First(slider => slider.ID == "alpha").Value;
Crosshair.SetColor(rSliderValue, gSliderValue, bSliderValue, aSliderValue);
// Update Buttons
HandleButtons();
}
// Calls the Update function on all Buttons to check if they were pressed, and execute their Action
private static void HandleButtons()
{
foreach(KeyValuePair<string, GUIButton> pair in m_buttons)
foreach(InputObject obj in m_inputs)
{
pair.Value.Update();
obj.Update();
}
}
}

View file

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace CrosshairMod
{
/*
* A button wrapper class that is used right now as I don't have access to
* the games buttons. Since UnityEngine.GUI only has a function to draw Buttons,
* I made this class for easy handling.
*
*/
class GUIButton : InputObject
{
// da_google thinks this Button Wrapper is stupid, so let's see what ths Button Wrapper thinks about him
private const bool IS_DA_GOOGLE_STUPID = true;
// Interesting.
// OnClick event
public event EventHandler OnClick;
// Label of the Button
public string label { get; set; } = "";
// Initialize Button
public GUIButton(float x, float y, float width, float height, string label, string ID, params EventHandler[] OnClickEvent)
: base(x, y, width, height, ID)
{
Logging.Debug.Log("Button Constructor");
// Assign position, dimension and label
this.label = label;
// Push OnClickEvents
foreach(EventHandler e in OnClickEvent)
OnClick += e;
}
public GUIButton(string ID)
: base(0, 0, 0, 0, ID)
{
// Empty
}
// Updates and Draws the Button.
public override float Update()
{
// Get if the Button was pressed and invoke OnClick event accordingly
bool buttonPressed = GUI.Button(new Rect(position, dimensions), label);
if (buttonPressed)
OnClick?.Invoke(this, EventArgs.Empty);
return (buttonPressed ? 1.0f : 0.0f);
}
}
}

View file

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace CrosshairMod
{
/*
* Base of all Input Objects.
*
* Any Input Object that wants to be displayed in the Interface must
* inherit from this class.
*/
abstract class InputObject
{
// position and dimension of the object
public Vector2 position, dimensions;
// ID of the Object
public readonly string ID;
// constructor to set position and size
public InputObject(float x, float y, float width, float height, string ID)
{
this.position = new Vector2(x, y);
this.dimensions = new Vector2(width, height);
this.ID = ID;
}
// the update method (that works as renderer) must be overriden by each object
public abstract float Update();
}
}

View file

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace CrosshairMod
{
class GUISlider : InputObject
{
// Min/Max values for the slider
public float Min { get; set; } = 0;
public float Max { get; set; } = 0;
// Current slider value
public float Value { get; set; } = 0;
public GUISlider(float x, float y, float width, float height, float min, float max, float init, string ID)
: base(x, y, width, height, ID)
{
Min = min;
Max = max;
Value = init;
}
public GUISlider(string ID)
:base (0, 0, 0, 0, ID)
{
}
public override float Update()
{
Value = GUI.HorizontalSlider(new Rect(position, dimensions), Value, Min, Max);
return Value;
}
}
}

View file

@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
@ -13,21 +13,53 @@ namespace CrosshairMod
// However, I prefer this over Debug.Log() since it doesn't include a stacktrace (Except for Errors)
public static class Logging
{
// The Prefix that gets put in front of every Log
public const string PREFIX = "[CROSSHAIRMOD]";
// A kind of sub-class that is used to Log messages that will only appear
// when the User installs a Debug build of the Mod. Release versions will
// not log Debug messages this way.
public static class Debug
{
public static void Log(string message)
{
#if DEBUG
Logging.Log(message);
#endif // DEBUG
}
public static void LogWarning(string message)
{
#if DEBUG
Logging.LogWarning(message);
#endif // DEBUG
}
public static void LogError(string message)
{
#if DEBUG
Logging.LogError(PREFIX + "Error: " + message);
#endif // DEBUG
}
}
// Logs information
public static void Log(string message)
{
Console.WriteLine(PREFIX + "Info: " + message);
}
// Logs warnings
public static void LogWarning(string message)
{
Console.WriteLine(PREFIX + "Warning: " + message);
}
// Logs errors
public static void LogError(string message)
{
Debug.Log(PREFIX + "Error: " + message);
UnityEngine.Debug.Log(PREFIX + "Error: " + message);
}
}
}

View file

@ -3,44 +3,60 @@
* the game Blackwake.
*
* @author Lauchmelder
* @version v0.2
* @version v0.3
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace CrosshairMod
{
/*
* This is the Main class that is responsible for
* handling initializing and updating the components
* of the crosshair mod.
*/
public class Main : MonoBehaviour
{
private const string MENU_OPEN_KEY = "H";
private const string CH_TOGGLE_KEY = "J";
// Define Hotkeys for Menu and Crosshair Toggle
private char MENU_OPEN_KEY = 'H';
private char CH_TOGGLE_KEY = 'J';
// This will be executed first
void Start()
{
// Update the settings
Settings.LoadSettings(".\\Blackwake_Data\\Managed\\Mods\\chSettings.sett");
// Create Crosshair
Crosshair.Create();
// Create Panel
Interface.Init();
// Load Hotkeys
MENU_OPEN_KEY = (char)Settings.GetValue("hotkeyCrosshairToggle", true, MENU_OPEN_KEY);
CH_TOGGLE_KEY = (char)Settings.GetValue("hotkeyGUIToggle", true, CH_TOGGLE_KEY);
}
// This gets called on every GUI Update (Can be multiple tiems per Frame)
void OnGUI()
{
// Check for Key press
if(Event.current.Equals(Event.KeyboardEvent(MENU_OPEN_KEY)))
// Check for Key presses
if (Event.current.Equals(Event.KeyboardEvent(MENU_OPEN_KEY.ToString())))
{
// Toggle Crosshair GUI
Interface.Toggle();
}
if (Event.current.Equals(Event.KeyboardEvent(CH_TOGGLE_KEY)))
if (Event.current.Equals(Event.KeyboardEvent(CH_TOGGLE_KEY.ToString())))
{
// Toggle Crosshair
Crosshair.Toggle();
}
@ -50,11 +66,12 @@ namespace CrosshairMod
Crosshair.Render();
}
// Will be called when the application is closed
void OnApplicationQuit()
{
// Save settings
Settings.SaveSettings(".\\Blackwake_Data\\Managed\\Mods\\chSettings.sett");
Logging.Log("Saved Settings");
Logging.Debug.Log("Saved Settings");
}
}
}

View file

@ -2,29 +2,58 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CrosshairMod
{
/*
* The class that is responsible for loading and storing all the
* necessary settings. There is much room for improvement.
*/
static class Settings
{
// Initialize Settings dictionary
private static Dictionary<string, int> m_settings = new Dictionary<string, int>();
// Load settings from file
public static void LoadSettings(string filepath)
{
Logging.Log("Accessing Settings at " + filepath);
Logging.Debug.Log("Accessing Settings at " + filepath);
// Try to read file contents into string
// If no file exists, create one
string settings = "";
if(!System.IO.File.Exists(filepath))
{
// No settings file found, create one
Logging.Debug.LogWarning("Settings file not found, creating one...");
try
{
System.IO.File.Create(filepath);
}
// If that fails then shit just hit the fan. React accordingly
catch(Exception e)
{
Logging.LogError("Something went wrong while creating a settings file... :(");
Logging.LogError(e.Message);
Logging.LogError(e.StackTrace);
return;
}
}
// Read file to string
try
{
settings = System.IO.File.ReadAllText(filepath);
}
// Something incredibly weird just happened
catch (Exception e)
{
// Log error and return invalid state
Logging.LogError("Something went wrong while reading a settings file... :(");
Logging.LogError(e.Message);
Logging.LogError(e.StackTrace);
return;
}
@ -43,7 +72,7 @@ namespace CrosshairMod
m_settings.Add(vals[0], Int32.Parse(vals[1])); // Store key and value in settings dictionary
}
Logging.Log("Successfully loaded settings!");
Logging.Log("Settings loaded.");
}
// Converts the dictionary to a sett file
@ -67,21 +96,21 @@ namespace CrosshairMod
m_settings.Add(key, value);
}
// Changes a settings value
// Changes a settings value, and adds it if specified
public static void SetSetting(string key, int newVal, bool addIfDoesntExist = false)
{
// If the setting doesn't exist, either add and set it, or print a Debug.Warning
if(!m_settings.ContainsKey(key))
{
if (!addIfDoesntExist)
{
Logging.LogError("Tried to change a setting with key \"" + key + "\" that doesn't exist.");
Logging.Debug.LogError("Tried to change a setting with key \"" + key + "\" that doesn't exist.");
return;
}
else
{
AddSetting(key, newVal);
Logging.LogWarning("Tried to change a setting with key \"" + key + "\" that doesn't exist. It has been added now.");
Logging.Debug.LogWarning("Tried to change a setting with key \"" + key + "\" that doesn't exist. It has been added now.");
}
}
@ -100,12 +129,12 @@ namespace CrosshairMod
{
if (!addIfDoesntExist)
{
Logging.LogError("Tried to access unknown setting: \"" + key + "\". Check your chSettings.sett for errors.");
Logging.Debug.LogError("Tried to access unknown setting: \"" + key + "\". Check your chSettings.sett for errors.");
}
else
{
AddSetting(key, initialValue);
Logging.LogWarning("Tried to access unknown setting: \"" + key + "\". A new setting with this key was created.");
Logging.Debug.LogWarning("Tried to access unknown setting: \"" + key + "\". A new setting with this key was created.");
return initialValue;
}
}

View file

@ -1,6 +0,0 @@
crosshairLength=15
crosshairThickness=3
crosshairColorRed=255
crosshairColorGreen=94
crosshairColorBlue=244
crosshairColorAlpha=100

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Lauchmelder
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,8 +1,7 @@
# CrosshairMod v0.3
A Blackwake Mod that adds a crosshair
Simply copy and paste the *CrosshairMod.dll* and the *chSettings.sett*
files from the *bin/Release* directory into your Blackwake directory under */Blackwake_Data/Managed/Mods*
Simply copy and paste the *CrosshairMod.dll* files from the *bin/Release* directory into your Blackwake directory under */Blackwake_Data/Managed/Mods*. Requires BWModLoader v0.3 or newer to be installed.
## Customization
With Version v0.3 you can now toggle the crosshair with *J*, and you can open a customization interface with *H*. This now lets you edit virtually every aspect of the current crosshair design, and even saves it to the settings so that your customization will still be there after restarting the game,
@ -28,3 +27,8 @@ With Version v0.3 you can now toggle the crosshair with *J*, and you can open a
* Colour
* Added Hotkeys to open GUI (H) and to toggle the Crosshair (J)
* Added auto-save, so any editing you do to the crosshair gets saved into the settings file.
#### v0.3.1
* Hotkeys can now be changed in the settings
* A missing settings file will no longer crash the mod
* Updated GUI Window. It looks nice now