/// <summary>Patch for slimes to damage monsters around Slimecharmer.</summary>
        private static void GreenSlimeUpdatePostfix(ref GreenSlime __instance, GameLocation location)
        {
            try
            {
                if (!Utility.AnyPlayerInLocationHasProfession("Slimecharmer", location))
                {
                    return;
                }

                foreach (var npc in __instance.currentLocation.characters.Where(npc => npc.IsMonster && npc is not GreenSlime))
                {
                    var monster    = (Monster)npc;
                    var monsterBox = monster.GetBoundingBox();
                    if (monster.IsInvisible || monster.isInvincible() || monster.isGlider.Value || !monsterBox.Intersects(__instance.GetBoundingBox()))
                    {
                        continue;
                    }

                    var damageToMonster = Math.Max(1, __instance.DamageToFarmer + Game1.random.Next(-__instance.DamageToFarmer / 4, __instance.DamageToFarmer / 4));
                    var trajectory      = SUtility.getAwayFromPositionTrajectory(monsterBox, __instance.Position) / 2f;
                    monster.takeDamage(damageToMonster, (int)trajectory.X, (int)trajectory.Y, isBomb: false, 1.0, hitSound: "slime");
                    monster.setInvincibleCountdown(225);
                    monster.currentLocation.debris.Add(new Debris(damageToMonster, new Vector2(monsterBox.Center.X + 16, monsterBox.Center.Y), new Color(255, 130, 0), 1f, monster));
                }
            }
            catch (Exception ex)
            {
                Monitor.Log($"Failed in {nameof(GreenSlimeUpdatePostfix)}:\n{ex}");
            }
        }
Ejemplo n.º 2
0
 /// <inheritdoc/>
 public override void OnDayStarted(object sender, DayStartedEventArgs e)
 {
     foreach (var location in Game1.locations)
     {
         foreach (var obj in location.Objects.Values)
         {
             if (obj is CrabPot crabpot && Game1.getFarmer(obj.owner.Value).IsLocalPlayer&& Utility.IsTrash(crabpot.heldObject.Value))
             {
                 AwesomeProfessions.Data.IncrementField($"{AwesomeProfessions.UniqueID}/WaterTrashCollectedThisSeason", amount: 1);
                 if (AwesomeProfessions.Data.ReadField($"{AwesomeProfessions.UniqueID}/WaterTrashCollectedThisSeason", uint.Parse) % 10 == 0)
                 {
                     SUtility.improveFriendshipWithEveryoneInRegion(Game1.player, 1, 2);
                 }
             }
         }
     }
 }
Ejemplo n.º 3
0
        /// <summary>Draw a tracking arrow pointer over a target on-screen.</summary>
        /// <param name="target">A target on the game location.</param>
        /// <param name="color">The color of the pointer.</param>
        /// <remarks>Note that the game will add a yellow tinge to the color supplied here. Credit to Bpendragon for this logic.</remarks>
        public static void DrawArrowPointerOverTarget(Vector2 target, Color color)
        {
            if (!SUtility.isOnScreen(target * 64f + new Vector2(32f, 32f), 64))
            {
                return;
            }

            ArrowPointer ??= new ArrowPointer(AwesomeProfessions.Content.Load <Texture2D>(Path.Combine("assets", "cursor.png")));

            var         srcRect       = new Rectangle(0, 0, 5, 4);
            const float renderScale   = 4f;
            var         targetPixel   = new Vector2((target.X * 64f) + 32f, (target.Y * 64f) + 32f) + ArrowPointer.GetOffset();
            var         adjustedPixel = Game1.GlobalToLocal(Game1.viewport, targetPixel);

            adjustedPixel = SUtility.ModifyCoordinatesForUIScale(adjustedPixel);
            Game1.spriteBatch.Draw(ArrowPointer.Texture, adjustedPixel, srcRect, color, (float)Math.PI, new Vector2(2f, 2f), renderScale, SpriteEffects.None, 1f);
        }
Ejemplo n.º 4
0
        /// <summary>Draw the notification to the game sprite batch.</summary>
        public override void draw(SpriteBatch b, int i)
        {
            var titleSafeArea = Game1.graphics.GraphicsDevice.Viewport.GetTitleSafeArea();

            if (noIcon)
            {
                var overrideX = titleSafeArea.Left + 16;
                var overrideY = ((Game1.uiViewport.Width < 1400) ? (-64) : 0) + titleSafeArea.Bottom - (i + 1) * 64 * 7 / 4 - 21 - (int)Game1.dialogueFont.MeasureString(message).Y;
                IClickableMenu.drawHoverText(b, message, Game1.dialogueFont, 0, 0, -1, null, -1, null, null, 0, -1, -1, overrideX, overrideY, transparency);
                return;
            }

            var itemBoxPosition = new Vector2(titleSafeArea.Left + 16, titleSafeArea.Bottom - (i + 1) * 64 * 7 / 4 - 64);

            if (Game1.isOutdoorMapSmallerThanViewport())
            {
                itemBoxPosition.X = Math.Max(titleSafeArea.Left + 16, -Game1.uiViewport.X + 16);
            }

            if (Game1.uiViewport.Width < 1400)
            {
                itemBoxPosition.Y -= 48f;
            }

            b.Draw(Game1.mouseCursors, itemBoxPosition, new Rectangle(293, 360, 26, 24), Color.White * transparency, 0f, Vector2.Zero, 4f, SpriteEffects.None, 1f);
            var messageWidth = Game1.smallFont.MeasureString(message).X;

            b.Draw(Game1.mouseCursors, new Vector2(itemBoxPosition.X + 104f, itemBoxPosition.Y), new Rectangle(319, 360, 1, 24), Color.White * transparency, 0f, Vector2.Zero, new Vector2(messageWidth, 4f), SpriteEffects.None, 1f);
            b.Draw(Game1.mouseCursors, new Vector2(itemBoxPosition.X + 104f + messageWidth, itemBoxPosition.Y), new Rectangle(323, 360, 6, 24), Color.White * transparency, 0f, Vector2.Zero, 4f, SpriteEffects.None, 1f);
            itemBoxPosition.X += 16f;
            itemBoxPosition.Y += 16f;
            b.Draw(_Icon, itemBoxPosition + new Vector2(8f, 8f) * 4f, new Rectangle(0, 0, 16, 16), Color.White * transparency, 0f, new Vector2(8f, 8f), 4f + Math.Max(0f, (timeLeft - 3000f) / 900f), SpriteEffects.None, 1f);
            itemBoxPosition.X += 51f;
            itemBoxPosition.Y += 51f;
            if (number > 1)
            {
                SUtility.drawTinyDigits(number, b, itemBoxPosition, 3f, 1f, Color.White * transparency);
            }

            itemBoxPosition.X += 32f;
            itemBoxPosition.Y -= 33f;
            SUtility.drawTextWithShadow(b, message, Game1.smallFont, itemBoxPosition, Game1.textColor * transparency, 1f, 1f, -1, -1, transparency);
        }
 /// <summary>
 /// Helper for transferring objects between <c>GameLocation</c>s such as Sheds.
 /// Beware this is destructive to the <paramref name="destination"/> location's contents.
 /// This does not remove the objects from the <paramref name="source"/>, merely copies their references.
 /// </summary>
 public static void TransferObjects(GameLocation source, GameLocation destination)
 {
     // Saved objects that should persist
     ClearAndAdd(source.resourceClumps, destination.resourceClumps);
     destination.terrainFeatures.Clear();
     foreach (var tfpair in source.terrainFeatures.Pairs)
     {
         destination.terrainFeatures.Add(tfpair.Key, tfpair.Value);
     }
     ClearAndAdd(source.largeTerrainFeatures, destination.largeTerrainFeatures);
     destination.objects.Clear();
     SDVUtility.transferPlacedObjectsFromOneLocationToAnother(source, destination);
     ClearAndAdd(source.characters, destination.characters);
     ClearAndAdd(source.furniture, destination.furniture);
     // Unsaved objects that should persist
     if (source.IsOutdoors)
     {
         ClearAndAdd(source.critters, destination.critters);
     }
     ClearAndAdd(source.debris, destination.debris);
     source.numberOfSpawnedObjectsOnMap = destination.numberOfSpawnedObjectsOnMap;
 }
Ejemplo n.º 6
0
        public override void Entry(IModHelper helper)
        {
#if DEBUG
            Monitor.Log("THIS IS A DEBUG BUILD...", LogLevel.Alert);
            Monitor.Log("...FOR DEBUGGING...", LogLevel.Alert);
            Monitor.Log("...AND STUFF...", LogLevel.Alert);
            if (ModManifest.Version.IsPrerelease())
            {
                Monitor.Log("oh wait this is a pre-release.", LogLevel.Info);
                Monitor.Log("carry on.", LogLevel.Info);
            }
            else
            {
                Monitor.Log("If you're Fayne, keep up the good work. :)", LogLevel.Alert);
                Monitor.Log("If you're not Fayne...", LogLevel.Alert);
                Monitor.Log("...please go yell at Fayne...", LogLevel.Alert);
                Monitor.Log("...because you shouldn't have this...", LogLevel.Alert);
                Monitor.Log("...it's for debugging. (:", LogLevel.Alert);
            }
#else
            if (ModManifest.Version.IsPrerelease())
            {
                Monitor.Log("WAIT A MINUTE.", LogLevel.Alert);
                Monitor.Log("FAYNE.", LogLevel.Alert);
                Monitor.Log("WHY DID YOU RELEASE A NON-DEBUG DEV BUILD?!", LogLevel.Alert);
                Monitor.Log("https://youtu.be/T3djXcx2ewQ", LogLevel.Alert);
            }
#endif
            if (Constants.TargetPlatform == GamePlatform.Android)
            {
                Monitor.Log("Discord RPC is not supported on Android.", LogLevel.Error);
                Monitor.Log("Aborting mod initialization.", LogLevel.Error);
                Dispose();
                return;
            }

            api    = new RichPresenceAPI(this);
            client = new DiscordRpcClient(clientId,
                                          autoEvents: false,
                                          logger: new MonitorLogger(Monitor));
            client.RegisterUriScheme(steamId);
            client.OnReady += (sender, e) => {
                Monitor.Log("Connected to Discord: " + e.User.ToString(), LogLevel.Info);
            };
            client.OnJoin += (sender, args) => {
                Monitor.Log("Attempting to join game: " + args.Secret, LogLevel.Info);
                JoinGame(args.Secret);
            };
            client.OnJoinRequested += (sender, msg) => {
                string name = msg.User.Username;
                string tag  = msg.User.ToString();
                ushort id   = (ushort)rand.Next(ushort.MinValue, ushort.MaxValue);
                requests[id]  = msg;
                lastRequestID = id;
                string hex = id.ToString("X");
                Monitor.Log(tag + " wants to join your game via Discord.", LogLevel.Alert);
                Monitor.Log("To respond type \"discord " + hex + " yes/no\" or just \"discord yes/no\"", LogLevel.Info);
                Game1.chatBox.addMessage(name + " wants to join your game via Discord.\nTo respond check the console or use Discord or its overlay.", blurple);
            };
            client.Initialize();
            client.SetSubscription(EventType.Join | EventType.JoinRequest);

            #region Console Commands
            Helper.ConsoleCommands.Add("discord",
                                       "Respond to a Discord join request.",
                                       (command, args) => {
                // Yes, I know this code is a mess.
                switch (args[0].ToLower())
                {
                case "yes":
                case "y":
                    Respond(lastRequestID, true);
                    break;

                case "no":
                case "n":
                    Respond(lastRequestID, false);
                    break;

                default:
                    try {
                        var id = ushort.Parse(args[0], System.Globalization.NumberStyles.HexNumber);
                        switch (args[1].ToLower())
                        {
                        case "yes":
                        case "y":
                            Respond(id, true);
                            break;

                        case "no":
                        case "n":
                            Respond(id, false);
                            break;

                        default:
                            Monitor.Log("Invalid response.", LogLevel.Error);
                            break;
                        }
                    } catch (Exception) {
                        Monitor.Log("Invalid request ID.", LogLevel.Error);
                    }
                    break;
                }
            }
                                       );
            Helper.ConsoleCommands.Add("DiscordRP_Join",
                                       "Join a co-op game via invite code.",
                                       (string command, string[] args) => {
                JoinGame(string.Join(" ", args));
            }
                                       );
            Helper.ConsoleCommands.Add("DiscordRP_Reload",
                                       "Reloads the config for Discord Rich Presence.",
                                       (string command, string[] args) => {
                LoadConfig();
                Monitor.Log("Config reloaded.", LogLevel.Info);
            }
                                       );
            Helper.ConsoleCommands.Add("DiscordRP_Format",
                                       "Formats and prints a provided configuration string.",
                                       (string command, string[] args) => {
                string text = api.FormatText(string.Join(" ", args));
                Monitor.Log("Result: " + text, LogLevel.Info);
            }
                                       );
            Helper.ConsoleCommands.Add("DiscordRP_Tags",
                                       "Lists tags usable for configuration strings.",
                                       (string command, string[] args) => {
                IDictionary <string, string> tags =
                    string.Join("", args).ToLower().StartsWith("all") ?
                    api.ListTags("[NULL]", "[ERROR]") : api.ListTags(removeNull: false);
                IDictionary <string, IDictionary <string, string> > groups =
                    new Dictionary <string, IDictionary <string, string> >();
                foreach (KeyValuePair <string, string> tag in tags)
                {
                    string owner = api.GetTagOwner(tag.Key) ?? "Unknown-Mod";
                    if (!groups.ContainsKey(owner))
                    {
                        groups[owner] = new Dictionary <string, string>();
                    }
                    groups[owner][tag.Key] = tag.Value;
                }
                IList <string> output = new List <string>(tags.Count + groups.Count)
                {
                    "Available Tags:"
                };
                int longest = 0;
                foreach (KeyValuePair <string, string> tag in groups[ModManifest.UniqueID])
                {
                    if (tag.Value != null)
                    {
                        longest = Math.Max(longest, tag.Key.Length);
                    }
                }
                int nulls = 0;
                foreach (KeyValuePair <string, string> tag in groups[ModManifest.UniqueID])
                {
                    if (tag.Value is null)
                    {
                        nulls++;
                    }
                    else
                    {
                        output.Add("  {{ " + tag.Key.PadLeft(longest) + " }}: " + tag.Value);
                    }
                }
                foreach (KeyValuePair <string, IDictionary <string, string> > group in groups)
                {
                    if (group.Key == ModManifest.UniqueID)
                    {
                        continue;
                    }
                    string head = group.Value.Count + " tag";
                    if (group.Value.Count != 1)
                    {
                        head += "s";
                    }
                    head += " from " + (Helper.ModRegistry.Get(group.Key)?.Manifest.Name ?? "an unknown mod");
                    output.Add(head);
                    longest = 0;
                    foreach (KeyValuePair <string, string> tag in group.Value)
                    {
                        if (tag.Value != null)
                        {
                            longest = Math.Max(longest, tag.Key.Length);
                        }
                    }
                    foreach (KeyValuePair <string, string> tag in group.Value)
                    {
                        if (tag.Value == null)
                        {
                            nulls++;
                        }
                        else
                        {
                            output.Add("  {{ " + tag.Key.PadLeft(longest) + " }}: " + tag.Value);
                        }
                    }
                }
                if (nulls > 0)
                {
                    output.Add(nulls + " tag" + (nulls != 1 ? "s" : "") + " unavailable; type `DiscordRP_Tags all` to show all");
                }
                Monitor.Log(string.Join(Environment.NewLine, output), LogLevel.Info);
            }
                                       );
            #endregion
            LoadConfig();

            Helper.Events.Input.ButtonReleased     += HandleButton;
            Helper.Events.GameLoop.UpdateTicked    += DoUpdate;
            Helper.Events.GameLoop.SaveLoaded      += SetTimestamp;
            Helper.Events.GameLoop.ReturnedToTitle += SetTimestamp;
            Helper.Events.GameLoop.SaveLoaded      += (object sender, SaveLoadedEventArgs e) =>
                                                      api.GamePresence = "Getting Started";
            Helper.Events.GameLoop.SaveCreated += (object sender, SaveCreatedEventArgs e) =>
                                                  api.GamePresence = "Starting a New Game";
            Helper.Events.GameLoop.GameLaunched += (object sender, GameLaunchedEventArgs e) => {
                SetTimestamp();
                timestampSession = Timestamps.Now;
            };

            ITagRegister tagReg = api.GetTagRegister(this);

            #region Default Tags

            tagReg.SetTag("Activity", () => api.GamePresence);
            tagReg.SetTag("ModCount", () => Helper.ModRegistry.GetAll().Count());
            tagReg.SetTag("SMAPIVersion", () => Constants.ApiVersion.ToString());
            tagReg.SetTag("StardewVersion", () => Game1.version);
            tagReg.SetTag("Song", () => Utility.getSongTitleFromCueName(Game1.currentSong?.Name ?? api.None));

            // All the tags below are only available while in-game.

            tagReg.SetTag("Name", () => Game1.player.Name, true);
            tagReg.SetTag("Farm", () => Game1.content.LoadString("Strings\\UI:Inventory_FarmName", api.GetTag("FarmName")), true);
            tagReg.SetTag("FarmName", () => Game1.player.farmName, true);
            tagReg.SetTag("PetName", () => Game1.player.hasPet() ? Game1.player.getPetDisplayName() : api.None, true);
            tagReg.SetTag("Location", () => Game1.currentLocation.Name, true);
            tagReg.SetTag("RomanticInterest", () => Utility.getTopRomanticInterest(Game1.player)?.getName() ?? api.None, true);
            tagReg.SetTag("PercentComplete", () => Utility.percentGameComplete(), true);

            tagReg.SetTag("Money", () => {
                // Copied from LoadGameMenu
                string text = Game1.content.LoadString("Strings\\StringsFromCSFiles:LoadGameMenu.cs.11020", Utility.getNumberWithCommas(Game1.player.Money));
                if (Game1.player.Money == 1 && LocalizedContentManager.CurrentLanguageCode == LocalizedContentManager.LanguageCode.pt)
                {
                    text = text.Substring(0, text.Length - 1);
                }
                return(text);
            }, true);
            tagReg.SetTag("MoneyNumber", () => Game1.player.Money, true);
            tagReg.SetTag("MoneyCommas", () => Utility.getNumberWithCommas(Game1.player.Money), true);
            tagReg.SetTag("Level", () => Game1.content.LoadString("Strings\\UI:Inventory_PortraitHover_Level", Game1.player.Level.ToString()), true);
            tagReg.SetTag("LevelNumber", () => Game1.player.Level, true);
            tagReg.SetTag("Title", () => Game1.player.getTitle(), true);
            tagReg.SetTag("TotalTime", () => Utility.getHoursMinutesStringFromMilliseconds(Game1.player.millisecondsPlayed), true);

            tagReg.SetTag("Health", () => Game1.player.health, true);
            tagReg.SetTag("HealthMax", () => Game1.player.maxHealth, true);
            tagReg.SetTag("HealthPercent", () => (double)Game1.player.health / Game1.player.maxHealth * 100, 2, true);
            tagReg.SetTag("Energy", () => Game1.player.Stamina.ToString(), true);
            tagReg.SetTag("EnergyMax", () => Game1.player.MaxStamina, true);
            tagReg.SetTag("EnergyPercent", () => (double)Game1.player.Stamina / Game1.player.MaxStamina * 100, 2, true);

            tagReg.SetTag("Time", () => Game1.getTimeOfDayString(Game1.timeOfDay), true);
            tagReg.SetTag("Date", () => Utility.getDateString(), true);
            tagReg.SetTag("Season", () => Utility.getSeasonNameFromNumber(Utility.getSeasonNumber(SDate.Now().Season)), true);
            tagReg.SetTag("DayOfWeek", () => Game1.shortDayDisplayNameFromDayOfSeason(SDate.Now().Day), true);

            tagReg.SetTag("Day", () => SDate.Now().Day, true);
            tagReg.SetTag("DayPad", () => $"{SDate.Now().Day:00}", true);
            tagReg.SetTag("DaySuffix", () => Utility.getNumberEnding(SDate.Now().Day), true);
            tagReg.SetTag("Year", () => SDate.Now().Year, true);
            tagReg.SetTag("YearSuffix", () => Utility.getNumberEnding(SDate.Now().Year), true);

            tagReg.SetTag("GameVerb", () =>
                          Context.IsMultiplayer && Context.IsMainPlayer ? "Hosting" : "Playing", true);
            tagReg.SetTag("GameNoun", () => Context.IsMultiplayer ? "Co-op" : "Solo", true);
            tagReg.SetTag("GameInfo", () => api.GetTag("GameVerb") + " " + api.GetTag("GameNoun"), true);
            #endregion
        }
Ejemplo n.º 7
0
        /// <summary>Draw a tracking arrow pointer on the edge of the screen pointing to a target off-screen.</summary>
        /// <param name="target">The target to point to.</param>
        /// <param name="color">The color of the pointer.</param>
        /// <remarks>Note that the game will add a yellow tinge to the color supplied here.</remarks>
        public static void DrawTrackingArrowPointer(Vector2 target, Color color)
        {
            if (SUtility.isOnScreen(target * 64f + new Vector2(32f, 32f), 64))
            {
                return;
            }

            ArrowPointer ??= new ArrowPointer(AwesomeProfessions.Content.Load <Texture2D>(Path.Combine("assets", "cursor.png")));

            var     vpbounds         = Game1.graphics.GraphicsDevice.Viewport.Bounds;
            Vector2 onScreenPosition = default;
            var     rotation         = 0f;

            if (target.X * 64f > Game1.viewport.MaxCorner.X - 64)
            {
                onScreenPosition.X = vpbounds.Right - 8;
                rotation           = (float)Math.PI / 2f;
            }
            else if (target.X * 64f < Game1.viewport.X)
            {
                onScreenPosition.X = 8f;
                rotation           = -(float)Math.PI / 2f;
            }
            else
            {
                onScreenPosition.X = target.X * 64f - Game1.viewport.X;
            }

            if (target.Y * 64f > Game1.viewport.MaxCorner.Y - 64)
            {
                onScreenPosition.Y = vpbounds.Bottom - 8;
                rotation           = (float)Math.PI;
            }
            else if (target.Y * 64f < Game1.viewport.Y)
            {
                onScreenPosition.Y = 8f;
            }
            else
            {
                onScreenPosition.Y = target.Y * 64f - Game1.viewport.Y;
            }

            if ((int)onScreenPosition.X == 8 && (int)onScreenPosition.Y == 8)
            {
                rotation += (float)Math.PI / 4f;
            }

            if ((int)onScreenPosition.X == 8 && (int)onScreenPosition.Y == vpbounds.Bottom - 8)
            {
                rotation += (float)Math.PI / 4f;
            }

            if ((int)onScreenPosition.X == vpbounds.Right - 8 && (int)onScreenPosition.Y == 8)
            {
                rotation -= (float)Math.PI / 4f;
            }

            if ((int)onScreenPosition.X == vpbounds.Right - 8 && (int)onScreenPosition.Y == vpbounds.Bottom - 8)
            {
                rotation -= (float)Math.PI / 4f;
            }

            var srcRect     = new Rectangle(0, 0, 5, 4);
            var renderScale = 4f;
            var safePos     = SUtility.makeSafe(renderSize: new Vector2(srcRect.Width * renderScale, srcRect.Height * renderScale), renderPos: onScreenPosition);

            Game1.spriteBatch.Draw(ArrowPointer.Texture, safePos, srcRect, color, rotation, new Vector2(2f, 2f), renderScale, SpriteEffects.None, 1f);
        }
        /// <summary>Patch to adjust fish pond UI for Aquarist increased max capacity.</summary>
        private static bool PondQueryMenuDrawPrefix(ref PondQueryMenu __instance, ref float ____age, ref Rectangle ____confirmationBoxRectangle, ref string ____confirmationText, ref SObject ____fishItem, ref FishPond ____pond, ref bool ___confirmingEmpty, ref string ___hoverText, SpriteBatch b)
        {
            try
            {
                var owner = Game1.getFarmer(____pond.owner.Value);
                if (!Utility.SpecificPlayerHasProfession("Aquarist", owner) || ____pond.lastUnlockedPopulationGate.Value < AwesomeProfessions.Reflection.GetField <FishPondData>(____pond, name: "_fishPondData").GetValue().PopulationGates.Keys.Max())
                {
                    return(true);                                                                                                                                                                                                                                                    // run original logic;
                }
                if (!Game1.globalFade)
                {
                    b.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.Black * 0.75f);
                    var hasUnresolvedNeeds = ____pond.neededItem.Value != null && ____pond.HasUnresolvedNeeds() && !____pond.hasCompletedRequest.Value;
                    var pondNameText       = Game1.content.LoadString("Strings\\UI:PondQuery_Name", ____fishItem.DisplayName);
                    var textSize           = Game1.smallFont.MeasureString(pondNameText);
                    Game1.DrawBox((int)((Game1.uiViewport.Width / 2) - (textSize.X + 64f) * 0.5f), __instance.yPositionOnScreen - 4 + 128, (int)(textSize.X + 64f), 64);
                    SUtility.drawTextWithShadow(b, pondNameText, Game1.smallFont, new Vector2((Game1.uiViewport.Width / 2) - textSize.X * 0.5f, __instance.yPositionOnScreen - 4 + 160f - textSize.Y * 0.5f), Color.Black);
                    var displayedText = AwesomeProfessions.Reflection.GetMethod(__instance, name: "getDisplayedText").Invoke <string>();
                    var extraHeight   = 0;
                    if (hasUnresolvedNeeds)
                    {
                        extraHeight += 116;
                    }

                    var extraTextHeight = AwesomeProfessions.Reflection.GetMethod(__instance, name: "measureExtraTextHeight").Invoke <int>(displayedText);
                    Game1.drawDialogueBox(__instance.xPositionOnScreen, __instance.yPositionOnScreen + 128, PondQueryMenu.width, PondQueryMenu.height - 128 + extraHeight + extraTextHeight, speaker: false, drawOnlyBox: true);
                    var populationText = Game1.content.LoadString("Strings\\UI:PondQuery_Population", string.Concat(____pond.FishCount), ____pond.maxOccupants.Value);
                    textSize = Game1.smallFont.MeasureString(populationText);
                    SUtility.drawTextWithShadow(b, populationText, Game1.smallFont, new Vector2((__instance.xPositionOnScreen + PondQueryMenu.width / 2) - textSize.X * 0.5f, __instance.yPositionOnScreen + IClickableMenu.spaceToClearTopBorder + 16 + 128), Game1.textColor);
                    var slotsToDraw = ____pond.maxOccupants.Value;
                    var slotSpacing = 11f;
                    var x           = 0;
                    var y           = 0;
                    for (var i = 0; i < slotsToDraw; ++i)
                    {
                        var yOffset = (float)Math.Sin(____age * 1f + x * 0.75f + y * 0.25f) * 2f;
                        if (i < ____pond.FishCount)
                        {
                            ____fishItem.drawInMenu(b, new Vector2((__instance.xPositionOnScreen - 20 + PondQueryMenu.width / 2) - slotSpacing * Math.Min(slotsToDraw, 5) * 4f * 0.5f + slotSpacing * 4f * x - 12f, (__instance.yPositionOnScreen + (int)(yOffset * 4f)) + (y * 4) * slotSpacing + 275.2f), 0.75f, 1f, 0f, StackDrawType.Hide, Color.White, drawShadow: false);
                        }
                        else
                        {
                            ____fishItem.drawInMenu(b, new Vector2((__instance.xPositionOnScreen - 20 + PondQueryMenu.width / 2) - slotSpacing * Math.Min(slotsToDraw, 5) * 4f * 0.5f + slotSpacing * 4f * x - 12f, (__instance.yPositionOnScreen + (int)(yOffset * 4f)) + (y * 4) * slotSpacing + 275.2f), 0.75f, 0.35f, 0f, StackDrawType.Hide, Color.Black, drawShadow: false);
                        }

                        ++x;
                        if (x != 6)
                        {
                            continue;
                        }

                        x = 0;
                        ++y;
                    }

                    textSize = Game1.smallFont.MeasureString(displayedText);
                    SUtility.drawTextWithShadow(b, displayedText, Game1.smallFont, new Vector2((__instance.xPositionOnScreen + PondQueryMenu.width / 2) - textSize.X * 0.5f, (__instance.yPositionOnScreen + PondQueryMenu.height + extraTextHeight - (hasUnresolvedNeeds ? 32 : 48)) - textSize.Y), Game1.textColor);
                    if (hasUnresolvedNeeds)
                    {
                        AwesomeProfessions.Reflection.GetMethod(__instance, name: "drawHorizontalPartition").Invoke(b, (int)((__instance.yPositionOnScreen + PondQueryMenu.height + extraTextHeight) - 48f));
                        SUtility.drawWithShadow(b, Game1.mouseCursors, new Vector2((__instance.xPositionOnScreen + 60) + 8f * Game1.dialogueButtonScale / 10f, __instance.yPositionOnScreen + PondQueryMenu.height + extraTextHeight + 28), new Rectangle(412, 495, 5, 4), Color.White, (float)Math.PI / 2f, Vector2.Zero);
                        var bringText = Game1.content.LoadString("Strings\\UI:PondQuery_StatusRequest_Bring");
                        textSize = Game1.smallFont.MeasureString(bringText);
                        var   leftX = __instance.xPositionOnScreen + 88;
                        float textX = leftX;
                        var   iconX = textX + textSize.X + 4f;
                        if (LocalizedContentManager.CurrentLanguageCode == LocalizedContentManager.LanguageCode.ja || LocalizedContentManager.CurrentLanguageCode == LocalizedContentManager.LanguageCode.ko || LocalizedContentManager.CurrentLanguageCode == LocalizedContentManager.LanguageCode.tr)
                        {
                            iconX = leftX - 8;
                            textX = leftX + 76;
                        }

                        SUtility.drawTextWithShadow(b, bringText, Game1.smallFont, new Vector2(textX, __instance.yPositionOnScreen + PondQueryMenu.height + extraTextHeight + 24), Game1.textColor);
                        b.Draw(Game1.objectSpriteSheet, new Vector2(iconX, __instance.yPositionOnScreen + PondQueryMenu.height + extraTextHeight + 4), Game1.getSourceRectForStandardTileSheet(Game1.objectSpriteSheet, ____pond.neededItem.Value.ParentSheetIndex, 16, 16), Color.Black * 0.4f, 0f, Vector2.Zero, 4f, SpriteEffects.None, 1f);
                        b.Draw(Game1.objectSpriteSheet, new Vector2(iconX + 4f, __instance.yPositionOnScreen + PondQueryMenu.height + extraTextHeight), Game1.getSourceRectForStandardTileSheet(Game1.objectSpriteSheet, ____pond.neededItem.Value.ParentSheetIndex, 16, 16), Color.White, 0f, Vector2.Zero, 4f, SpriteEffects.None, 1f);
                        if (____pond.neededItemCount.Value > 1)
                        {
                            SUtility.drawTinyDigits(____pond.neededItemCount.Value, b, new Vector2(iconX + 48f, __instance.yPositionOnScreen + PondQueryMenu.height + extraTextHeight + 48), 3f, 1f, Color.White);
                        }
                    }

                    __instance.okButton.draw(b);
                    __instance.emptyButton.draw(b);
                    __instance.changeNettingButton.draw(b);
                    if (___confirmingEmpty)
                    {
                        b.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.Black * 0.75f);
                        var padding = 16;
                        ____confirmationBoxRectangle.Width  += padding;
                        ____confirmationBoxRectangle.Height += padding;
                        ____confirmationBoxRectangle.X      -= padding / 2;
                        ____confirmationBoxRectangle.Y      -= padding / 2;
                        Game1.DrawBox(____confirmationBoxRectangle.X, ____confirmationBoxRectangle.Y, ____confirmationBoxRectangle.Width, ____confirmationBoxRectangle.Height);
                        ____confirmationBoxRectangle.Width  -= padding;
                        ____confirmationBoxRectangle.Height -= padding;
                        ____confirmationBoxRectangle.X      += padding / 2;
                        ____confirmationBoxRectangle.Y      += padding / 2;
                        b.DrawString(Game1.smallFont, ____confirmationText, new Vector2(____confirmationBoxRectangle.X, ____confirmationBoxRectangle.Y), Game1.textColor);
                        __instance.yesButton.draw(b);
                        __instance.noButton.draw(b);
                    }
                    else if (!string.IsNullOrEmpty(___hoverText))
                    {
                        IClickableMenu.drawHoverText(b, ___hoverText, Game1.smallFont);
                    }
                }

                __instance.drawMouse(b);
                return(false);                // don't run original logic
            }
            catch (Exception ex)
            {
                Monitor.Log($"Failed in {nameof(PondQueryMenuDrawPrefix)}:\n{ex}");
                return(true);                // default to original logic
            }
        }
Ejemplo n.º 9
0
        public override void Entry(IModHelper helper)
        {
#if DEBUG
            Monitor.Log("THIS IS A DEBUG BUILD...", LogLevel.Alert);
            Monitor.Log("...FOR DEBUGGING...", LogLevel.Alert);
            Monitor.Log("...AND STUFF...", LogLevel.Alert);
            if (ModManifest.Version.IsPrerelease())
            {
                Monitor.Log("oh wait this is a pre-release.", LogLevel.Info);
                Monitor.Log("carry on.", LogLevel.Info);
            }
            else
            {
                Monitor.Log("If you're Fayne, keep up the good work. :)", LogLevel.Alert);
                Monitor.Log("If you're not Fayne...", LogLevel.Alert);
                Monitor.Log("...please go yell at Fayne...", LogLevel.Alert);
                Monitor.Log("...because you shouldn't have this...", LogLevel.Alert);
                Monitor.Log("...it's for debugging. (:", LogLevel.Alert);
            }
#else
            if (ModManifest.Version.IsPrerelease())
            {
                Monitor.Log("WAIT A MINUTE.", LogLevel.Alert);
                Monitor.Log("FAYNE.", LogLevel.Alert);
                Monitor.Log("WHY DID YOU RELEASE A NON-DEBUG DEV BUILD?!", LogLevel.Alert);
                Monitor.Log("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", LogLevel.Alert);
            }
#endif

            AppDomain.CurrentDomain.AssemblyResolve += (object sender, ResolveEventArgs e) => {
                try {
                    var name = new AssemblyName(e.Name);
                    foreach (FileInfo dll in new DirectoryInfo(Helper.DirectoryPath).EnumerateFiles("*.dll"))
                    {
                        if (name.Name.Equals(AssemblyName.GetAssemblyName(dll.FullName).Name, StringComparison.InvariantCultureIgnoreCase))
                        {
                            return(Assembly.LoadFrom(dll.FullName));
                        }
                    }
                } catch { }
                return(null);
            };

            api = new RichPresenceAPI(this);
            try {
                discord = new Discord.Discord(clientId, (ulong)CreateFlags.NoRequireDiscord);
            } catch (Discord.ResultException e) {
                Monitor.Log("Failed to initialize Discord SDK: " + e.Message, LogLevel.Error);
                Monitor.Log("Rich Presence cannot be activated. Restart the game to try again.", LogLevel.Error);
                Dispose();
                return;
            }
            discord.SetLogHook(Discord.LogLevel.Debug, (Discord.LogLevel level, string message) => {
                LogLevel sdlevel = LogLevel.Trace;
                switch (level)
                {
                case Discord.LogLevel.Error:
                    sdlevel = LogLevel.Error;
                    break;

                case Discord.LogLevel.Warn:
                    sdlevel = LogLevel.Warn;
                    break;

                case Discord.LogLevel.Info:
                    sdlevel = LogLevel.Info;
                    break;

                case Discord.LogLevel.Debug:
                    sdlevel = LogLevel.Debug;
                    break;
                }
                ;
                Monitor.Log("DISCORD: " + message, sdlevel);
            });
            discord.GetUserManager().OnCurrentUserUpdate += () => {
                User user = discord.GetUserManager().GetCurrentUser();
                Monitor.Log("Connected to Discord: " + GetDiscordTag(user, true), LogLevel.Info);
            };
            activityManager = discord.GetActivityManager();
            activityManager.RegisterSteam(steamId);

            activityManager.OnActivityJoin += (string secret) => {
                Monitor.Log("Attempting to join game", LogLevel.Info);
                JoinGame(secret);
            };

            activityManager.OnActivityJoinRequest += (ref User user) => {
                string tag = GetDiscordTag(user);
                Monitor.Log(tag + " is requesting to join your game.", LogLevel.Alert);
                Monitor.Log("You can respond to this request in Discord Overlay.", LogLevel.Info);
                Game1.chatBox.addInfoMessage(tag + " is requesting to join your game. You can respond to this request in Discord Overlay.");
            };

            Helper.ConsoleCommands.Add("DiscordRP_TestJoin",
                                       "Command for debugging.",
                                       (string command, string[] args) => {
                JoinGame(string.Join(" ", args));
            }
                                       );
            Helper.ConsoleCommands.Add("DiscordRP_Reload",
                                       "Reloads the config for Discord Rich Presence.",
                                       (string command, string[] args) => {
                LoadConfig();
                Monitor.Log("Config reloaded.", LogLevel.Info);
            }
                                       );
            Helper.ConsoleCommands.Add("DiscordRP_Format",
                                       "Formats and prints a provided configuration string.",
                                       (string command, string[] args) => {
                string text = api.FormatText(string.Join(" ", args));
                Monitor.Log("Result: " + text, LogLevel.Info);
            }
                                       );
            Helper.ConsoleCommands.Add("DiscordRP_Tags",
                                       "Lists tags usable for configuration strings.",
                                       (string command, string[] args) => {
                IDictionary <string, string> tags =
                    string.Join("", args).ToLower().StartsWith("all") ?
                    api.ListTags("[NULL]", "[ERROR]") : api.ListTags(removeNull: false);
                IDictionary <string, IDictionary <string, string> > groups =
                    new Dictionary <string, IDictionary <string, string> >();
                foreach (KeyValuePair <string, string> tag in tags)
                {
                    string owner = api.GetTagOwner(tag.Key) ?? "Unknown-Mod";
                    if (!groups.ContainsKey(owner))
                    {
                        groups[owner] = new Dictionary <string, string>();
                    }
                    groups[owner][tag.Key] = tag.Value;
                }
                IList <string> output = new List <string>(tags.Count + groups.Count)
                {
                    "Available Tags:"
                };
                int longest = 0;
                foreach (KeyValuePair <string, string> tag in groups[ModManifest.UniqueID])
                {
                    if (tag.Value != null)
                    {
                        longest = Math.Max(longest, tag.Key.Length);
                    }
                }
                int nulls = 0;
                foreach (KeyValuePair <string, string> tag in groups[ModManifest.UniqueID])
                {
                    if (tag.Value is null)
                    {
                        nulls++;
                    }
                    else
                    {
                        output.Add("  {{ " + tag.Key.PadLeft(longest) + " }}: " + tag.Value);
                    }
                }
                foreach (KeyValuePair <string, IDictionary <string, string> > group in groups)
                {
                    if (group.Key == ModManifest.UniqueID)
                    {
                        continue;
                    }
                    string head = group.Value.Count + " tag";
                    if (group.Value.Count != 1)
                    {
                        head += "s";
                    }
                    head += " from " + (Helper.ModRegistry.Get(group.Key)?.Manifest.Name ?? "an unknown mod");
                    output.Add(head);
                    longest = 0;
                    foreach (KeyValuePair <string, string> tag in group.Value)
                    {
                        if (tag.Value != null)
                        {
                            longest = Math.Max(longest, tag.Key.Length);
                        }
                    }
                    foreach (KeyValuePair <string, string> tag in group.Value)
                    {
                        if (tag.Value == null)
                        {
                            nulls++;
                        }
                        else
                        {
                            output.Add("  {{ " + tag.Key.PadLeft(longest) + " }}: " + tag.Value);
                        }
                    }
                }
                if (nulls > 0)
                {
                    output.Add(nulls + " tag" + (nulls != 1 ? "s" : "") + " unavailable; type `DiscordRP_Tags all` to show all");
                }
                Monitor.Log(string.Join(Environment.NewLine, output), LogLevel.Info);
            }
                                       );
            LoadConfig();

            Helper.Events.Input.ButtonReleased     += HandleButton;
            Helper.Events.GameLoop.UpdateTicked    += DoUpdate;
            Helper.Events.GameLoop.SaveLoaded      += SetTimestamp;
            Helper.Events.GameLoop.ReturnedToTitle += SetTimestamp;
            Helper.Events.GameLoop.SaveLoaded      += (object sender, SaveLoadedEventArgs e) =>
                                                      api.GamePresence = "Getting Started";
            Helper.Events.GameLoop.SaveCreated += (object sender, SaveCreatedEventArgs e) =>
                                                  api.GamePresence = "Starting a New Game";
            Helper.Events.GameLoop.GameLaunched += (object sender, GameLaunchedEventArgs e) => {
                SetTimestamp();
                timestampSession = Now();
            };

            ITagRegister tagReg = api.GetTagRegister(this);

            tagReg.SetTag("Activity", () => api.GamePresence);
            tagReg.SetTag("ModCount", () => Helper.ModRegistry.GetAll().Count());
            tagReg.SetTag("SMAPIVersion", () => Constants.ApiVersion.ToString());
            tagReg.SetTag("StardewVersion", () => Game1.version);
            tagReg.SetTag("Song", () => Utility.getSongTitleFromCueName(Game1.currentSong?.Name ?? api.None));

            // All the tags below are only available while in-game.

            tagReg.SetTag("Name", () => Game1.player.Name, true);
            tagReg.SetTag("Farm", () => Game1.content.LoadString("Strings\\UI:Inventory_FarmName", api.GetTag("FarmName")), true);
            tagReg.SetTag("FarmName", () => Game1.player.farmName, true);
            tagReg.SetTag("PetName", () => Game1.player.hasPet() ? Game1.player.getPetDisplayName() : api.None, true);
            tagReg.SetTag("Location", () => Game1.currentLocation.Name, true);
            tagReg.SetTag("RomanticInterest", () => Utility.getTopRomanticInterest(Game1.player)?.getName() ?? api.None, true);
            tagReg.SetTag("PercentComplete", () => Utility.percentGameComplete(), true);

            tagReg.SetTag("Money", () => {
                // Copied from LoadGameMenu
                string text = Game1.content.LoadString("Strings\\StringsFromCSFiles:LoadGameMenu.cs.11020", Utility.getNumberWithCommas(Game1.player.Money));
                if (Game1.player.Money == 1 && LocalizedContentManager.CurrentLanguageCode == LocalizedContentManager.LanguageCode.pt)
                {
                    text = text.Substring(0, text.Length - 1);
                }
                return(text);
            }, true);
            tagReg.SetTag("MoneyNumber", () => Game1.player.Money, true);
            tagReg.SetTag("MoneyCommas", () => Utility.getNumberWithCommas(Game1.player.Money), true);
            tagReg.SetTag("Level", () => Game1.content.LoadString("Strings\\UI:Inventory_PortraitHover_Level", Game1.player.Level.ToString()), true);
            tagReg.SetTag("LevelNumber", () => Game1.player.Level, true);
            tagReg.SetTag("Title", () => Game1.player.getTitle(), true);
            tagReg.SetTag("TotalTime", () => Utility.getHoursMinutesStringFromMilliseconds(Game1.player.millisecondsPlayed), true);

            tagReg.SetTag("Health", () => Game1.player.health, true);
            tagReg.SetTag("HealthMax", () => Game1.player.maxHealth, true);
            tagReg.SetTag("HealthPercent", () => (double)Game1.player.health / Game1.player.maxHealth * 100, 2, true);
            tagReg.SetTag("Energy", () => Game1.player.Stamina.ToString(), true);
            tagReg.SetTag("EnergyMax", () => Game1.player.MaxStamina, true);
            tagReg.SetTag("EnergyPercent", () => (double)Game1.player.Stamina / Game1.player.MaxStamina * 100, 2, true);

            tagReg.SetTag("Time", () => Game1.getTimeOfDayString(Game1.timeOfDay), true);
            tagReg.SetTag("Date", () => Utility.getDateString(), true);
            tagReg.SetTag("Season", () => Utility.getSeasonNameFromNumber(Utility.getSeasonNumber(SDate.Now().Season)), true);
            tagReg.SetTag("DayOfWeek", () => Game1.shortDayDisplayNameFromDayOfSeason(SDate.Now().Day), true);

            tagReg.SetTag("Day", () => SDate.Now().Day, true);
            tagReg.SetTag("DayPad", () => $"{SDate.Now().Day:00}", true);
            tagReg.SetTag("DaySuffix", () => Utility.getNumberEnding(SDate.Now().Day), true);
            tagReg.SetTag("Year", () => SDate.Now().Year, true);
            tagReg.SetTag("YearSuffix", () => Utility.getNumberEnding(SDate.Now().Year), true);

            tagReg.SetTag("GameVerb", () =>
                          Context.IsMultiplayer && Context.IsMainPlayer ? "Hosting" : "Playing", true);
            tagReg.SetTag("GameNoun", () => Context.IsMultiplayer ? "Co-op" : "Solo", true);
            tagReg.SetTag("GameInfo", () => api.GetTag("GameVerb") + " " + api.GetTag("GameNoun"), true);
        }