190 lines
6.1 KiB
C#
190 lines
6.1 KiB
C#
|
using Microsoft.Xna.Framework;
|
|||
|
using Mono.Cecil;
|
|||
|
using Mono.Cecil.Cil;
|
|||
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.IO;
|
|||
|
using System.Linq;
|
|||
|
using System.Reflection;
|
|||
|
using System.Text;
|
|||
|
using System.Threading.Tasks;
|
|||
|
using StardewModdingAPI;
|
|||
|
|
|||
|
namespace StardewInjector
|
|||
|
{
|
|||
|
public class Stardew_Hooker
|
|||
|
{
|
|||
|
private AssemblyDefinition m_vAsmDefinition = null;
|
|||
|
private ModuleDefinition m_vModDefinition = null;
|
|||
|
private Assembly m_vAssembly = null;
|
|||
|
|
|||
|
public bool Initialize()
|
|||
|
{
|
|||
|
Console.WriteLine("Initiating StarDew_Injector....");
|
|||
|
try
|
|||
|
{
|
|||
|
this.m_vAsmDefinition = AssemblyDefinition.ReadAssembly(@"Stardew Valley.exe");
|
|||
|
this.m_vModDefinition = this.m_vAsmDefinition.MainModule;
|
|||
|
return true;
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
Program.LogError(ex);
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public bool Finalize()
|
|||
|
{
|
|||
|
Console.WriteLine("Finalizing StarDew_Injector....");
|
|||
|
try
|
|||
|
{
|
|||
|
if (this.m_vAsmDefinition == null)
|
|||
|
return false;
|
|||
|
|
|||
|
using (MemoryStream mStream = new MemoryStream())
|
|||
|
{
|
|||
|
// Write the edited data to the memory stream..
|
|||
|
this.m_vAsmDefinition.Write(mStream);
|
|||
|
|
|||
|
// Load the new assembly from the memory stream buffer..
|
|||
|
this.m_vAssembly = Assembly.Load(mStream.GetBuffer());
|
|||
|
|
|||
|
Program.StardewAssembly = m_vAssembly;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
Program.LogError(ex);
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public bool Run()
|
|||
|
{
|
|||
|
if (this.m_vAssembly == null)
|
|||
|
return false;
|
|||
|
|
|||
|
Console.WriteLine("Starting Stardew Valley...");
|
|||
|
|
|||
|
m_vAssembly.EntryPoint.Invoke(null, new object[] {new string[0]});
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
public void ApplyHooks()
|
|||
|
{
|
|||
|
Console.WriteLine("Applying StarDew_Injector....");
|
|||
|
try
|
|||
|
{
|
|||
|
InjectMovementSpeed();
|
|||
|
|
|||
|
if (Config.SecondsPerTenMinutes != 7)
|
|||
|
InjectClockScale();
|
|||
|
|
|||
|
if (Config.EnableEasyFishing)
|
|||
|
InjectEasyFishing();
|
|||
|
|
|||
|
if (Config.EnableAlwaysSpawnFishingBubble)
|
|||
|
InjectMoreBubbles();
|
|||
|
|
|||
|
/*
|
|||
|
if (Config.EnableDebugMode)
|
|||
|
InjectDebugMode();
|
|||
|
*/
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
Program.LogError(ex);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
private void InjectDebugMode()
|
|||
|
{
|
|||
|
this.m_vModDefinition.FindMethod("StardewValley.Program::.cctor")
|
|||
|
.FindSetField("releaseBuild").Previous()
|
|||
|
.ReplaceCreate(OpCodes.Ldc_I4_0);
|
|||
|
|
|||
|
Console.WriteLine("Enabled debug mode.");
|
|||
|
}
|
|||
|
|
|||
|
private void InjectMoreBubbles()
|
|||
|
{
|
|||
|
this.m_vModDefinition.FindMethod("StardewValley.GameLocation::performTenMinuteUpdate")
|
|||
|
.FindLoadField("currentLocation").Next(i => i.ToString().Contains("NextDouble")).Next()
|
|||
|
.ReplaceCreate(OpCodes.Ldc_R8, 1.1);
|
|||
|
|
|||
|
Console.WriteLine("Forced each area to always spawn a fishing bubble.");
|
|||
|
}
|
|||
|
|
|||
|
private void InjectEasyFishing()
|
|||
|
{
|
|||
|
this.m_vModDefinition.FindMethod("StardewValley.Menus.BobberBar::update")
|
|||
|
.FindLoadConstant(694)
|
|||
|
.Next(i => i.OpCode == OpCodes.Ldc_R4)
|
|||
|
.ReplaceCreate(OpCodes.Ldc_R4, 0.001f)
|
|||
|
.Next(i => i.OpCode == OpCodes.Ldc_R4)
|
|||
|
.ReplaceCreate(OpCodes.Ldc_R4, 0.001f);
|
|||
|
|
|||
|
Console.WriteLine("Replaced fish escape constants for all bobbers & bobber id 694 with 0.001, slowing it down.");
|
|||
|
}
|
|||
|
|
|||
|
private void InjectClockScale()
|
|||
|
{
|
|||
|
int timeScale = Config.SecondsPerTenMinutes;
|
|||
|
timeScale *= 1000;
|
|||
|
|
|||
|
this.m_vModDefinition.FindMethod("StardewValley.Game1::UpdateGameClock")
|
|||
|
.FindLoadConstant(7000f)
|
|||
|
.ReplaceCreate(OpCodes.Ldc_R4, timeScale*1.0f)
|
|||
|
.Next(i => i.OpCode == OpCodes.Ldc_R4 && (float) i.Operand == 7000f)
|
|||
|
.ReplaceCreate(OpCodes.Ldc_R4, timeScale*1.0f)
|
|||
|
.Next(i => i.OpCode == OpCodes.Ldc_I4 && (int) i.Operand == 7000)
|
|||
|
.ReplaceCreate(OpCodes.Ldc_I4, timeScale);
|
|||
|
|
|||
|
Console.WriteLine("Updated lighting for new timescale ({0}).", timeScale);
|
|||
|
}
|
|||
|
|
|||
|
private void InjectMovementSpeed()
|
|||
|
{
|
|||
|
|
|||
|
|
|||
|
if (Config.EnableTweakedDiagonalMovement)
|
|||
|
{
|
|||
|
this.m_vModDefinition.FindMethod("StardewValley.Farmer::getMovementSpeed")
|
|||
|
.FindLoadField("movementDirections").Next(i => i.OpCode == OpCodes.Ldc_I4_1)
|
|||
|
.ReplaceCreate(OpCodes.Ldc_I4_4);
|
|||
|
|
|||
|
Console.WriteLine("Removed diagonal movement check.");
|
|||
|
}
|
|||
|
|
|||
|
if (Config.RunSpeed > 0)
|
|||
|
{
|
|||
|
this.m_vModDefinition.FindMethod("StardewValley.Farmer::getMovementSpeed")
|
|||
|
.FindLoadField("movementDirections").Last().CreateBefore(OpCodes.Ldc_R4, (float) Config.RunSpeed).CreateAfter(OpCodes.Add);
|
|||
|
|
|||
|
Console.WriteLine("Added run speed: " + Config.RunSpeed);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
private void DumpInstructionsToFile(MethodDefinition methodDefinition)
|
|||
|
{
|
|||
|
var fileName = string.Format("{0}.{1}.txt", methodDefinition.DeclaringType.Name, methodDefinition.Name);
|
|||
|
|
|||
|
using (var stream = File.OpenWrite(Path.Combine(".", fileName)))
|
|||
|
using (var writer = new StreamWriter(stream))
|
|||
|
{
|
|||
|
var ilProcessor = methodDefinition.Body.GetILProcessor();
|
|||
|
for (int i = 0; i < ilProcessor.Body.Instructions.Count; i++)
|
|||
|
writer.WriteLine((i) + ":" + ilProcessor.Body.Instructions[i]);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|