/// <summary>Get the locale codes (like <c>ja-JP</c>) used in asset keys.</summary> /// <param name="reflection">Simplifies access to private game code.</param> private IDictionary <string, LanguageCode> GetKeyLocales(Reflector reflection) { // get the private code field directly to avoid changed-code logic IReflectedField <LanguageCode> codeField = reflection.GetField <LanguageCode>(typeof(LocalizedContentManager), "_currentLangCode"); // remember previous settings LanguageCode previousCode = codeField.GetValue(); string previousOverride = this.LanguageCodeOverride; // create locale => code map IDictionary <string, LanguageCode> map = new Dictionary <string, LanguageCode>(StringComparer.InvariantCultureIgnoreCase); this.LanguageCodeOverride = null; foreach (LanguageCode code in Enum.GetValues(typeof(LanguageCode))) { codeField.SetValue(code); map[this.GetKeyLocale.Invoke <string>()] = code; } // restore previous settings codeField.SetValue(previousCode); this.LanguageCodeOverride = previousOverride; return(map); }
public void MenuEvents_MenuChanged(object sender, EventArgsClickableMenuChanged e) { System.Console.Write($"Type of new menu is {e.NewMenu.GetType().ToString()}"); if (e.NewMenu is CraftingPage page && !(e.NewMenu is ListenerCraftingPage)) { ListenerCraftingPage replacement = new ListenerCraftingPage(page, this.replacer); Game1.activeClickableMenu = replacement; } if (e.NewMenu is GameMenu) { IReflectedField <List <IClickableMenu> > pages = this.Helper.Reflection.GetField <List <IClickableMenu> >(e.NewMenu, "pages"); var menuPages = pages.GetValue(); for (int i = 0; i < menuPages.Count; i++) { if (menuPages[i] is CraftingPage craftingPage) { menuPages[i] = new ListenerCraftingPage(craftingPage, this.replacer); break; } } pages.SetValue(menuPages); } }
/// <summary> /// Adds in a junimo actor at the current location. Allows for multiple. /// </summary> /// <param name="EventManager"></param> /// <param name="EventData"></param> public static void AddInJumimoActorForEvent(EventManager EventManager, string EventData) { string[] splits = EventData.Split(' '); string name = splits[0]; string actorName = splits[1]; int xPos = Convert.ToInt32(splits[2]); int yPos = Convert.ToInt32(splits[3]); Color color = new Color(Convert.ToInt32(splits[4]), Convert.ToInt32(splits[5]), Convert.ToInt32(splits[6])); bool flipped = Convert.ToBoolean(splits[7]); List <NPC> actors = Game1.CurrentEvent.actors; Junimo junimo = new Junimo(new Vector2(xPos * 64, yPos * 64), -1, false); junimo.Name = actorName; junimo.EventActor = true; junimo.flip = flipped; IReflectedField <NetColor> colorF = StardustCore.ModCore.ModHelper.Reflection.GetField <NetColor>(junimo, "color", true); NetColor c = colorF.GetValue(); c.R = color.R; c.G = color.G; c.B = color.B; colorF.SetValue(c); actors.Add((NPC)junimo); ++Game1.CurrentEvent.CurrentCommand; //I've been told ++<int> is more efficient than <int>++; }
private void PlayerUsedTool(object sender, UpdateTickedEventArgs e) { if (Game1.player.UsingTool != UsingToolOnPreviousTick) //if UsingTool has changed since the previous tick { UsingToolOnPreviousTick = Game1.player.UsingTool; //update the "last tick" value if (Game1.player.UsingTool && !(Game1.player.CurrentTool is Axe)) //if the player stopped using a tool on this tick { Vector2 targetTile = new Vector2((int)(Game1.player.GetToolLocation().X / Game1.tileSize), (int)(Game1.player.GetToolLocation().Y / Game1.tileSize)); //get the tile on which the tool was used Rectangle targetBox = new Rectangle(((int)targetTile.X) * 64, ((int)targetTile.Y) * 64, Game1.tileSize, Game1.tileSize); //get a rectangle representing the target tile var ltf = Game1.currentLocation.largeTerrainFeatures; //alias the current location's large terrain feature list for (int x = ltf.Count - 1; x >= 0; x--) //for each large terrain feature at the current location (looping backward for removal purposes) { if (ltf[x] is LargeResourceClump clump) //if this is a large resource clump { if (clump.getBoundingBox(clump.tilePosition.Value).Intersects(targetBox)) //if this was hit by the tool { //NOTE: the reflection below prevents a non-fatal error when an Axe or Pickaxe is used for the first time to hit a LargeResourceClump containing a GiantCrop; // this is due to the "lastUser" field being null during the first use of those tool subclasses IReflectedField <Farmer> lastUser = Utility.Helper.Reflection.GetField <Farmer>(Game1.player.CurrentTool, "lastUser", false); //get the protected "lastUser" field of this tool lastUser?.SetValue(Game1.player); //set "lastUser" to the player clump.performToolAction(Game1.player.CurrentTool, 0, targetTile, Game1.currentLocation); //make the inner ResourceClump react to being hit by the tool } } } } } }
private void Junimo_Lives(object sender, EventArgs args) { if (!Context.IsWorldReady) { return; } if (Game1.currentMinigame is MineCart game) { IReflectedField <int> livesLeft = this.Helper.Reflection.GetField <int>(game, "livesLeft"); if (livesLeft != null) { if (livesLeft.GetValue() < 3) { livesLeft.SetValue(3); //this.Monitor.Log("Set Cart Lives to 3"); } else { return; } } } else { return; } }
private void TemporarilyFakeInteraction(Action action) { // get references // (Note: change net values directly to avoid sync bugs, since the value will be reset when we're done.) Farmer player = Game1.player; NetRef <Horse> mountField = this.Reflection.GetField <NetRef <Horse> >(Game1.player, "netMount").GetValue(); IReflectedField <Horse> mountFieldValue = this.Reflection.GetField <Horse>(mountField, "value"); IReflectedField <Vector2> mountPositionValue = this.Reflection.GetField <Vector2>(player.mount.position.Field, "value"); // save current state Horse mount = mountField.Value; Vector2 mountPosition = mount.Position; WateringCan wateringCan = player.CurrentTool as WateringCan; int waterInCan = wateringCan?.WaterLeft ?? 0; float stamina = player.stamina; Vector2 position = player.Position; int facingDirection = player.FacingDirection; int currentToolIndex = player.CurrentToolIndex; bool canMove = player.canMove; // fix player frozen due to animations when performing an action // move mount out of the way mountFieldValue.SetValue(null); mountPositionValue.SetValue(new Vector2(-5, -5)); // perform action try { action(); } finally { // move mount back mountPositionValue.SetValue(mountPosition); mountFieldValue.SetValue(mount); // restore previous state if (wateringCan != null) { wateringCan.WaterLeft = waterInCan; } player.stamina = stamina; player.Position = position; player.FacingDirection = facingDirection; player.CurrentToolIndex = currentToolIndex; player.canMove = canMove; } }
/* * Private Methods */ private void GameLoop_TimeChanged(object sender, TimeChangedEventArgs e) { if (e.NewTime == 2500) { IReflectedField <int> timePass = this.Helper.Reflection.GetField <int>(typeof(Game1), "timeOfDay"); timePass.SetValue(2400); } //throw new NotImplementedException(); }
/// <summary>Temporarily dismount and set up the player to interact with a tile, then return it to the previous state afterwards.</summary> /// <param name="action">The action to perform.</param> private void TemporarilyFakeInteraction(Action action) { // get references SFarmer player = Game1.player; IReflectedField <Horse> mountField = this.Reflection.GetField <Horse>(Game1.player, "mount"); // save current state Horse mount = mountField.GetValue(); Vector2 mountPosition = this.Position; WateringCan wateringCan = player.CurrentTool as WateringCan; int waterInCan = wateringCan?.WaterLeft ?? 0; float stamina = player.stamina; Vector2 position = player.Position; int facingDirection = player.FacingDirection; int currentToolIndex = player.CurrentToolIndex; bool canMove = Game1.player.canMove; // fix player frozen due to animations when performing an action // move mount out of the way mountField.SetValue(null); this.Position = new Vector2(-5, -5); // perform action try { action(); } finally { // move mount back this.Position = mountPosition; mountField.SetValue(mount); // restore previous state if (wateringCan != null) { wateringCan.WaterLeft = waterInCan; } player.stamina = stamina; player.Position = position; player.FacingDirection = facingDirection; player.CurrentToolIndex = currentToolIndex; Game1.player.canMove = canMove; } }
public static void AutoFishing(BobberBar bar) { AutoFishingCounter = (AutoFishingCounter + 1) % 3; if (AutoFishingCounter > 0) { return; } IReflectedField <float> bobberSpeed = Reflection.GetField <float>(bar, "bobberBarSpeed"); float barPos = Reflection.GetField <float>(bar, "bobberBarPos").GetValue(); int barHeight = Reflection.GetField <int>(bar, "bobberBarHeight").GetValue(); float fishPos = Reflection.GetField <float>(bar, "bobberPosition").GetValue(); float treasurePos = Reflection.GetField <float>(bar, "treasurePosition").GetValue(); float distanceFromCatching = Reflection.GetField <float>(bar, "distanceFromCatching").GetValue(); bool treasureCaught = Reflection.GetField <bool>(bar, "treasureCaught").GetValue(); bool treasure = Reflection.GetField <bool>(bar, "treasure").GetValue(); float treasureApeearTimer = Reflection.GetField <float>(bar, "treasureAppearTimer").GetValue(); float bobberBarSpeed = bobberSpeed.GetValue(); float top = barPos; if (treasure && treasureApeearTimer <= 0 && !treasureCaught) { if (!CatchingTreasure && distanceFromCatching > 0.7f) { CatchingTreasure = true; } if (CatchingTreasure && distanceFromCatching < 0.3f) { CatchingTreasure = false; } if (CatchingTreasure) { fishPos = treasurePos; } } if (fishPos > barPos + (barHeight / 2f)) { return; } float strength = (fishPos - (barPos + barHeight / 2f)) / 18f; float distance = fishPos - top; float threshold = Util.Cap(InstanceHolder.Config.CpuThresholdFishing, 0, 0.5f); if (distance < threshold * barHeight || distance > (1 - threshold) * barHeight) { bobberBarSpeed = strength; } bobberSpeed.SetValue(bobberBarSpeed); }
private void MenuChanged(object sender, MenuChangedEventArgs e) { var shop = e.NewMenu as StardewValley.Menus.ShopMenu; if (shop == null || Game1.currentLocation.Name != "Forest" || !shop.potraitPersonDialogue.Contains("hats")) { return; } // no bundles for Joja members if (Game1.player.hasOrWillReceiveMail("JojaMember")) { return; } CommunityCenter communityCenter = Game1.locations.OfType <CommunityCenter>().First(); if (communityCenter.areAllAreasComplete()) { return; } IReflectedField <Dictionary <Item, int[]> > inventoryInformation = this.Helper.Reflection.GetField <Dictionary <Item, int[]> >(shop, "itemPriceAndStock"); Dictionary <Item, int[]> itemPriceAndStock = inventoryInformation.GetValue(); IReflectedField <List <Item> > forSaleInformation = this.Helper.Reflection.GetField <List <Item> >(shop, "forSale"); List <Item> forSale = forSaleInformation.GetValue(); foreach (var bundle in GetBundles()) { if (communityCenter.isBundleComplete(bundle.ID)) { continue; } foreach (var ing in bundle.Ingredients) { if (communityCenter.bundles[bundle.ID][ing.Index]) { continue; } int itemId = ing.ItemID; var objectToAdd = new StardewValley.Object(Vector2.Zero, itemId, ing.Stack); objectToAdd.Quality = ing.Quality; if (objectToAdd.Name.Contains("rror")) { continue; } itemPriceAndStock.Add(objectToAdd, new int[2] { 5000, ing.Stack }); forSale.Add(objectToAdd); } } inventoryInformation.SetValue(itemPriceAndStock); forSaleInformation.SetValue(forSale); }
public static void Postfix(Farm __instance) { IReflectedField <NetRectangle> houseSource = FarmHouseStates.reflector.GetField <NetRectangle>(__instance, "houseSource"); IReflectedField <NetRectangle> greenhouseSource = FarmHouseStates.reflector.GetField <NetRectangle>(__instance, "greenhouseSource"); houseSource.SetValue(new NetRectangle(new Microsoft.Xna.Framework.Rectangle(0, 0, 0, 0))); greenhouseSource.SetValue(new NetRectangle(new Microsoft.Xna.Framework.Rectangle(0, 0, 0, 0))); FarmState.init(); FarmState.setUpFarm(__instance); }
public void SaveEvents_AfterLoad(object sender, EventArgs e) { //this.Monitor.Log($"SaveEvents_AfterLoad called"); IReflectedField <NetEvent0> passOutEventField = this.Helper.Reflection.GetField <NetEvent0>(Game1.player, "passOutEvent"); NetEvent0 passOutEvent = new NetEvent0(); passOutEvent.onEvent += new NetEvent0.Event(this.performPassOut); passOutEventField.SetValue(passOutEvent); //this.Monitor.Log($"SaveEvents_AfterLoad ended"); }
/// <summary>Build base texture by combining face, nose, bottom, and shoe choice.</summary> /// <param name="player">The player to patch.</param> /// <param name="config">The config to patch.</param> /// <param name="which">The config number to use.</param> public void PatchBaseTexture(SFarmer player, LocalConfig config, int which = 0) { if (config.MutiplayerFix) { player.FarmerRenderer.textureName.Set("KisekaeBase_" + (player.IsMale ? "male_" : "female_") + config.ChosenFace[which] + "_" + config.ChosenNose[which] + "_" + config.ChosenBottoms[which] + "_" + config.ChosenShoes[which]); } else { Texture2D playerTextures = LoadBaseTexture(player.isMale ? "male" : "female", new int[] { config.ChosenFace[which], config.ChosenNose[which], config.ChosenBottoms[which], config.ChosenShoes[which] }); IReflectedField <Texture2D> curBaseTexture = m_env.Helper.Reflection.GetField <Texture2D>(player.FarmerRenderer, "baseTexture"); curBaseTexture.SetValue(playerTextures); } }
public void TougleMultiplayerFix() { m_config.MutiplayerFix = !m_config.MutiplayerFix; if (!m_config.MutiplayerFix) { //m_env.Helper.Reflection.GetMethod(m_farmer.FarmerRenderer); //m_farmer.FarmerRenderer.textureName.fieldChangeEvent -= ; IReflectedField <string> tn = m_env.Helper.Reflection.GetField <string>(m_farmer.FarmerRenderer.textureName, "value"); tn.SetValue(Path.Combine("Characters", "Farmer", m_farmer.IsMale ? "farmer_base" : "farmer_girl_base")); //m_farmer.FarmerRenderer.textureName.MarkClean(); } //FixBase(); }
public static void JunimoNoteMenu_ctor_Postfix( JunimoNoteMenu __instance, bool fromGameMenu, int area, bool fromThisMenu) { CommunityCenter cc = Bundles.CC; if (Bundles.IsAbandonedJojaMartBundleAvailableOrComplete()) { return; } IReflectedField <int> whichAreaField = Reflection.GetField <int> (__instance, "whichArea"); bool isAreaSet = false; bool isNavigationSet = false; foreach (string areaName in Bundles.GetAllAreaNames()) { int areaNumber = CommunityCenter.getAreaNumberFromName(areaName); // Set default area for menu view with custom areas if (!isAreaSet && fromGameMenu && !fromThisMenu && !isAreaSet && cc.shouldNoteAppearInArea(areaNumber) && !Bundles.IsAreaComplete(cc: cc, areaNumber: areaNumber)) { area = areaNumber; whichAreaField.SetValue(area); isAreaSet = true; } // Show navigation arrows when custom areas if (!isNavigationSet && areaNumber >= 0 && areaNumber != area && cc.shouldNoteAppearInArea(areaNumber)) { __instance.areaNextButton.visible = true; __instance.areaBackButton.visible = true; isNavigationSet = true; } if (isAreaSet && isNavigationSet) { break; } } }
/// <summary> /// Reverts the mushroom stump back into a tree exactly like its done in StardewValley.TerrainFeatures.Tree.dayUpdate, but only if it's not a chopped down tree /// </summary> /// <param name="tree">current mushroom tree</param> private void FixMushroomStump(Tree tree) { if (tree.treeType.Value == (int)TreeType.mushroomTree) { IReflectedField <float> shakeRotation = Helper.Reflection.GetField <float>(tree, "shakeRotation"); // if the value is higher than this the game considers the tree as falling or having fallen if (Math.Abs(shakeRotation.GetValue()) < 1.5707963267948966) { tree.stump.Set(false); tree.health.Set(10f); shakeRotation.SetValue(0f); } } }
private void GameEvents_UpdateTick(object sender, EventArgs e) { if (Game1.activeClickableMenu is CarpenterMenu carpenter) { if (carpenter.CurrentBlueprint.name == "Aquaponics") { IReflectedField <Building> cBuilding = Helper.Reflection.GetField <Building>(carpenter, "currentBuilding"); if (!(cBuilding.GetValue() is Aquaponics)) { cBuilding.SetValue(new Aquaponics(Vector2.Zero, Game1.getFarm())); } } } }
private void Events_Rendering(object sender, EventArgs e) { if (Game1.activeClickableMenu is Billboard) { #region accessing Billboard Billboard menu = (Billboard)Game1.activeClickableMenu; FieldInfo calendarField = menu.GetType().GetField("calendarDays", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (calendarField == null) { this.Monitor.Log("Could not find field 'calendarDays' in Billboard!", LogLevel.Error); return; } List <ClickableTextureComponent> calendarDays = (List <ClickableTextureComponent>)calendarField.GetValue(menu); IReflectedField <string> privateField = this.Helper.Reflection.GetField <string>(menu, "hoverText"); string hoverText = privateField.GetValue(); #endregion if (calendarDays != null && !(hoverText.Contains("Moon") || hoverText.Contains("moon"))) { for (int day = 1; day <= 28; day++) { ClickableTextureComponent component = calendarDays[day - 1]; // 0 - 27 if (component.bounds.Contains(Game1.getMouseX(), Game1.getMouseY())) { if (moon.LunarCalendar[day] >= 0 && moon.LunarCalendar[day] <= 3) { if (hoverText.Length > 0) { hoverText += "\n"; } hoverText += $"{moon.CalculatePhaseName(moon.LunarCalendar[day])} moon"; } else { hoverText += ""; break; } } } privateField.SetValue(hoverText); } } }
private void Junimo_Lives(object sender, UpdateTickedEventArgs args) { if (!Context.IsWorldReady) { return; } if (Game1.currentMinigame is MineCart game && args.IsMultipleOf(30)) { IReflectedField <int> livesLeft = Helper.Reflection.GetField <int>(game, "livesLeft"); if (livesLeft?.GetValue() < 3) { livesLeft.SetValue(3); //this.Monitor.Log("Set Cart Lives to 3"); } } }
public void Shake(Vector2 tileLocation, bool doEvenIfStillShaking) { if (!((double)_maxShake.GetValue() == 0.0 | doEvenIfStillShaking)) { return; } _shakeLeft.SetValue((double)Game1.player.getTileLocation().X > (double)tileLocation.X || (double)Game1.player.getTileLocation().X == (double)tileLocation.X && Game1.random.NextDouble() < 0.5); _maxShake.SetValue((float)Math.PI / 128f); if (!townBush && tileSheetOffset == 1 && inBloom(Game1.currentSeason, Game1.dayOfMonth)) { int parentSheetIndex = -1; string currentSeason = Game1.currentSeason; if (currentSeason != "spring") { if (currentSeason == "fall") { parentSheetIndex = 410; } } else { parentSheetIndex = 296; } if (parentSheetIndex == -1) { return; } tileSheetOffset.Value = 0; setUpSourceRect(); int num = new Random((int)tileLocation.X + (int)tileLocation.Y * 5000 + (int)Game1.uniqueIDForThisGame + (int)Game1.stats.DaysPlayed).Next(1, 2) + Game1.player.ForagingLevel / 4; for (int index = 0; index < num; ++index) { Game1.createItemDebris((Item) new StardewValley.Object(parentSheetIndex, 1, false, -1, Game1.player.professions.Contains(16) ? 4 : 0), Utility.PointToVector2(getBoundingBox().Center), Game1.random.Next(1, 4), (GameLocation)null); } DelayedAction.playSoundAfterDelay("leafrustle", 100); } else { if ((double)tileLocation.X != 20.0f || (double)tileLocation.Y != 8.0f || (Game1.dayOfMonth != 28 || Game1.timeOfDay != 1200) || Game1.player.mailReceived.Contains("junimoPlush")) { return; } Game1.player.addItemByMenuIfNecessaryElseHoldUp((Item) new Furniture(1733, Vector2.Zero), new ItemGrabMenu.behaviorOnItemSelect(junimoPlushCallback)); } }
public void FruitTryToHarvestHere() { if (this.currentLocation == null) { return; } if (this.currentLocation.terrainFeatures.ContainsKey(this.getTileLocation()) && this.currentLocation.terrainFeatures[this.getTileLocation()] is HoeDirt && (this.currentLocation.terrainFeatures[this.getTileLocation()] as HoeDirt).readyForHarvest() || IsAdjacentReadyToHarvestFruitTree(this.getTileLocation(), currentLocation)) { fharvestTimer.SetValue(2000); hasAttemptedHarvestThisInterval = false; } else { this.FruitPokeToHarvest(); } }
public static void setShippingCrateLocation(Vector2 tileLocation) { Farm farm = Game1.getFarm(); shippingCrateLocation = new Vector2(tileLocation.X, tileLocation.Y); IReflectedField <Rectangle> shippingBinLidOpenArea = FarmHouseStates.reflector.GetField <Rectangle>(farm, "shippingBinLidOpenArea"); shippingBinLidOpenArea.SetValue(new Rectangle((int)(shippingCrateLocation.X - 1) * 64, (int)(shippingCrateLocation.Y - 1) * 64, 256, 192)); Map map = farm.map; Logger.Log("Moving shipping crate tiles..."); removeVanillaShippingCrateTiles(map); removeEverythingFromTile(farm, shippingCrateLocation); removeEverythingFromTile(farm, shippingCrateLocation + new Vector2(1, 0)); buildShippingCrate(map); }
private void StartMinigameEndFunction(FishingRod rod, Farmer user, int fish) { rod.isReeling = true; rod.hit = false; // Animation switch (user.FacingDirection) { case 1: user.FarmerSprite.setCurrentSingleFrame(48); break; case 3: user.FarmerSprite.setCurrentSingleFrame(48, 32000, false, true); break; default: break; } user.FarmerSprite.PauseForSingleAnimation = true; // Distance from bobber to land IReflectedField <int> clearWaterDistanceField = ModFishing.Instance.Helper.Reflection.GetField <int>(rod, "clearWaterDistance"); clearWaterDistanceField.SetValue(FishingRod.distanceToLand((int)(rod.bobber.X / 64.0 - 1.0), (int)(rod.bobber.Y / 64.0 - 1.0), user.currentLocation)); // Calculate size of fish float num = 1f * (clearWaterDistanceField.GetValue() / 5f) * (Game1.random.Next(1 + Math.Min(10, user.FishingLevel) / 2, 6) / 5f); if (rod.favBait) { num *= 1.2f; } float fishSize = Math.Max(0.0f, Math.Min(1f, num * (float)(1.0 + Game1.random.Next(-10, 10) / 100.0))); // Check if there should be treasure bool treasure = !Game1.isFestival(); treasure &= user.fishCaught != null && user.fishCaught.Count > 1; treasure &= Game1.random.NextDouble() < ModFishing.Instance.Api.GetTreasureChance(user, rod); Game1.activeClickableMenu = new CustomBobberBar(user, fish, fishSize, treasure, rod.attachments[1]?.ParentSheetIndex ?? -1); }
public static void CollectMailAttachmentsAndQuests(LetterViewerMenu menu) { IReflectedField <int> questIdField = Reflection.GetField <int>(menu, "questID"); int questId = questIdField.GetValue(); if (menu.itemsLeftToGrab()) { foreach (ClickableComponent component in menu.itemsToGrab.ToArray()) { if (component.item == null || !CanPlayerAcceptsItemPartially(component.item)) { continue; } int stack = component.item.Stack; Game1.playSound("coin"); int remain = Util.AddItemIntoInventory(component.item); Logger.Log($"You collected {component.item.DisplayName}{(stack - remain > 1 ? " x" + (stack - remain) : "")}."); if (remain == 0) { component.item = null; } else { component.item.Stack = remain; } } } if (questId == -1) { return; } Logger.Log($"You started Quest: '{Quest.getQuestFromId(questId).questTitle}'."); Game1.player.addQuest(questId); Game1.playSound("newArtifact"); questIdField.SetValue(-1); }
/// <summary>Calls <see cref="Fire(GameLocation, int, int, int, StardewValley.Farmer)"/> if this is not an automatic slingshot.</summary> public override void DoFunction(GameLocation location, int x, int y, int power, StardewValley.Farmer who) { this.indexOfMenuItemView = this.initialParentTileIndex; who.usingSlingshot = false; who.canReleaseTool = true; who.usingTool = false; who.canMove = true; if (!this.baseCanPlaySound.GetValue()) { if (this.GetPullBackRange(x, y, (int)startingMousePos.X, (int)startingMousePos.Y) > 4) { if (!this.isAutomatic || (this.couldHaveFired && !this.didFire)) { this.Fire(location, x, y, power, who); } } } baseCanPlaySound.SetValue(true); who.Halt(); }
private static bool Prefix(Dialogue __instance, string masterDialogue, NPC speaker) { // get private members bool nameArraysTranslated = DialogueErrorPatch.Reflection.GetField <bool>(typeof(Dialogue), "nameArraysTranslated").GetValue(); IReflectedMethod translateArraysOfStrings = DialogueErrorPatch.Reflection.GetMethod(typeof(Dialogue), "TranslateArraysOfStrings"); IReflectedMethod parseDialogueString = DialogueErrorPatch.Reflection.GetMethod(__instance, "parseDialogueString"); IReflectedMethod checkForSpecialDialogueAttributes = DialogueErrorPatch.Reflection.GetMethod(__instance, "checkForSpecialDialogueAttributes"); IReflectedField <List <string> > dialogues = DialogueErrorPatch.Reflection.GetField <List <string> >(__instance, "dialogues"); // replicate base constructor if (dialogues.GetValue() == null) { dialogues.SetValue(new List <string>()); } // duplicate code with try..catch try { if (!nameArraysTranslated) { translateArraysOfStrings.Invoke(); } __instance.speaker = speaker; parseDialogueString.Invoke(masterDialogue); checkForSpecialDialogueAttributes.Invoke(); } catch (Exception baseEx) when(baseEx.InnerException is TargetInvocationException invocationEx && invocationEx.InnerException is Exception ex) { string name = !string.IsNullOrWhiteSpace(speaker?.Name) ? speaker.Name : null; DialogueErrorPatch.MonitorForGame.Log($"Failed parsing dialogue string{(name != null ? $" for {name}" : "")}:\n{masterDialogue}\n{ex}", LogLevel.Error); parseDialogueString.Invoke("..."); checkForSpecialDialogueAttributes.Invoke(); } return(false); } }
/// <summary>The event called after the list of NPCs in a location changes.</summary> /// <param name="sender">The event sender.</param> /// <param name="e">The event arguments.</param> private void OnNpcListChanged(object sender, NpcListChangedEventArgs e) { if (!this.IsEnabled) { return; } // workaround for mines not spawning a ladder on monster levels if (e.Location is MineShaft mine && e.IsCurrentLocation) { if (mine.mustKillAllMonstersToAdvance() && mine.characters.All(p => p is Horse)) { IReflectedField <bool> hasLadder = this.Helper.Reflection.GetField <bool>(mine, "ladderHasSpawned"); if (!hasLadder.GetValue()) { mine.createLadderAt(mine.mineEntrancePosition(Game1.player)); hasLadder.SetValue(true); } } } // workaround for instantly-built tractors spawning a horse if (Context.IsMainPlayer && e.Location is BuildableGameLocation buildableLocation) { Horse[] horses = e.Added.OfType <Horse>().ToArray(); if (horses.Any()) { HashSet <Guid> tractorIDs = new HashSet <Guid>(this.GetGaragesIn(buildableLocation).Select(p => p.HorseId)); foreach (Horse horse in horses) { if (tractorIDs.Contains(horse.HorseId) && !TractorManager.IsTractor(horse)) { horse.Name = TractorManager.GetTractorName(horse.HorseId); } } } } }
public override void performHoverAction(int x, int y) { base.performHoverAction(x, y); if (_movingAnimal.GetValue()) { Vector2 tile = new Vector2((float)((x + Game1.viewport.X) / Game1.tileSize), (float)((y + Game1.viewport.Y) / Game1.tileSize)); Farm locationFromName = Game1.getLocationFromName("Farm") as Farm; Building buildingAt = locationFromName.getBuildingAt(tile); if (buildingAt != null) { if (buildingAt.color.Equals(Color.LightGreen * 0.8f) && PregnancyController.IsAnimalPregnant(this._farmAnimal.myID.Value) && PregnancyController.CheckBuildingLimit((buildingAt.indoors.Value as AnimalHouse))) { buildingAt.color.Value = Color.Red * 0.8f; } } } else { if (this.meatButton != null) { if (this.meatButton.containsPoint(x, y)) { this.meatButton.scale = Math.Min(4.1f, this.meatButton.scale + 0.05f); _hoverText.SetValue(DataLoader.i18n.Get("Menu.AnimalQueryMenu.ExchangeAnimalForMeat")); } else { this.meatButton.scale = Math.Max(4f, this.sellButton.scale - 0.05f); } } if (this.pregnantStatus != null) { if (this.pregnantStatus.containsPoint(x, y)) { int daysUtilBirth = PregnancyController.GetPregnancyItem(this._farmAnimal.myID.Value).DaysUntilBirth; if (daysUtilBirth > 1) { _hoverText.SetValue(DataLoader.i18n.Get("Menu.AnimalQueryMenu.DaysUntilBirth", new { numberOfDays = daysUtilBirth })); } else { _hoverText.SetValue(DataLoader.i18n.Get("Menu.AnimalQueryMenu.ReadyForBirth")); } } } if (this.treatStatus != null) { if (this.treatStatus.containsPoint(x, y)) { int daysUntilNextTreat = TreatsController.DaysUntilNextTreat(this._farmAnimal); if (daysUntilNextTreat > 1) { _hoverText.SetValue(DataLoader.i18n.Get("Menu.AnimalQueryMenu.WantsTreatInDays", new { numberOfDays = daysUntilNextTreat })); } else if (daysUntilNextTreat == 1) { _hoverText.SetValue(DataLoader.i18n.Get("Menu.AnimalQueryMenu.WantsTreatTomorrow")); } else { _hoverText.SetValue(DataLoader.i18n.Get("Menu.AnimalQueryMenu.WantsTreat")); } } } if (this.animalContestIndicator != null) { if (this.animalContestIndicator.containsPoint(x, y)) { if (AnimalContestController.CanChangeParticipant(this._farmAnimal)) { _hoverText.SetValue(DataLoader.i18n.Get("Menu.AnimalQueryMenu.ChangeParticipant")); } else if (AnimalContestController.HasParticipated(this._farmAnimal) && AnimalContestController.HasWon(this._farmAnimal)) { SDate date = AnimalContestController.GetParticipantDate(this._farmAnimal); _hoverText.SetValue(DataLoader.i18n.Get("Menu.AnimalQueryMenu.Winner", new { contestDate = Utility.getDateStringFor(date.Day, Utility.getSeasonNumber(date.Season), date.Year) })); } else { SDate date = AnimalContestController.GetParticipantDate(this._farmAnimal); _hoverText.SetValue(DataLoader.i18n.Get("Menu.AnimalQueryMenu.ContestParticipant", new { contestDate = Utility.getDateStringFor(date.Day, Utility.getSeasonNumber(date.Season), date.Year) })); } } } } }
// Change: Added functionality to show a preview of a lost book. public override void performHoverAction(int x, int y) { IReflectedField <int> secretNoteImageRef = ModEntry.CommonServices.ReflectionHelper.GetField <int>(this, "secretNoteImage"); int currentTab = ModEntry.CommonServices.ReflectionHelper.GetField <int>(this, "currentTab").GetValue(); int currentPage = ModEntry.CommonServices.ReflectionHelper.GetField <int>(this, "currentPage").GetValue(); descriptionTextRef.SetValue(""); hoverTextRef.SetValue(""); valueRef.SetValue(-1); secretNoteImageRef.SetValue(-1); foreach (ClickableTextureComponent sideTab in this.sideTabs) { if (sideTab.containsPoint(x, y)) { hoverTextRef.SetValue(sideTab.hoverText); return; } } foreach (ClickableTextureComponent textureComponent in this.collections[currentTab][currentPage]) { if (textureComponent.containsPoint(x, y)) { textureComponent.scale = Math.Min(textureComponent.scale + 0.02f, textureComponent.baseScale + 0.1f); if (currentTab != achievementsTab) { // Draw [unknown] tooltip if item hasn't been encountered yet if (!Convert.ToBoolean(textureComponent.name.Split(' ')[1])) { hoverTextRef.SetValue("???"); continue; } // Book has already been found -> show book preview if (currentTab == lostBooksTabPageIndex) { string index = textureComponent.name.Split(' ')[2]; string message = Game1.content.LoadString("Strings\\Notes:" + index).Replace('\n', '^'); string title = message.Split('^')[0].Trim(); if (title.Length > BOOK_PREVIEW_LENGTH) { title = title.Substring(0, BOOK_PREVIEW_LENGTH) + "..."; } // Set hover text to book content preview. hoverTextRef.SetValue(title); continue; } } hoverTextRef.SetValue(this.createDescription(Convert.ToInt32(textureComponent.name.Split(' ')[0]))); } else { textureComponent.scale = Math.Max(textureComponent.scale - 0.02f, textureComponent.baseScale); } } this.forwardButton.tryHover(x, y, 0.5f); this.backButton.tryHover(x, y, 0.5f); }
/// <summary>Generates a monster and places it on the specified map and tile.</summary> /// <param name="monsterType">The monster type's name and an optional dictionary of monster-specific settings.</param> /// <param name="location">The GameLocation where the monster should be spawned.</param> /// <param name="tile">The x/y coordinates of the tile where the monster should be spawned.</param> /// <param name="areaID">The UniqueAreaID of the related SpawnArea. Required for log messages.</param> /// <returns>Returns the monster's ID value, or null if the spawn process failed.</returns> public static int?SpawnMonster(MonsterType monsterType, GameLocation location, Vector2 tile, string areaID = "") { Monster monster = null; //an instatiated monster, to be spawned into the world later Color?color = null; //the monster's color (used by specific monster types) if (monsterType.Settings != null) //if settings were provided { if (monsterType.Settings.ContainsKey("Color")) //if this setting was provided { string[] colorText = ((string)monsterType.Settings["Color"]).Trim().Split(' '); //split the setting string into strings for each number List <int> colorNumbers = new List <int>(); foreach (string text in colorText) //for each string { int num = Convert.ToInt32(text); //convert it to a number if (num < 0) { num = 0; } //minimum 0 else if (num > 255) { num = 255; } //maximum 255 colorNumbers.Add(num); //add it to the list } //convert strings into RGBA values int r = Convert.ToInt32(colorNumbers[0]); int g = Convert.ToInt32(colorNumbers[1]); int b = Convert.ToInt32(colorNumbers[2]); int a; if (colorNumbers.Count > 3) //if the setting included an "A" value { a = Convert.ToInt32(colorNumbers[3]); } else //if the setting did not include an "A" value { a = 255; //default to no transparency } color = new Color(r, g, b, a); //set the color } else if (monsterType.Settings.ContainsKey("MinColor") && monsterType.Settings.ContainsKey("MaxColor")) //if color wasn't provided, but mincolor & maxcolor were { string[] minColorText = ((string)monsterType.Settings["MinColor"]).Trim().Split(' '); //split the setting string into strings for each number List <int> minColorNumbers = new List <int>(); foreach (string text in minColorText) //for each string { int num = Convert.ToInt32(text); //convert it to a number if (num < 0) { num = 0; } //minimum 0 else if (num > 255) { num = 255; } //maximum 255 minColorNumbers.Add(num); //add it to the list } string[] maxColorText = ((string)monsterType.Settings["MaxColor"]).Trim().Split(' '); //split the setting string into strings for each number List <int> maxColorNumbers = new List <int>(); foreach (string text in maxColorText) //for each string { int num = Convert.ToInt32(text); //convert it to a number if (num < 0) { num = 0; } //minimum 0 else if (num > 255) { num = 255; } //maximum 255 maxColorNumbers.Add(num); //convert to number } for (int x = 0; x < minColorNumbers.Count && x < maxColorNumbers.Count; x++) //for each pair of values { if (minColorNumbers[x] > maxColorNumbers[x]) //if min > max { //swap min and max int temp = minColorNumbers[x]; minColorNumbers[x] = maxColorNumbers[x]; maxColorNumbers[x] = temp; } } //pick random RGBA values between min and max int r = RNG.Next(minColorNumbers[0], maxColorNumbers[0] + 1); int g = RNG.Next(minColorNumbers[1], maxColorNumbers[1] + 1); int b = RNG.Next(minColorNumbers[2], maxColorNumbers[2] + 1); int a; if (minColorNumbers.Count > 3 && maxColorNumbers.Count > 3) //if both settings included an "A" value { a = RNG.Next(minColorNumbers[3], maxColorNumbers[3] + 1); } else //if one/both of the settings did not include an "A" value { a = 255; //default to no transparency } color = new Color(r, g, b, a); //set the color } } bool seesPlayers = false; //whether the monster automatically "sees" players at spawn (handled differently by some monster types) if (monsterType.Settings != null) //if settings were provided { if (monsterType.Settings.ContainsKey("SeesPlayersAtSpawn")) //if this setting was provided { seesPlayers = (bool)monsterType.Settings["SeesPlayersAtSpawn"]; //use the provided setting } } //create a new monster based on the provided name & apply type-specific settings switch (monsterType.MonsterName.ToLower()) //avoid most casing issues by making this lower-case { case "bat": monster = new BatFTM(tile, 0); break; case "frostbat": case "frost bat": monster = new BatFTM(tile, 40); break; case "lavabat": case "lava bat": monster = new BatFTM(tile, 80); break; case "iridiumbat": case "iridium bat": monster = new BatFTM(tile, 171); break; case "doll": case "curseddoll": case "cursed doll": monster = new BatFTM(tile, -666); break; case "skull": case "hauntedskull": case "haunted skull": monster = new BatFTM(tile, 77377); break; case "bigslime": case "big slime": case "biggreenslime": case "big green slime": monster = new BigSlimeFTM(tile, 0); if (color.HasValue) //if color was provided { ((BigSlimeFTM)monster).c.Value = color.Value; //set its color after creation } if (seesPlayers) //if the "SeesPlayersAtSpawn" setting is true { monster.IsWalkingTowardPlayer = true; } break; case "bigblueslime": case "big blue slime": case "bigfrostjelly": case "big frost jelly": monster = new BigSlimeFTM(tile, 40); if (color.HasValue) //if color was provided { ((BigSlimeFTM)monster).c.Value = color.Value; //set its color after creation } if (seesPlayers) //if the "SeesPlayersAtSpawn" setting is true { monster.IsWalkingTowardPlayer = true; } break; case "bigredslime": case "big red slime": case "bigredsludge": case "big red sludge": monster = new BigSlimeFTM(tile, 80); if (color.HasValue) //if color was provided { ((BigSlimeFTM)monster).c.Value = color.Value; //set its color after creation } if (seesPlayers) //if the "SeesPlayersAtSpawn" setting is true { monster.IsWalkingTowardPlayer = true; } break; case "bigpurpleslime": case "big purple slime": case "bigpurplesludge": case "big purple sludge": monster = new BigSlimeFTM(tile, 121); if (color.HasValue) //if color was provided { ((BigSlimeFTM)monster).c.Value = color.Value; //set its color after creation } if (seesPlayers) //if the "SeesPlayersAtSpawn" setting is true { monster.IsWalkingTowardPlayer = true; } break; case "bug": monster = new Bug(tile, 0); break; case "armoredbug": case "armored bug": monster = new Bug(tile, 121); break; case "dino": case "dinomonster": case "dino monster": case "pepper": case "pepperrex": case "pepper rex": case "rex": monster = new DinoMonster(tile); break; case "duggy": monster = new DuggyFTM(tile); break; case "dust": case "sprite": case "dustsprite": case "dust sprite": case "spirit": case "dustspirit": case "dust spirit": monster = new DustSpirit(tile); break; case "ghost": monster = new GhostFTM(tile); break; case "carbonghost": case "carbon ghost": monster = new GhostFTM(tile, "Carbon Ghost"); break; case "slime": case "greenslime": case "green slime": monster = new GreenSlime(tile, 0); if (color.HasValue) //if color was also provided { ((GreenSlime)monster).color.Value = color.Value; //set its color after creation } break; case "blueslime": case "blue slime": case "frostjelly": case "frost jelly": monster = new GreenSlime(tile, 40); if (color.HasValue) //if color was also provided { ((GreenSlime)monster).color.Value = color.Value; //set its color after creation } break; case "redslime": case "red slime": case "redsludge": case "red sludge": monster = new GreenSlime(tile, 80); if (color.HasValue) //if color was also provided { ((GreenSlime)monster).color.Value = color.Value; //set its color after creation } break; case "purpleslime": case "purple slime": case "purplesludge": case "purple sludge": monster = new GreenSlime(tile, 121); if (color.HasValue) //if color was also provided { ((GreenSlime)monster).color.Value = color.Value; //set its color after creation } break; case "grub": case "cavegrub": case "cave grub": monster = new GrubFTM(tile, false); break; case "fly": case "cavefly": case "cave fly": monster = new FlyFTM(tile, false); break; case "mutantgrub": case "mutant grub": monster = new GrubFTM(tile, true); break; case "mutantfly": case "mutant fly": monster = new FlyFTM(tile, true); break; case "metalhead": case "metal head": monster = new MetalHead(tile, 0); if (color.HasValue) //if color was provided { ((MetalHead)monster).c.Value = color.Value; //set its color after creation } break; case "mummy": monster = new MummyFTM(tile); break; case "rockcrab": case "rock crab": monster = new RockCrab(tile); break; case "lavacrab": case "lava crab": monster = new LavaCrab(tile); break; case "iridiumcrab": case "iridium crab": monster = new RockCrab(tile, "Iridium Crab"); break; case "rockgolem": case "rock golem": case "stonegolem": case "stone golem": monster = new RockGolemFTM(tile); break; case "wildernessgolem": case "wilderness golem": monster = new RockGolemFTM(tile, Game1.player.CombatLevel); break; case "serpent": monster = new SerpentFTM(tile); break; case "brute": case "shadowbrute": case "shadow brute": monster = new ShadowBrute(tile); break; case "shaman": case "shadowshaman": case "shadow shaman": monster = new ShadowShaman(tile); break; case "skeleton": monster = new Skeleton(tile); if (seesPlayers) //if the "SeesPlayersAtSpawn" setting is true { IReflectedField <bool> spottedPlayer = Helper.Reflection.GetField <bool>(monster, "spottedPlayer"); //try to access this skeleton's private "spottedPlayer" field spottedPlayer.SetValue(true); monster.IsWalkingTowardPlayer = true; } break; case "squid": case "kid": case "squidkid": case "squid kid": monster = new SquidKidFTM(tile); break; default: //if the name doesn't match any directly known monster types Type externalType = GetTypeFromName(monsterType.MonsterName, typeof(Monster)); //find a monster subclass with a matching name monster = (Monster)Activator.CreateInstance(externalType, tile); //create a monster with the Vector2 constructor break; } if (monster == null) { Monitor.Log($"The monster to be spawned (\"{monsterType.MonsterName}\") doesn't match any known monster types. Make sure that name isn't misspelled in your config file.", LogLevel.Info); return(null); } int?ID = MonsterTracker.AddMonster(monster); //generate an ID for this monster if (!ID.HasValue) { Monitor.Log("A new monster ID could not be generated. This is may be due to coding issue; please report it to this mod's developer. This monster won't be spawned.", LogLevel.Warn); return(null); } monster.id = ID.Value; //assign the ID to this monster monster.MaxHealth = monster.Health; //some monster types set Health on creation and expect MaxHealth to be updated like this ApplyMonsterSettings(monster, monsterType.Settings, areaID); //adjust the monster based on any other provided optional settings //spawn the completed monster at the target location Monitor.VerboseLog($"Spawning monster. Type: {monsterType.MonsterName}. Location: {tile.X},{tile.Y} ({location.Name})."); monster.currentLocation = location; monster.setTileLocation(tile); location.addCharacter(monster); return(monster.id); }