From c14ceff074d6ff7f1720989dbf5ae484edb9f673 Mon Sep 17 00:00:00 2001 From: yangzhi <@4F!xZpJwly&KbWq> Date: Mon, 6 May 2019 09:49:38 +0800 Subject: [PATCH] Console support --- DllRewrite/DllRewrite/MethodPatcher.cs | 878 ++++++----- DllRewrite/DllRewrite/Program.cs | 15 +- src/GameConsole.cs | 118 ++ src/Mod.csproj | 1 + src/ModEntry.cs | 13 +- .../ContentManagers/GameContentManager.cs | 2 +- src/SMAPI/Framework/Monitor.cs | 4 + .../Framework/RewriteFacades/Game1Methods.cs | 6 - src/SMAPI/Framework/SCore.cs | 86 +- src/SMAPI/Framework/SGame.cs | 1344 +++++++++-------- .../StateTracking/WorldLocationsTracker.cs | 4 +- src/SMAPI/Framework/WatcherCore.cs | 2 +- src/SMAPI/Metadata/InstructionMetadata.cs | 8 +- src/SMAPI/Patches/LoadForNewGamePatch.cs | 4 +- 14 files changed, 1401 insertions(+), 1084 deletions(-) create mode 100644 src/GameConsole.cs diff --git a/DllRewrite/DllRewrite/MethodPatcher.cs b/DllRewrite/DllRewrite/MethodPatcher.cs index 9060739f..ce1f0614 100644 --- a/DllRewrite/DllRewrite/MethodPatcher.cs +++ b/DllRewrite/DllRewrite/MethodPatcher.cs @@ -6,34 +6,42 @@ using Mono.Cecil.Cil; namespace DllRewrite { - class MethodPatcher + internal class MethodPatcher { - DefaultAssemblyResolver resolver; - AssemblyDefinition StardewModdingAPI; - AssemblyDefinition StardewValley; - AssemblyDefinition MonoGame_Framework; - AssemblyDefinition mscorlib; - Dictionary methodDict = new Dictionary(); - Dictionary fieldDict = new Dictionary(); - Dictionary typeDict = new Dictionary(); + private readonly Dictionary fieldDict = new Dictionary(); + private readonly Dictionary methodDict = new Dictionary(); + private AssemblyDefinition MonoGame_Framework; + private readonly AssemblyDefinition mscorlib; + private readonly DefaultAssemblyResolver resolver; + private readonly AssemblyDefinition StardewModdingAPI; + private readonly AssemblyDefinition StardewValley; + private readonly Dictionary typeDict = new Dictionary(); + public MethodPatcher() { this.resolver = new DefaultAssemblyResolver(); this.resolver.AddSearchDirectory("./assemblies"); this.mscorlib = this.resolver.Resolve(new AssemblyNameReference("mscorlib", new Version("0.0.0.0"))); - this.MonoGame_Framework = this.resolver.Resolve(new AssemblyNameReference("MonoGame.Framework", new Version("0.0.0.0"))); - this.StardewValley = this.resolver.Resolve(new AssemblyNameReference("StardewValley", new Version("1.3.0.0"))); - this.StardewModdingAPI = this.resolver.Resolve(new AssemblyNameReference("StardewModdingAPI", new Version("0.0.0.0"))); + this.MonoGame_Framework = + this.resolver.Resolve(new AssemblyNameReference("MonoGame.Framework", new Version("0.0.0.0"))); + this.StardewValley = + this.resolver.Resolve(new AssemblyNameReference("StardewValley", new Version("1.3.0.0"))); + this.StardewModdingAPI = + this.resolver.Resolve(new AssemblyNameReference("StardewModdingAPI", new Version("0.0.0.0"))); } + public void InsertModHook(string name, TypeReference[] paraTypes, string[] paraNames, TypeReference returnType) { - TypeDefinition typeModHooksObject = this.StardewValley.MainModule.GetType("StardewValley.ModHooks"); - var hook = new MethodDefinition(name, MethodAttributes.Virtual | MethodAttributes.Public | MethodAttributes.NewSlot | MethodAttributes.HideBySig, returnType); - for(int i = 0; i< paraTypes.Length; i++) + var typeModHooksObject = this.StardewValley.MainModule.GetType("StardewValley.ModHooks"); + var hook = new MethodDefinition(name, + MethodAttributes.Virtual | MethodAttributes.Public | MethodAttributes.NewSlot | + MethodAttributes.HideBySig, returnType); + for (int i = 0; i < paraTypes.Length; i++) { - ParameterDefinition define = new ParameterDefinition(paraNames[i], ParameterAttributes.None, paraTypes[i]); + var define = new ParameterDefinition(paraNames[i], ParameterAttributes.None, paraTypes[i]); hook.Parameters.Add(define); } + switch (returnType.FullName) { case "System.Void": @@ -46,100 +54,109 @@ namespace DllRewrite hook.Body.Instructions.Add(Instruction.Create(OpCodes.Ldnull)); break; } - hook.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); - if(typeModHooksObject.Methods.FirstOrDefault(item=>item.Name == hook.Name) == null) - { - typeModHooksObject.Methods.Add(hook); - } + hook.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); + if (typeModHooksObject.Methods.FirstOrDefault(item => item.Name == hook.Name) == null) + typeModHooksObject.Methods.Add(hook); } + public TypeReference GetTypeReference(string fullname) { return this.GetTypeReference(fullname, this.mscorlib); } + public TypeReference GetTypeReference(string fullname, AssemblyDefinition assemblyDefinition) { - if (this.typeDict.ContainsKey(fullname)) - { - return this.typeDict[fullname]; - } + if (this.typeDict.ContainsKey(fullname)) return this.typeDict[fullname]; TypeReference type = assemblyDefinition.MainModule.GetType(fullname).Resolve(); type = this.StardewValley.MainModule.ImportReference(type); this.typeDict.Add(fullname, type); return type; } + public FieldReference GetFieldReference(string name, string typename, AssemblyDefinition assemblyDefinition) { string fullname = typename + "::" + name; - if (this.fieldDict.ContainsKey(fullname)) - { - return this.fieldDict[fullname]; - } - FieldReference field = assemblyDefinition.MainModule.Types.FirstOrDefault(item => item.FullName == typename).Fields.FirstOrDefault(item => item.Name == name); - if(assemblyDefinition.FullName != this.StardewValley.FullName) - { + if (this.fieldDict.ContainsKey(fullname)) return this.fieldDict[fullname]; + FieldReference field = assemblyDefinition.MainModule.Types.FirstOrDefault(item => item.FullName == typename) + .Fields.FirstOrDefault(item => item.Name == name); + if (assemblyDefinition.FullName != this.StardewValley.FullName) field = this.StardewValley.MainModule.ImportReference(field); - } this.fieldDict.Add(fullname, field); return field; } + public MethodReference GetMethodReference(string name, string typename, AssemblyDefinition assemblyDefinition) { string fullname = typename + "." + name; - if (this.fieldDict.ContainsKey(fullname)) - { - return this.methodDict[fullname]; - } - MethodReference method = assemblyDefinition.MainModule.Types.FirstOrDefault(item => item.FullName == typename).Methods.FirstOrDefault(item => item.Name == name); + if (this.fieldDict.ContainsKey(fullname)) return this.methodDict[fullname]; + MethodReference method = assemblyDefinition.MainModule.Types + .FirstOrDefault(item => item.FullName == typename).Methods.FirstOrDefault(item => item.Name == name); if (assemblyDefinition.FullName != this.StardewValley.FullName) - { method = this.StardewValley.MainModule.ImportReference(method); - } this.methodDict.Add(fullname, method); return method; } + public void ApplyGamePatch() { // Game.hook - TypeDefinition typeGame1 = this.StardewValley.MainModule.GetType("StardewValley.Game1"); - MethodDefinition constructor = typeGame1.Methods.FirstOrDefault(m => m.IsConstructor); - ILProcessor processor = constructor.Body.GetILProcessor(); - List instructions = new List(); - Instruction jointPoint = constructor.Body.Instructions[0]; - instructions.Add(processor.Create(OpCodes.Newobj, this.GetMethodReference(".ctor", "SMDroid.ModEntry", this.StardewModdingAPI))); - instructions.Add(processor.Create(OpCodes.Stsfld, this.GetFieldReference("hooks", "StardewValley.Game1", this.StardewValley))); + var typeGame1 = this.StardewValley.MainModule.GetType("StardewValley.Game1"); + var constructor = typeGame1.Methods.FirstOrDefault(m => m.IsConstructor); + var processor = constructor.Body.GetILProcessor(); + var instructions = new List(); + var jointPoint = constructor.Body.Instructions[0]; + instructions.Add(processor.Create(OpCodes.Newobj, + this.GetMethodReference(".ctor", "SMDroid.ModEntry", this.StardewModdingAPI))); + instructions.Add(processor.Create(OpCodes.Stsfld, + this.GetFieldReference("hooks", "StardewValley.Game1", this.StardewValley))); this.InsertInstructions(processor, jointPoint, instructions); + var updateMethod = typeGame1.Methods.FirstOrDefault(m => m.Name == "Update"); + jointPoint = updateMethod.Body.Instructions.First(ins => ins.OpCode == OpCodes.Call); + var jointPointA = updateMethod.Body.Instructions.First(ins => ins.OpCode == OpCodes.Call).Previous.Previous; + processor = updateMethod.Body.GetILProcessor(); + processor.InsertBefore(jointPointA, processor.Create(OpCodes.Ldsfld, this.GetFieldReference("IsHalt", "SMDroid.ModEntry", this.StardewModdingAPI))); + processor.InsertBefore(jointPointA, processor.Create(OpCodes.Brtrue_S, jointPoint.Next)); // isRaining and isDebrisWeather - PropertyDefinition propertyDefinition = new PropertyDefinition("isRaining", PropertyAttributes.None, this.GetTypeReference("System.Boolean")); - propertyDefinition.GetMethod = new MethodDefinition("get_isRaining", MethodAttributes.Public | MethodAttributes.ReuseSlot | MethodAttributes.SpecialName | MethodAttributes.Static | MethodAttributes.HideBySig, this.GetTypeReference("System.Boolean")); + var propertyDefinition = new PropertyDefinition("isRaining", PropertyAttributes.None, + this.GetTypeReference("System.Boolean")); + propertyDefinition.GetMethod = new MethodDefinition("get_isRaining", + MethodAttributes.Public | MethodAttributes.ReuseSlot | MethodAttributes.SpecialName | + MethodAttributes.Static | MethodAttributes.HideBySig, this.GetTypeReference("System.Boolean")); propertyDefinition.GetMethod.SemanticsAttributes = MethodSemanticsAttributes.Getter; processor = propertyDefinition.GetMethod.Body.GetILProcessor(); - TypeDefinition typeRainManager = this.StardewValley.MainModule.GetType("StardewValley.RainManager"); - MethodDefinition getMethod = typeRainManager.Methods.FirstOrDefault(m => m.Name == "get_Instance"); + var typeRainManager = this.StardewValley.MainModule.GetType("StardewValley.RainManager"); + var getMethod = typeRainManager.Methods.FirstOrDefault(m => m.Name == "get_Instance"); processor.Emit(OpCodes.Callvirt, getMethod); - FieldReference isRainingField = this.GetFieldReference("isRaining", "StardewValley.RainManager", this.StardewValley); + var isRainingField = this.GetFieldReference("isRaining", "StardewValley.RainManager", this.StardewValley); processor.Emit(OpCodes.Ldfld, isRainingField); processor.Emit(OpCodes.Ret); typeGame1.Methods.Add(propertyDefinition.GetMethod); typeGame1.Properties.Add(propertyDefinition); - propertyDefinition = new PropertyDefinition("isDebrisWeather", PropertyAttributes.None, this.GetTypeReference("System.Boolean")); - propertyDefinition.GetMethod = new MethodDefinition("get_isDebrisWeather", MethodAttributes.Public | MethodAttributes.ReuseSlot | MethodAttributes.SpecialName | MethodAttributes.Static | MethodAttributes.HideBySig, this.GetTypeReference("System.Boolean")); + propertyDefinition = new PropertyDefinition("isDebrisWeather", PropertyAttributes.None, + this.GetTypeReference("System.Boolean")); + propertyDefinition.GetMethod = new MethodDefinition("get_isDebrisWeather", + MethodAttributes.Public | MethodAttributes.ReuseSlot | MethodAttributes.SpecialName | + MethodAttributes.Static | MethodAttributes.HideBySig, this.GetTypeReference("System.Boolean")); propertyDefinition.GetMethod.SemanticsAttributes = MethodSemanticsAttributes.Getter; processor = propertyDefinition.GetMethod.Body.GetILProcessor(); - TypeDefinition typeWeatherDebrisManager = this.StardewValley.MainModule.GetType("StardewValley.WeatherDebrisManager"); + var typeWeatherDebrisManager = this.StardewValley.MainModule.GetType("StardewValley.WeatherDebrisManager"); getMethod = typeWeatherDebrisManager.Methods.FirstOrDefault(m => m.Name == "get_Instance"); processor.Emit(OpCodes.Callvirt, getMethod); - FieldReference isDebrisWeatherField = this.GetFieldReference("isDebrisWeather", "StardewValley.WeatherDebrisManager", this.StardewValley); + var isDebrisWeatherField = this.GetFieldReference("isDebrisWeather", "StardewValley.WeatherDebrisManager", + this.StardewValley); processor.Emit(OpCodes.Ldfld, isDebrisWeatherField); processor.Emit(OpCodes.Ret); typeGame1.Methods.Add(propertyDefinition.GetMethod); typeGame1.Properties.Add(propertyDefinition); //HUDMessage..ctor - TypeDefinition typeHUDMessage = this.StardewValley.MainModule.GetType("StardewValley.HUDMessage"); - MethodDefinition hudConstructor = new MethodDefinition(".ctor", MethodAttributes.Public | MethodAttributes.ReuseSlot | MethodAttributes.RTSpecialName | MethodAttributes.SpecialName | MethodAttributes.HideBySig, this.GetTypeReference("System.Void")); + var typeHUDMessage = this.StardewValley.MainModule.GetType("StardewValley.HUDMessage"); + var hudConstructor = new MethodDefinition(".ctor", + MethodAttributes.Public | MethodAttributes.ReuseSlot | MethodAttributes.RTSpecialName | + MethodAttributes.SpecialName | MethodAttributes.HideBySig, this.GetTypeReference("System.Void")); hudConstructor.Parameters.Add(new ParameterDefinition(this.GetTypeReference("System.String"))); hudConstructor.Parameters.Add(new ParameterDefinition(this.GetTypeReference("System.Int32"))); processor = hudConstructor.Body.GetILProcessor(); @@ -147,12 +164,12 @@ namespace DllRewrite processor.Emit(OpCodes.Ldarg_1); processor.Emit(OpCodes.Ldarg_2); processor.Emit(OpCodes.Ldc_I4_M1); - MethodDefinition targetConstructor = typeHUDMessage.Methods.FirstOrDefault(item => { - if(item.Parameters.Count == 3 && item.Parameters[0].ParameterType.FullName == "System.String" - && item.Parameters[1].ParameterType.FullName == "System.Int32" && item.Parameters[1].ParameterType.FullName == "System.Int32") - { + var targetConstructor = typeHUDMessage.Methods.FirstOrDefault(item => + { + if (item.Parameters.Count == 3 && item.Parameters[0].ParameterType.FullName == "System.String" + && item.Parameters[1].ParameterType.FullName == "System.Int32" && + item.Parameters[1].ParameterType.FullName == "System.Int32") return true; - } return false; }); processor.Emit(OpCodes.Call, targetConstructor); @@ -161,164 +178,169 @@ namespace DllRewrite // Back Button Fix - MethodDefinition method = typeGame1.Methods.FirstOrDefault(m => m.Name == "_updateAndroidMenus"); + var method = typeGame1.Methods.FirstOrDefault(m => m.Name == "_updateAndroidMenus"); processor = method.Body.GetILProcessor(); - FieldReference inputField = this.GetFieldReference("input", "StardewValley.Game1", this.StardewValley); + var inputField = this.GetFieldReference("input", "StardewValley.Game1", this.StardewValley); processor.Replace(method.Body.Instructions[0], processor.Create(OpCodes.Ldsfld, inputField)); - TypeDefinition typeInputState = this.StardewValley.MainModule.GetType("StardewValley.InputState"); + var typeInputState = this.StardewValley.MainModule.GetType("StardewValley.InputState"); var GetGamePadState = typeInputState.Methods.FirstOrDefault(m => m.Name == "GetGamePadState"); processor.Replace(method.Body.Instructions[1], processor.Create(OpCodes.Callvirt, GetGamePadState)); } - public void ApplyCommonMidHookEntry(TypeDefinition targetType, Func methodChecker, Func jointPointChecker, int jointPointOffset, string hookname) + + public void ApplyCommonMidHookEntry(TypeDefinition targetType, Func methodChecker, + Func jointPointChecker, int jointPointOffset, string hookname) { byte i, j; - MethodDefinition targetMethod = targetType.Methods.FirstOrDefault(method => methodChecker(method)); + var targetMethod = targetType.Methods.FirstOrDefault(method => methodChecker(method)); string qualifyName = targetType.FullName + "." + targetMethod.Name + "_mid"; - MethodDefinition prefixHook = this.StardewValley.MainModule.GetType("StardewValley.ModHooks").Methods.FirstOrDefault(m => m.Name == (hookname + "_Prefix")); - ILProcessor iLProcessor = targetMethod.Body.GetILProcessor(); - FieldReference field = this.GetFieldReference("hooks", "StardewValley.Game1", this.StardewValley); - List instructions = new List(); + var prefixHook = this.StardewValley.MainModule.GetType("StardewValley.ModHooks").Methods + .FirstOrDefault(m => m.Name == hookname + "_Prefix"); + var iLProcessor = targetMethod.Body.GetILProcessor(); + var field = this.GetFieldReference("hooks", "StardewValley.Game1", this.StardewValley); + var instructions = new List(); byte returnIndex = 0; byte parameterIndexBegin = 0; byte parameterIndexEnd = 0; - byte parameterOffset = targetMethod.IsStatic ? ((byte)0) : ((byte)1); - byte stateIndex = (byte)targetMethod.Body.Variables.Count; + byte parameterOffset = targetMethod.IsStatic ? (byte) 0 : (byte) 1; + byte stateIndex = (byte) targetMethod.Body.Variables.Count; targetMethod.Body.Variables.Add(new VariableDefinition(this.GetTypeReference("System.Boolean"))); if (targetMethod.ReturnType.FullName != "System.Void") { - returnIndex = (byte)targetMethod.Body.Variables.Count; + returnIndex = (byte) targetMethod.Body.Variables.Count; targetMethod.Body.Variables.Add(new VariableDefinition(this.GetTypeReference("System.Object"))); } - parameterIndexBegin = (byte)targetMethod.Body.Variables.Count; - for (i = 0; i < targetMethod.Parameters.Count; i = (byte)(i + 1)) + + parameterIndexBegin = (byte) targetMethod.Body.Variables.Count; + for (i = 0; i < targetMethod.Parameters.Count; i = (byte) (i + 1)) { targetMethod.Body.Variables.Add(new VariableDefinition(this.GetTypeReference("System.Object"))); - parameterIndexEnd = (byte)targetMethod.Body.Variables.Count; + parameterIndexEnd = (byte) targetMethod.Body.Variables.Count; } + if (prefixHook != null) { - Instruction jointPoint = targetMethod.Body.Instructions.FirstOrDefault(ins => jointPointChecker(ins)); - for (int x = jointPointOffset; x < 0; x++) - { - jointPoint = jointPoint.Previous; - } - for (int x = 0; x < jointPointOffset; x++) - { - jointPoint = jointPoint.Next; - } + var jointPoint = targetMethod.Body.Instructions.FirstOrDefault(ins => jointPointChecker(ins)); + for (int x = jointPointOffset; x < 0; x++) jointPoint = jointPoint.Previous; + for (int x = 0; x < jointPointOffset; x++) jointPoint = jointPoint.Next; i = parameterOffset; - for (j = parameterIndexBegin; i < (targetMethod.Parameters.Count + parameterOffset); j = (byte)(j + 1)) + for (j = parameterIndexBegin; i < targetMethod.Parameters.Count + parameterOffset; j = (byte) (j + 1)) { instructions.Add(this._createLdargsInstruction(iLProcessor, i)); if (targetMethod.Parameters[i - parameterOffset].ParameterType.IsValueType) - { - instructions.Add(iLProcessor.Create(OpCodes.Box, targetMethod.Parameters[i - parameterOffset].ParameterType)); - } + instructions.Add(iLProcessor.Create(OpCodes.Box, + targetMethod.Parameters[i - parameterOffset].ParameterType)); instructions.Add(this._createStlocInstruction(iLProcessor, j)); - i = (byte)(i + 1); + i = (byte) (i + 1); } + instructions.Add(iLProcessor.Create(OpCodes.Ldsfld, field)); instructions.Add(iLProcessor.Create(OpCodes.Ldstr, qualifyName)); if (!targetMethod.IsStatic) { instructions.Add(iLProcessor.Create(OpCodes.Ldarg_0)); - if (targetType.IsValueType) - { - instructions.Add(iLProcessor.Create(OpCodes.Box, targetType)); - } + if (targetType.IsValueType) instructions.Add(iLProcessor.Create(OpCodes.Box, targetType)); } + i = parameterOffset; - for (j = parameterIndexBegin; i < (targetMethod.Parameters.Count + parameterOffset); j = (byte)(j + 1)) + for (j = parameterIndexBegin; i < targetMethod.Parameters.Count + parameterOffset; j = (byte) (j + 1)) { instructions.Add(iLProcessor.Create(OpCodes.Ldloca_S, j)); - i = (byte)(i + 1); + i = (byte) (i + 1); } - while (i < (prefixHook.Parameters.Count - 2)) + + while (i < prefixHook.Parameters.Count - 2) { - instructions.Add(iLProcessor.Create(OpCodes.Ldloca_S, (byte)0)); - i = (byte)(i + 1); + instructions.Add(iLProcessor.Create(OpCodes.Ldloca_S, (byte) 0)); + i = (byte) (i + 1); } + instructions.Add(iLProcessor.Create(OpCodes.Ldloca_S, returnIndex)); instructions.Add(iLProcessor.Create(OpCodes.Callvirt, prefixHook)); instructions.Add(this._createStlocInstruction(iLProcessor, stateIndex)); i = parameterOffset; - for (j = parameterIndexBegin; i < (targetMethod.Parameters.Count + parameterOffset); j = (byte)(j + 1)) + for (j = parameterIndexBegin; i < targetMethod.Parameters.Count + parameterOffset; j = (byte) (j + 1)) { instructions.Add(this._createLdlocInstruction(iLProcessor, j)); if (targetMethod.Parameters[i - parameterOffset].ParameterType.IsValueType) - { - instructions.Add(iLProcessor.Create(OpCodes.Unbox_Any, targetMethod.Parameters[i - parameterOffset].ParameterType)); - } + instructions.Add(iLProcessor.Create(OpCodes.Unbox_Any, + targetMethod.Parameters[i - parameterOffset].ParameterType)); else - { - instructions.Add(iLProcessor.Create(OpCodes.Castclass, targetMethod.Parameters[i - parameterOffset].ParameterType)); - } + instructions.Add(iLProcessor.Create(OpCodes.Castclass, + targetMethod.Parameters[i - parameterOffset].ParameterType)); instructions.Add(iLProcessor.Create(OpCodes.Starg_S, i)); - i = (byte)(i + 1); + i = (byte) (i + 1); } + instructions.Add(this._createLdlocInstruction(iLProcessor, stateIndex)); instructions.Add(iLProcessor.Create(OpCodes.Brtrue, jointPoint)); if (targetMethod.ReturnType.FullName != "System.Void") { instructions.Add(this._createLdlocInstruction(iLProcessor, returnIndex)); if (targetMethod.ReturnType.IsValueType) - { instructions.Add(iLProcessor.Create(OpCodes.Unbox_Any, targetMethod.ReturnType)); - } else - { instructions.Add(iLProcessor.Create(OpCodes.Castclass, targetMethod.ReturnType)); - } } + instructions.Add(iLProcessor.Create(OpCodes.Ret)); this.InsertInstructions(iLProcessor, jointPoint, instructions); } } - - public void ApplyCommonHookEntry(TypeDefinition targetType, string methodname, string hookname, bool patchPrefix = true, bool patchPostfix = true, Func methodFilter = null, string qualifyNameSuffix = "") + + public void ApplyCommonHookEntry(TypeDefinition targetType, string methodname, string hookname, + bool patchPrefix = true, bool patchPostfix = true, Func methodFilter = null, + string qualifyNameSuffix = "", FieldReference hooksField = null, MethodReference prefixHook = null, MethodReference postfixHook = null) { string qualifyName = $"{targetType.FullName}.{methodname}{qualifyNameSuffix}"; - TypeDefinition typeModHooksObject = this.StardewValley.MainModule.GetType("StardewValley.ModHooks"); - var targetMethod = targetType.Methods.FirstOrDefault(method => (method.HasBody && method.Name == methodname && (methodFilter == null || methodFilter(method)))); - var prefixHook = typeModHooksObject.Methods.FirstOrDefault(m => m.Name == hookname + "_Prefix"); - var postfixHook = typeModHooksObject.Methods.FirstOrDefault(m => m.Name == hookname + "_Postfix"); + var targetMethod = targetType.Methods.FirstOrDefault(method => + method.HasBody && method.Name == methodname && (methodFilter == null || methodFilter(method))); + var typeModHooksObject = this.StardewValley.MainModule.GetType("StardewValley.ModHooks"); + if(prefixHook == null) + prefixHook = typeModHooksObject.Methods.FirstOrDefault(m => m.Name == hookname + "_Prefix"); + if (postfixHook == null) + postfixHook = typeModHooksObject.Methods.FirstOrDefault(m => m.Name == hookname + "_Postfix"); var processor = targetMethod.Body.GetILProcessor(); - FieldReference hooksField = this.GetFieldReference("hooks", "StardewValley.Game1", this.StardewValley); + if(hooksField == null) + hooksField = this.GetFieldReference("Instance", "SMDroid.ModEntry", this.StardewModdingAPI); Instruction jointPoint; List instructions; byte i, j, k; // state byte returnIndex = 0; byte parameterIndexBegin = 0, parameterIndexEnd = 0; - byte parameterOffset = (targetMethod.IsStatic ? (byte)0 : (byte)1); - byte stateIndex = (byte)targetMethod.Body.Variables.Count; + byte parameterOffset = targetMethod.IsStatic ? (byte) 0 : (byte) 1; + byte stateIndex = (byte) targetMethod.Body.Variables.Count; targetMethod.Body.Variables.Add(new VariableDefinition(this.GetTypeReference("System.Boolean"))); if (targetMethod.ReturnType.FullName != "System.Void") { // return - returnIndex = (byte)targetMethod.Body.Variables.Count; + returnIndex = (byte) targetMethod.Body.Variables.Count; targetMethod.Body.Variables.Add(new VariableDefinition(this.GetTypeReference("System.Object"))); } - parameterIndexBegin = (byte)targetMethod.Body.Variables.Count; + + parameterIndexBegin = (byte) targetMethod.Body.Variables.Count; for (i = 0; i < targetMethod.Parameters.Count; i++) { targetMethod.Body.Variables.Add(new VariableDefinition(this.GetTypeReference("System.Object"))); - parameterIndexEnd = (byte)targetMethod.Body.Variables.Count; + parameterIndexEnd = (byte) targetMethod.Body.Variables.Count; } + if (patchPrefix && prefixHook != null) { instructions = new List(); jointPoint = targetMethod.Body.Instructions[0]; - k = (byte)(targetMethod.Parameters.Count + parameterOffset > prefixHook.Parameters.Count - 2 ? prefixHook.Parameters.Count - 2 - parameterOffset : targetMethod.Parameters.Count + parameterOffset); + k = (byte) (targetMethod.Parameters.Count + parameterOffset > prefixHook.Parameters.Count - 2 + ? prefixHook.Parameters.Count - 2 - parameterOffset + : targetMethod.Parameters.Count + parameterOffset); for (i = parameterOffset, j = parameterIndexBegin; i < k; i++, j++) { instructions.Add(this._createLdargsInstruction(processor, i)); - if(targetMethod.Parameters[i - parameterOffset].ParameterType.IsValueType) - { - instructions.Add(processor.Create(OpCodes.Box, targetMethod.Parameters[i - parameterOffset].ParameterType)); - } + if (targetMethod.Parameters[i - parameterOffset].ParameterType.IsValueType) + instructions.Add(processor.Create(OpCodes.Box, + targetMethod.Parameters[i - parameterOffset].ParameterType)); instructions.Add(this._createStlocInstruction(processor, j)); } + instructions.Add(processor.Create(OpCodes.Ldsfld, hooksField)); instructions.Add(processor.Create(OpCodes.Ldstr, qualifyName)); if (!targetMethod.IsStatic) @@ -328,43 +350,45 @@ namespace DllRewrite instructions.Add(processor.Create(OpCodes.Box, targetType)); } - k = (byte)(targetMethod.Parameters.Count + parameterOffset > prefixHook.Parameters.Count - 2 ? prefixHook.Parameters.Count - 2 - parameterOffset : targetMethod.Parameters.Count + parameterOffset); + k = (byte) (targetMethod.Parameters.Count + parameterOffset > prefixHook.Parameters.Count - 2 + ? prefixHook.Parameters.Count - 2 - parameterOffset + : targetMethod.Parameters.Count + parameterOffset); for (i = parameterOffset, j = parameterIndexBegin; i < k; i++, j++) - { instructions.Add(processor.Create(OpCodes.Ldloca_S, j)); - } for (; i < prefixHook.Parameters.Count - 2; i++) - { - instructions.Add(processor.Create(OpCodes.Ldloca_S, (byte)0)); - } + instructions.Add(processor.Create(OpCodes.Ldloca_S, (byte) 0)); instructions.Add(processor.Create(OpCodes.Ldloca_S, returnIndex)); instructions.Add(processor.Create(OpCodes.Callvirt, prefixHook)); instructions.Add(this._createStlocInstruction(processor, stateIndex)); - for (i = parameterOffset, j = parameterIndexBegin; i < targetMethod.Parameters.Count + parameterOffset; i++, j++) + for (i = parameterOffset, j = parameterIndexBegin; + i < targetMethod.Parameters.Count + parameterOffset; + i++, j++) { instructions.Add(this._createLdlocInstruction(processor, j)); if (targetMethod.Parameters[i - parameterOffset].ParameterType.IsValueType) - { - instructions.Add(processor.Create(OpCodes.Unbox_Any, targetMethod.Parameters[i - parameterOffset].ParameterType)); - } + instructions.Add(processor.Create(OpCodes.Unbox_Any, + targetMethod.Parameters[i - parameterOffset].ParameterType)); else - { - instructions.Add(processor.Create(OpCodes.Castclass, targetMethod.Parameters[i - parameterOffset].ParameterType)); - } + instructions.Add(processor.Create(OpCodes.Castclass, + targetMethod.Parameters[i - parameterOffset].ParameterType)); instructions.Add(processor.Create(OpCodes.Starg_S, i)); } + instructions.Add(this._createLdlocInstruction(processor, stateIndex)); instructions.Add(processor.Create(OpCodes.Brtrue, jointPoint)); - if (targetMethod.ReturnType.FullName != "System.Void") { + if (targetMethod.ReturnType.FullName != "System.Void") + { instructions.Add(this._createLdlocInstruction(processor, returnIndex)); if (targetMethod.ReturnType.IsValueType) instructions.Add(processor.Create(OpCodes.Unbox_Any, targetMethod.ReturnType)); else instructions.Add(processor.Create(OpCodes.Castclass, targetMethod.ReturnType)); } + instructions.Add(processor.Create(OpCodes.Ret)); this.InsertInstructions(processor, jointPoint, instructions); } + if (patchPostfix && postfixHook != null) { instructions = new List(); @@ -374,16 +398,19 @@ namespace DllRewrite instructions.Add(processor.Create(OpCodes.Box, targetMethod.ReturnType)); instructions.Add(this._createStlocInstruction(processor, returnIndex)); } - k = (byte)(targetMethod.Parameters.Count + parameterOffset > postfixHook.Parameters.Count - 3 ? prefixHook.Parameters.Count - 3 - parameterOffset : targetMethod.Parameters.Count + parameterOffset); + + k = (byte) (targetMethod.Parameters.Count + parameterOffset > postfixHook.Parameters.Count - 3 + ? prefixHook.Parameters.Count - 3 - parameterOffset + : targetMethod.Parameters.Count + parameterOffset); for (i = parameterOffset, j = parameterIndexBegin; i < k; i++, j++) { instructions.Add(this._createLdargsInstruction(processor, i)); if (targetMethod.Parameters[i - parameterOffset].ParameterType.IsValueType) - { - instructions.Add(processor.Create(OpCodes.Box, targetMethod.Parameters[i - parameterOffset].ParameterType)); - } + instructions.Add(processor.Create(OpCodes.Box, + targetMethod.Parameters[i - parameterOffset].ParameterType)); instructions.Add(this._createStlocInstruction(processor, j)); } + instructions.Add(processor.Create(OpCodes.Ldsfld, hooksField)); instructions.Add(processor.Create(OpCodes.Ldstr, qualifyName)); if (!targetMethod.IsStatic) @@ -392,15 +419,14 @@ namespace DllRewrite if (targetType.IsValueType) instructions.Add(processor.Create(OpCodes.Box, targetType)); } - k = (byte)(targetMethod.Parameters.Count + parameterOffset > postfixHook.Parameters.Count - 3 ? prefixHook.Parameters.Count - 3 - parameterOffset : targetMethod.Parameters.Count + parameterOffset); + + k = (byte) (targetMethod.Parameters.Count + parameterOffset > postfixHook.Parameters.Count - 3 + ? prefixHook.Parameters.Count - 3 - parameterOffset + : targetMethod.Parameters.Count + parameterOffset); for (i = parameterOffset, j = parameterIndexBegin; i < k; i++, j++) - { instructions.Add(processor.Create(OpCodes.Ldloca_S, j)); - } for (; i < postfixHook.Parameters.Count - 3; i++) - { - instructions.Add(processor.Create(OpCodes.Ldloca_S, (byte)0)); - } + instructions.Add(processor.Create(OpCodes.Ldloca_S, (byte) 0)); if (targetMethod.ReturnType.FullName != "System.Void") { instructions.Add(processor.Create(OpCodes.Ldloca_S, stateIndex)); @@ -418,10 +444,11 @@ namespace DllRewrite instructions.Add(processor.Create(OpCodes.Ldloca_S, returnIndex)); instructions.Add(processor.Create(OpCodes.Callvirt, postfixHook)); } + this.InsertInstructions(processor, jointPoint, instructions); for (int x = 0; x < targetMethod.Body.Instructions.Count - 1; x++) { - Instruction origin = targetMethod.Body.Instructions[x]; + var origin = targetMethod.Body.Instructions[x]; _patchPostfixReturn(processor, instructions, origin); } } @@ -429,46 +456,44 @@ namespace DllRewrite public void ApplyHookEntry(TypeDefinition targetType, string methodname, string hookname, bool isPrefix) { - TypeDefinition typeModHooksObject = this.StardewValley.MainModule.GetType("StardewValley.ModHooks"); + var typeModHooksObject = this.StardewValley.MainModule.GetType("StardewValley.ModHooks"); var hook = typeModHooksObject.Methods.FirstOrDefault(m => m.Name == hookname); - foreach (MethodDefinition method in targetType.Methods) - { + foreach (var method in targetType.Methods) if (!method.IsConstructor && method.HasBody && method.Name == methodname) { var processor = method.Body.GetILProcessor(); Instruction jointPoint; - List instructions = new List(); - FieldReference hooksField = this.GetFieldReference("hooks", "StardewValley.Game1", this.StardewValley); + var instructions = new List(); + var hooksField = this.GetFieldReference("hooks", "StardewValley.Game1", this.StardewValley); if (!isPrefix) { jointPoint = method.Body.Instructions[method.Body.Instructions.Count - 1]; if (method.ReturnType.FullName != "System.Void") { method.Body.Variables.Add(new VariableDefinition(method.ReturnType)); - instructions.Add(this._createStlocInstruction(processor, (byte)(method.Body.Variables.Count - 1))); + instructions.Add(this._createStlocInstruction(processor, + (byte) (method.Body.Variables.Count - 1))); } + instructions.Add(processor.Create(OpCodes.Ldsfld, hooksField)); - if (!method.IsStatic) - { - instructions.Add(processor.Create(OpCodes.Ldarg_0)); - } - for(byte i = (method.IsStatic ? (byte)0 : (byte)1); i < method.Parameters.Count + (method.IsStatic ? (byte)0 : (byte)1); i++) - { - instructions.Add(this._createLdargsInstruction(processor, i)); - } + if (!method.IsStatic) instructions.Add(processor.Create(OpCodes.Ldarg_0)); + for (byte i = method.IsStatic ? (byte) 0 : (byte) 1; + i < method.Parameters.Count + (method.IsStatic ? (byte) 0 : (byte) 1); + i++) instructions.Add(this._createLdargsInstruction(processor, i)); if (method.ReturnType.FullName != "System.Void") { - instructions.Add(processor.Create(OpCodes.Ldloca_S, (byte)(method.Body.Variables.Count - 1))); + instructions.Add(processor.Create(OpCodes.Ldloca_S, + (byte) (method.Body.Variables.Count - 1))); instructions.Add(processor.Create(OpCodes.Callvirt, hook)); - instructions.Add(this._createLdlocInstruction(processor, (byte)(method.Body.Variables.Count - 1))); + instructions.Add(this._createLdlocInstruction(processor, + (byte) (method.Body.Variables.Count - 1))); } else - { instructions.Add(processor.Create(OpCodes.Callvirt, hook)); - } - for(int i = 0; i < method.Body.Instructions.Count - 1; i++) + + for (int i = 0; i < method.Body.Instructions.Count - 1; i++) { - Instruction origin = method.Body.Instructions[i]; + var origin = method.Body.Instructions[i]; _patchPostfixReturn(processor, instructions, origin); } } @@ -476,24 +501,20 @@ namespace DllRewrite { jointPoint = method.Body.Instructions[0]; if (method.ReturnType.FullName != "System.Void") - { method.Body.Variables.Add(new VariableDefinition(method.ReturnType)); - } instructions.Add(processor.Create(OpCodes.Ldsfld, hooksField)); - if (!method.IsStatic) - { - instructions.Add(processor.Create(OpCodes.Ldarg_0)); - } - for (byte i = (method.IsStatic ? (byte)0 : (byte)1); i < method.Parameters.Count + (method.IsStatic ? (byte)0 : (byte)1); i++) - { - instructions.Add(this._createLdargsInstruction(processor, i)); - } + if (!method.IsStatic) instructions.Add(processor.Create(OpCodes.Ldarg_0)); + for (byte i = method.IsStatic ? (byte) 0 : (byte) 1; + i < method.Parameters.Count + (method.IsStatic ? (byte) 0 : (byte) 1); + i++) instructions.Add(this._createLdargsInstruction(processor, i)); if (method.ReturnType.FullName != "System.Void") { - instructions.Add(processor.Create(OpCodes.Ldloca_S, (byte)(method.Body.Variables.Count - 1))); + instructions.Add(processor.Create(OpCodes.Ldloca_S, + (byte) (method.Body.Variables.Count - 1))); instructions.Add(processor.Create(OpCodes.Callvirt, hook)); instructions.Add(processor.Create(OpCodes.Brtrue, jointPoint)); - instructions.Add(this._createLdlocInstruction(processor, (byte)(method.Body.Variables.Count - 1))); + instructions.Add(this._createLdlocInstruction(processor, + (byte) (method.Body.Variables.Count - 1))); instructions.Add(processor.Create(OpCodes.Ret)); } else @@ -503,100 +524,98 @@ namespace DllRewrite instructions.Add(processor.Create(OpCodes.Ret)); } } + this.InsertInstructions(processor, jointPoint, instructions); } - } - } - private static void _patchPostfixReturn(ILProcessor processor, List instructions, Instruction origin) + private static void _patchPostfixReturn(ILProcessor processor, List instructions, + Instruction origin) { if (origin.OpCode == OpCodes.Ret) - { processor.Replace(origin, processor.Create(OpCodes.Br, instructions[0])); - } else { - if ((origin.OpCode == OpCodes.Br && ((Instruction)origin.Operand).OpCode == OpCodes.Ret) - || (origin.OpCode == OpCodes.Br_S && ((Instruction)origin.Operand).OpCode == OpCodes.Ret)) + if (origin.OpCode == OpCodes.Br && ((Instruction) origin.Operand).OpCode == OpCodes.Ret + || origin.OpCode == OpCodes.Br_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Br; origin.Operand = instructions[0]; } - else if ((origin.OpCode == OpCodes.Brfalse && ((Instruction)origin.Operand).OpCode == OpCodes.Ret) - || (origin.OpCode == OpCodes.Brfalse_S && ((Instruction)origin.Operand).OpCode == OpCodes.Ret)) + else if (origin.OpCode == OpCodes.Brfalse && ((Instruction) origin.Operand).OpCode == OpCodes.Ret + || origin.OpCode == OpCodes.Brfalse_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Brfalse; origin.Operand = instructions[0]; } - else if ((origin.OpCode == OpCodes.Brtrue && ((Instruction)origin.Operand).OpCode == OpCodes.Ret) - || (origin.OpCode == OpCodes.Brtrue_S && ((Instruction)origin.Operand).OpCode == OpCodes.Ret)) + else if (origin.OpCode == OpCodes.Brtrue && ((Instruction) origin.Operand).OpCode == OpCodes.Ret + || origin.OpCode == OpCodes.Brtrue_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Brtrue; origin.Operand = instructions[0]; } - else if ((origin.OpCode == OpCodes.Beq && ((Instruction)origin.Operand).OpCode == OpCodes.Ret) - || (origin.OpCode == OpCodes.Beq_S && ((Instruction)origin.Operand).OpCode == OpCodes.Ret)) + else if (origin.OpCode == OpCodes.Beq && ((Instruction) origin.Operand).OpCode == OpCodes.Ret + || origin.OpCode == OpCodes.Beq_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Beq; origin.Operand = instructions[0]; } - else if ((origin.OpCode == OpCodes.Bge && ((Instruction)origin.Operand).OpCode == OpCodes.Ret) - || (origin.OpCode == OpCodes.Bge_S && ((Instruction)origin.Operand).OpCode == OpCodes.Ret)) + else if (origin.OpCode == OpCodes.Bge && ((Instruction) origin.Operand).OpCode == OpCodes.Ret + || origin.OpCode == OpCodes.Bge_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Bge; origin.Operand = instructions[0]; } - else if ((origin.OpCode == OpCodes.Bge_Un && ((Instruction)origin.Operand).OpCode == OpCodes.Ret) - || (origin.OpCode == OpCodes.Bge_Un_S && ((Instruction)origin.Operand).OpCode == OpCodes.Ret)) + else if (origin.OpCode == OpCodes.Bge_Un && ((Instruction) origin.Operand).OpCode == OpCodes.Ret + || origin.OpCode == OpCodes.Bge_Un_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Bge_Un; origin.Operand = instructions[0]; } - else if ((origin.OpCode == OpCodes.Bgt && ((Instruction)origin.Operand).OpCode == OpCodes.Ret) - || (origin.OpCode == OpCodes.Bgt_S && ((Instruction)origin.Operand).OpCode == OpCodes.Ret)) + else if (origin.OpCode == OpCodes.Bgt && ((Instruction) origin.Operand).OpCode == OpCodes.Ret + || origin.OpCode == OpCodes.Bgt_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Bgt; origin.Operand = instructions[0]; } - else if ((origin.OpCode == OpCodes.Bgt_Un && ((Instruction)origin.Operand).OpCode == OpCodes.Ret) - || (origin.OpCode == OpCodes.Bgt_Un_S && ((Instruction)origin.Operand).OpCode == OpCodes.Ret)) + else if (origin.OpCode == OpCodes.Bgt_Un && ((Instruction) origin.Operand).OpCode == OpCodes.Ret + || origin.OpCode == OpCodes.Bgt_Un_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Bgt_Un; origin.Operand = instructions[0]; } - else if ((origin.OpCode == OpCodes.Ble && ((Instruction)origin.Operand).OpCode == OpCodes.Ret) - || (origin.OpCode == OpCodes.Ble_S && ((Instruction)origin.Operand).OpCode == OpCodes.Ret)) + else if (origin.OpCode == OpCodes.Ble && ((Instruction) origin.Operand).OpCode == OpCodes.Ret + || origin.OpCode == OpCodes.Ble_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Ble; origin.Operand = instructions[0]; } - else if ((origin.OpCode == OpCodes.Ble_Un && ((Instruction)origin.Operand).OpCode == OpCodes.Ret) - || (origin.OpCode == OpCodes.Ble_Un_S && ((Instruction)origin.Operand).OpCode == OpCodes.Ret)) + else if (origin.OpCode == OpCodes.Ble_Un && ((Instruction) origin.Operand).OpCode == OpCodes.Ret + || origin.OpCode == OpCodes.Ble_Un_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Ble_Un; origin.Operand = instructions[0]; } - else if ((origin.OpCode == OpCodes.Blt && ((Instruction)origin.Operand).OpCode == OpCodes.Ret) - || (origin.OpCode == OpCodes.Blt_S && ((Instruction)origin.Operand).OpCode == OpCodes.Ret)) + else if (origin.OpCode == OpCodes.Blt && ((Instruction) origin.Operand).OpCode == OpCodes.Ret + || origin.OpCode == OpCodes.Blt_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Blt; origin.Operand = instructions[0]; } - else if ((origin.OpCode == OpCodes.Blt_Un && ((Instruction)origin.Operand).OpCode == OpCodes.Ret) - || (origin.OpCode == OpCodes.Blt_Un_S && ((Instruction)origin.Operand).OpCode == OpCodes.Ret)) + else if (origin.OpCode == OpCodes.Blt_Un && ((Instruction) origin.Operand).OpCode == OpCodes.Ret + || origin.OpCode == OpCodes.Blt_Un_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Blt_Un; origin.Operand = instructions[0]; } - else if ((origin.OpCode == OpCodes.Bne_Un && ((Instruction)origin.Operand).OpCode == OpCodes.Ret) - || (origin.OpCode == OpCodes.Bne_Un_S && ((Instruction)origin.Operand).OpCode == OpCodes.Ret)) + else if (origin.OpCode == OpCodes.Bne_Un && ((Instruction) origin.Operand).OpCode == OpCodes.Ret + || origin.OpCode == OpCodes.Bne_Un_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Bne_Un; origin.Operand = instructions[0]; } - else if ((origin.OpCode == OpCodes.Leave && ((Instruction)origin.Operand).OpCode == OpCodes.Ret) - || (origin.OpCode == OpCodes.Leave_S && ((Instruction)origin.Operand).OpCode == OpCodes.Ret)) + else if (origin.OpCode == OpCodes.Leave && ((Instruction) origin.Operand).OpCode == OpCodes.Ret + || origin.OpCode == OpCodes.Leave_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Leave; origin.Operand = instructions[0]; @@ -620,6 +639,7 @@ namespace DllRewrite return processor.Create(OpCodes.Stloc_S, index); } } + private Instruction _createLdlocInstruction(ILProcessor processor, byte index) { switch (index) @@ -636,6 +656,7 @@ namespace DllRewrite return processor.Create(OpCodes.Ldloc_S, index); } } + private Instruction _createLdargsInstruction(ILProcessor processor, byte index) { switch (index) @@ -652,271 +673,388 @@ namespace DllRewrite return processor.Create(OpCodes.Ldarg_S, index); } } + private void InsertInstructions(ILProcessor processor, Instruction jointPoint, List instructions) { - foreach(Instruction instruction in instructions) - { - processor.InsertBefore(jointPoint, instruction); - } + foreach (var instruction in instructions) processor.InsertBefore(jointPoint, instruction); } + public AssemblyDefinition InsertModHooks() { this.ApplyGamePatch(); - this.InsertModHook("OnCommonHook_Prefix", new[] { - this.GetTypeReference("System.String"), - this.GetTypeReference("System.Object"), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object"))}, - new[] { + this.InsertModHook("OnCommonHook_Prefix", new[] + { + this.GetTypeReference("System.String"), + this.GetTypeReference("System.Object"), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")) + }, + new[] + { "hookName", "__instance", "param1", "param2", "param3", - "param4", "__result"}, + "param4", "__result" + }, this.GetTypeReference("System.Boolean")); - this.InsertModHook("OnCommonStaticHook_Prefix", new[] { - this.GetTypeReference("System.String"), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object"))}, - new[] { + this.InsertModHook("OnCommonStaticHook_Prefix", new[] + { + this.GetTypeReference("System.String"), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")) + }, + new[] + { "hookName", "param1", "param2", "param3", "param4", - "param5", "__result"}, + "param5", "__result" + }, this.GetTypeReference("System.Boolean")); - this.InsertModHook("OnCommonHook_Postfix", new[] { - this.GetTypeReference("System.String"), - this.GetTypeReference("System.Object"), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Boolean")), - new ByReferenceType(this.GetTypeReference("System.Object"))}, - new[] { + this.InsertModHook("OnCommonHook_Postfix", new[] + { + this.GetTypeReference("System.String"), + this.GetTypeReference("System.Object"), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Boolean")), + new ByReferenceType(this.GetTypeReference("System.Object")) + }, + new[] + { "hookName", "__instance", "param1", "param2", "param3", - "param4", "__state", "__result"}, + "param4", "__state", "__result" + }, this.GetTypeReference("System.Void")); - this.InsertModHook("OnCommonStaticHook_Postfix", new[] { - this.GetTypeReference("System.String"), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Boolean")), - new ByReferenceType(this.GetTypeReference("System.Object"))}, - new[] { + this.InsertModHook("OnCommonStaticHook_Postfix", new[] + { + this.GetTypeReference("System.String"), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Boolean")), + new ByReferenceType(this.GetTypeReference("System.Object")) + }, + new[] + { "hookName", "param1", "param2", "param3", "param4", - "param5", "__state", "__result"}, + "param5", "__state", "__result" + }, this.GetTypeReference("System.Void")); - this.InsertModHook("OnCommonHook10_Prefix", new[] { - this.GetTypeReference("System.String"), - this.GetTypeReference("System.Object"), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object"))}, - new[] { + this.InsertModHook("OnCommonHook10_Prefix", new[] + { + this.GetTypeReference("System.String"), + this.GetTypeReference("System.Object"), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")) + }, + new[] + { "hookName", "__instance", "param1", "param2", "param3", "param4", "param5", "param6", "param7", "param8", "param9", - "__result"}, + "__result" + }, this.GetTypeReference("System.Boolean")); - this.InsertModHook("OnCommonStaticHook10_Prefix", new[] { - this.GetTypeReference("System.String"), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object"))}, - new[] { + this.InsertModHook("OnCommonStaticHook10_Prefix", new[] + { + this.GetTypeReference("System.String"), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")) + }, + new[] + { "hookName", "param1", "param2", "param3", "param4", "param5", "param6", "param7", "param8", "param9", "param10", - "__result"}, + "__result" + }, this.GetTypeReference("System.Boolean")); - this.InsertModHook("OnCommonHook10_Postfix", new[] { - this.GetTypeReference("System.String"), - this.GetTypeReference("System.Object"), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Boolean")), - new ByReferenceType(this.GetTypeReference("System.Object"))}, - new[] { + this.InsertModHook("OnCommonHook10_Postfix", new[] + { + this.GetTypeReference("System.String"), + this.GetTypeReference("System.Object"), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Boolean")), + new ByReferenceType(this.GetTypeReference("System.Object")) + }, + new[] + { "hookName", "__instance", "param1", "param2", "param3", "param4", "param5", "param6", "param7", "param8", "param9", - "__state", "__result"}, + "__state", "__result" + }, this.GetTypeReference("System.Void")); - this.InsertModHook("OnCommonStaticHook10_Postfix", new[] { - this.GetTypeReference("System.String"), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Object")), - new ByReferenceType(this.GetTypeReference("System.Boolean")), - new ByReferenceType(this.GetTypeReference("System.Object"))}, - new[] { + this.InsertModHook("OnCommonStaticHook10_Postfix", new[] + { + this.GetTypeReference("System.String"), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Object")), + new ByReferenceType(this.GetTypeReference("System.Boolean")), + new ByReferenceType(this.GetTypeReference("System.Object")) + }, + new[] + { "hookName", "param1", "param2", "param3", "param4", "param5", "param6", "param7", "param8", "param9", "param10", - "__state", "__result"}, + "__state", "__result" + }, this.GetTypeReference("System.Void")); // On Game1 hooks - TypeDefinition typeGame1 = this.StardewValley.MainModule.GetType("StardewValley.Game1"); + var typeGame1 = this.StardewValley.MainModule.GetType("StardewValley.Game1"); this.ApplyCommonHookEntry(typeGame1, "Update", "OnCommonHook"); this.ApplyCommonHookEntry(typeGame1, "_draw", "OnCommonHook"); this.ApplyCommonHookEntry(typeGame1, "getSourceRectForStandardTileSheet", "OnCommonStaticHook"); this.ApplyCommonHookEntry(typeGame1, "tryToCheckAt", "OnCommonStaticHook", false); this.ApplyCommonHookEntry(typeGame1, "getLocationRequest", "OnCommonStaticHook", true, false); - this.ApplyCommonHookEntry(typeGame1, "getLocationRequest", "OnCommonStaticHook", true, false); this.ApplyCommonHookEntry(typeGame1, "loadForNewGame", "OnCommonStaticHook"); - this.ApplyCommonHookEntry(typeGame1, "warpFarmer", "OnCommonStaticHook10", true, false, method => method.Parameters.Count == 6); + this.ApplyCommonHookEntry(typeGame1, "warpFarmer", "OnCommonStaticHook10", true, false, + method => method.Parameters.Count == 6); this.ApplyCommonHookEntry(typeGame1, ".ctor", "OnCommonHook", false); - this.ApplyCommonHookEntry(typeGame1, "LoadContent", "OnCommonHook", false); + this.ApplyCommonHookEntry(typeGame1, "saveWholeBackup", "OnCommonStaticHook"); + this.ApplyCommonHookEntry(typeGame1, "MakeFullBackup", "OnCommonStaticHook"); TypeDefinition targetType = null; - foreach (TypeDefinition definition21 in this.StardewValley.MainModule.GetTypes()) - { + foreach (var definition21 in this.StardewValley.MainModule.GetTypes()) if (definition21.FullName == "StardewValley.Game1/<>c") - { targetType = definition21; - } - } - this.ApplyCommonMidHookEntry(targetType, method => method.FullName.Contains("showEndOfNightStuff"), ins => (ins.OpCode == OpCodes.Ldstr) && (((string)ins.Operand) == "newRecord"), -2, "OnCommonHook"); + this.ApplyCommonMidHookEntry(targetType, method => method.FullName.Contains("showEndOfNightStuff"), + ins => ins.OpCode == OpCodes.Ldstr && (string) ins.Operand == "newRecord", -2, "OnCommonHook"); // On Object hooks - TypeDefinition typeObject = this.StardewValley.MainModule.GetType("StardewValley.Object"); + var typeObject = this.StardewValley.MainModule.GetType("StardewValley.Object"); this.ApplyCommonHookEntry(typeObject, "canBePlacedHere", "OnCommonHook", true, false); this.ApplyCommonHookEntry(typeObject, "checkForAction", "OnCommonHook", true, false); this.ApplyCommonHookEntry(typeObject, "isIndexOkForBasicShippedCategory", "OnCommonStaticHook"); this.ApplyCommonHookEntry(typeObject, "drawWhenHeld", "OnCommonHook", true, false); this.ApplyCommonHookEntry(typeObject, "drawInMenuWithColour", "OnCommonHook10"); - this.ApplyCommonHookEntry(typeObject, "draw", "OnCommonHook", true, false, method => method.Parameters.Count == 4); - this.ApplyCommonHookEntry(typeObject, "draw", "OnCommonHook10", true, false, method => method.Parameters.Count == 5); + this.ApplyCommonHookEntry(typeObject, "draw", "OnCommonHook", true, false, + method => method.Parameters.Count == 4); + this.ApplyCommonHookEntry(typeObject, "draw", "OnCommonHook10", true, false, + method => method.Parameters.Count == 5); this.ApplyCommonHookEntry(typeObject, "getDescription", "OnCommonHook", true, false); + this.ApplyCommonHookEntry(typeObject, "maximumStackSize", "OnCommonHook", true, false); + this.ApplyCommonHookEntry(typeObject, "addToStack", "OnCommonHook", true, false); + this.ApplyCommonHookEntry(typeObject, "performObjectDropInAction", "OnCommonHook", true, false); + // On ReadyCheckDialog hooks - TypeDefinition typeReadyCheckDialog = this.StardewValley.MainModule.GetType("StardewValley.Menus.ReadyCheckDialog"); + var typeReadyCheckDialog = this.StardewValley.MainModule.GetType("StardewValley.Menus.ReadyCheckDialog"); this.ApplyCommonHookEntry(typeReadyCheckDialog, "update", "OnCommonHook"); // On IClickableMenu hooks - TypeDefinition typeIClickableMenu = this.StardewValley.MainModule.GetType("StardewValley.Menus.IClickableMenu"); + var typeIClickableMenu = this.StardewValley.MainModule.GetType("StardewValley.Menus.IClickableMenu"); this.ApplyCommonHookEntry(typeIClickableMenu, "drawToolTip", "OnCommonStaticHook10", false); // On Dialogue hooks - TypeDefinition typeDialogue = this.StardewValley.MainModule.GetType("StardewValley.Dialogue"); - this.ApplyCommonHookEntry(typeDialogue, ".ctor", "OnCommonHook", true, false, method => method.Parameters.Count == 2); + var typeDialogue = this.StardewValley.MainModule.GetType("StardewValley.Dialogue"); + this.ApplyCommonHookEntry(typeDialogue, ".ctor", "OnCommonHook", true, false, + method => method.Parameters.Count == 2); + + // On CraftingRecipe hooks + var typeCraftingRecipe = this.StardewValley.MainModule.GetType("StardewValley.CraftingRecipe"); + this.ApplyCommonHookEntry(typeCraftingRecipe, "consumeIngredients", "OnCommonHook", true, false); // On Building hooks - TypeDefinition typeBuilding = this.StardewValley.MainModule.GetType("StardewValley.Buildings.Building"); + var typeBuilding = this.StardewValley.MainModule.GetType("StardewValley.Buildings.Building"); this.ApplyCommonHookEntry(typeBuilding, "load", "OnCommonHook"); // On GameLocation hooks - TypeDefinition typeGameLocation = this.StardewValley.MainModule.GetType("StardewValley.GameLocation"); + var typeGameLocation = this.StardewValley.MainModule.GetType("StardewValley.GameLocation"); this.ApplyCommonHookEntry(typeGameLocation, "performTouchAction", "OnCommonHook", true, false); this.ApplyCommonHookEntry(typeGameLocation, "isActionableTile", "OnCommonHook", false); this.ApplyCommonHookEntry(typeGameLocation, "tryToAddCritters", "OnCommonHook", true, false); this.ApplyCommonHookEntry(typeGameLocation, "getSourceRectForObject", "OnCommonStaticHook"); this.ApplyCommonHookEntry(typeGameLocation, "answerDialogue", "OnCommonHook", true, false); - this.ApplyCommonHookEntry(typeGameLocation, "Equals", "OnCommonHook", true, false, method => method.Parameters[0].ParameterType == this.GetTypeReference("StardewValley.GameLocation", this.StardewValley)); + this.ApplyCommonHookEntry(typeGameLocation, "Equals", "OnCommonHook", true, false, + method => method.Parameters[0].ParameterType == + this.GetTypeReference("StardewValley.GameLocation", this.StardewValley)); this.ApplyCommonHookEntry(typeGameLocation, "performAction", "OnCommonHook", true, false); // On Objects.TV hooks - TypeDefinition typeObjectsTV = this.StardewValley.MainModule.GetType("StardewValley.Objects.TV"); + var typeObjectsTV = this.StardewValley.MainModule.GetType("StardewValley.Objects.TV"); this.ApplyCommonHookEntry(typeObjectsTV, "checkForAction", "OnCommonHook"); // On Furniture hooks - TypeDefinition typeFurniture = this.StardewValley.MainModule.GetType("StardewValley.Objects.Furniture"); + var typeFurniture = this.StardewValley.MainModule.GetType("StardewValley.Objects.Furniture"); this.ApplyCommonHookEntry(typeFurniture, "draw", "OnCommonHook", true, false); // On ColoredObject hooks - TypeDefinition typeColoredObject = this.StardewValley.MainModule.GetType("StardewValley.Objects.ColoredObject"); + var typeColoredObject = this.StardewValley.MainModule.GetType("StardewValley.Objects.ColoredObject"); this.ApplyCommonHookEntry(typeColoredObject, "drawInMenu", "OnCommonHook10", false); // On HoeDirt hooks - TypeDefinition typeHoeDirt = this.StardewValley.MainModule.GetType("StardewValley.TerrainFeatures.HoeDirt"); + var typeHoeDirt = this.StardewValley.MainModule.GetType("StardewValley.TerrainFeatures.HoeDirt"); this.ApplyCommonHookEntry(typeHoeDirt, "dayUpdate", "OnCommonHook", true, false); + this.ApplyCommonHookEntry(typeHoeDirt, "canPlantThisSeedHere", "OnCommonHook"); + + // On Tree hooks + var typeTree = this.StardewValley.MainModule.GetType("StardewValley.TerrainFeatures.Tree"); + this.ApplyCommonHookEntry(typeTree, "performToolAction", "OnCommonHook", true, false); + + // On FruitTree hooks + var typeFruitTree = this.StardewValley.MainModule.GetType("StardewValley.TerrainFeatures.FruitTree"); + this.ApplyCommonHookEntry(typeFruitTree, "performToolAction", "OnCommonHook", true, false); + + // On ResourceClump hooks + var typeResourceClump = this.StardewValley.MainModule.GetType("StardewValley.TerrainFeatures.ResourceClump"); + this.ApplyCommonHookEntry(typeResourceClump, "performToolAction", "OnCommonHook", true, false); + + // On Crop hooks + var typeCrop = this.StardewValley.MainModule.GetType("StardewValley.Crop"); + this.ApplyCommonHookEntry(typeCrop, "newDay", "OnCommonHook10", true, false); // On Utility hooks - TypeDefinition typeUtility = this.StardewValley.MainModule.GetType("StardewValley.Utility"); + var typeUtility = this.StardewValley.MainModule.GetType("StardewValley.Utility"); this.ApplyCommonHookEntry(typeUtility, "pickFarmEvent", "OnCommonStaticHook", false); // On Farmer hooks - TypeDefinition typeFarmer = this.StardewValley.MainModule.GetType("StardewValley.Farmer"); + var typeFarmer = this.StardewValley.MainModule.GetType("StardewValley.Farmer"); this.ApplyCommonHookEntry(typeFarmer, "doneEating", "OnCommonHook", false); - + this.ApplyCommonHookEntry(typeFarmer, "hasItemInInventory", "OnCommonHook", false); + this.ApplyCommonHookEntry(typeFarmer, "getTallyOfObject", "OnCommonHook", false); + // On MeleeWeapon hooks - TypeDefinition typeMeleeWeapon = this.StardewValley.MainModule.GetType("StardewValley.Tools.MeleeWeapon"); - this.ApplyCommonHookEntry(typeMeleeWeapon, "drawDuringUse", "OnCommonStaticHook10", true, false, method => method.IsStatic); + var typeMeleeWeapon = this.StardewValley.MainModule.GetType("StardewValley.Tools.MeleeWeapon"); + this.ApplyCommonHookEntry(typeMeleeWeapon, "drawDuringUse", "OnCommonStaticHook10", true, false, + method => method.IsStatic); // On Multiplayer hooks - TypeDefinition typeMultiplayer = this.StardewValley.MainModule.GetType("StardewValley.Multiplayer"); + var typeMultiplayer = this.StardewValley.MainModule.GetType("StardewValley.Multiplayer"); this.ApplyCommonHookEntry(typeMultiplayer, "processIncomingMessage", "OnCommonHook", true, false); // On GameServer hooks - TypeDefinition typeGameServer = this.StardewValley.MainModule.GetType("StardewValley.Network.GameServer"); + var typeGameServer = this.StardewValley.MainModule.GetType("StardewValley.Network.GameServer"); this.ApplyCommonHookEntry(typeGameServer, "sendServerIntroduction", "OnCommonHook", false); // On NPC hooks - TypeDefinition typeNPC = this.StardewValley.MainModule.GetType("StardewValley.NPC"); + var typeNPC = this.StardewValley.MainModule.GetType("StardewValley.NPC"); this.ApplyCommonHookEntry(typeNPC, "receiveGift", "OnCommonHook10", false); + // On Farm hooks + var typeFarm = this.StardewValley.MainModule.GetType("StardewValley.Farm"); + this.ApplyCommonHookEntry(typeFarm, "addCrows", "OnCommonHook", true, false); + // On GameMenu hooks - TypeDefinition definition19 = this.StardewValley.MainModule.GetType("StardewValley.Menus.GameMenu"); - this.ApplyCommonHookEntry(definition19, "getTabNumberFromName", "OnCommonHook", false); + var typeGameMenu = this.StardewValley.MainModule.GetType("StardewValley.Menus.GameMenu"); + this.ApplyCommonHookEntry(typeGameMenu, "getTabNumberFromName", "OnCommonHook", false); // On FarmHouse hooks - TypeDefinition definition20 = this.StardewValley.MainModule.GetType("StardewValley.Locations.FarmHouse"); - this.ApplyCommonHookEntry(definition20, "loadSpouseRoom", "OnCommonHook", true, false); + var typeFarmHouse = this.StardewValley.MainModule.GetType("StardewValley.Locations.FarmHouse"); + this.ApplyCommonHookEntry(typeFarmHouse, "loadSpouseRoom", "OnCommonHook", true, false); - this.InsertModHook("OnGame1_CreateContentManager_Prefix", new[] { - this.GetTypeReference("StardewValley.Game1", this.StardewValley), - this.GetTypeReference("System.IServiceProvider"), - this.GetTypeReference("System.String"), - new ByReferenceType(this.GetTypeReference("StardewValley.LocalizedContentManager", this.StardewValley)) }, - new[] { "game1", "serviceProvider", "rootDirectory", "__result"}, + // On Tool hooks + var typeTool = this.StardewValley.MainModule.GetType("StardewValley.Tool"); + this.ApplyCommonHookEntry(typeTool, "tilesAffected", "OnCommonHook", false); + this.ApplyCommonHookEntry(typeTool, "get_Name", "OnCommonHook", true, false); + this.ApplyCommonHookEntry(typeTool, "get_DisplayName", "OnCommonHook", true, false); + // On Pickaxe hooks + var typePickaxe = this.StardewValley.MainModule.GetType("StardewValley.Tools.Pickaxe"); + this.ApplyCommonHookEntry(typePickaxe, "DoFunction", "OnCommonHook10", true, false); + + + + + this.InsertModHook("OnGame1_CreateContentManager_Prefix", new[] + { + this.GetTypeReference("StardewValley.Game1", this.StardewValley), + this.GetTypeReference("System.IServiceProvider"), + this.GetTypeReference("System.String"), + new ByReferenceType(this.GetTypeReference("StardewValley.LocalizedContentManager", + this.StardewValley)) + }, + new[] {"game1", "serviceProvider", "rootDirectory", "__result"}, this.GetTypeReference("System.Boolean")); this.ApplyHookEntry(typeGame1, "CreateContentManager", "OnGame1_CreateContentManager_Prefix", true); return this.StardewValley; } + + public AssemblyDefinition InsertMonoHooks() + { + // On SpriteBatch hooks + var typeSpriteBatch = this.MonoGame_Framework.MainModule.GetType("Microsoft.Xna.Framework.Graphics.SpriteBatch"); + FieldReference hooksField = this.MonoGame_Framework.MainModule.ImportReference(this.GetFieldReference("Instance", "SMDroid.ModEntry", + this.StardewModdingAPI)); + var typeModHooksObject = this.StardewValley.MainModule.GetType("StardewValley.ModHooks"); + var prefixHook = typeModHooksObject.Methods.FirstOrDefault(m => m.Name == "OnCommonHook_Prefix"); + var prefixHook10 = typeModHooksObject.Methods.FirstOrDefault(m => m.Name == "OnCommonHook10_Prefix"); + var prefixHookRef = this.MonoGame_Framework.MainModule.ImportReference(prefixHook); + var prefixHook10Ref = this.MonoGame_Framework.MainModule.ImportReference(prefixHook10); + this.ApplyCommonHookEntry(typeSpriteBatch, "Draw", "OnCommonHook", true, false, + method => method.Parameters.Count == 3 && + method.Parameters[1].ParameterType.FullName == "Microsoft.Xna.Framework.Rectangle", "", + hooksField, prefixHookRef); + this.ApplyCommonHookEntry(typeSpriteBatch, "Draw", "OnCommonHook", true, false, + method => method.Parameters.Count == 3 && + method.Parameters[1].ParameterType.FullName == "Microsoft.Xna.Framework.Vector2", "Vector", + hooksField, prefixHookRef); + this.ApplyCommonHookEntry(typeSpriteBatch, "Draw", "OnCommonHook", true, false, + method => method.Parameters.Count == 4 && + method.Parameters[1].ParameterType.FullName == "Microsoft.Xna.Framework.Rectangle", "2", + hooksField, prefixHookRef); + this.ApplyCommonHookEntry(typeSpriteBatch, "Draw", "OnCommonHook", true, false, + method => method.Parameters.Count == 4 && + method.Parameters[1].ParameterType.FullName == "Microsoft.Xna.Framework.Vector2", "Vector2", + hooksField, prefixHookRef); + this.ApplyCommonHookEntry(typeSpriteBatch, "Draw", "OnCommonHook10", true, false, + method => method.Parameters.Count == 8, "8", hooksField, prefixHook10Ref); + this.ApplyCommonHookEntry(typeSpriteBatch, "Draw", "OnCommonHook10", true, false, + method => method.Parameters.Count == 9 && + method.Parameters[6].ParameterType.FullName == "Microsoft.Xna.Framework.Vector2", "9", + hooksField, prefixHook10Ref); + return this.MonoGame_Framework; + } } } diff --git a/DllRewrite/DllRewrite/Program.cs b/DllRewrite/DllRewrite/Program.cs index 610eb7c2..2b2b9a63 100644 --- a/DllRewrite/DllRewrite/Program.cs +++ b/DllRewrite/DllRewrite/Program.cs @@ -14,20 +14,9 @@ namespace DllRewrite { MethodPatcher mp = new MethodPatcher(); AssemblyDefinition StardewValley = mp.InsertModHooks(); - TypeDefinition typeModHooksObject = StardewValley.MainModule.GetType("StardewValley.ModHooks"); - - TypeDefinition typeObject = StardewValley.MainModule.GetType("StardewValley.Object"); - //foreach (MethodDefinition method in typeObject.Methods) { - // if(!method.IsConstructor && method.HasBody) - // { - // var processor = method.Body.GetILProcessor(); - // var hook = typeModHooksObject.Methods.FirstOrDefault(m => m.Name == "OnObject_xxx"); - // var newInstruction = processor.Create(OpCodes.Callvirt, hook); - // var firstInstruction = method.Body.Instructions[0]; - // processor.InsertBefore(firstInstruction, newInstruction); - // } - //} StardewValley.Write("./StardewValley.dll"); + //AssemblyDefinition MonoFramework = mp.InsertMonoHooks(); + //MonoFramework.Write("./MonoGame.Framework.dll"); } } } diff --git a/src/GameConsole.cs b/src/GameConsole.cs new file mode 100644 index 00000000..b9754e70 --- /dev/null +++ b/src/GameConsole.cs @@ -0,0 +1,118 @@ +using System.Collections.Generic; +using System.Reflection; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using StardewModdingAPI.Framework; +using StardewModdingAPI.Internal.ConsoleWriting; +using StardewValley; +using StardewValley.Menus; + +namespace StardewModdingAPI +{ + class GameConsole : IClickableMenu + { + public static GameConsole Instance; + public bool IsVisible; + + private readonly LinkedList> _consoleMessageQueue = new LinkedList>(); + private readonly TextBox Textbox; + private Rectangle TextboxBounds; + + private SpriteFont _smallFont; + + internal GameConsole() + { + Instance = this; + this.IsVisible = true; + this.Textbox = new TextBox(null, null, Game1.dialogueFont, Game1.textColor) + { + X = 0, + Y = 0, + Width = 1280, + Height = 320 + }; + this.TextboxBounds = new Rectangle(this.Textbox.X, this.Textbox.Y, this.Textbox.Width, this.Textbox.Height); + } + + internal void InitContent(LocalizedContentManager content) + { + this._smallFont = content.Load(@"Fonts\SmallFont"); + } + + public void Show() + { + Game1.activeClickableMenu = this; + this.IsVisible = true; + } + + public override void receiveLeftClick(int x, int y, bool playSound = true) + { + if (this.TextboxBounds.Contains(x, y)) + { + this.Textbox.OnEnterPressed += sender => { SGame.instance.CommandQueue.Enqueue(sender.Text); this.Textbox.Text = ""; }; + Game1.keyboardDispatcher.Subscriber = this.Textbox; + typeof(TextBox).GetMethod("ShowAndroidKeyboard", BindingFlags.NonPublic | BindingFlags.Instance)?.Invoke(this.Textbox, new object[] { }); + } + else + { + Game1.activeClickableMenu = null; + this.IsVisible = false; + } + } + + public void WriteLine(string consoleMessage, ConsoleLogLevel level) + { + lock (this._consoleMessageQueue) + { + this._consoleMessageQueue.AddFirst(new KeyValuePair(level, consoleMessage)); + if (this._consoleMessageQueue.Count > 2000) + { + this._consoleMessageQueue.RemoveLast(); + } + } + } + + public override void draw(SpriteBatch spriteBatch) + { + Vector2 size = this._smallFont.MeasureString("aA"); + float y = Game1.game1.screen.Height - size.Y * 2; + lock (this._consoleMessageQueue) + { + foreach (var log in this._consoleMessageQueue) + { + string text = log.Value; + switch (log.Key) + { + case ConsoleLogLevel.Critical: + case ConsoleLogLevel.Error: + spriteBatch.DrawString(this._smallFont, text, new Vector2(16, y), Color.Red); + break; + case ConsoleLogLevel.Alert: + case ConsoleLogLevel.Warn: + spriteBatch.DrawString(this._smallFont, text, new Vector2(16, y), Color.Orange); + break; + case ConsoleLogLevel.Info: + case ConsoleLogLevel.Success: + spriteBatch.DrawString(this._smallFont, text, new Vector2(16, y), Color.AntiqueWhite); + break; + case ConsoleLogLevel.Debug: + case ConsoleLogLevel.Trace: + spriteBatch.DrawString(this._smallFont, text, new Vector2(16, y), Color.LightGray); + break; + default: + spriteBatch.DrawString(this._smallFont, text, new Vector2(16, y), Color.LightGray); + break; + } + + size = this._smallFont.MeasureString(text); + if (y < 0) + { + break; + } + y -= size.Y; + } + } + } + } +} diff --git a/src/Mod.csproj b/src/Mod.csproj index 2ca0afa8..403b32d9 100644 --- a/src/Mod.csproj +++ b/src/Mod.csproj @@ -426,6 +426,7 @@ + diff --git a/src/ModEntry.cs b/src/ModEntry.cs index 129bf523..fbe105cb 100644 --- a/src/ModEntry.cs +++ b/src/ModEntry.cs @@ -5,6 +5,7 @@ using StardewModdingAPI.Framework; using System.Threading; using Microsoft.Xna.Framework.Graphics; using System.IO; +using StardewModdingAPI; using StardewValley.Menus; using StardewValley.Buildings; using StardewValley.Objects; @@ -21,9 +22,16 @@ namespace SMDroid /// SMAPI's content manager. private ContentCoordinator ContentCore { get; set; } + public static bool ContextInitialize = true; + + public static ModEntry Instance; + + public static bool IsHalt = false; public ModEntry() { + Instance = this; + new GameConsole(); this.core = new SCore(Path.Combine(Android.OS.Environment.ExternalStorageDirectory.Path, "SMDroid/Mods"), false); } public override bool OnGame1_CreateContentManager_Prefix(Game1 game1, IServiceProvider serviceProvider, string rootDirectory, ref LocalizedContentManager __result) @@ -35,7 +43,8 @@ namespace SMDroid this.ContentCore = new ContentCoordinator(serviceProvider, rootDirectory, Thread.CurrentThread.CurrentUICulture, SGame.ConstructorHack.Monitor, SGame.ConstructorHack.Reflection, SGame.ConstructorHack.JsonHelper, SGame.OnLoadingFirstAsset ?? SGame.ConstructorHack?.OnLoadingFirstAsset); this.NextContentManagerIsMain = true; __result = this.ContentCore.CreateGameContentManager("Game1._temporaryContent"); - this.core.RunInteractively(this.ContentCore); + ContextInitialize = true; + this.core.RunInteractively(this.ContentCore, __result); return false; } // Game1.content initialising from LoadContent @@ -43,6 +52,8 @@ namespace SMDroid { this.NextContentManagerIsMain = false; __result = this.ContentCore.MainContentManager; + GameConsole.Instance.InitContent(__result); + ContextInitialize = false; return false; } diff --git a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs index f159f035..3518c9f8 100644 --- a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs +++ b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs @@ -62,7 +62,7 @@ namespace StardewModdingAPI.Framework.ContentManagers public override T Load(string assetName, LanguageCode language) { // raise first-load callback - if (GameContentManager.IsFirstLoad) + if (!SMDroid.ModEntry.ContextInitialize && GameContentManager.IsFirstLoad) { GameContentManager.IsFirstLoad = false; this.OnLoadingFirstAsset(); diff --git a/src/SMAPI/Framework/Monitor.cs b/src/SMAPI/Framework/Monitor.cs index 617bfd85..97b85441 100644 --- a/src/SMAPI/Framework/Monitor.cs +++ b/src/SMAPI/Framework/Monitor.cs @@ -146,6 +146,10 @@ namespace StardewModdingAPI.Framework this.ConsoleWriter.WriteLine(consoleMessage, level); }); } + else if (this.ShowTraceInConsole || level != ConsoleLogLevel.Trace) + { + GameConsole.Instance.WriteLine(consoleMessage, level); + } // write to log file this.LogFile.WriteLine(fullMessage); diff --git a/src/SMAPI/Framework/RewriteFacades/Game1Methods.cs b/src/SMAPI/Framework/RewriteFacades/Game1Methods.cs index ad62c105..811d9f92 100644 --- a/src/SMAPI/Framework/RewriteFacades/Game1Methods.cs +++ b/src/SMAPI/Framework/RewriteFacades/Game1Methods.cs @@ -31,11 +31,5 @@ namespace StardewModdingAPI.Framework.RewriteFacades { warpFarmer(locationName, tileX, tileY, facingDirectionAfterWarp, false, true, false); } - - [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Windows.")] - public static new void warpFarmer(string locationName, int tileX, int tileY, int facingDirectionAfterWarp, bool isStructure) - { - warpFarmer(locationName, tileX, tileY, facingDirectionAfterWarp, isStructure, true, false); - } } } diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index f2c0c6f8..88d3cf0e 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -11,10 +11,12 @@ using System.Security; using System.Text; using System.Text.RegularExpressions; using System.Threading; +using Microsoft.Xna.Framework.Graphics; #if SMAPI_FOR_WINDOWS using System.Windows.Forms; #endif using Newtonsoft.Json; +using SMDroid; using StardewModdingAPI.Events; using StardewModdingAPI.Framework.Events; using StardewModdingAPI.Framework.Exceptions; @@ -82,7 +84,7 @@ namespace StardewModdingAPI.Framework /// Whether the program has been disposed. private bool IsDisposed; - + /// Regex patterns which match console messages to suppress from the console and log. private readonly Regex[] SuppressConsolePatterns = { @@ -159,6 +161,7 @@ namespace StardewModdingAPI.Framework // init logging this.Monitor.Log($"SMAPI {Constants.ApiVersion} with Stardew Valley {Constants.GameVersion} on {EnvironmentUtility.GetFriendlyPlatformName(Constants.Platform)}", LogLevel.Info); + this.Monitor.Log($"SMDroid 1.4.0 for Stardew Valley Android release {MainActivity.instance.GetBuild()}", LogLevel.Info); this.Monitor.Log($"Mods go here: {modsPath}", LogLevel.Info); if (modsPath != Constants.DefaultModsPath) this.Monitor.Log("(Using custom --mods-path argument.)", LogLevel.Trace); @@ -181,7 +184,6 @@ namespace StardewModdingAPI.Framework // add more leniant assembly resolvers AppDomain.CurrentDomain.AssemblyResolve += (sender, e) => AssemblyLoader.ResolveAssembly(e.Name); SGame.ConstructorHack = new SGameConstructorHack(this.Monitor, this.Reflection, this.Toolkit.JsonHelper, this.InitialiseBeforeFirstAssetLoaded); - // validate platform //#if SMAPI_FOR_WINDOWS // if (Constants.Platform != Platform.Windows) @@ -202,7 +204,7 @@ namespace StardewModdingAPI.Framework /// Launch SMAPI. [HandleProcessCorruptedStateExceptions, SecurityCritical] // let try..catch handle corrupted state exceptions - public void RunInteractively(ContentCoordinator contentCore) + public void RunInteractively(ContentCoordinator contentCore, LocalizedContentManager contentManager) { // initialise SMAPI try @@ -244,7 +246,6 @@ namespace StardewModdingAPI.Framework new ObjectErrorPatch(), new LoadForNewGamePatch(this.Reflection, this.GameInstance.OnLoadStageChanged) ); - //// add exit handler //new Thread(() => //{ @@ -309,8 +310,8 @@ namespace StardewModdingAPI.Framework this.Monitor.Log($"You have SMAPI for developers, so the console will be much more verbose. You can disable developer mode by installing the non-developer version of SMAPI, or by editing {Constants.ApiConfigPath}.", LogLevel.Info); if (!this.Settings.CheckForUpdates) this.Monitor.Log($"You configured SMAPI to not check for updates. Running an old version of SMAPI is not recommended. You can enable update checks by reinstalling SMAPI or editing {Constants.ApiConfigPath}.", LogLevel.Warn); - if (!this.Monitor.WriteToConsole) - this.Monitor.Log("Writing to the terminal is disabled because the --no-terminal argument was received. This usually means launching the terminal failed.", LogLevel.Warn); + //if (!this.Monitor.WriteToConsole) + // this.Monitor.Log("Writing to the terminal is disabled because the --no-terminal argument was received. This usually means launching the terminal failed.", LogLevel.Warn); this.Monitor.VerboseLog("Verbose logging enabled."); // update window titles @@ -365,46 +366,51 @@ namespace StardewModdingAPI.Framework return; } - // load mod data - ModToolkit toolkit = new ModToolkit(); - ModDatabase modDatabase = toolkit.GetModDatabase(Constants.ApiMetadataPath); - - // load mods + this.GameInstance.IsSuspended = true; + new Thread(() => { - this.Monitor.Log("Loading mod metadata...", LogLevel.Trace); - ModResolver resolver = new ModResolver(); - - // load manifests - IModMetadata[] mods = resolver.ReadManifests(toolkit, this.ModsPath, modDatabase).ToArray(); - - // filter out ignored mods - foreach (IModMetadata mod in mods.Where(p => p.IsIgnored)) - this.Monitor.Log($" Skipped {mod.RelativeDirectoryPath} (folder name starts with a dot).", LogLevel.Trace); - mods = mods.Where(p => !p.IsIgnored).ToArray(); + // load mod data + ModToolkit toolkit = new ModToolkit(); + ModDatabase modDatabase = toolkit.GetModDatabase(Constants.ApiMetadataPath); // load mods - resolver.ValidateManifests(mods, Constants.ApiVersion, toolkit.GetUpdateUrl); - mods = resolver.ProcessDependencies(mods, modDatabase).ToArray(); - this.LoadMods(mods, this.Toolkit.JsonHelper, this.ContentCore, modDatabase); - - // write metadata file - if (this.Settings.DumpMetadata) { - ModFolderExport export = new ModFolderExport + this.Monitor.Log("Loading mod metadata...", LogLevel.Trace); + ModResolver resolver = new ModResolver(); + + // load manifests + IModMetadata[] mods = resolver.ReadManifests(toolkit, this.ModsPath, modDatabase).ToArray(); + + // filter out ignored mods + foreach (IModMetadata mod in mods.Where(p => p.IsIgnored)) + this.Monitor.Log($" Skipped {mod.RelativeDirectoryPath} (folder name starts with a dot).", LogLevel.Trace); + mods = mods.Where(p => !p.IsIgnored).ToArray(); + + // load mods + resolver.ValidateManifests(mods, Constants.ApiVersion, toolkit.GetUpdateUrl); + mods = resolver.ProcessDependencies(mods, modDatabase).ToArray(); + this.LoadMods(mods, this.Toolkit.JsonHelper, this.ContentCore, modDatabase); + + // write metadata file + if (this.Settings.DumpMetadata) { - Exported = DateTime.UtcNow.ToString("O"), - ApiVersion = Constants.ApiVersion.ToString(), - GameVersion = Constants.GameVersion.ToString(), - ModFolderPath = this.ModsPath, - Mods = mods - }; - this.Toolkit.JsonHelper.WriteJsonFile(Path.Combine(Constants.LogDir, $"{Constants.LogNamePrefix}metadata-dump.json"), export); + ModFolderExport export = new ModFolderExport + { + Exported = DateTime.UtcNow.ToString("O"), + ApiVersion = Constants.ApiVersion.ToString(), + GameVersion = Constants.GameVersion.ToString(), + ModFolderPath = this.ModsPath, + Mods = mods + }; + this.Toolkit.JsonHelper.WriteJsonFile(Path.Combine(Constants.LogDir, $"{Constants.LogNamePrefix}metadata-dump.json"), export); + } + + // check for updates + //this.CheckForUpdatesAsync(mods); } - - // check for updates - //this.CheckForUpdatesAsync(mods); - } - + GameConsole.Instance.IsVisible = false; + this.GameInstance.IsSuspended = false; + }).Start(); // update window titles //int modsLoaded = this.ModRegistry.GetAll().Count(); //Game1.game1.Window.Title = $"Stardew Valley {Constants.GameVersion} - running SMAPI {Constants.ApiVersion} with {modsLoaded} mods"; diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs index c9003842..4ac947d0 100644 --- a/src/SMAPI/Framework/SGame.cs +++ b/src/SMAPI/Framework/SGame.cs @@ -14,6 +14,7 @@ using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; #endif using Netcode; +using SMDroid; using StardewModdingAPI.Enums; using StardewModdingAPI.Events; using StardewModdingAPI.Framework.Events; @@ -96,6 +97,11 @@ namespace StardewModdingAPI.Framework internal bool OnCommonStaticHook_Prefix(string hookName, ref object param1, ref object param2, ref object param3, ref object param4, ref object param5, ref object __result) { + if (hookName == "StardewValley.Game1.saveWholeBackup" || hookName == "StardewValley.Game1.MakeFullBackup") + { + this.Events.Saving.RaiseEmpty(); + return true; + } foreach (IMod mod in this.HookReceiver) { if (!mod.OnCommonStaticHook_Prefix(hookName, ref param1, ref param2, ref param3, ref param4, ref param5, ref __result)) @@ -108,6 +114,11 @@ namespace StardewModdingAPI.Framework internal bool OnCommonStaticHook_Postfix(string hookName, ref object param1, ref object param2, ref object param3, ref object param4, ref object param5, ref bool __state, ref object __result) { + if (hookName == "StardewValley.Game1.saveWholeBackup" || hookName == "StardewValley.Game1.MakeFullBackup") + { + this.Events.Saved.RaiseEmpty(); + return true; + } foreach (IMod mod in this.HookReceiver) { mod.OnCommonStaticHook_Postfix(hookName, ref param1, ref param2, ref param3, ref param4, ref param5, ref __state, ref __result); @@ -163,7 +174,7 @@ namespace StardewModdingAPI.Framework /// A callback to invoke the first time *any* game content manager loads an asset. public static Action OnLoadingFirstAsset; - + /// A callback to invoke after the game finishes initialising. private readonly Action OnGameInitialised; @@ -222,6 +233,7 @@ namespace StardewModdingAPI.Framework public List HookReceiver = new List(); + public bool IsSuspended; /********* @@ -266,7 +278,7 @@ namespace StardewModdingAPI.Framework //Game1.hooks = new SModHooks(this.OnNewDayAfterFade); // init observables - //Game1.locations = new ObservableCollection(); + Game1.locations = new ObservableCollection(); SGame.instance = this; } @@ -307,16 +319,6 @@ namespace StardewModdingAPI.Framework this.Events.DayEnding.RaiseEmpty(); } - public void Update_Postfix(GameTime time) - { - this.Events.UnvalidatedUpdateTicked.RaiseEmpty(); -#if !SMAPI_3_0_STRICT - this.Events.Legacy_UnvalidatedUpdateTick.Raise(); -#endif - if (this.validTicking) - this.Events.UpdateTicked.RaiseEmpty(); - } - /// A callback invoked when a mod message is received. /// The message to deliver to applicable mods. private void OnModMessageReceived(ModMessageModel message) @@ -384,6 +386,17 @@ namespace StardewModdingAPI.Framework /// A snapshot of the game timing state. public bool Update(GameTime gameTime) { + if (this.IsSuspended) + { + if (Game1.graphics.GraphicsDevice != null) + { + this.Reflection.GetMethod(Game1.game1, "_updateAudioEngine").Invoke(); + this.Reflection.GetMethod(Game1.game1, "_updateOptionsAndToggleFullScreen").Invoke(); + this.Reflection.GetMethod(Game1.game1, "_updateInput").Invoke(); + } + ModEntry.IsHalt = true; + return true; + } var events = this.Events; this.validTicking = false; try @@ -810,7 +823,8 @@ namespace StardewModdingAPI.Framework if (optionsPage != null) { List options = this.Reflection.GetField>(optionsPage, "options").GetValue(); - foreach(IModMetadata modMetadata in this.ModRegistry.GetAll()) + options.Insert(0, new OptionsButton("Console", () => GameConsole.Instance.Show())); + foreach (IModMetadata modMetadata in this.ModRegistry.GetAll()) { if(modMetadata.Mod != null) { @@ -1081,6 +1095,20 @@ namespace StardewModdingAPI.Framework return true; } + public void Update_Postfix(GameTime time) + { + if (this.IsSuspended) + return; + if (ModEntry.IsHalt) + ModEntry.IsHalt = false; + this.Events.UnvalidatedUpdateTicked.RaiseEmpty(); +#if !SMAPI_3_0_STRICT + this.Events.Legacy_UnvalidatedUpdateTick.Raise(); +#endif + if (this.validTicking) + this.Events.UpdateTicked.RaiseEmpty(); + } + /// The method called to draw everything to the screen. /// A snapshot of the game timing state. public bool Draw(GameTime gameTime, RenderTarget2D toBuffer) @@ -1088,6 +1116,19 @@ namespace StardewModdingAPI.Framework Context.IsInDrawLoop = true; try { + if (GameConsole.Instance.IsVisible) + { + Game1.game1.GraphicsDevice.SetRenderTarget(Game1.game1.screen); + Game1.game1.GraphicsDevice.Clear(Color.Black); + Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, null); + GameConsole.Instance.draw(Game1.spriteBatch); + Game1.spriteBatch.End(); + Game1.game1.GraphicsDevice.SetRenderTarget(null); + Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullNone, null, null); + Game1.spriteBatch.Draw(Game1.game1.screen, Vector2.Zero, new Rectangle?(Game1.game1.screen.Bounds), Color.White, 0f, Vector2.Zero, Game1.options.zoomLevel, SpriteEffects.None, 1f); + Game1.spriteBatch.End(); + return false; + } this._draw(gameTime, null); this.DrawCrashTimer.Reset(); } @@ -1187,6 +1228,8 @@ namespace StardewModdingAPI.Framework SpriteText.drawStringWithScrollCenteredAt(Game1.spriteBatch, Game1.content.LoadString("Strings\\UI:please_wait"), Game1.game1.GraphicsDevice.Viewport.Width / 2, Game1.game1.GraphicsDevice.Viewport.Height / 2, "", 1f, -1, 0, 0.088f, false); Game1.spriteBatch.End(); } + + return; } else { @@ -1277,24 +1320,145 @@ namespace StardewModdingAPI.Framework _spriteBatchEnd.Invoke(); Game1.RestoreViewportAndZoom(); } + return; } - else + Game1.game1.GraphicsDevice.Clear(bgColor.GetValue()); + if (((Game1.activeClickableMenu != null) && Game1.options.showMenuBackground) && Game1.activeClickableMenu.showWithoutTransparencyIfOptionIsSet()) { - Game1.game1.GraphicsDevice.Clear(bgColor.GetValue()); - if (((Game1.activeClickableMenu != null) && Game1.options.showMenuBackground) && Game1.activeClickableMenu.showWithoutTransparencyIfOptionIsSet()) + Matrix value = Matrix.CreateScale(1f); + Game1.SetSpriteBatchBeginNextID("C"); + _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, new Matrix?(value)); + events.Rendering.RaiseEmpty(); + try + { + Game1.activeClickableMenu.drawBackground(Game1.spriteBatch); + events.RenderingActiveMenu.RaiseEmpty(); +#if !SMAPI_3_0_STRICT + events.Legacy_OnPreRenderGuiEvent.Raise(); +#endif + Game1.activeClickableMenu.drawBackground(Game1.spriteBatch); + Game1.activeClickableMenu.draw(Game1.spriteBatch); + events.RenderedActiveMenu.RaiseEmpty(); +#if !SMAPI_3_0_STRICT + events.Legacy_OnPostRenderGuiEvent.Raise(); +#endif + } + catch (Exception ex) + { + this.Monitor.Log($"The {Game1.activeClickableMenu.GetType().FullName} menu crashed while drawing itself. SMAPI will force it to exit to avoid crashing the game.\n{ex.GetLogSummary()}", LogLevel.Error); + Game1.activeClickableMenu.exitThisMenu(); + } + events.Rendered.RaiseEmpty(); +#if !SMAPI_3_0_STRICT + events.Legacy_OnPostRenderEvent.Raise(); +#endif + _spriteBatchEnd.Invoke(); + drawOverlays.Invoke(Game1.spriteBatch); + renderScreenBuffer.Invoke(BlendState.AlphaBlend); + if (Game1.overlayMenu != null) + { + Game1.SetSpriteBatchBeginNextID("D"); + _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, null); + Game1.overlayMenu.draw(Game1.spriteBatch); + _spriteBatchEnd.Invoke(); + } + return; + } + if (Game1.emergencyLoading) + { + if (!Game1.SeenConcernedApeLogo) + { + Game1.SetSpriteBatchBeginNextID("E"); + _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, null); + if (Game1.logoFadeTimer < 5000) + { + Game1.spriteBatch.Draw(Game1.staminaRect, new Rectangle(0, 0, Game1.viewport.Width, Game1.viewport.Height), Color.White); + } + if (Game1.logoFadeTimer > 4500) + { + float scale = Math.Min((float)1f, (float)(((float)(Game1.logoFadeTimer - 4500)) / 500f)); + Game1.spriteBatch.Draw(Game1.staminaRect, new Rectangle(0, 0, Game1.viewport.Width, Game1.viewport.Height), Color.Black * scale); + } + Game1.spriteBatch.Draw(Game1.titleButtonsTexture, new Vector2((float)(Game1.viewport.Width / 2), (float)((Game1.viewport.Height / 2) - 90)), new Rectangle(171 + ((((Game1.logoFadeTimer / 100) % 2) == 0) ? 111 : 0), 311, 111, 60), Color.White * ((Game1.logoFadeTimer < 500) ? (((float)Game1.logoFadeTimer) / 500f) : ((Game1.logoFadeTimer > 4500) ? (1f - (((float)(Game1.logoFadeTimer - 0x1194)) / 500f)) : 1f)), 0f, Vector2.Zero, (float)3f, SpriteEffects.None, 0.2f); + Game1.spriteBatch.Draw(Game1.titleButtonsTexture, new Vector2((float)((Game1.viewport.Width / 2) - 261), (float)((Game1.viewport.Height / 2) - 102)), new Rectangle((((Game1.logoFadeTimer / 100) % 2) == 0) ? 85 : 0, 306, 85, 69), Color.White * ((Game1.logoFadeTimer < 500) ? (((float)Game1.logoFadeTimer) / 500f) : ((Game1.logoFadeTimer > 4500) ? (1f - (((float)(Game1.logoFadeTimer - 0x1194)) / 500f)) : 1f)), 0f, Vector2.Zero, (float)3f, SpriteEffects.None, 0.2f); + _spriteBatchEnd.Invoke(); + } + Game1.logoFadeTimer -= gameTime.ElapsedGameTime.Milliseconds; + } + if (Game1.gameMode == 11) + { + Game1.SetSpriteBatchBeginNextID("F"); + _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, null); + events.Rendering.RaiseEmpty(); + Game1.spriteBatch.DrawString(Game1.dialogueFont, Game1.content.LoadString(@"Strings\StringsFromCSFiles:Game1.cs.3685"), new Vector2(16f, 16f), Color.HotPink); + Game1.spriteBatch.DrawString(Game1.dialogueFont, Game1.content.LoadString(@"Strings\StringsFromCSFiles:Game1.cs.3686"), new Vector2(16f, 32f), new Color(0, 0xff, 0)); + Game1.spriteBatch.DrawString(Game1.dialogueFont, Game1.parseText(Game1.errorMessage, Game1.dialogueFont, Game1.graphics.GraphicsDevice.Viewport.Width, 1f), new Vector2(16f, 48f), Color.White); + events.Rendered.RaiseEmpty(); +#if !SMAPI_3_0_STRICT + events.Legacy_OnPostRenderEvent.Raise(); +#endif + _spriteBatchEnd.Invoke(); + return; + } + if (Game1.currentMinigame != null) + { + Game1.currentMinigame.draw(Game1.spriteBatch); + if ((Game1.globalFade && !Game1.menuUp) && (!Game1.nameSelectUp || Game1.messagePause)) + { + Game1.SetSpriteBatchBeginNextID("G"); + _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, null); + Game1.spriteBatch.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.Black * ((Game1.gameMode == 0) ? (1f - Game1.fadeToBlackAlpha) : Game1.fadeToBlackAlpha)); + _spriteBatchEnd.Invoke(); + } + drawOverlays.Invoke(Game1.spriteBatch); +#if !SMAPI_3_0_STRICT + this.RaisePostRender(needsNewBatch: true); +#endif + renderScreenBuffer.Invoke(BlendState.AlphaBlend); + if (Game1.currentMinigame is FishingGame && Game1.activeClickableMenu != null) + { + Game1.SetSpriteBatchBeginNextID("A-A"); + SpriteBatchBegin.Invoke(1f); + Game1.activeClickableMenu.draw(Game1.spriteBatch); + _spriteBatchEnd.Invoke(); + drawOverlays.Invoke(Game1.spriteBatch); + return; + } + if (Game1.currentMinigame is FantasyBoardGame && Game1.activeClickableMenu != null) + { + if (Game1.IsActiveClickableMenuNativeScaled) + { + Game1.BackupViewportAndZoom(true); + Game1.SetSpriteBatchBeginNextID("A1"); + SpriteBatchBegin.Invoke(Game1.NativeZoomLevel); + Game1.activeClickableMenu.draw(Game1.spriteBatch); + _spriteBatchEnd.Invoke(); + Game1.RestoreViewportAndZoom(); + return; + } + Game1.BackupViewportAndZoom(false); + Game1.SetSpriteBatchBeginNextID("A2"); + SpriteBatchBegin.Invoke(1f); + Game1.activeClickableMenu.draw(Game1.spriteBatch); + _spriteBatchEnd.Invoke(); + Game1.RestoreViewportAndZoom(); + } + } + if (Game1.showingEndOfNightStuff) + { + renderScreenBuffer.Invoke(BlendState.Opaque); + Game1.BackupViewportAndZoom(true); + Game1.SetSpriteBatchBeginNextID("A-B"); + SpriteBatchBegin.Invoke(Game1.NativeZoomLevel); + events.Rendering.RaiseEmpty(); + if (Game1.activeClickableMenu != null) { - Matrix matrix = Matrix.CreateScale((float)1f); - Game1.SetSpriteBatchBeginNextID("C"); - _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, new Matrix?(matrix)); - events.Rendering.RaiseEmpty(); try { - Game1.activeClickableMenu.drawBackground(Game1.spriteBatch); events.RenderingActiveMenu.RaiseEmpty(); #if !SMAPI_3_0_STRICT events.Legacy_OnPreRenderGuiEvent.Raise(); #endif - Game1.activeClickableMenu.drawBackground(Game1.spriteBatch); Game1.activeClickableMenu.draw(Game1.spriteBatch); events.RenderedActiveMenu.RaiseEmpty(); #if !SMAPI_3_0_STRICT @@ -1303,657 +1467,551 @@ namespace StardewModdingAPI.Framework } catch (Exception ex) { - this.Monitor.Log($"The {Game1.activeClickableMenu.GetType().FullName} menu crashed while drawing itself. SMAPI will force it to exit to avoid crashing the game.\n{ex.GetLogSummary()}", LogLevel.Error); + this.Monitor.Log($"The {Game1.activeClickableMenu.GetType().FullName} menu crashed while drawing itself during end-of-night-stuff. SMAPI will force it to exit to avoid crashing the game.\n{ex.GetLogSummary()}", LogLevel.Error); Game1.activeClickableMenu.exitThisMenu(); } - events.Rendered.RaiseEmpty(); + } + events.Rendered.RaiseEmpty(); #if !SMAPI_3_0_STRICT - events.Legacy_OnPostRenderEvent.Raise(); + events.Legacy_OnPostRenderEvent.Raise(); #endif + _spriteBatchEnd.Invoke(); + drawOverlays.Invoke(Game1.spriteBatch); + Game1.RestoreViewportAndZoom(); + return; + } + if ((Game1.gameMode == 6) || ((Game1.gameMode == 3) && (Game1.currentLocation == null))) + { + events.Rendering.RaiseEmpty(); + DrawLoadingDotDotDot.Invoke(gameTime); + events.Rendered.RaiseEmpty(); +#if !SMAPI_3_0_STRICT + events.Legacy_OnPostRenderEvent.Raise(); +#endif + drawOverlays.Invoke(Game1.spriteBatch); + renderScreenBuffer.Invoke(BlendState.AlphaBlend); + if (Game1.overlayMenu != null) + { + Game1.SetSpriteBatchBeginNextID("H"); + _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, null); + Game1.overlayMenu.draw(Game1.spriteBatch); _spriteBatchEnd.Invoke(); - drawOverlays.Invoke(Game1.spriteBatch); - renderScreenBuffer.Invoke(BlendState.AlphaBlend); - if (Game1.overlayMenu != null) + } + //base.Draw(gameTime); + return; + } + byte batchOpens = 0; // used for rendering event + if (Game1.gameMode == 0) + { + Game1.SetSpriteBatchBeginNextID("I"); + _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, null); + if (++batchOpens == 1) + events.Rendering.RaiseEmpty(); + } + else if (!Game1.drawGame) + { + Game1.SetSpriteBatchBeginNextID("J"); + _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointClamp, null, null, null, null); + if (++batchOpens == 1) + events.Rendering.RaiseEmpty(); + } + else if (Game1.drawGame) + { + if (Game1.drawLighting) + { + Game1.game1.GraphicsDevice.SetRenderTarget(Game1.lightmap); + Game1.game1.GraphicsDevice.Clear(Color.White * 0f); + Game1.SetSpriteBatchBeginNextID("K"); + _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointClamp, null, null, null, null); + if (++batchOpens == 1) + events.Rendering.RaiseEmpty(); + Game1.spriteBatch.Draw(Game1.staminaRect, Game1.lightmap.Bounds, Game1.currentLocation.Name.StartsWith("UndergroundMine") ? Game1.mine.getLightingColor(gameTime) : ((!Game1.ambientLight.Equals(Color.White) && (!RainManager.Instance.isRaining || (!Game1.currentLocation.IsOutdoors))) ? Game1.ambientLight : Game1.outdoorLight)); + for (int i = 0; i < Game1.currentLightSources.Count; i++) { - Game1.SetSpriteBatchBeginNextID("D"); - _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, null); - Game1.overlayMenu.draw(Game1.spriteBatch); - _spriteBatchEnd.Invoke(); + if (Utility.isOnScreen((Vector2)Game1.currentLightSources.ElementAt(i).position, (int)((Game1.currentLightSources.ElementAt(i).radius * 64f) * 4f))) + { + Game1.spriteBatch.Draw(Game1.currentLightSources.ElementAt(i).lightTexture, Game1.GlobalToLocal(Game1.viewport, (Vector2)Game1.currentLightSources.ElementAt(i).position) / ((float)(Game1.options.lightingQuality / 2)), new Rectangle?(Game1.currentLightSources.ElementAt(i).lightTexture.Bounds), (Color)Game1.currentLightSources.ElementAt(i).color, 0f, new Vector2((float)Game1.currentLightSources.ElementAt(i).lightTexture.Bounds.Center.X, (float)Game1.currentLightSources.ElementAt(i).lightTexture.Bounds.Center.Y), (float)(Game1.currentLightSources.ElementAt(i).radius / ((float)(Game1.options.lightingQuality / 2))), SpriteEffects.None, 0.9f); + } + } + _spriteBatchEnd.Invoke(); + Game1.game1.GraphicsDevice.SetRenderTarget((Game1.options.zoomLevel == 1f) ? null : Game1.game1.screen); + } + if (Game1.bloomDay && (Game1.bloom != null)) + { + Game1.bloom.BeginDraw(); + } + Game1.game1.GraphicsDevice.Clear(bgColor.GetValue()); + Game1.SetSpriteBatchBeginNextID("L"); + _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, null); + if (++batchOpens == 1) + events.Rendering.RaiseEmpty(); + events.RenderingWorld.RaiseEmpty(); + if (Game1.background != null) + { + Game1.background.draw(Game1.spriteBatch); + } + Game1.mapDisplayDevice.BeginScene(Game1.spriteBatch); + try + { + Game1.currentLocation?.Map.GetLayer("Back").Draw(Game1.mapDisplayDevice, Game1.viewport, Location.Origin, false, 4); + } + catch (KeyNotFoundException exception) + { + CheckToReloadGameLocationAfterDrawFail.Invoke("Back", exception); + } + Game1.currentLocation?.drawWater(Game1.spriteBatch); + _farmerShadows.GetValue().Clear(); + if (((Game1.currentLocation.currentEvent != null) && !Game1.currentLocation.currentEvent.isFestival) && (Game1.currentLocation.currentEvent.farmerActors.Count > 0)) + { + foreach (Farmer farmer in Game1.currentLocation.currentEvent.farmerActors) + { + if ((farmer.IsLocalPlayer && Game1.displayFarmer) || (!farmer.hidden)) + { + _farmerShadows.GetValue().Add(farmer); + } } } - else + else if(Game1.currentLocation != null) { - Matrix? nullable; - if (Game1.emergencyLoading) + foreach (Farmer farmer2 in Game1.currentLocation.farmers) { - if (!Game1.SeenConcernedApeLogo) + if ((farmer2.IsLocalPlayer && Game1.displayFarmer) || (!farmer2.hidden)) { - Game1.SetSpriteBatchBeginNextID("E"); - nullable = null; - _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, nullable); - if (Game1.logoFadeTimer < 0x1388) - { - Game1.spriteBatch.Draw(Game1.staminaRect, new Rectangle(0, 0, Game1.viewport.Width, Game1.viewport.Height), Color.White); - } - if (Game1.logoFadeTimer > 0x1194) - { - float num = Math.Min((float)1f, (float)(((float)(Game1.logoFadeTimer - 0x1194)) / 500f)); - Game1.spriteBatch.Draw(Game1.staminaRect, new Rectangle(0, 0, Game1.viewport.Width, Game1.viewport.Height), Color.Black * num); - } - Game1.spriteBatch.Draw(Game1.titleButtonsTexture, new Vector2((float)(Game1.viewport.Width / 2), (float)((Game1.viewport.Height / 2) - 90)), new Rectangle(0xab + ((((Game1.logoFadeTimer / 100) % 2) == 0) ? 0x6f : 0), 0x137, 0x6f, 60), Color.White * ((Game1.logoFadeTimer < 500) ? (((float)Game1.logoFadeTimer) / 500f) : ((Game1.logoFadeTimer > 0x1194) ? (1f - (((float)(Game1.logoFadeTimer - 0x1194)) / 500f)) : 1f)), 0f, Vector2.Zero, (float)3f, SpriteEffects.None, 0.2f); - Game1.spriteBatch.Draw(Game1.titleButtonsTexture, new Vector2((float)((Game1.viewport.Width / 2) - 0x105), (float)((Game1.viewport.Height / 2) - 0x66)), new Rectangle((((Game1.logoFadeTimer / 100) % 2) == 0) ? 0x55 : 0, 0x132, 0x55, 0x45), Color.White * ((Game1.logoFadeTimer < 500) ? (((float)Game1.logoFadeTimer) / 500f) : ((Game1.logoFadeTimer > 0x1194) ? (1f - (((float)(Game1.logoFadeTimer - 0x1194)) / 500f)) : 1f)), 0f, Vector2.Zero, (float)3f, SpriteEffects.None, 0.2f); - _spriteBatchEnd.Invoke(); - } - Game1.logoFadeTimer -= gameTime.ElapsedGameTime.Milliseconds; - } - if (Game1.gameMode == 11) - { - Game1.SetSpriteBatchBeginNextID("F"); - _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, null); - events.Rendering.RaiseEmpty(); - Game1.spriteBatch.DrawString(Game1.dialogueFont, Game1.content.LoadString(@"Strings\StringsFromCSFiles:Game1.cs.3685"), new Vector2(16f, 16f), Color.HotPink); - Game1.spriteBatch.DrawString(Game1.dialogueFont, Game1.content.LoadString(@"Strings\StringsFromCSFiles:Game1.cs.3686"), new Vector2(16f, 32f), new Color(0, 0xff, 0)); - Game1.spriteBatch.DrawString(Game1.dialogueFont, Game1.parseText(Game1.errorMessage, Game1.dialogueFont, Game1.graphics.GraphicsDevice.Viewport.Width, 1f), new Vector2(16f, 48f), Color.White); - events.Rendered.RaiseEmpty(); -#if !SMAPI_3_0_STRICT - events.Legacy_OnPostRenderEvent.Raise(); -#endif - _spriteBatchEnd.Invoke(); - } - else if (Game1.currentMinigame != null) - { - Game1.currentMinigame.draw(Game1.spriteBatch); - if ((Game1.globalFade && !Game1.menuUp) && (!Game1.nameSelectUp || Game1.messagePause)) - { - Game1.SetSpriteBatchBeginNextID("G"); - _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, null); - Game1.spriteBatch.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.Black * ((Game1.gameMode == 0) ? (1f - Game1.fadeToBlackAlpha) : Game1.fadeToBlackAlpha)); - _spriteBatchEnd.Invoke(); - } - drawOverlays.Invoke(Game1.spriteBatch); -#if !SMAPI_3_0_STRICT - this.RaisePostRender(needsNewBatch: true); -#endif - renderScreenBuffer.Invoke(BlendState.AlphaBlend); - if (((Game1.currentMinigame is FishingGame) || (Game1.currentMinigame is FantasyBoardGame)) && (Game1.activeClickableMenu != null)) - { - Game1.SetSpriteBatchBeginNextID("A-A"); - SpriteBatchBegin.Invoke(1f); - Game1.activeClickableMenu.draw(Game1.spriteBatch); - _spriteBatchEnd.Invoke(); - drawOverlays.Invoke(Game1.spriteBatch); + _farmerShadows.GetValue().Add(farmer2); } } - else if (Game1.showingEndOfNightStuff) + } + if (Game1.currentLocation != null && !Game1.currentLocation.shouldHideCharacters()) + { + if (Game1.CurrentEvent == null) { - renderScreenBuffer.Invoke(BlendState.Opaque); - Game1.BackupViewportAndZoom(true); - Game1.SetSpriteBatchBeginNextID("A-B"); - SpriteBatchBegin.Invoke(Game1.NativeZoomLevel); - events.Rendering.RaiseEmpty(); - if (Game1.activeClickableMenu != null) + foreach (NPC npc in Game1.currentLocation.characters) { - try + if (((!npc.swimming) && !npc.HideShadow) && (!npc.IsInvisible && !Game1.currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(npc.getTileLocation()))) { - events.RenderingActiveMenu.RaiseEmpty(); -#if !SMAPI_3_0_STRICT - events.Legacy_OnPreRenderGuiEvent.Raise(); -#endif - Game1.activeClickableMenu.draw(Game1.spriteBatch); - events.RenderedActiveMenu.RaiseEmpty(); -#if !SMAPI_3_0_STRICT - events.Legacy_OnPostRenderGuiEvent.Raise(); -#endif - } - catch (Exception ex) - { - this.Monitor.Log($"The {Game1.activeClickableMenu.GetType().FullName} menu crashed while drawing itself during end-of-night-stuff. SMAPI will force it to exit to avoid crashing the game.\n{ex.GetLogSummary()}", LogLevel.Error); - Game1.activeClickableMenu.exitThisMenu(); + Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.viewport, npc.Position + new Vector2(((float)(npc.Sprite.SpriteWidth * 4)) / 2f, (float)(npc.GetBoundingBox().Height + (npc.IsMonster ? 0 : 12)))), new Rectangle?(Game1.shadowTexture.Bounds), Color.White, 0f, new Vector2((float)Game1.shadowTexture.Bounds.Center.X, (float)Game1.shadowTexture.Bounds.Center.Y), (float)((4f + (((float)npc.yJumpOffset) / 40f)) * npc.scale), SpriteEffects.None, Math.Max((float)0f, (float)(((float)npc.getStandingY()) / 10000f)) - 1E-06f); } } - events.Rendered.RaiseEmpty(); -#if !SMAPI_3_0_STRICT - events.Legacy_OnPostRenderEvent.Raise(); -#endif - _spriteBatchEnd.Invoke(); - drawOverlays.Invoke(Game1.spriteBatch); - Game1.RestoreViewportAndZoom(); - } - else if ((Game1.gameMode == 6) || ((Game1.gameMode == 3) && (Game1.currentLocation == null))) - { - events.Rendering.RaiseEmpty(); - DrawLoadingDotDotDot.Invoke(gameTime); - events.Rendered.RaiseEmpty(); -#if !SMAPI_3_0_STRICT - events.Legacy_OnPostRenderEvent.Raise(); -#endif - drawOverlays.Invoke(Game1.spriteBatch); - renderScreenBuffer.Invoke(BlendState.AlphaBlend); - if (Game1.overlayMenu != null) - { - Game1.SetSpriteBatchBeginNextID("H"); - _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, null); - Game1.overlayMenu.draw(Game1.spriteBatch); - _spriteBatchEnd.Invoke(); - } - //base.Draw(gameTime); } else { - byte batchOpens = 0; // used for rendering event - if (Game1.gameMode == 0) + foreach (NPC npc2 in Game1.CurrentEvent.actors) { - Game1.SetSpriteBatchBeginNextID("I"); - _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, null); - if (++batchOpens == 1) - events.Rendering.RaiseEmpty(); - } - else if (!Game1.drawGame) - { - Game1.SetSpriteBatchBeginNextID("J"); - _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointClamp, null, null, null, null); - if (++batchOpens == 1) - events.Rendering.RaiseEmpty(); - } - else if (Game1.drawGame) - { - if (Game1.drawLighting) + if (((!npc2.swimming) && !npc2.HideShadow) && !Game1.currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(npc2.getTileLocation())) { - Game1.game1.GraphicsDevice.SetRenderTarget(Game1.lightmap); - Game1.game1.GraphicsDevice.Clear(Color.White * 0f); - Game1.SetSpriteBatchBeginNextID("K"); - nullable = null; - _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointClamp, null, null, null, nullable); - if (++batchOpens == 1) - events.Rendering.RaiseEmpty(); - Game1.spriteBatch.Draw(Game1.staminaRect, Game1.lightmap.Bounds, Game1.currentLocation.Name.StartsWith("UndergroundMine") ? Game1.mine.getLightingColor(gameTime) : ((!Game1.ambientLight.Equals(Color.White) && (!RainManager.Instance.isRaining || (!Game1.currentLocation.IsOutdoors))) ? Game1.ambientLight : Game1.outdoorLight)); - for (int i = 0; i < Game1.currentLightSources.Count; i++) - { - if (Utility.isOnScreen((Vector2)Game1.currentLightSources.ElementAt(i).position, (int)((Game1.currentLightSources.ElementAt(i).radius * 64f) * 4f))) - { - Game1.spriteBatch.Draw(Game1.currentLightSources.ElementAt(i).lightTexture, Game1.GlobalToLocal(Game1.viewport, (Vector2)Game1.currentLightSources.ElementAt(i).position) / ((float)(Game1.options.lightingQuality / 2)), new Rectangle?(Game1.currentLightSources.ElementAt(i).lightTexture.Bounds), (Color)Game1.currentLightSources.ElementAt(i).color, 0f, new Vector2((float)Game1.currentLightSources.ElementAt(i).lightTexture.Bounds.Center.X, (float)Game1.currentLightSources.ElementAt(i).lightTexture.Bounds.Center.Y), (float)(Game1.currentLightSources.ElementAt(i).radius / ((float)(Game1.options.lightingQuality / 2))), SpriteEffects.None, 0.9f); - } - } - _spriteBatchEnd.Invoke(); - Game1.game1.GraphicsDevice.SetRenderTarget((Game1.options.zoomLevel == 1f) ? null : Game1.game1.screen); - } - if (Game1.bloomDay && (Game1.bloom != null)) - { - Game1.bloom.BeginDraw(); - } - Game1.game1.GraphicsDevice.Clear(bgColor.GetValue()); - Game1.SetSpriteBatchBeginNextID("L"); - nullable = null; - _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, nullable); - if (++batchOpens == 1) - events.Rendering.RaiseEmpty(); - events.RenderingWorld.RaiseEmpty(); - if (Game1.background != null) - { - Game1.background.draw(Game1.spriteBatch); - } - Game1.mapDisplayDevice.BeginScene(Game1.spriteBatch); - try - { - Game1.currentLocation.Map.GetLayer("Back").Draw(Game1.mapDisplayDevice, Game1.viewport, Location.Origin, false, 4); - } - catch (KeyNotFoundException exception) - { - CheckToReloadGameLocationAfterDrawFail.Invoke("Back", exception); - } - Game1.currentLocation.drawWater(Game1.spriteBatch); - _farmerShadows.GetValue().Clear(); - if (((Game1.currentLocation.currentEvent != null) && !Game1.currentLocation.currentEvent.isFestival) && (Game1.currentLocation.currentEvent.farmerActors.Count > 0)) - { - foreach (Farmer farmer in Game1.currentLocation.currentEvent.farmerActors) - { - if ((farmer.IsLocalPlayer && Game1.displayFarmer) || (!farmer.hidden)) - { - _farmerShadows.GetValue().Add(farmer); - } - } - } - else - { - foreach (Farmer farmer2 in Game1.currentLocation.farmers) - { - if ((farmer2.IsLocalPlayer && Game1.displayFarmer) || (!farmer2.hidden)) - { - _farmerShadows.GetValue().Add(farmer2); - } - } - } - if (!Game1.currentLocation.shouldHideCharacters()) - { - if (Game1.CurrentEvent == null) - { - foreach (NPC npc in Game1.currentLocation.characters) - { - if (((!npc.swimming) && !npc.HideShadow) && (!npc.IsInvisible && !Game1.currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(npc.getTileLocation()))) - { - Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.viewport, npc.Position + new Vector2(((float)(npc.Sprite.SpriteWidth * 4)) / 2f, (float)(npc.GetBoundingBox().Height + (npc.IsMonster ? 0 : 12)))), new Rectangle?(Game1.shadowTexture.Bounds), Color.White, 0f, new Vector2((float)Game1.shadowTexture.Bounds.Center.X, (float)Game1.shadowTexture.Bounds.Center.Y), (float)((4f + (((float)npc.yJumpOffset) / 40f)) * npc.scale), SpriteEffects.None, Math.Max((float)0f, (float)(((float)npc.getStandingY()) / 10000f)) - 1E-06f); - } - } - } - else - { - foreach (NPC npc2 in Game1.CurrentEvent.actors) - { - if (((!npc2.swimming) && !npc2.HideShadow) && !Game1.currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(npc2.getTileLocation())) - { - Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.viewport, npc2.Position + new Vector2(((float)(npc2.Sprite.SpriteWidth * 4)) / 2f, (float)(npc2.GetBoundingBox().Height + (npc2.IsMonster ? 0 : ((npc2.Sprite.SpriteHeight <= 0x10) ? -4 : 12))))), new Rectangle?(Game1.shadowTexture.Bounds), Color.White, 0f, new Vector2((float)Game1.shadowTexture.Bounds.Center.X, (float)Game1.shadowTexture.Bounds.Center.Y), (float)((4f + (((float)npc2.yJumpOffset) / 40f)) * npc2.scale), SpriteEffects.None, Math.Max((float)0f, (float)(((float)npc2.getStandingY()) / 10000f)) - 1E-06f); - } - } - } - foreach (Farmer farmer3 in _farmerShadows.GetValue()) - { - if (((!farmer3.swimming) && !farmer3.isRidingHorse()) && ((Game1.currentLocation == null) || !Game1.currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(farmer3.getTileLocation()))) - { - Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(farmer3.Position + new Vector2(32f, 24f)), new Rectangle?(Game1.shadowTexture.Bounds), Color.White, 0f, new Vector2((float)Game1.shadowTexture.Bounds.Center.X, (float)Game1.shadowTexture.Bounds.Center.Y), (float)(4f - (((farmer3.running || farmer3.UsingTool) && (farmer3.FarmerSprite.currentAnimationIndex > 1)) ? (Math.Abs(FarmerRenderer.featureYOffsetPerFrame[farmer3.FarmerSprite.CurrentFrame]) * 0.5f) : 0f)), SpriteEffects.None, 0f); - } - } - } - try - { - Game1.currentLocation.Map.GetLayer("Buildings").Draw(Game1.mapDisplayDevice, Game1.viewport, Location.Origin, false, 4); - } - catch (KeyNotFoundException exception2) - { - CheckToReloadGameLocationAfterDrawFail.Invoke("Buildings", exception2); - } - Game1.mapDisplayDevice.EndScene(); - if (Game1.currentLocation.tapToMove.targetNPC != null) - { - Game1.spriteBatch.Draw(Game1.mouseCursors, Game1.GlobalToLocal(Game1.viewport, Game1.currentLocation.tapToMove.targetNPC.Position + new Vector2((((float)(Game1.currentLocation.tapToMove.targetNPC.Sprite.SpriteWidth * 4)) / 2f) - 32f, (float)((Game1.currentLocation.tapToMove.targetNPC.GetBoundingBox().Height + (Game1.currentLocation.tapToMove.targetNPC.IsMonster ? 0 : 12)) - 0x20))), new Rectangle(0xc2, 0x184, 0x10, 0x10), Color.White, 0f, Vector2.Zero, (float)4f, SpriteEffects.None, 0.58f); - } - _spriteBatchEnd.Invoke(); - Game1.SetSpriteBatchBeginNextID("M"); - nullable = null; - _spriteBatchBegin.Invoke(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, nullable); - if (!Game1.currentLocation.shouldHideCharacters()) - { - if (Game1.CurrentEvent == null) - { - foreach (NPC npc3 in Game1.currentLocation.characters) - { - if (((!npc3.swimming) && !npc3.HideShadow) && Game1.currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(npc3.getTileLocation())) - { - Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.viewport, npc3.Position + new Vector2(((float)(npc3.Sprite.SpriteWidth * 4)) / 2f, (float)(npc3.GetBoundingBox().Height + (npc3.IsMonster ? 0 : 12)))), new Rectangle?(Game1.shadowTexture.Bounds), Color.White, 0f, new Vector2((float)Game1.shadowTexture.Bounds.Center.X, (float)Game1.shadowTexture.Bounds.Center.Y), (float)((4f + (((float)npc3.yJumpOffset) / 40f)) * npc3.scale), SpriteEffects.None, Math.Max((float)0f, (float)(((float)npc3.getStandingY()) / 10000f)) - 1E-06f); - } - } - } - else - { - foreach (NPC npc4 in Game1.CurrentEvent.actors) - { - if (((!npc4.swimming) && !npc4.HideShadow) && Game1.currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(npc4.getTileLocation())) - { - Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.viewport, npc4.Position + new Vector2(((float)(npc4.Sprite.SpriteWidth * 4)) / 2f, (float)(npc4.GetBoundingBox().Height + (npc4.IsMonster ? 0 : 12)))), new Rectangle?(Game1.shadowTexture.Bounds), Color.White, 0f, new Vector2((float)Game1.shadowTexture.Bounds.Center.X, (float)Game1.shadowTexture.Bounds.Center.Y), (float)((4f + (((float)npc4.yJumpOffset) / 40f)) * npc4.scale), SpriteEffects.None, Math.Max((float)0f, (float)(((float)npc4.getStandingY()) / 10000f)) - 1E-06f); - } - } - } - foreach (Farmer farmer4 in _farmerShadows.GetValue()) - { - if (((!farmer4.swimming) && !farmer4.isRidingHorse()) && ((Game1.currentLocation != null) && Game1.currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(farmer4.getTileLocation()))) - { - Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(farmer4.Position + new Vector2(32f, 24f)), new Rectangle?(Game1.shadowTexture.Bounds), Color.White, 0f, new Vector2((float)Game1.shadowTexture.Bounds.Center.X, (float)Game1.shadowTexture.Bounds.Center.Y), (float)(4f - (((farmer4.running || farmer4.UsingTool) && (farmer4.FarmerSprite.currentAnimationIndex > 1)) ? (Math.Abs(FarmerRenderer.featureYOffsetPerFrame[farmer4.FarmerSprite.CurrentFrame]) * 0.5f) : 0f)), SpriteEffects.None, 0f); - } - } - } - if ((Game1.eventUp || Game1.killScreen) && (!Game1.killScreen && (Game1.currentLocation.currentEvent != null))) - { - Game1.currentLocation.currentEvent.draw(Game1.spriteBatch); - } - if (((Game1.player.currentUpgrade != null) && (Game1.player.currentUpgrade.daysLeftTillUpgradeDone <= 3)) && Game1.currentLocation.Name.Equals("Farm")) - { - Game1.spriteBatch.Draw(Game1.player.currentUpgrade.workerTexture, Game1.GlobalToLocal(Game1.viewport, Game1.player.currentUpgrade.positionOfCarpenter), new Rectangle?(Game1.player.currentUpgrade.getSourceRectangle()), Color.White, 0f, Vector2.Zero, (float)1f, SpriteEffects.None, (Game1.player.currentUpgrade.positionOfCarpenter.Y + 48f) / 10000f); - } - Game1.currentLocation.draw(Game1.spriteBatch); - if (((Game1.player.ActiveObject == null) && (Game1.player.UsingTool || Game1.pickingTool)) && ((Game1.player.CurrentTool != null) && (!Game1.player.CurrentTool.Name.Equals("Seeds") || Game1.pickingTool))) - { - Game1.drawTool(Game1.player); - } - if (Game1.currentLocation.Name.Equals("Farm")) - { - drawFarmBuildings.Invoke(); - } - if (Game1.tvStation >= 0) - { - Game1.spriteBatch.Draw(Game1.tvStationTexture, Game1.GlobalToLocal(Game1.viewport, new Vector2(400f, 160f)), new Rectangle(Game1.tvStation * 0x18, 0, 0x18, 15), Color.White, 0f, Vector2.Zero, (float)4f, SpriteEffects.None, 1E-08f); - } - if (Game1.panMode) - { - Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Rectangle((((int)Math.Floor((double)(((double)(Game1.getOldMouseX() + Game1.viewport.X)) / 64.0))) * 0x40) - Game1.viewport.X, (((int)Math.Floor((double)(((double)(Game1.getOldMouseY() + Game1.viewport.Y)) / 64.0))) * 0x40) - Game1.viewport.Y, 0x40, 0x40), Color.Lime * 0.75f); - foreach (Warp warp in Game1.currentLocation.warps) - { - Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Rectangle((warp.X * 0x40) - Game1.viewport.X, (warp.Y * 0x40) - Game1.viewport.Y, 0x40, 0x40), Color.Red * 0.75f); - } - } - Game1.mapDisplayDevice.BeginScene(Game1.spriteBatch); - try - { - Game1.currentLocation.Map.GetLayer("Front").Draw(Game1.mapDisplayDevice, Game1.viewport, Location.Origin, false, 4); - } - catch (KeyNotFoundException exception3) - { - CheckToReloadGameLocationAfterDrawFail.Invoke("Front", exception3); - } - Game1.mapDisplayDevice.EndScene(); - Game1.currentLocation.drawAboveFrontLayer(Game1.spriteBatch); - if ((((Game1.currentLocation.tapToMove.targetNPC == null) && (Game1.displayHUD || Game1.eventUp)) && (((Game1.currentBillboard == 0) && (Game1.gameMode == 3)) && (!Game1.freezeControls && !Game1.panMode))) && !Game1.HostPaused) - { - DrawTapToMoveTarget.Invoke(); - } - _spriteBatchEnd.Invoke(); - Game1.SetSpriteBatchBeginNextID("N"); - nullable = null; - _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, nullable); - if (((Game1.displayFarmer && (Game1.player.ActiveObject != null)) && ((Game1.player.ActiveObject.bigCraftable != null) && Game1.game1.checkBigCraftableBoundariesForFrontLayer())) && (Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location(Game1.player.getStandingX(), Game1.player.getStandingY()), Game1.viewport.Size) == null)) - { - Game1.drawPlayerHeldObject(Game1.player); - } - else if ((Game1.displayFarmer && (Game1.player.ActiveObject != null)) && (((Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location((int)Game1.player.Position.X, ((int)Game1.player.Position.Y) - 0x26), Game1.viewport.Size) != null) && !Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location((int)Game1.player.Position.X, ((int)Game1.player.Position.Y) - 0x26), Game1.viewport.Size).TileIndexProperties.ContainsKey("FrontAlways")) || ((Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location(Game1.player.GetBoundingBox().Right, ((int)Game1.player.Position.Y) - 0x26), Game1.viewport.Size) != null) && !Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location(Game1.player.GetBoundingBox().Right, ((int)Game1.player.Position.Y) - 0x26), Game1.viewport.Size).TileIndexProperties.ContainsKey("FrontAlways")))) - { - Game1.drawPlayerHeldObject(Game1.player); - } - if (((Game1.player.UsingTool || Game1.pickingTool) && (Game1.player.CurrentTool != null)) && ((!Game1.player.CurrentTool.Name.Equals("Seeds") || Game1.pickingTool) && ((Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location(Game1.player.getStandingX(), ((int)Game1.player.Position.Y) - 0x26), Game1.viewport.Size) != null) && (Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location(Game1.player.getStandingX(), Game1.player.getStandingY()), Game1.viewport.Size) == null)))) - { - Game1.drawTool(Game1.player); - } - if (Game1.currentLocation.Map.GetLayer("AlwaysFront") != null) - { - Game1.mapDisplayDevice.BeginScene(Game1.spriteBatch); - try - { - Game1.currentLocation.Map.GetLayer("AlwaysFront").Draw(Game1.mapDisplayDevice, Game1.viewport, Location.Origin, false, 4); - } - catch (KeyNotFoundException exception4) - { - CheckToReloadGameLocationAfterDrawFail.Invoke("AlwaysFront", exception4); - } - Game1.mapDisplayDevice.EndScene(); - } - if (((Game1.toolHold > 400f) && (Game1.player.CurrentTool.UpgradeLevel >= 1)) && Game1.player.canReleaseTool) - { - Color white = Color.White; - switch ((((int)(Game1.toolHold / 600f)) + 2)) - { - case 1: - white = Tool.copperColor; - break; - - case 2: - white = Tool.steelColor; - break; - - case 3: - white = Tool.goldColor; - break; - - case 4: - white = Tool.iridiumColor; - break; - } - Game1.spriteBatch.Draw(Game1.littleEffect, new Rectangle(((int)Game1.player.getLocalPosition(Game1.viewport).X) - 2, (((int)Game1.player.getLocalPosition(Game1.viewport).Y) - (Game1.player.CurrentTool.Name.Equals("Watering Can") ? 0 : 0x40)) - 2, ((int)((Game1.toolHold % 600f) * 0.08f)) + 4, 12), Color.Black); - Game1.spriteBatch.Draw(Game1.littleEffect, new Rectangle((int)Game1.player.getLocalPosition(Game1.viewport).X, ((int)Game1.player.getLocalPosition(Game1.viewport).Y) - (Game1.player.CurrentTool.Name.Equals("Watering Can") ? 0 : 0x40), (int)((Game1.toolHold % 600f) * 0.08f), 8), white); - } - if ((WeatherDebrisManager.Instance.isDebrisWeather && Game1.currentLocation.IsOutdoors) && ((!Game1.currentLocation.ignoreDebrisWeather) && !Game1.currentLocation.Name.Equals("Desert"))) - { - WeatherDebrisManager.Instance.Draw(Game1.spriteBatch); - } - if (Game1.farmEvent != null) - { - Game1.farmEvent.draw(Game1.spriteBatch); - } - if ((Game1.currentLocation.LightLevel > 0f) && (Game1.timeOfDay < 0x7d0)) - { - Game1.spriteBatch.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.Black * Game1.currentLocation.LightLevel); - } - if (Game1.screenGlow) - { - Game1.spriteBatch.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Game1.screenGlowColor * Game1.screenGlowAlpha); - } - Game1.currentLocation.drawAboveAlwaysFrontLayer(Game1.spriteBatch); - if (((Game1.player.CurrentTool != null) && (Game1.player.CurrentTool is FishingRod)) && (((Game1.player.CurrentTool as FishingRod).isTimingCast || ((Game1.player.CurrentTool as FishingRod).castingChosenCountdown > 0f)) || ((Game1.player.CurrentTool as FishingRod).fishCaught || (Game1.player.CurrentTool as FishingRod).showingTreasure))) - { - Game1.player.CurrentTool.draw(Game1.spriteBatch); - } - if (((RainManager.Instance.isRaining && Game1.currentLocation.IsOutdoors) && (!Game1.currentLocation.Name.Equals("Desert") && !(Game1.currentLocation is Summit))) && (!Game1.eventUp || Game1.currentLocation.isTileOnMap(new Vector2((float)(Game1.viewport.X / 0x40), (float)(Game1.viewport.Y / 0x40))))) - { - RainManager.Instance.Draw(Game1.spriteBatch); - } - _spriteBatchEnd.Invoke(); - Game1.SetSpriteBatchBeginNextID("O"); - nullable = null; - _spriteBatchBegin.Invoke(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, nullable); - if (Game1.eventUp && (Game1.currentLocation.currentEvent != null)) - { - Game1.currentLocation.currentEvent.drawAboveAlwaysFrontLayer(Game1.spriteBatch); - foreach (NPC npc5 in Game1.currentLocation.currentEvent.actors) - { - if (npc5.isEmoting) - { - Vector2 position = npc5.getLocalPosition(Game1.viewport); - position.Y -= 140f; - if (npc5.Age == 2) - { - position.Y += 32f; - } - else if (npc5.Gender == 1) - { - position.Y += 10f; - } - Game1.spriteBatch.Draw(Game1.emoteSpriteSheet, position, new Rectangle((npc5.CurrentEmoteIndex * 0x10) % Game1.emoteSpriteSheet.Width, ((npc5.CurrentEmoteIndex * 0x10) / Game1.emoteSpriteSheet.Width) * 0x10, 0x10, 0x10), Color.White, 0f, Vector2.Zero, (float)4f, SpriteEffects.None, ((float)npc5.getStandingY()) / 10000f); - } - } - } - _spriteBatchEnd.Invoke(); - if (Game1.drawLighting) - { - Game1.SetSpriteBatchBeginNextID("P"); - nullable = null; - _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, lightingBlend.GetValue(), SamplerState.LinearClamp, null, null, null, nullable); - Game1.spriteBatch.Draw(Game1.lightmap, Vector2.Zero, new Rectangle?(Game1.lightmap.Bounds), Color.White, 0f, Vector2.Zero, (float)(Game1.options.lightingQuality / 2), SpriteEffects.None, 1f); - if ((RainManager.Instance.isRaining && (Game1.currentLocation.IsOutdoors)) && !(Game1.currentLocation is Desert)) - { - Game1.spriteBatch.Draw(Game1.staminaRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.OrangeRed * 0.45f); - } - _spriteBatchEnd.Invoke(); - } - Game1.SetSpriteBatchBeginNextID("Q"); - _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, null); - events.RenderedWorld.RaiseEmpty(); - if (Game1.drawGrid) - { - int x = -Game1.viewport.X % 0x40; - float num5 = -Game1.viewport.Y % 0x40; - for (int i = x; i < Game1.graphics.GraphicsDevice.Viewport.Width; i += 0x40) - { - Game1.spriteBatch.Draw(Game1.staminaRect, new Rectangle(i, (int)num5, 1, Game1.graphics.GraphicsDevice.Viewport.Height), Color.Red * 0.5f); - } - for (float j = num5; j < Game1.graphics.GraphicsDevice.Viewport.Height; j += 64f) - { - Game1.spriteBatch.Draw(Game1.staminaRect, new Rectangle(x, (int)j, Game1.graphics.GraphicsDevice.Viewport.Width, 1), Color.Red * 0.5f); - } - } - if (((Game1.displayHUD || Game1.eventUp) && ((Game1.currentBillboard == 0) && (Game1.gameMode == 3))) && ((!Game1.freezeControls && !Game1.panMode) && !Game1.HostPaused)) - { - _drawHUD.SetValue(true); - if (Game1.isOutdoorMapSmallerThanViewport()) - { - Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Rectangle(0, 0, -Math.Min(Game1.viewport.X, 0x1000), Game1.graphics.GraphicsDevice.Viewport.Height), Color.Black); - Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Rectangle(-Game1.viewport.X + (Game1.currentLocation.map.Layers[0].LayerWidth * 0x40), 0, Math.Min(0x1000, Game1.graphics.GraphicsDevice.Viewport.Width - (-Game1.viewport.X + (Game1.currentLocation.map.Layers[0].LayerWidth * 0x40))), Game1.graphics.GraphicsDevice.Viewport.Height), Color.Black); - } - DrawGreenPlacementBounds.Invoke(); + Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.viewport, npc2.Position + new Vector2(((float)(npc2.Sprite.SpriteWidth * 4)) / 2f, (float)(npc2.GetBoundingBox().Height + (npc2.IsMonster ? 0 : ((npc2.Sprite.SpriteHeight <= 0x10) ? -4 : 12))))), new Rectangle?(Game1.shadowTexture.Bounds), Color.White, 0f, new Vector2((float)Game1.shadowTexture.Bounds.Center.X, (float)Game1.shadowTexture.Bounds.Center.Y), (float)((4f + (((float)npc2.yJumpOffset) / 40f)) * npc2.scale), SpriteEffects.None, Math.Max((float)0f, (float)(((float)npc2.getStandingY()) / 10000f)) - 1E-06f); } } - if (Game1.farmEvent != null) + } + foreach (Farmer farmer3 in _farmerShadows.GetValue()) + { + if (((!farmer3.swimming) && !farmer3.isRidingHorse()) && ((Game1.currentLocation == null) || !Game1.currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(farmer3.getTileLocation()))) { - Game1.farmEvent.draw(Game1.spriteBatch); + Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(farmer3.Position + new Vector2(32f, 24f)), new Rectangle?(Game1.shadowTexture.Bounds), Color.White, 0f, new Vector2((float)Game1.shadowTexture.Bounds.Center.X, (float)Game1.shadowTexture.Bounds.Center.Y), (float)(4f - (((farmer3.running || farmer3.UsingTool) && (farmer3.FarmerSprite.currentAnimationIndex > 1)) ? (Math.Abs(FarmerRenderer.featureYOffsetPerFrame[farmer3.FarmerSprite.CurrentFrame]) * 0.5f) : 0f)), SpriteEffects.None, 0f); } - if (((Game1.dialogueUp && !Game1.nameSelectUp) && !Game1.messagePause) && ((Game1.activeClickableMenu == null) || !(Game1.activeClickableMenu is DialogueBox))) - { - drawDialogueBox.Invoke(); - } - if (Game1.progressBar) - { - Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Rectangle((Game1.graphics.GraphicsDevice.Viewport.GetTitleSafeArea().Width - Game1.dialogueWidth) / 2, Game1.graphics.GraphicsDevice.Viewport.GetTitleSafeArea().Bottom - 0x80, Game1.dialogueWidth, 0x20), Color.LightGray); - Game1.spriteBatch.Draw(Game1.staminaRect, new Rectangle((Game1.graphics.GraphicsDevice.Viewport.GetTitleSafeArea().Width - Game1.dialogueWidth) / 2, Game1.graphics.GraphicsDevice.Viewport.GetTitleSafeArea().Bottom - 0x80, (int)((Game1.pauseAccumulator / Game1.pauseTime) * Game1.dialogueWidth), 0x20), Color.DimGray); - } - if ((RainManager.Instance.isRaining && (Game1.currentLocation != null)) && ((Game1.currentLocation.IsOutdoors) && !(Game1.currentLocation is Desert))) - { - Game1.spriteBatch.Draw(Game1.staminaRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.Blue * 0.2f); - } - if ((Game1.messagePause || Game1.globalFade) && Game1.dialogueUp) - { - drawDialogueBox.Invoke(); - } - using (List.Enumerator enumerator6 = Game1.screenOverlayTempSprites.GetEnumerator()) - { - while (enumerator6.MoveNext()) - { - enumerator6.Current.draw(Game1.spriteBatch, true, 0, 0, 1f); - } - } - if (Game1.debugMode) - { - StringBuilder text = _debugStringBuilder.GetValue(); - text.Clear(); - if (Game1.panMode) - { - text.Append((int)((Game1.getOldMouseX() + Game1.viewport.X) / 0x40)); - text.Append(","); - text.Append((int)((Game1.getOldMouseY() + Game1.viewport.Y) / 0x40)); - } - else - { - text.Append("player: "); - text.Append((int)(Game1.player.getStandingX() / 0x40)); - text.Append(", "); - text.Append((int)(Game1.player.getStandingY() / 0x40)); - } - text.Append(" mouseTransparency: "); - text.Append(Game1.mouseCursorTransparency); - text.Append(" mousePosition: "); - text.Append(Game1.getMouseX()); - text.Append(","); - text.Append(Game1.getMouseY()); - text.Append(Environment.NewLine); - text.Append("debugOutput: "); - text.Append(Game1.debugOutput); - Game1.spriteBatch.DrawString(Game1.smallFont, text, new Vector2((float)Game1.game1.GraphicsDevice.Viewport.GetTitleSafeArea().X, (float)(Game1.game1.GraphicsDevice.Viewport.GetTitleSafeArea().Y + (Game1.smallFont.LineSpacing * 8))), Color.Red, 0f, Vector2.Zero, (float)1f, SpriteEffects.None, 0.09999999f); - } - if (Game1.showKeyHelp) - { - Game1.spriteBatch.DrawString(Game1.smallFont, Game1.keyHelpString, new Vector2(64f, ((Game1.viewport.Height - 0x40) - (Game1.dialogueUp ? (0xc0 + (Game1.isQuestion ? (Game1.questionChoices.Count * 0x40) : 0)) : 0)) - Game1.smallFont.MeasureString(Game1.keyHelpString).Y), Color.LightGray, 0f, Vector2.Zero, (float)1f, SpriteEffects.None, 0.9999999f); - } - if (Game1.activeClickableMenu != null) - { - try - { - _drawActiveClickableMenu.SetValue(true); - events.RenderingActiveMenu.RaiseEmpty(); -#if !SMAPI_3_0_STRICT - events.Legacy_OnPreRenderGuiEvent.Raise(); -#endif - if (Game1.activeClickableMenu is CarpenterMenu) - { - ((CarpenterMenu)Game1.activeClickableMenu).DrawPlacementSquares(Game1.spriteBatch); - } - else if (Game1.activeClickableMenu is MuseumMenu) - { - ((MuseumMenu)Game1.activeClickableMenu).DrawPlacementGrid(Game1.spriteBatch); - } - if (!Game1.IsActiveClickableMenuUnscaled && !Game1.IsActiveClickableMenuNativeScaled) - { - Game1.activeClickableMenu.draw(Game1.spriteBatch); - } - events.RenderedActiveMenu.RaiseEmpty(); -#if !SMAPI_3_0_STRICT - events.Legacy_OnPostRenderGuiEvent.Raise(); -#endif - } - catch (Exception ex) - { - this.Monitor.Log($"The {Game1.activeClickableMenu.GetType().FullName} menu crashed while drawing itself. SMAPI will force it to exit to avoid crashing the game.\n{ex.GetLogSummary()}", LogLevel.Error); - Game1.activeClickableMenu.exitThisMenu(); - } - } - else if (Game1.farmEvent != null) - { - Game1.farmEvent.drawAboveEverything(Game1.spriteBatch); - } - if (Game1.HostPaused) - { - string s = Game1.content.LoadString(@"Strings\StringsFromCSFiles:DayTimeMoneyBox.cs.10378"); - SpriteText.drawStringWithScrollBackground(Game1.spriteBatch, s, 0x60, 0x20, "", 1f, -1, 0.088f); - } - events.Rendered.RaiseEmpty(); -#if !SMAPI_3_0_STRICT - events.Legacy_OnPostRenderEvent.Raise(); -#endif - _spriteBatchEnd.Invoke(); - drawOverlays.Invoke(Game1.spriteBatch); - renderScreenBuffer.Invoke(BlendState.Opaque); - if (_drawHUD.GetValue()) - { - DrawDayTimeMoneyBox.Invoke(); - Game1.SetSpriteBatchBeginNextID("A-C"); - SpriteBatchBegin.Invoke(1f); - events.RenderingHud.RaiseEmpty(); -#if !SMAPI_3_0_STRICT - events.Legacy_OnPreRenderHudEvent.Raise(); -#endif - DrawHUD.Invoke(); - if (((Game1.currentLocation != null) && !(Game1.activeClickableMenu is GameMenu)) && !(Game1.activeClickableMenu is QuestLog)) - { - Game1.currentLocation.drawAboveAlwaysFrontLayerText(Game1.spriteBatch); - } - DrawAfterMap.Invoke(); - events.RenderedHud.RaiseEmpty(); -#if !SMAPI_3_0_STRICT - events.Legacy_OnPostRenderHudEvent.Raise(); -#endif - _spriteBatchEnd.Invoke(); - if (Game1.tutorialManager != null) - { - Game1.SetSpriteBatchBeginNextID("A-D"); - SpriteBatchBegin.Invoke(Game1.options.zoomLevel); - Game1.tutorialManager.draw(Game1.spriteBatch); - _spriteBatchEnd.Invoke(); - } - DrawToolbar.Invoke(); - DrawVirtualJoypad.Invoke(); - } - DrawFadeToBlackFullScreenRect.Invoke(); - Game1.SetSpriteBatchBeginNextID("A-E"); - SpriteBatchBegin.Invoke(1f); - DrawChatBox.Invoke(); - _spriteBatchEnd.Invoke(); - if (_drawActiveClickableMenu.GetValue()) - { - DrawDialogueBoxForPinchZoom.Invoke(); - DrawUnscaledActiveClickableMenuForPinchZoom.Invoke(); - DrawNativeScaledActiveClickableMenuForPinchZoom.Invoke(); - } - if ((_drawHUD.GetValue() && (Game1.hudMessages.Count > 0)) && (!Game1.eventUp || Game1.isFestival())) - { - Game1.SetSpriteBatchBeginNextID("A-F"); - SpriteBatchBegin.Invoke(Game1.NativeZoomLevel); - DrawHUDMessages.Invoke(); - _spriteBatchEnd.Invoke(); - } - if (((Game1.CurrentEvent != null) && Game1.CurrentEvent.skippable) && ((Game1.activeClickableMenu == null) || ((Game1.activeClickableMenu != null) && !(Game1.activeClickableMenu is MenuWithInventory)))) - { - Game1.SetSpriteBatchBeginNextID("A-G"); - SpriteBatchBegin.Invoke(Game1.NativeZoomLevel); - Game1.CurrentEvent.DrawSkipButton(Game1.spriteBatch); - _spriteBatchEnd.Invoke(); - } - DrawTutorialUI.Invoke(); } } + try + { + Game1.currentLocation?.Map.GetLayer("Buildings").Draw(Game1.mapDisplayDevice, Game1.viewport, Location.Origin, false, 4); + } + catch (KeyNotFoundException exception2) + { + CheckToReloadGameLocationAfterDrawFail.Invoke("Buildings", exception2); + } + Game1.mapDisplayDevice.EndScene(); + if (Game1.currentLocation?.tapToMove.targetNPC != null) + { + Game1.spriteBatch.Draw(Game1.mouseCursors, Game1.GlobalToLocal(Game1.viewport, Game1.currentLocation.tapToMove.targetNPC.Position + new Vector2((((float)(Game1.currentLocation.tapToMove.targetNPC.Sprite.SpriteWidth * 4)) / 2f) - 32f, (float)((Game1.currentLocation.tapToMove.targetNPC.GetBoundingBox().Height + (Game1.currentLocation.tapToMove.targetNPC.IsMonster ? 0 : 12)) - 0x20))), new Rectangle(0xc2, 0x184, 0x10, 0x10), Color.White, 0f, Vector2.Zero, (float)4f, SpriteEffects.None, 0.58f); + } + _spriteBatchEnd.Invoke(); + Game1.SetSpriteBatchBeginNextID("M"); + _spriteBatchBegin.Invoke(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, null); + if (Game1.currentLocation != null && !Game1.currentLocation.shouldHideCharacters()) + { + if (Game1.CurrentEvent == null) + { + foreach (NPC npc3 in Game1.currentLocation.characters) + { + if (((!npc3.swimming) && !npc3.HideShadow) && Game1.currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(npc3.getTileLocation())) + { + Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.viewport, npc3.Position + new Vector2(((float)(npc3.Sprite.SpriteWidth * 4)) / 2f, (float)(npc3.GetBoundingBox().Height + (npc3.IsMonster ? 0 : 12)))), new Rectangle?(Game1.shadowTexture.Bounds), Color.White, 0f, new Vector2((float)Game1.shadowTexture.Bounds.Center.X, (float)Game1.shadowTexture.Bounds.Center.Y), (float)((4f + (((float)npc3.yJumpOffset) / 40f)) * npc3.scale), SpriteEffects.None, Math.Max((float)0f, (float)(((float)npc3.getStandingY()) / 10000f)) - 1E-06f); + } + } + } + else + { + foreach (NPC npc4 in Game1.CurrentEvent.actors) + { + if (((!npc4.swimming) && !npc4.HideShadow) && Game1.currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(npc4.getTileLocation())) + { + Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(Game1.viewport, npc4.Position + new Vector2(((float)(npc4.Sprite.SpriteWidth * 4)) / 2f, (float)(npc4.GetBoundingBox().Height + (npc4.IsMonster ? 0 : 12)))), new Rectangle?(Game1.shadowTexture.Bounds), Color.White, 0f, new Vector2((float)Game1.shadowTexture.Bounds.Center.X, (float)Game1.shadowTexture.Bounds.Center.Y), (float)((4f + (((float)npc4.yJumpOffset) / 40f)) * npc4.scale), SpriteEffects.None, Math.Max((float)0f, (float)(((float)npc4.getStandingY()) / 10000f)) - 1E-06f); + } + } + } + foreach (Farmer farmer4 in _farmerShadows.GetValue()) + { + if (((!farmer4.swimming) && !farmer4.isRidingHorse()) && ((Game1.currentLocation != null) && Game1.currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(farmer4.getTileLocation()))) + { + Game1.spriteBatch.Draw(Game1.shadowTexture, Game1.GlobalToLocal(farmer4.Position + new Vector2(32f, 24f)), new Rectangle?(Game1.shadowTexture.Bounds), Color.White, 0f, new Vector2((float)Game1.shadowTexture.Bounds.Center.X, (float)Game1.shadowTexture.Bounds.Center.Y), (float)(4f - (((farmer4.running || farmer4.UsingTool) && (farmer4.FarmerSprite.currentAnimationIndex > 1)) ? (Math.Abs(FarmerRenderer.featureYOffsetPerFrame[farmer4.FarmerSprite.CurrentFrame]) * 0.5f) : 0f)), SpriteEffects.None, 0f); + } + } + } + + if (Game1.currentLocation != null) + { + if ((Game1.eventUp || Game1.killScreen) && (!Game1.killScreen && (Game1.currentLocation.currentEvent != null))) + { + Game1.currentLocation.currentEvent.draw(Game1.spriteBatch); + } + if (((Game1.player.currentUpgrade != null) && (Game1.player.currentUpgrade.daysLeftTillUpgradeDone <= 3)) && Game1.currentLocation.Name.Equals("Farm")) + { + Game1.spriteBatch.Draw(Game1.player.currentUpgrade.workerTexture, Game1.GlobalToLocal(Game1.viewport, Game1.player.currentUpgrade.positionOfCarpenter), new Rectangle?(Game1.player.currentUpgrade.getSourceRectangle()), Color.White, 0f, Vector2.Zero, (float)1f, SpriteEffects.None, (Game1.player.currentUpgrade.positionOfCarpenter.Y + 48f) / 10000f); + } + Game1.currentLocation.draw(Game1.spriteBatch); + if (((Game1.player.ActiveObject == null) && (Game1.player.UsingTool || Game1.pickingTool)) && ((Game1.player.CurrentTool != null) && (!Game1.player.CurrentTool.Name.Equals("Seeds") || Game1.pickingTool))) + { + Game1.drawTool(Game1.player); + } + if (Game1.currentLocation.Name.Equals("Farm")) + { + drawFarmBuildings.Invoke(); + } + if (Game1.tvStation >= 0) + { + Game1.spriteBatch.Draw(Game1.tvStationTexture, Game1.GlobalToLocal(Game1.viewport, new Vector2(400f, 160f)), new Rectangle(Game1.tvStation * 0x18, 0, 0x18, 15), Color.White, 0f, Vector2.Zero, (float)4f, SpriteEffects.None, 1E-08f); + } + if (Game1.panMode) + { + Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Rectangle((((int)Math.Floor((double)(((double)(Game1.getOldMouseX() + Game1.viewport.X)) / 64.0))) * 0x40) - Game1.viewport.X, (((int)Math.Floor((double)(((double)(Game1.getOldMouseY() + Game1.viewport.Y)) / 64.0))) * 0x40) - Game1.viewport.Y, 0x40, 0x40), Color.Lime * 0.75f); + foreach (Warp warp in Game1.currentLocation.warps) + { + Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Rectangle((warp.X * 0x40) - Game1.viewport.X, (warp.Y * 0x40) - Game1.viewport.Y, 0x40, 0x40), Color.Red * 0.75f); + } + } + Game1.mapDisplayDevice.BeginScene(Game1.spriteBatch); + try + { + Game1.currentLocation.Map.GetLayer("Front").Draw(Game1.mapDisplayDevice, Game1.viewport, Location.Origin, false, 4); + } + catch (KeyNotFoundException exception3) + { + CheckToReloadGameLocationAfterDrawFail.Invoke("Front", exception3); + } + Game1.mapDisplayDevice.EndScene(); + Game1.currentLocation.drawAboveFrontLayer(Game1.spriteBatch); + if ((((Game1.currentLocation.tapToMove.targetNPC == null) && (Game1.displayHUD || Game1.eventUp)) && (((Game1.currentBillboard == 0) && (Game1.gameMode == 3)) && (!Game1.freezeControls && !Game1.panMode))) && !Game1.HostPaused) + { + DrawTapToMoveTarget.Invoke(); + } + } + _spriteBatchEnd.Invoke(); + Game1.SetSpriteBatchBeginNextID("N"); + _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, null); + if (Game1.currentLocation != null) + { + if (((Game1.displayFarmer && (Game1.player.ActiveObject != null)) && ((Game1.player.ActiveObject.bigCraftable != null) && Game1.game1.checkBigCraftableBoundariesForFrontLayer())) && (Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location(Game1.player.getStandingX(), Game1.player.getStandingY()), Game1.viewport.Size) == null)) + { + Game1.drawPlayerHeldObject(Game1.player); + } + else if ((Game1.displayFarmer && (Game1.player.ActiveObject != null)) && (((Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location((int)Game1.player.Position.X, ((int)Game1.player.Position.Y) - 0x26), Game1.viewport.Size) != null) && !Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location((int)Game1.player.Position.X, ((int)Game1.player.Position.Y) - 0x26), Game1.viewport.Size).TileIndexProperties.ContainsKey("FrontAlways")) || ((Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location(Game1.player.GetBoundingBox().Right, ((int)Game1.player.Position.Y) - 0x26), Game1.viewport.Size) != null) && !Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location(Game1.player.GetBoundingBox().Right, ((int)Game1.player.Position.Y) - 0x26), Game1.viewport.Size).TileIndexProperties.ContainsKey("FrontAlways")))) + { + Game1.drawPlayerHeldObject(Game1.player); + } + if (((Game1.player.UsingTool || Game1.pickingTool) && (Game1.player.CurrentTool != null)) && ((!Game1.player.CurrentTool.Name.Equals("Seeds") || Game1.pickingTool) && ((Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location(Game1.player.getStandingX(), ((int)Game1.player.Position.Y) - 0x26), Game1.viewport.Size) != null) && (Game1.currentLocation.Map.GetLayer("Front").PickTile(new Location(Game1.player.getStandingX(), Game1.player.getStandingY()), Game1.viewport.Size) == null)))) + { + Game1.drawTool(Game1.player); + } + if (Game1.currentLocation.Map.GetLayer("AlwaysFront") != null) + { + Game1.mapDisplayDevice.BeginScene(Game1.spriteBatch); + try + { + Game1.currentLocation.Map.GetLayer("AlwaysFront").Draw(Game1.mapDisplayDevice, Game1.viewport, Location.Origin, false, 4); + } + catch (KeyNotFoundException exception4) + { + CheckToReloadGameLocationAfterDrawFail.Invoke("AlwaysFront", exception4); + } + Game1.mapDisplayDevice.EndScene(); + } + if (((Game1.toolHold > 400f) && (Game1.player.CurrentTool.UpgradeLevel >= 1)) && Game1.player.canReleaseTool) + { + Color white = Color.White; + switch ((((int)(Game1.toolHold / 600f)) + 2)) + { + case 1: + white = Tool.copperColor; + break; + + case 2: + white = Tool.steelColor; + break; + + case 3: + white = Tool.goldColor; + break; + + case 4: + white = Tool.iridiumColor; + break; + } + Game1.spriteBatch.Draw(Game1.littleEffect, new Rectangle(((int)Game1.player.getLocalPosition(Game1.viewport).X) - 2, (((int)Game1.player.getLocalPosition(Game1.viewport).Y) - (Game1.player.CurrentTool.Name.Equals("Watering Can") ? 0 : 0x40)) - 2, ((int)((Game1.toolHold % 600f) * 0.08f)) + 4, 12), Color.Black); + Game1.spriteBatch.Draw(Game1.littleEffect, new Rectangle((int)Game1.player.getLocalPosition(Game1.viewport).X, ((int)Game1.player.getLocalPosition(Game1.viewport).Y) - (Game1.player.CurrentTool.Name.Equals("Watering Can") ? 0 : 0x40), (int)((Game1.toolHold % 600f) * 0.08f), 8), white); + } + if ((WeatherDebrisManager.Instance.isDebrisWeather && Game1.currentLocation.IsOutdoors) && ((!Game1.currentLocation.ignoreDebrisWeather) && !Game1.currentLocation.Name.Equals("Desert"))) + { + WeatherDebrisManager.Instance.Draw(Game1.spriteBatch); + } + if (Game1.farmEvent != null) + { + Game1.farmEvent.draw(Game1.spriteBatch); + } + if ((Game1.currentLocation.LightLevel > 0f) && (Game1.timeOfDay < 0x7d0)) + { + Game1.spriteBatch.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.Black * Game1.currentLocation.LightLevel); + } + if (Game1.screenGlow) + { + Game1.spriteBatch.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Game1.screenGlowColor * Game1.screenGlowAlpha); + } + Game1.currentLocation.drawAboveAlwaysFrontLayer(Game1.spriteBatch); + if (((Game1.player.CurrentTool != null) && (Game1.player.CurrentTool is FishingRod)) && (((Game1.player.CurrentTool as FishingRod).isTimingCast || ((Game1.player.CurrentTool as FishingRod).castingChosenCountdown > 0f)) || ((Game1.player.CurrentTool as FishingRod).fishCaught || (Game1.player.CurrentTool as FishingRod).showingTreasure))) + { + Game1.player.CurrentTool.draw(Game1.spriteBatch); + } + if (((RainManager.Instance.isRaining && Game1.currentLocation.IsOutdoors) && (!Game1.currentLocation.Name.Equals("Desert") && !(Game1.currentLocation is Summit))) && (!Game1.eventUp || Game1.currentLocation.isTileOnMap(new Vector2((float)(Game1.viewport.X / 0x40), (float)(Game1.viewport.Y / 0x40))))) + { + RainManager.Instance.Draw(Game1.spriteBatch); + } + } + _spriteBatchEnd.Invoke(); + Game1.SetSpriteBatchBeginNextID("O"); + _spriteBatchBegin.Invoke(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, null); + if (Game1.eventUp && (Game1.currentLocation?.currentEvent != null)) + { + Game1.currentLocation.currentEvent.drawAboveAlwaysFrontLayer(Game1.spriteBatch); + foreach (NPC npc5 in Game1.currentLocation.currentEvent.actors) + { + if (npc5.isEmoting) + { + Vector2 position = npc5.getLocalPosition(Game1.viewport); + position.Y -= 140f; + if (npc5.Age == 2) + { + position.Y += 32f; + } + else if (npc5.Gender == 1) + { + position.Y += 10f; + } + Game1.spriteBatch.Draw(Game1.emoteSpriteSheet, position, new Rectangle((npc5.CurrentEmoteIndex * 0x10) % Game1.emoteSpriteSheet.Width, ((npc5.CurrentEmoteIndex * 0x10) / Game1.emoteSpriteSheet.Width) * 0x10, 0x10, 0x10), Color.White, 0f, Vector2.Zero, (float)4f, SpriteEffects.None, ((float)npc5.getStandingY()) / 10000f); + } + } + } + _spriteBatchEnd.Invoke(); + if (Game1.drawLighting && Game1.currentLocation != null) + { + Game1.SetSpriteBatchBeginNextID("P"); + _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, lightingBlend.GetValue(), SamplerState.LinearClamp, null, null, null, null); + Game1.spriteBatch.Draw(Game1.lightmap, Vector2.Zero, new Rectangle?(Game1.lightmap.Bounds), Color.White, 0f, Vector2.Zero, (float)(Game1.options.lightingQuality / 2), SpriteEffects.None, 1f); + if ((RainManager.Instance.isRaining && (Game1.currentLocation.IsOutdoors)) && !(Game1.currentLocation is Desert)) + { + Game1.spriteBatch.Draw(Game1.staminaRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.OrangeRed * 0.45f); + } + _spriteBatchEnd.Invoke(); + } + Game1.SetSpriteBatchBeginNextID("Q"); + _spriteBatchBegin.Invoke(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, null); + events.RenderedWorld.RaiseEmpty(); + if (Game1.drawGrid) + { + int x = -Game1.viewport.X % 0x40; + float num5 = -Game1.viewport.Y % 0x40; + for (int i = x; i < Game1.graphics.GraphicsDevice.Viewport.Width; i += 0x40) + { + Game1.spriteBatch.Draw(Game1.staminaRect, new Rectangle(i, (int)num5, 1, Game1.graphics.GraphicsDevice.Viewport.Height), Color.Red * 0.5f); + } + for (float j = num5; j < Game1.graphics.GraphicsDevice.Viewport.Height; j += 64f) + { + Game1.spriteBatch.Draw(Game1.staminaRect, new Rectangle(x, (int)j, Game1.graphics.GraphicsDevice.Viewport.Width, 1), Color.Red * 0.5f); + } + } + if (((Game1.displayHUD || Game1.eventUp) && ((Game1.currentBillboard == 0) && (Game1.gameMode == 3))) && ((!Game1.freezeControls && !Game1.panMode) && !Game1.HostPaused)) + { + _drawHUD.SetValue(true); + if (Game1.isOutdoorMapSmallerThanViewport() && Game1.currentLocation != null) + { + Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Rectangle(0, 0, -Math.Min(Game1.viewport.X, 0x1000), Game1.graphics.GraphicsDevice.Viewport.Height), Color.Black); + Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Rectangle(-Game1.viewport.X + (Game1.currentLocation.map.Layers[0].LayerWidth * 0x40), 0, Math.Min(0x1000, Game1.graphics.GraphicsDevice.Viewport.Width - (-Game1.viewport.X + (Game1.currentLocation.map.Layers[0].LayerWidth * 0x40))), Game1.graphics.GraphicsDevice.Viewport.Height), Color.Black); + } + DrawGreenPlacementBounds.Invoke(); + } } + if (Game1.farmEvent != null) + { + Game1.farmEvent.draw(Game1.spriteBatch); + } + if (((Game1.dialogueUp && !Game1.nameSelectUp) && !Game1.messagePause) && ((Game1.activeClickableMenu == null) || !(Game1.activeClickableMenu is DialogueBox))) + { + drawDialogueBox.Invoke(); + } + if (Game1.progressBar) + { + Game1.spriteBatch.Draw(Game1.fadeToBlackRect, new Rectangle((Game1.graphics.GraphicsDevice.Viewport.GetTitleSafeArea().Width - Game1.dialogueWidth) / 2, Game1.graphics.GraphicsDevice.Viewport.GetTitleSafeArea().Bottom - 0x80, Game1.dialogueWidth, 0x20), Color.LightGray); + Game1.spriteBatch.Draw(Game1.staminaRect, new Rectangle((Game1.graphics.GraphicsDevice.Viewport.GetTitleSafeArea().Width - Game1.dialogueWidth) / 2, Game1.graphics.GraphicsDevice.Viewport.GetTitleSafeArea().Bottom - 0x80, (int)((Game1.pauseAccumulator / Game1.pauseTime) * Game1.dialogueWidth), 0x20), Color.DimGray); + } + if ((RainManager.Instance.isRaining && (Game1.currentLocation != null)) && ((Game1.currentLocation.IsOutdoors) && !(Game1.currentLocation is Desert))) + { + Game1.spriteBatch.Draw(Game1.staminaRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.Blue * 0.2f); + } + if ((Game1.messagePause || Game1.globalFade) && Game1.dialogueUp) + { + drawDialogueBox.Invoke(); + } + using (List.Enumerator enumerator6 = Game1.screenOverlayTempSprites.GetEnumerator()) + { + while (enumerator6.MoveNext()) + { + enumerator6.Current.draw(Game1.spriteBatch, true, 0, 0, 1f); + } + } + if (Game1.debugMode) + { + StringBuilder text = _debugStringBuilder.GetValue(); + text.Clear(); + if (Game1.panMode) + { + text.Append((int)((Game1.getOldMouseX() + Game1.viewport.X) / 0x40)); + text.Append(","); + text.Append((int)((Game1.getOldMouseY() + Game1.viewport.Y) / 0x40)); + } + else + { + text.Append("player: "); + text.Append((int)(Game1.player.getStandingX() / 0x40)); + text.Append(", "); + text.Append((int)(Game1.player.getStandingY() / 0x40)); + } + text.Append(" mouseTransparency: "); + text.Append(Game1.mouseCursorTransparency); + text.Append(" mousePosition: "); + text.Append(Game1.getMouseX()); + text.Append(","); + text.Append(Game1.getMouseY()); + text.Append(Environment.NewLine); + text.Append("debugOutput: "); + text.Append(Game1.debugOutput); + Game1.spriteBatch.DrawString(Game1.smallFont, text, new Vector2((float)Game1.game1.GraphicsDevice.Viewport.GetTitleSafeArea().X, (float)(Game1.game1.GraphicsDevice.Viewport.GetTitleSafeArea().Y + (Game1.smallFont.LineSpacing * 8))), Color.Red, 0f, Vector2.Zero, (float)1f, SpriteEffects.None, 0.09999999f); + } + if (Game1.showKeyHelp) + { + Game1.spriteBatch.DrawString(Game1.smallFont, Game1.keyHelpString, new Vector2(64f, ((Game1.viewport.Height - 0x40) - (Game1.dialogueUp ? (0xc0 + (Game1.isQuestion ? (Game1.questionChoices.Count * 0x40) : 0)) : 0)) - Game1.smallFont.MeasureString(Game1.keyHelpString).Y), Color.LightGray, 0f, Vector2.Zero, (float)1f, SpriteEffects.None, 0.9999999f); + } + if (Game1.activeClickableMenu != null) + { + try + { + _drawActiveClickableMenu.SetValue(true); + events.RenderingActiveMenu.RaiseEmpty(); +#if !SMAPI_3_0_STRICT + events.Legacy_OnPreRenderGuiEvent.Raise(); +#endif + if (Game1.activeClickableMenu is CarpenterMenu) + { + ((CarpenterMenu)Game1.activeClickableMenu).DrawPlacementSquares(Game1.spriteBatch); + } + else if (Game1.activeClickableMenu is MuseumMenu) + { + ((MuseumMenu)Game1.activeClickableMenu).DrawPlacementGrid(Game1.spriteBatch); + } + if (!Game1.IsActiveClickableMenuUnscaled && !Game1.IsActiveClickableMenuNativeScaled) + { + Game1.activeClickableMenu.draw(Game1.spriteBatch); + } + events.RenderedActiveMenu.RaiseEmpty(); +#if !SMAPI_3_0_STRICT + events.Legacy_OnPostRenderGuiEvent.Raise(); +#endif + } + catch (Exception ex) + { + this.Monitor.Log($"The {Game1.activeClickableMenu.GetType().FullName} menu crashed while drawing itself. SMAPI will force it to exit to avoid crashing the game.\n{ex.GetLogSummary()}", LogLevel.Error); + Game1.activeClickableMenu.exitThisMenu(); + } + } + else if (Game1.farmEvent != null) + { + Game1.farmEvent.drawAboveEverything(Game1.spriteBatch); + } + if (Game1.HostPaused) + { + string s = Game1.content.LoadString(@"Strings\StringsFromCSFiles:DayTimeMoneyBox.cs.10378"); + SpriteText.drawStringWithScrollBackground(Game1.spriteBatch, s, 0x60, 0x20, "", 1f, -1, 0.088f); + } + events.Rendered.RaiseEmpty(); +#if !SMAPI_3_0_STRICT + events.Legacy_OnPostRenderEvent.Raise(); +#endif + _spriteBatchEnd.Invoke(); + drawOverlays.Invoke(Game1.spriteBatch); + renderScreenBuffer.Invoke(BlendState.Opaque); + if (_drawHUD.GetValue()) + { + DrawDayTimeMoneyBox.Invoke(); + Game1.SetSpriteBatchBeginNextID("A-C"); + SpriteBatchBegin.Invoke(1f); + events.RenderingHud.RaiseEmpty(); +#if !SMAPI_3_0_STRICT + events.Legacy_OnPreRenderHudEvent.Raise(); +#endif + DrawHUD.Invoke(); + if (((Game1.currentLocation != null) && !(Game1.activeClickableMenu is GameMenu)) && !(Game1.activeClickableMenu is QuestLog)) + { + Game1.currentLocation.drawAboveAlwaysFrontLayerText(Game1.spriteBatch); + } + DrawAfterMap.Invoke(); + events.RenderedHud.RaiseEmpty(); +#if !SMAPI_3_0_STRICT + events.Legacy_OnPostRenderHudEvent.Raise(); +#endif + _spriteBatchEnd.Invoke(); + if (Game1.tutorialManager != null) + { + Game1.SetSpriteBatchBeginNextID("A-D"); + SpriteBatchBegin.Invoke(Game1.options.zoomLevel); + Game1.tutorialManager.draw(Game1.spriteBatch); + _spriteBatchEnd.Invoke(); + } + DrawToolbar.Invoke(); + DrawVirtualJoypad.Invoke(); + } + DrawFadeToBlackFullScreenRect.Invoke(); + Game1.SetSpriteBatchBeginNextID("A-E"); + SpriteBatchBegin.Invoke(1f); + DrawChatBox.Invoke(); + _spriteBatchEnd.Invoke(); + if (_drawActiveClickableMenu.GetValue()) + { + DrawDialogueBoxForPinchZoom.Invoke(); + DrawUnscaledActiveClickableMenuForPinchZoom.Invoke(); + DrawNativeScaledActiveClickableMenuForPinchZoom.Invoke(); + } + if ((_drawHUD.GetValue() && (Game1.hudMessages.Count > 0)) && (!Game1.eventUp || Game1.isFestival())) + { + Game1.SetSpriteBatchBeginNextID("A-F"); + SpriteBatchBegin.Invoke(Game1.NativeZoomLevel); + DrawHUDMessages.Invoke(); + _spriteBatchEnd.Invoke(); + } + if (Game1.CurrentEvent != null && Game1.CurrentEvent.skippable && !Game1.CurrentEvent.skipped && (Game1.activeClickableMenu == null || (Game1.activeClickableMenu != null && !(Game1.activeClickableMenu is MenuWithInventory)))) + { + Game1.SetSpriteBatchBeginNextID("A-G"); + SpriteBatchBegin.Invoke(Game1.NativeZoomLevel); + Game1.CurrentEvent.DrawSkipButton(Game1.spriteBatch); + _spriteBatchEnd.Invoke(); + } + DrawTutorialUI.Invoke(); } } } diff --git a/src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs b/src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs index c9d6a703..f09c69c1 100644 --- a/src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs +++ b/src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs @@ -53,9 +53,9 @@ namespace StardewModdingAPI.Framework.StateTracking /// Construct an instance. /// The game's list of locations. /// The game's list of active mine locations. - public WorldLocationsTracker(List locations, IList activeMineLocations) + public WorldLocationsTracker(ObservableCollection locations, IList activeMineLocations) { - this.LocationListWatcher = WatcherFactory.ForReferenceList(locations); + this.LocationListWatcher = WatcherFactory.ForObservableCollection(locations); this.MineLocationListWatcher = WatcherFactory.ForReferenceList(activeMineLocations); } diff --git a/src/SMAPI/Framework/WatcherCore.cs b/src/SMAPI/Framework/WatcherCore.cs index b1f00a68..b2be6021 100644 --- a/src/SMAPI/Framework/WatcherCore.cs +++ b/src/SMAPI/Framework/WatcherCore.cs @@ -65,7 +65,7 @@ namespace StardewModdingAPI.Framework this.WindowSizeWatcher = WatcherFactory.ForEquatable(() => new Point(Game1.viewport.Width, Game1.viewport.Height)); this.TimeWatcher = WatcherFactory.ForEquatable(() => Game1.timeOfDay); this.ActiveMenuWatcher = WatcherFactory.ForReference(() => Game1.activeClickableMenu); - this.LocationsWatcher = new WorldLocationsTracker(Game1.locations, MineShaft.activeMines); + this.LocationsWatcher = new WorldLocationsTracker((ObservableCollection) Game1.locations, MineShaft.activeMines); this.LocaleWatcher = WatcherFactory.ForGenericEquality(() => LocalizedContentManager.CurrentLanguageCode); this.Watchers.AddRange(new IWatcher[] { diff --git a/src/SMAPI/Metadata/InstructionMetadata.cs b/src/SMAPI/Metadata/InstructionMetadata.cs index 90a6d111..14eb45d0 100644 --- a/src/SMAPI/Metadata/InstructionMetadata.cs +++ b/src/SMAPI/Metadata/InstructionMetadata.cs @@ -37,8 +37,6 @@ namespace StardewModdingAPI.Metadata // rewrite for Stardew Valley 1.3 yield return new StaticFieldToConstantRewriter(typeof(Game1), "tileSize", Game1.tileSize); - yield return new TypeReferenceRewriter("System.Collections.Generic.IList`1", typeof(List)); - yield return new TypeReferenceRewriter("System.Collections.Generic.IList`1", typeof(List)); yield return new FieldToPropertyRewriter(typeof(Game1), "player"); @@ -61,11 +59,11 @@ namespace StardewModdingAPI.Metadata yield return new FieldToPropertyRewriter(typeof(Game1), "isDebrisWeather"); - yield return new MethodParentRewriter(typeof(IClickableMenu), typeof(IClickableMenuMethods), onlyIfPlatformChanged: true); + yield return new MethodParentRewriter(typeof(IClickableMenu), typeof(IClickableMenuMethods)); - yield return new MethodParentRewriter(typeof(Game1), typeof(Game1Methods), onlyIfPlatformChanged: true); + yield return new MethodParentRewriter(typeof(Game1), typeof(Game1Methods)); - yield return new MethodParentRewriter(typeof(Farmer), typeof(FarmerMethods), onlyIfPlatformChanged: true); + yield return new MethodParentRewriter(typeof(Farmer), typeof(FarmerMethods)); /**** ** detect mod issues diff --git a/src/SMAPI/Patches/LoadForNewGamePatch.cs b/src/SMAPI/Patches/LoadForNewGamePatch.cs index b766c94d..f803feb4 100644 --- a/src/SMAPI/Patches/LoadForNewGamePatch.cs +++ b/src/SMAPI/Patches/LoadForNewGamePatch.cs @@ -76,7 +76,7 @@ namespace StardewModdingAPI.Patches if (LoadForNewGamePatch.IsCreating) { // raise CreatedBasicInfo after locations are cleared twice - List locations = Game1.locations; + IList locations = Game1.locations; //locations.CollectionChanged += LoadForNewGamePatch.OnLocationListChanged; } @@ -90,7 +90,7 @@ namespace StardewModdingAPI.Patches if (LoadForNewGamePatch.IsCreating) { // clean up - List locations = Game1.locations; + IList locations = Game1.locations; //locations.CollectionChanged -= LoadForNewGamePatch.OnLocationListChanged; // raise stage changed