private static void fakeOne(int harvestIndex, bool giantCrop, Farmer near = null) { near ??= Game1.player; GameLocation location = near.currentLocation; var tiles = Utility.recursiveFindOpenTiles(location, near.getTileLocation(), 1, 100); if (tiles.Count == 0) { throw new Exception($"Could not find an open tile in {location.Name}."); } Vector2 tileLocation = tiles[0]; Crop crop = Utilities.MakeNonceCrop(harvestIndex, Utility.Vector2ToPoint(tileLocation)); TerrainFeature feature; if (giantCrop) { if (location is Farm farm) { feature = new GiantCrop(harvestIndex, tileLocation); farm.resourceClumps.Add(feature as ResourceClump); } else { throw new Exception($"Cannot fake a Giant Cropbeast in {location.Name}."); } } else { feature = new HoeDirt(0, crop); location.terrainFeatures.Add(tileLocation, feature); } CropTile cropTile = new CropTile(feature, crop, giantCrop, location, Utility.Vector2ToPoint(tileLocation), fake: true); if (cropTile.state != CropTile.State.Available) { cropTile.cleanUpFake(); throw new Exception($"Faked a {cropTile.logDescription} but it had no available cropbeast mapping."); } cropTile.choose(); DelayedAction.functionAfterDelay(() => Spawner.Spawn(cropTile), 0); }
// Chooses a crop tile to become a cropbeast. public virtual CropTile choose(bool console = false, bool force = false, string filter = null) { // Use a predictable RNG seeded by game, day and time. Random rng = new Random((int)Game1.uniqueIDForThisGame / 2 + (int)Game1.stats.DaysPlayed + Game1.timeOfDay); // Choose the location to evaluate. GameLocation location = Game1.currentLocation; if (!(location.IsFarm && location.IsOutdoors) && !location.IsGreenhouse) { GameLocation farm = Game1.getLocationFromName("Farm"); GameLocation greenhouse = Game1.getLocationFromName("Greenhouse"); if (farm != null && farm.farmers.Count() > 0) { location = farm; } else if (greenhouse != null && greenhouse.farmers.Count() > 0) { location = greenhouse; } } // Check preconditions unless forced. if (!force && !shouldChoose(rng, location, console: console)) { return(null); } // Get the maximum distance on the current map for weighting use. var mapLayer = location.map.Layers[0]; float mapDiagonal = Vector2.Distance(new Vector2(0, 0), new Vector2(mapLayer.LayerWidth, mapLayer.LayerHeight)); // Find all candidate crop tiles, sort pseudorandomly with // weighting based on type and towards those closer to a farmer. SortedList <double, CropTile> candidates = new SortedList <double, CropTile> (findCandidateCropTiles(location, filter, console) .ToDictionary((tile) => { Utilities.FindNearestFarmer(tile.location, tile.tileLocation, out float distance); return(rng.NextDouble() * 1.5 - tile.mapping.choiceWeight + distance / mapDiagonal * 2.0); })); // If the list is empty, give up. if (candidates.Count == 0) { Monitor.Log($"At {Game1.getTimeOfDayString (Game1.timeOfDay)}, {location.Name} met preconditions but had no candidate crops to become cropbeasts.", console ? LogLevel.Warn : LogLevel.Trace); return(null); } // Choose the top of the list as the winner. CropTile winner = candidates.First().Value; Monitor.Log($"At {Game1.getTimeOfDayString (Game1.timeOfDay)}, chose {winner.logDescription} to become {winner.mapping.beastName}.", console ? LogLevel.Info : LogLevel.Debug); // Record progress towards daily limit for location. if (location.IsOutdoors) { ++outdoorSpawnCount; } else { ++indoorSpawnCount; } // Make sure this tile isn't chosen again. chosenTiles[winner.tileLocation] = winner; winner.choose(); return(winner); }