So I'm making a good bit of headway into my mod, I've got it to where it loads easily enough without inhibiting the startup of StardewModdingAPI, and it's recognized and accounted for. HOWEVER, when I go to trigger the event to test it, It displays this message: [02:41: 35.166 PM] An exception occured in XNA UpdateTick: System.NullReferenceException: Object reference not set to an instance of an object. at FriendlierNPCs.FriendlierNPCs.GameEvents_UpdateTick(Object sender, EventArgs e) at System.EventHandler.Invoke(Object sender, EventArgs e) at StardewModdingAPI.Events.GameEvents.InvokeUpdateTick() From what I looked up and what Visual Studio's telling me, I'm getting a null error because of a field never being used, and a field is never assigned to, and therefore defaulting the value to null. Here's the code in its latest iteration before building the DLL using StardewModdingAPI; using StardewValley; using System; namespace FriendlierNPCs { public class FriendlierNPCs : Mod { private Event currentEvent; private readonly object name; private readonly object Mine; public override void Entry(params object[] objects) { StardewModdingAPI.Events.GameEvents.UpdateTick += GameEvents_UpdateTick; } private void GameEvents_UpdateTick(object sender, EventArgs e) { if (Game1.killScreen && !Game1.eventUp) { if (this.name.Equals("Mine")) { string text; string text2; switch (Game1.random.Next(7)) { case 0: text = "Robin"; text2 = "Data\\ExtraDialogue:Mines_PlayerKilled_Robin"; goto IL_B8; case 1: text = "Clint"; text2 = "Data\\ExtraDialogue:Mines_PlayerKilled_Clint"; goto IL_B8; case 2: text = "Abigail"; text2 = ((Game1.player.spouse != null && Game1.player.spouse.Equals("Abigail")) ? "Data\\ExtraDialogue:Mines_PlayerKilled_Abigail_Spouse" : "Data\\ExtraDialogue:Mines_PlayerKilled_Abigail_NotSpouse"); goto IL_B8; case 3: text = "Alex"; text2 = ((Game1.player.spouse != null && Game1.player.spouse.Equals("Alex")) ? "Data\\ExtraDialogue:Mines_PlayerKilled_Alex_Spouse" : "Data\\ExtraDialogue:Mines_PlayerKilled_Alex_NotSpouse"); goto IL_B8; case 4: text = "Elliot"; text2 = ((Game1.player.spouse != null && Game1.player.spouse.Equals("Elliot")) ? "Data\\ExtraDialogue:Mines_PlayerKilled_Elliott_Spouse" : "Data\\ExtraDialogue:Mines_PlayerKilled_Elliott_NotSpouse"); goto IL_B8; case 5: text = "Emily"; text2 = ((Game1.player.spouse != null && Game1.player.spouse.Equals("Emily")) ? "Data\\ExtraDialogue:Mines_PlayerKilled_Emily_Spouse" : "Data\\ExtraDialogue:Mines_PlayerKilled_Emily_NotSpouse"); goto IL_B8; case 6: text = "Haley"; text2 = ((Game1.player.spouse != null && Game1.player.spouse.Equals("Haley")) ? "Data\\ExtraDialogue:Mines_PlayerKilled_Haley_Spouse" : "Data\\ExtraDialogue:Mines_PlayerKilled_Haley_NotSpouse"); goto IL_B8; case 7: text = "Harvey"; text2 = ((Game1.player.spouse != null && Game1.player.spouse.Equals("Harvey")) ? "Data\\ExtraDialogue:Mines_PlayerKilled_Harvey_Spouse" : "Data\\ExtraDialogue:Mines_PlayerKilled_Harvey_NotSpouse"); goto IL_B8; case 8: text = "Leah"; text2 = ((Game1.player.spouse != null && Game1.player.spouse.Equals("Leah")) ? "Data\\ExtraDialogue:Mines_PlayerKilled_Leah_Spouse" : "Data\\ExtraDialogue:Mines_PlayerKilled_Leah_NotSpouse"); goto IL_B8; case 9: text = "Maru"; text2 = ((Game1.player.spouse != null && Game1.player.spouse.Equals("Maru")) ? "Data\\ExtraDialogue:Mines_PlayerKilled_Maru_Spouse" : "Data\\ExtraDialogue:Mines_PlayerKilled_Maru_NotSpouse"); goto IL_B8; case 10: text = "Penny"; text2 = ((Game1.player.spouse != null && Game1.player.spouse.Equals("Penny")) ? "Data\\ExtraDialogue:Mines_PlayerKilled_Penny_Spouse" : "Data\\ExtraDialogue:Mines_PlayerKilled_Penny_NotSpouse"); goto IL_B8; case 11: text = "Sam"; text2 = ((Game1.player.spouse != null && Game1.player.spouse.Equals("Sam")) ? "Data\\ExtraDialogue:Mines_PlayerKilled_Sam_Spouse" : "Data\\ExtraDialogue:Mines_PlayerKilled_Sam_NotSpouse"); goto IL_B8; case 12: text = "Sebastian"; text2 = ((Game1.player.spouse != null && Game1.player.spouse.Equals("Sebastian")) ? "Data\\ExtraDialogue:Mines_PlayerKilled_Sebastian_Spouse" : "Data\\ExtraDialogue:Mines_PlayerKilled_Sebastian_NotSpouse"); goto IL_B8; case 13: text = "Shane"; text2 = ((Game1.player.spouse != null && Game1.player.spouse.Equals("Shane")) ? "Data\\ExtraDialogue:Mines_PlayerKilled_Shane_Spouse" : "Data\\ExtraDialogue:Mines_PlayerKilled_Shane_NotSpouse"); goto IL_B8; } text = "Linus"; text2 = "Data\\ExtraDialogue:Mines_PlayerKilled_Linus"; IL_B8: if (Game1.random.NextDouble() < 0.1 && Game1.player.spouse != null && !Game1.player.spouse.Contains("engaged") && Game1.player.spouse.Length > 1) { text = Game1.player.spouse; text2 = (Game1.player.isMale ? "Data\\ExtraDialogue:Mines_PlayerKilled_Spouse_PlayerMale" : "Data\\ExtraDialogue:Mines_PlayerKilled_Spouse_PlayerFemale"); } this.currentEvent = new Event(Game1.content.LoadString("Data\\Events\\Mine: PlayerKilled", new object[] { text, text2, Game1.player.name }), -1); } else if (this.name.Equals("Hospital")) { this.currentEvent = new Event(Game1.content.LoadString("Data\\Events\\Hospital: PlayerKilled", new object[] { Game1.player.name }), -1); } } } } } I'm not very sure how to rectify this, and I'd gladly appreciate any pointers. Sometimes the solution's right in my face and I can't see it until it's pointed to me. Cheers!
try deleting private readonly object Mine; // you're not using this? and change private readonly object name; >> private string name = "mine"; you dont even initialized 'name'.
Well I applied what you said, and it got the errors to stop, although it's not creating the desired results of what I want, plus it produced a bug that's mainly aesthetic in nature None of the listed NPCs are going to retrieve the player aside from the default four, and when the death animation is triggered, the sprite freezes in the last frame to the action before death.
Essentially, I'm trying to get the rest of the bachelors/bachelorettes to go to the caves to retrieve the player when they die, like Maru does. This mod is supposed to give those permissions to the rest of the NPCs to go in the caves and retrieve the player. So say for instance Maru can go in there despite not being a spouse, and get the player. I'm trying to give that ability to Elliott,Sam,Sebastian,Alex,Shane and Harvey along with Emily, Abigail, Leah, Penny, and Haley. What's not working is the game isn't choosing from the listed NPC list I provided, despite having the extraDialogue.xnb edited and prepared to give them dialogue in this setting.
Ah, it's fine. It's better than it was before at least. Maybe it has something to do with when the mod itself is loaded into SMAPI
check each line from top to bottom and see if its doing what it supposed to do, if{} everything...xD... log.aSync("passed")... log all strings... one step at a time
I'd change these - string text; string text2; to string text = string.empty; string text2 = string.empty; and maybe your events - this.currentEvent = new Event(Game1.content.LoadString("Data\\Events\\XXXXXX: PlayerKilled", new object[] { Game1.player.name }), -1); to var killedString = Game1.content.LoadString("Data\\Events\\XXXXXX: PlayerKilled", new object[] { Game1.player.name }); if (killedString != null) { this.currentEvent = new Event(Game1.content.LoadString("Data\\Events\\XXXXXX: PlayerKilled", new object[] { Game1.player.name }), -1); } Also, maybe throw some debug messages to dump current values for those variables. Any that are null should still throw an error instead of printing.
I just now tried that, and it gave me a whole new slew of ; expected errors and one ) expected error. I'm not very sure if I'm rewriting these right
Intellisense and all that should be catching most of those and telling you where the errors are("if (killedString != null) ;" should be "if (killedString != null)" for starters), what tool are you using to write your code? Edit: The below code should compile - using StardewModdingAPI; using StardewValley; using System; namespace FriendlierNPCs { public class FriendlierNPCs : Mod { private Event currentEvent; private string name = "Mine"; public override void Entry(params object[] objects) { StardewModdingAPI.Events.GameEvents.UpdateTick += GameEvents_UpdateTick; } private void GameEvents_UpdateTick(object sender, EventArgs e) { if (Game1.killScreen && !Game1.eventUp) { if (this.name.Equals("Mine")) { string text = string.Empty; string text2 = string.Empty; switch (Game1.random.Next(14)) { case 0: text = "Robin"; text2 = "Data\\ExtraDialogue:Mines_PlayerKilled_Robin"; goto IL_B8; case 1: text = "Clint"; text2 = "Data\\ExtraDialogue:Mines_PlayerKilled_Clint"; goto IL_B8; case 2: text = "Abigail"; text2 = ((Game1.player.spouse != null && Game1.player.spouse.Equals("Abigail")) ? "Data\\ExtraDialogue:Mines_PlayerKilled_Abigail_Spouse" : "Data\\ExtraDialogue:Mines_PlayerKilled_Abigail_NotSpouse"); goto IL_B8; case 3: text = "Alex"; text2 = ((Game1.player.spouse != null && Game1.player.spouse.Equals("Alex")) ? "Data\\ExtraDialogue:Mines_PlayerKilled_Alex_Spouse" : "Data\\ExtraDialogue:Mines_PlayerKilled_Alex_NotSpouse"); goto IL_B8; case 4: text = "Elliot"; text2 = ((Game1.player.spouse != null && Game1.player.spouse.Equals("Elliot")) ? "Data\\ExtraDialogue:Mines_PlayerKilled_Elliott_Spouse" : "Data\\ExtraDialogue:Mines_PlayerKilled_Elliott_NotSpouse"); goto IL_B8; case 5: text = "Emily"; text2 = ((Game1.player.spouse != null && Game1.player.spouse.Equals("Emily")) ? "Data\\ExtraDialogue:Mines_PlayerKilled_Emily_Spouse" : "Data\\ExtraDialogue:Mines_PlayerKilled_Emily_NotSpouse"); goto IL_B8; case 6: text = "Haley"; text2 = ((Game1.player.spouse != null && Game1.player.spouse.Equals("Haley")) ? "Data\\ExtraDialogue:Mines_PlayerKilled_Haley_Spouse" : "Data\\ExtraDialogue:Mines_PlayerKilled_Haley_NotSpouse"); goto IL_B8; case 7: text = "Harvey"; text2 = ((Game1.player.spouse != null && Game1.player.spouse.Equals("Harvey")) ? "Data\\ExtraDialogue:Mines_PlayerKilled_Harvey_Spouse" : "Data\\ExtraDialogue:Mines_PlayerKilled_Harvey_NotSpouse"); goto IL_B8; case 8: text = "Leah"; text2 = ((Game1.player.spouse != null && Game1.player.spouse.Equals("Leah")) ? "Data\\ExtraDialogue:Mines_PlayerKilled_Leah_Spouse" : "Data\\ExtraDialogue:Mines_PlayerKilled_Leah_NotSpouse"); goto IL_B8; case 9: text = "Maru"; text2 = ((Game1.player.spouse != null && Game1.player.spouse.Equals("Maru")) ? "Data\\ExtraDialogue:Mines_PlayerKilled_Maru_Spouse" : "Data\\ExtraDialogue:Mines_PlayerKilled_Maru_NotSpouse"); goto IL_B8; case 10: text = "Penny"; text2 = ((Game1.player.spouse != null && Game1.player.spouse.Equals("Penny")) ? "Data\\ExtraDialogue:Mines_PlayerKilled_Penny_Spouse" : "Data\\ExtraDialogue:Mines_PlayerKilled_Penny_NotSpouse"); goto IL_B8; case 11: text = "Sam"; text2 = ((Game1.player.spouse != null && Game1.player.spouse.Equals("Sam")) ? "Data\\ExtraDialogue:Mines_PlayerKilled_Sam_Spouse" : "Data\\ExtraDialogue:Mines_PlayerKilled_Sam_NotSpouse"); goto IL_B8; case 12: text = "Sebastian"; text2 = ((Game1.player.spouse != null && Game1.player.spouse.Equals("Sebastian")) ? "Data\\ExtraDialogue:Mines_PlayerKilled_Sebastian_Spouse" : "Data\\ExtraDialogue:Mines_PlayerKilled_Sebastian_NotSpouse"); goto IL_B8; case 13: text = "Shane"; text2 = ((Game1.player.spouse != null && Game1.player.spouse.Equals("Shane")) ? "Data\\ExtraDialogue:Mines_PlayerKilled_Shane_Spouse" : "Data\\ExtraDialogue:Mines_PlayerKilled_Shane_NotSpouse"); goto IL_B8; } text = "Linus"; text2 = "Data\\ExtraDialogue:Mines_PlayerKilled_Linus"; IL_B8: if (Game1.random.NextDouble() < 0.1 && Game1.player.spouse != null && !Game1.player.spouse.Contains("engaged") && Game1.player.spouse.Length > 1) { text = Game1.player.spouse; text2 = (Game1.player.isMale ? "Data\\ExtraDialogue:Mines_PlayerKilled_Spouse_PlayerMale" : "Data\\ExtraDialogue:Mines_PlayerKilled_Spouse_PlayerFemale"); } var killedString = Game1.content.LoadString(Game1.content.LoadString("Data\\Events\\MinelayerKilled", new object[] { text, text2, Game1.player.name })); if (killedString != null) { this.currentEvent = new Event(killedString, -1); } } else if (this.name.Equals("Hospital")) { this.currentEvent = new Event(Game1.content.LoadString("Data\\Events\\HospitallayerKilled", new object[] { Game1.player.name }), -1); } } } } }
I'm using Microsoft Visual Studio 2016, and as for the code you provided I ran through SMAPI I got this error: [10:06:10.400 PM] An exception occured in XNA UpdateTick: System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary. at System.Collections.Generic.Dictionary`2.get_Item(TKey key) at StardewValley.LocalizedContentManager.LoadString(String path, Object[] substitutions) at FriendlierNPCs.FriendlierNPCs.GameEvents_UpdateTick(Object sender, EventArgs e) at System.EventHandler.Invoke(Object sender, EventArgs e) at StardewModdingAPI.Events.GameEvents.InvokeUpdateTick() Sorry if I seem to not understand a lot of stuff, first time modding that isn't simple xnb modding.
No worries. That error basically says that whatever string you passed to LoadString as a key wasn't found. If you wrap that block in a try/catch, you can spit out whatever string it was.
How exactly would I put that into play? Because to tell you the truth this is my first time looking at C# and trying to write in it.
I hope you don't mind, I've cleaned it up a bit to make it a bit easier to work with for you. I only partially implemented the NPC dictionary though, so you'll need to flesh that out, but this gives an example of a try/catch as well as hopefully an easier to manage list of NPCs. This should functionally be identical to your original code, I haven't tested it however. using StardewModdingAPI; using StardewValley; using System; using System.Collections.Generic; namespace FriendlierNPCs { public class FriendlierNPCs : Mod { private Event currentEvent; private string name = "Mine"; private Dictionary<int, string> npcDictionary; public override void Entry(params object[] objects) { StardewModdingAPI.Events.GameEvents.UpdateTick += GameEvents_UpdateTick; _InitializeDictionary(); } private void _InitializeDictionary() { //Add new NPCs here npcDictionary = new Dictionary<int, string>(); npcDictionary.Add(0, "Robin"); npcDictionary.Add(1, "Clint"); npcDictionary.Add(2, "Abigail"); npcDictionary.Add(3, "Alex"); } private void GameEvents_UpdateTick(object sender, EventArgs e) { if (Game1.killScreen && !Game1.eventUp) { string location = string.Empty; string text = string.Empty; string text2 = string.Empty; if (this.name.Equals("Mine")) { location = "Mine"; var index = Game1.random.Next(14); if (npcDictionary.ContainsKey(index)) { text = npcDictionary[index]; } else { text = "Linus"; } if (text == "Robin" || text == "Clint" || text == "Linus") { text2 = "Data\\ExtraDialogue:Mines_PlayerKilled_" + text; } else { text2 = ((Game1.player.spouse != null && Game1.player.spouse.Equals(text)) ? "Data\\ExtraDialogue:Mines_PlayerKilled_" + text + "_Spouse" : "Data\\ExtraDialogue:Mines_PlayerKilled_" + text + "_NotSpouse"); } if (Game1.random.NextDouble() < 0.1 && Game1.player.spouse != null && !Game1.player.spouse.Contains("engaged") && Game1.player.spouse.Length > 1) { text = Game1.player.spouse; text2 = (Game1.player.isMale ? "Data\\ExtraDialogue:Mines_PlayerKilled_Spouse_PlayerMale" : "Data\\ExtraDialogue:Mines_PlayerKilled_Spouse_PlayerFemale"); } var killedString = Game1.content.LoadString(Game1.content.LoadString("Data\\Events\\MinelayerKilled", new object[] { text, text2, Game1.player.name })); if (killedString != null) { this.currentEvent = new Event(killedString, -1); } } // Unless "name" is reset, this'll never fire. else if (this.name.Equals("Hospital")) { location = "Hospital"; } var killerString = string.Empty; try { var obj = location == "Mine" ? new object[] { text, text2, Game1.player.name } : new object[] { Game1.player.name }; killerString = Game1.content.LoadString("Data\\Events\\" + location + "layerKilled", obj); this.currentEvent = new Event(killerString); } catch(Exception ex) { Log.Error("ERROR: Dumping Values"); Log.Error("Text = " + text); Log.Error("Text2 = " + text2); Log.Error("Location = " + location); Log.Error("KillerString = " + killerString); Log.Error("ERROR: " + ex.Message); } } } } }
At the top of your tick event, add the following: Code: if(!Game1.hasLoadedGame) return; From what I can tell, you are running code before a savegame is loaded, and a lot of properties are not yet set without a savegame. Also, `this.name.Equals` should be `Game1.currentLocation.name.Equals`
So regarding the above code, it should look something like this?: Code: public class FriendlierNPCs : Mod { private Event currentEvent; private string name = "Mine"; public override void Entry(params object[] objects) { if (!Game1.hasLoadedGame) return; StardewModdingAPI.Events.GameEvents.UpdateTick += GameEvents_UpdateTick; } private void GameEvents_UpdateTick(object sender, EventArgs e) If so it seems to not throw a bunch of errors in my face, although it still loads before choosing a save file.
No, you need to add the if to the updateTick event, as it currently stands your event never even registers.
I'm probably doing this all wrong then, because I've put it in various spots and have gotten the same result. Mod loading before a chosen save state and all.