private void OnWarped(object s, WarpedEventArgs e) { if (e.Player == Game1.MasterPlayer) { if (ParkingSpots.ContainsKey(e.NewLocation.Name)) { this.SpawnCars(e.NewLocation, .8); } if (e.OldLocation.farmers.Count == 0 && e.OldLocation.map.Properties.ContainsKey("TouristCount")) { foreach (var character in new List <Tourist>(e.OldLocation.characters.Where(_ => _ is Tourist).Cast <Tourist>())) { if (character.Special != null) { character.TickAge = short.MaxValue; character.RandomizeLook(); } e.OldLocation.characters.Remove(character); } } if (e.NewLocation.farmers.Count == 1 && e.NewLocation.map.Properties.ContainsKey("TouristCount")) { SundropCityMod.SpawnTourists(e.NewLocation, Convert.ToInt32((string)e.NewLocation.map.Properties["TouristCount"])); } } }
/// <summary>Raised after the game is launched, right before the first update tick. This happens once per game session (unrelated to loading saves). All mods are loaded and initialized at this point, so this is a good time to set up mod integrations.</summary> /// <param name="sender">The event sender.</param> /// <param name="e">The event data.</param> private void OnGameLaunched(object sender, GameLaunchedEventArgs e) { this.Monitor.Log("Performing init...", LogLevel.Debug); List <Task> tasks = new List <Task>(); var start = DateTime.Now; var helper = this.Helper; #if !DISABLE_SOUND // Load sounds tasks.Add(Task.Run(() => { using (var monitor = new LogBuffer(this.Monitor)) { monitor.Log("Initializing sounds...", LogLevel.Trace); try { Sound = SoundEffect.FromStream(File.OpenRead(Path.Combine(this.Helper.DirectoryPath, "assets", "Sounds", "SundropBeachNight.wav"))); } catch (Exception err) { monitor.Log("Exception loading Sundrop music: " + err, LogLevel.Error); } } })); #endif // Load maps tasks.Add(Task.Run(() => { using (var monitor = new LogBuffer(this.Monitor)) { monitor.Log("Initializing maps...", LogLevel.Trace); foreach (string file in Directory.EnumerateFiles(Path.Combine(this.Helper.DirectoryPath, "assets", "Maps"))) { string ext = Path.GetExtension(file); if (ext == null || !ext.Equals(".tbin")) { continue; } string map = Path.GetFileName(file); if (map == null) { continue; } try { monitor.Log("Map found: " + map, LogLevel.Trace); var mapFile = this.Helper.Content.Load <Map>(Path.Combine("assets", "Maps", map)); foreach (string prop in SystemData.Layers) { if (mapFile.Properties.ContainsKey(prop)) { string rep = mapFile.Properties[prop].ToString().Replace("\n", " ").Replace(";", " ").Replace(" ", " "); while (rep.Contains(" ")) { rep = rep.Replace(" ", " "); } mapFile.Properties[prop] = rep; } } foreach (TileSheet sheet in mapFile.TileSheets) { if (sheet.ImageSource.EndsWith(".png")) { monitor.Log(sheet.ImageSource, LogLevel.Trace); string xnb = sheet.ImageSource.Substring(0, sheet.ImageSource.Length - 4); try { Game1.content.Load <Texture2D>(xnb); sheet.ImageSource = xnb; } catch { Game1.content.Load <Texture2D>(sheet.ImageSource); } } } var mapInst = new GameLocation(this.Helper.Content.GetActualAssetKey(Path.Combine("assets", "Maps", map)), Path.GetFileNameWithoutExtension(map)); this.Maps.Add(map); Tourist.WarpCache.Add(Path.GetFileNameWithoutExtension(map), Tourist.GetSpawnPoints(mapInst, new HashSet <int>() { Tourist.TILE_WARP_DOWN, Tourist.TILE_WARP_LEFT, Tourist.TILE_WARP_RIGHT, Tourist.TILE_WARP_UP })); Tourist.SpawnCache.Add(Path.GetFileNameWithoutExtension(map), Tourist.GetSpawnPoints(mapInst, new HashSet <int>() { Tourist.TILE_ARROW_DOWN, Tourist.TILE_ARROW_LEFT, Tourist.TILE_ARROW_RIGHT, Tourist.TILE_ARROW_UP, Tourist.TILE_BROWSE, Tourist.TILE_SPAWN })); } catch (Exception err) { monitor.Log("Unable to prepare `" + map + "` location, error follows\n" + err, LogLevel.Error); } } } })); tasks.Add(Task.Run(() => { this.Monitor.Log("Mapping tourist graphics...", LogLevel.Trace); Tourist.LoadResources(); })); tasks.Add(Task.Run(() => { using (var monitor = new LogBuffer(this.Monitor)) { monitor.Log("Reading parking data...", LogLevel.Trace); foreach (var map in helper.Content.Load <JObject>("assets/Data/ParkingSpots.json").Properties()) { monitor.Log("Parsing parking data for the `" + map.Name + "` map, found (" + (map.Value as JArray)?.Count + ") parking spaces.", LogLevel.Trace); var spotsArr = map.Value.ToObject <JArray>(); string name = map.Name; var spots = new List <ParkingSpot>(); foreach (var spotArr in spotsArr.Cast <JArray>()) { var values = spotArr.Children().ToArray(); spots.Add(new ParkingSpot(new Vector2(values[0].ToObject <int>(), values[1].ToObject <int>()), (from JValue facing in values[2].ToObject <JArray>() select(Facing) Enum.Parse(typeof(Facing), facing.ToObject <string>())).ToArray())); } ParkingSpots.Add(map.Name, spots.ToArray()); } } })); tasks.Add(Task.Run(() => { using (var monitor = new LogBuffer(this.Monitor)) { monitor.Log("Reading car data...", LogLevel.Trace); SundropCar.CarTypes = helper.Data.ReadJsonFile <List <CarType> >("assets/Data/Cars.json"); if (SundropCar.CarTypes == null) { monitor.Log("Unable to read Cars.json for car data, cars will not be able to spawn!", LogLevel.Error); SundropCar.CarTypes = new List <CarType>(); } else { Parallel.ForEach(SundropCar.CarTypes, car => { car.Base = helper.Content.GetActualAssetKey("assets/TerrainFeatures/Drivables/" + car.Base + ".png"); car.Recolor = helper.Content.GetActualAssetKey("assets/TerrainFeatures/Drivables/" + car.Recolor + ".png"); car.Decals = car.Decals.Select(pair => new KeyValuePair <string, string>(pair.Key, helper.Content.GetActualAssetKey("assets/TerrainFeatures/Drivables/" + pair.Value + ".png"))).ToDictionary(pair => pair.Key, pair => pair.Value); }); } } })); tasks.Add(Task.Run(() => { using (var monitor = new LogBuffer(this.Monitor)) { monitor.Log("Preparing characters...", LogLevel.Trace); // NPC Spawning foreach (var info in this.Helper.Data.ReadJsonFile <CharacterInfo[]>("assets/Data/CharacterSpawns.json")) { try { // ReSharper disable once ObjectCreationAsStatement new NPC(new AnimatedSprite(this.Helper.Content.GetActualAssetKey("assets/Characters/Sprites/" + info.Texture + ".png"), 18, 16, 32), Vector2.Zero, 2, info.Name) { Portrait = this.Helper.Content.Load <Texture2D>("assets/Characters/Portraits/" + info.Texture + ".png"), }; } catch (Exception err) { monitor.Log("Unable to prepare villager by name of `" + info.Name + "` due to a unexpected issue.\n" + err, LogLevel.Error); } } } })); tasks.Add(Task.Run(() => { using (var monitor = new LogBuffer(this.Monitor)) { monitor.Log("Preparing cameos...", LogLevel.Trace); foreach (var info in this.Helper.Data.ReadJsonFile <CharacterInfo[]>("assets/Data/CameoSpawns.json")) { try { // ReSharper disable once ObjectCreationAsStatement new NPC(new AnimatedSprite(this.Helper.Content.GetActualAssetKey("assets/Characters/Cameos/" + info.Name + "Sprite.png"), 18, 16, 32), Vector2.Zero, 2, info.Name) { Portrait = this.Helper.Content.Load <Texture2D>("assets/Characters/Cameos/" + info.Name + "Portrait.png"), }; } catch (Exception err) { monitor.Log("Unable to prepare cameo character for `" + info.Name + "` due to a unexpected issue.\n" + err, LogLevel.Warn); } } } })); tasks.Add(Task.Run(() => { this.Monitor.Log("Registering commands...", LogLevel.Trace); if (Config.DebugFlags.HasFlag(DebugFlags.Functions)) { helper.ConsoleCommands.Add("sundrop_debug", "For debug use only", (cmd, args) => { if (args.Length == 0) { this.Monitor.Log("Debug command should only be used if you are asked to by a Sundrop developer!", LogLevel.Error); return; } switch (args[0]) { case "tourists": SundropCityMod.SpawnTourists(Game1.player.currentLocation, Convert.ToInt32(args[1])); this.Monitor.Log("Executed tourist spawning algorithm.", LogLevel.Alert); break; case "code": this.TriggerDebugCode(); this.Monitor.Log("Debug code triggered.", LogLevel.Alert); break; case "warp": if (args.Length == 1) { Game1.warpFarmer("Town", 100, 58, 1); } else { if (Game1.getLocationFromName(args[1]) == null) { this.Monitor.Log("Unable to warp, target destination does not exist.", LogLevel.Error); break; } Game1.warpFarmer(args[1], Convert.ToSByte(args[2]) + 64, Convert.ToSByte(args[3]) + 64, false); } this.Monitor.Log("Warping player to target.", LogLevel.Alert); break; case "hotelMenu": Game1.activeClickableMenu = new Hotel.Menu(); this.Monitor.Log("Menu for hotel triggered.", LogLevel.Alert); break; #if !DISABLE_SOUND case "playSound": this.Monitor.Log("Playing custom music...", LogLevel.Alert); Game1.stopMusicTrack(Game1.MusicContext.Default); Sound.Play(1, 1, 1); break; case "reportSound": this.Monitor.Log("AudioEngine Wrapper: " + Game1.audioEngine.GetType().Name, LogLevel.Alert); this.Monitor.Log("Wrapper Disposed: " + Game1.audioEngine.IsDisposed, LogLevel.Alert); this.Monitor.Log("AudioEngine Source: " + Game1.audioEngine.Engine.GetType().Name, LogLevel.Alert); this.Monitor.Log("Source Disposed: " + Game1.audioEngine.Engine.IsDisposed, LogLevel.Alert); break; #endif case "map": this.Monitor.Log("Name of current map: " + Game1.currentLocation.Name, LogLevel.Alert); break; case "position": var p = Game1.player.getTileLocationPoint(); this.Monitor.Log($"Current tile position: {p.X}x {p.Y}y", LogLevel.Alert); break; } }); } })); Task.WaitAll(tasks.ToArray()); var end = DateTime.Now; var time = end.Subtract(start); this.Monitor.Log("Init took " + time.TotalMilliseconds + " milliseconds.", LogLevel.Debug); }
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); }
/// <summary>Raised after the game is launched, right before the first update tick. This happens once per game session (unrelated to loading saves). All mods are loaded and initialized at this point, so this is a good time to set up mod integrations.</summary> /// <param name="sender">The event sender.</param> /// <param name="e">The event data.</param> private void OnGameLaunched(object sender, GameLaunchedEventArgs e) { this.Monitor.Log("Performing init...", LogLevel.Trace); var helper = this.Helper; this.Maps = new List <string>(); // Load maps this.Monitor.Log("Preparing maps...", LogLevel.Trace); foreach (string file in Directory.EnumerateFiles(Path.Combine(this.Helper.DirectoryPath, "assets", "Maps"))) { string ext = Path.GetExtension(file); if (ext == null || !ext.Equals(".tbin")) { continue; } string map = Path.GetFileName(file); this.Monitor.Log("Currently preparing: " + map, LogLevel.Trace); if (map == null) { continue; } try { var mapFile = this.Helper.Content.Load <Map>(Path.Combine("assets", "Maps", map)); if (mapFile.Properties.ContainsKey("Light")) { mapFile.Properties["Light"] = mapFile.Properties["Light"].ToString().Replace("\n", "").Replace(";", ""); } var mapInst = new GameLocation(this.Helper.Content.GetActualAssetKey(Path.Combine("assets", "Maps", map)), Path.GetFileNameWithoutExtension(map)); this.Maps.Add(map); Tourist.WarpCache.Add(Path.GetFileNameWithoutExtension(map), Tourist.GetSpawnPoints(mapInst, new HashSet <int>() { Tourist.TILE_WARP_DOWN, Tourist.TILE_WARP_LEFT, Tourist.TILE_WARP_RIGHT, Tourist.TILE_WARP_UP })); Tourist.SpawnCache.Add(Path.GetFileNameWithoutExtension(map), Tourist.GetSpawnPoints(mapInst, new HashSet <int>() { Tourist.TILE_ARROW_DOWN, Tourist.TILE_ARROW_LEFT, Tourist.TILE_ARROW_RIGHT, Tourist.TILE_ARROW_UP, Tourist.TILE_BROWSE, Tourist.TILE_SPAWN })); } catch (Exception err) { this.Monitor.Log("Unable to prepare [" + map + "] location, error follows\n" + err, LogLevel.Error); } } this.Monitor.Log("Mapping tourist graphics...", LogLevel.Trace); Tourist.LoadResources(); this.Monitor.Log("Reading parking data...", LogLevel.Trace); foreach (var map in helper.Content.Load <JObject>("assets/Data/ParkingSpots.json").Properties()) { this.Monitor.Log("Parsing parking data for the [" + map.Name + "] map, found [" + (map.Value as JArray)?.Count + "] parking spaces.", LogLevel.Trace); var spotsArr = map.Value.ToObject <JArray>(); string key = map.Name; var spots = new List <ParkingSpot>(); foreach (var spotArr in spotsArr.Cast <JArray>()) { var values = spotArr.Children().ToArray(); spots.Add(new ParkingSpot(new Vector2(values[0].ToObject <int>(), values[1].ToObject <int>()), (from JValue facing in values[2].ToObject <JArray>() select(Facing) Enum.Parse(typeof(Facing), facing.ToObject <string>())).ToArray())); } lock (ParkingSpots) ParkingSpots.Add(key, spots.ToArray()); } this.Monitor.Log("Reading car data...", LogLevel.Trace); SundropCar.CarTypes = helper.Data.ReadJsonFile <List <CarType> >("assets/Data/Cars.json")?.Select(type => { type.Base = helper.Content.GetActualAssetKey("assets/TerrainFeatures/Drivables/" + type.Base + ".png"); type.Recolor = helper.Content.GetActualAssetKey("assets/TerrainFeatures/Drivables/" + type.Recolor + ".png"); type.Decals = type.Decals.Select(pair => new KeyValuePair <string, string>(pair.Key, helper.Content.GetActualAssetKey("assets/TerrainFeatures/Drivables/" + pair.Value + ".png"))).ToDictionary(pair => pair.Key, pair => pair.Value); return(type); }).ToList(); if (SundropCar.CarTypes == null) { this.Monitor.Log("Unable to read Cars.json for car data, cars will not be able to spawn!", LogLevel.Error); SundropCar.CarTypes = new List <CarType>(); } this.Monitor.Log("Preparing characters...", LogLevel.Trace); // NPC Spawning foreach (var info in this.Helper.Data.ReadJsonFile <CharacterInfo[]>("assets/Data/CharacterSpawns.json")) { try { // ReSharper disable once ObjectCreationAsStatement new NPC(new AnimatedSprite(this.Helper.Content.GetActualAssetKey("assets/Characters/Sprites/" + info.Texture + ".png"), 18, 16, 32), Vector2.Zero, 2, info.Name) { Portrait = this.Helper.Content.Load <Texture2D>("assets/Characters/Portraits/" + info.Texture + ".png"), }; } catch (Exception err) { this.Monitor.Log("Unable to prepare villager by name of [" + info.Name + "] due to a unexpected issue.\n" + err, LogLevel.Error); } } this.Monitor.Log("Preparing cameos...", LogLevel.Trace); foreach (var info in this.Helper.Data.ReadJsonFile <CharacterInfo[]>("assets/Data/CameoSpawns.json")) { try { // ReSharper disable once ObjectCreationAsStatement new NPC(new AnimatedSprite(this.Helper.Content.GetActualAssetKey("assets/Characters/Cameos/" + info.Name + "Sprite.png"), 18, 16, 32), Vector2.Zero, 2, info.Name) { Portrait = this.Helper.Content.Load <Texture2D>("assets/Characters/Cameos/" + info.Name + "Portrait.png"), }; } catch (Exception err) { this.Monitor.Log("Unable to prepare cameo character for [" + info.Name + "] due to a unexpected issue.\n" + err, LogLevel.Warn); } } this.Monitor.Log("Registering commands...", LogLevel.Trace); helper.ConsoleCommands.Add("sundrop_debug", "For debug use only", (cmd, args) => { switch (args[0]) { case "car": Facing facing = (Facing)Enum.Parse(typeof(Facing), args[1]); Game1.currentLocation.largeTerrainFeatures.Add(new SundropCar(Game1.player.getTileLocation(), facing)); this.Monitor.Log("Spawned car.", LogLevel.Alert); break; case "parking": this.SpawnCars(Game1.currentLocation, Convert.ToDouble(args[1])); this.Monitor.Log("Performed car spawning algorithm.", LogLevel.Alert); break; case "tourist": Game1.player.currentLocation.addCharacter(new Tourist(Utility.getRandomAdjacentOpenTile(Game1.player.getTileLocation(), Game1.player.currentLocation) * 64)); this.Monitor.Log("Spawned ", LogLevel.Alert); break; case "touristHorde": int amount = Convert.ToInt32(args[1]); var loc = Game1.player.currentLocation; SundropCityMod.SpawnTourists(loc, amount); break; } }); this.Monitor.Log("Init complete!", LogLevel.Trace); }