private void LateSetup() { this.Monitor.Log("Performing late setup...", LogLevel.Debug); var promenade = Game1.getLocationFromName("SundropPromenade"); var start = DateTime.Now; if (promenade == null) { return; } List <Task> tasks = new List <Task> { Task.Run(() => { this.Monitor.Log("Repairing locations...", LogLevel.Trace); Parallel.ForEach(Game1.locations, loc => { if (loc.map.Properties.ContainsKey("IsSundropLocation")) { this.SetupLocation(loc); } }); }), Task.Run(() => { this.Monitor.Log("Spawning characters...", LogLevel.Trace); Parallel.ForEach(this.Helper.Data.ReadJsonFile <CharacterInfo[]>("assets/Data/CharacterSpawns.json"), info => { if (Game1.getLocationFromName(info.Map) == null) { this.Monitor.Log("Unable to add villager by name of `" + info.Name + "` because their default map failed to load, this character will not appear in your game as a result.", LogLevel.Error); } else { try { this.Monitor.Log("Adding sundrop villager: " + info.Name, LogLevel.Trace); var pos = new Vector2(info.Position[0], info.Position[1]); var villagerNpc = new NPC(new AnimatedSprite(this.Helper.Content.GetActualAssetKey("assets/Characters/Sprites/" + info.Texture + ".png"), 18, 16, 32), pos * 64f, 2, info.Name) { Portrait = this.Helper.Content.Load <Texture2D>("assets/Characters/Portraits/" + info.Texture + ".png"), DefaultMap = info.Map, DefaultPosition = pos * 64f }; villagerNpc.setNewDialogue(info.Message); lock (Game1.getLocationFromName(info.Map)) Game1.getLocationFromName(info.Map).addCharacter(villagerNpc); } catch (Exception err) { this.Monitor.Log("Unable to add villager by name of `" + info.Name + "` due to a unexpected issue, this character will not appear in your game as a result.\n" + err, LogLevel.Error); } } }); }), Task.Run(() => { this.Monitor.Log("Spawning cameos...", LogLevel.Trace); Parallel.ForEach(this.Helper.Data.ReadJsonFile <CharacterInfo[]>("assets/Data/CameoSpawns.json"), info => { if (Game1.getLocationFromName(info.Map) == null) { this.Monitor.Log("Unable to add cameo character for `" + info.Name + "` because their default map failed to load, this character will not appear in your game as a result.", LogLevel.Warn); } else { try { this.Monitor.Log("Adding sundrop cameo: " + info.Name, LogLevel.Trace); var pos = new Vector2(info.Position[0], info.Position[1]); var cameoNpc = new NPC(new AnimatedSprite(this.Helper.Content.GetActualAssetKey("assets/Characters/Cameos/" + info.Name + "Sprite.png"), 18, 16, 32), pos * 64f, 2, info.Name) { Portrait = this.Helper.Content.Load <Texture2D>("assets/Characters/Cameos/" + info.Name + "Portrait.png"), DefaultMap = info.Map, DefaultPosition = pos * 64f }; cameoNpc.setNewDialogue(info.Message); lock (Game1.getLocationFromName(info.Map)) Game1.getLocationFromName(info.Map).addCharacter(cameoNpc); } catch (Exception err) { this.Monitor.Log("Unable to add cameo character for `" + info.Name + "` due to a unexpected issue, this character will not appear in your game as a result.\n" + err, LogLevel.Warn); } } }); }) }; Task.WaitAll(tasks.ToArray()); var npc = Game1.getCharacterFromName("Joe"); if (npc != null) { npc.Schedule = this.Helper.Reflection.GetMethod(npc, "parseMasterSchedule").Invoke <Dictionary <int, SchedulePathDescription> >("610 80 20 2/630 23 20 2/710 80 20 2/730 23 20 2/810 80 20 2/830 23 20 2/910 80 20 2/930 23 20 2"); NPC cake = new MrCake(new Vector2(22, 20), npc); cake.setNewDialogue("Mr. Cake looks at you with approval."); promenade?.addCharacter(cake); } SundropCityMod.FixBushes(); var end = DateTime.Now; var time = end.Subtract(start); this.Monitor.Log("Late setup took " + time.TotalMilliseconds + " milliseconds, sundrop is now ready for you - enjoy!", LogLevel.Debug); }