From 27dece2cf445147c5e2848f9ec26f38a101f50fc Mon Sep 17 00:00:00 2001 From: Gormogon Date: Sun, 29 May 2016 18:23:01 -0400 Subject: Attempt to migrate to new directory structure. --- src/BobberBar Private Fields.txt | 40 + src/Game1 Static Fields.txt | 329 ++++ src/StardewModdingAPI.sln | 46 + src/StardewModdingAPI/App.config | 10 + src/StardewModdingAPI/Command.cs | 109 ++ src/StardewModdingAPI/Config.cs | 176 ++ src/StardewModdingAPI/Constants.cs | 67 + src/StardewModdingAPI/Entities/SCharacter.cs | 6 + src/StardewModdingAPI/Entities/SFarm.cs | 6 + src/StardewModdingAPI/Entities/SFarmAnimal.cs | 6 + src/StardewModdingAPI/Entities/SNpc.cs | 6 + src/StardewModdingAPI/Entities/SPlayer.cs | 33 + src/StardewModdingAPI/Events/Controls.cs | 58 + src/StardewModdingAPI/Events/EventArgs.cs | 272 +++ src/StardewModdingAPI/Events/Game.cs | 123 ++ src/StardewModdingAPI/Events/Graphics.cs | 162 ++ src/StardewModdingAPI/Events/Location.cs | 30 + src/StardewModdingAPI/Events/Menu.cs | 21 + src/StardewModdingAPI/Events/Mine.cs | 14 + src/StardewModdingAPI/Events/Player.cs | 35 + src/StardewModdingAPI/Events/Time.cs | 42 + src/StardewModdingAPI/Extensions.cs | 135 ++ src/StardewModdingAPI/FodyWeavers.xml | 5 + .../Inheritance/ItemStackChange.cs | 18 + .../Inheritance/Menus/SBobberBar.cs | 288 ++++ .../Inheritance/Menus/SGameMenu.cs | 48 + .../Inheritance/Menus/SInventoryPage.cs | 19 + .../Inheritance/Minigames/SMinigameBase.cs | 34 + src/StardewModdingAPI/Inheritance/SBareObject.cs | 20 + src/StardewModdingAPI/Inheritance/SGame.cs | 1726 ++++++++++++++++++++ src/StardewModdingAPI/Inheritance/SObject.cs | 277 ++++ src/StardewModdingAPI/JsonResolver.cs | 212 +++ src/StardewModdingAPI/Logger.cs | 326 ++++ src/StardewModdingAPI/Manifest.cs | 89 + src/StardewModdingAPI/Mod.cs | 51 + src/StardewModdingAPI/ModItem.cs | 15 + src/StardewModdingAPI/Program.cs | 451 +++++ src/StardewModdingAPI/Properties/AssemblyInfo.cs | 39 + src/StardewModdingAPI/StardewModdingAPI.csproj | 216 +++ src/StardewModdingAPI/Version.cs | 23 + src/StardewModdingAPI/icon.ico | Bin 0 -> 4286 bytes src/StardewModdingAPI/packages.config | 7 + src/StardewModdingAPI/steam_appid.txt | 1 + src/TrainerMod/FodyWeavers.xml | 5 + src/TrainerMod/Properties/AssemblyInfo.cs | 39 + src/TrainerMod/TrainerMod.cs | 768 +++++++++ src/TrainerMod/TrainerMod.csproj | 115 ++ src/TrainerMod/manifest.json | 6 + src/TrainerMod/packages.config | 5 + src/Vanilla Items List.txt | 541 ++++++ 50 files changed, 7070 insertions(+) create mode 100644 src/BobberBar Private Fields.txt create mode 100644 src/Game1 Static Fields.txt create mode 100644 src/StardewModdingAPI.sln create mode 100644 src/StardewModdingAPI/App.config create mode 100644 src/StardewModdingAPI/Command.cs create mode 100644 src/StardewModdingAPI/Config.cs create mode 100644 src/StardewModdingAPI/Constants.cs create mode 100644 src/StardewModdingAPI/Entities/SCharacter.cs create mode 100644 src/StardewModdingAPI/Entities/SFarm.cs create mode 100644 src/StardewModdingAPI/Entities/SFarmAnimal.cs create mode 100644 src/StardewModdingAPI/Entities/SNpc.cs create mode 100644 src/StardewModdingAPI/Entities/SPlayer.cs create mode 100644 src/StardewModdingAPI/Events/Controls.cs create mode 100644 src/StardewModdingAPI/Events/EventArgs.cs create mode 100644 src/StardewModdingAPI/Events/Game.cs create mode 100644 src/StardewModdingAPI/Events/Graphics.cs create mode 100644 src/StardewModdingAPI/Events/Location.cs create mode 100644 src/StardewModdingAPI/Events/Menu.cs create mode 100644 src/StardewModdingAPI/Events/Mine.cs create mode 100644 src/StardewModdingAPI/Events/Player.cs create mode 100644 src/StardewModdingAPI/Events/Time.cs create mode 100644 src/StardewModdingAPI/Extensions.cs create mode 100644 src/StardewModdingAPI/FodyWeavers.xml create mode 100644 src/StardewModdingAPI/Inheritance/ItemStackChange.cs create mode 100644 src/StardewModdingAPI/Inheritance/Menus/SBobberBar.cs create mode 100644 src/StardewModdingAPI/Inheritance/Menus/SGameMenu.cs create mode 100644 src/StardewModdingAPI/Inheritance/Menus/SInventoryPage.cs create mode 100644 src/StardewModdingAPI/Inheritance/Minigames/SMinigameBase.cs create mode 100644 src/StardewModdingAPI/Inheritance/SBareObject.cs create mode 100644 src/StardewModdingAPI/Inheritance/SGame.cs create mode 100644 src/StardewModdingAPI/Inheritance/SObject.cs create mode 100644 src/StardewModdingAPI/JsonResolver.cs create mode 100644 src/StardewModdingAPI/Logger.cs create mode 100644 src/StardewModdingAPI/Manifest.cs create mode 100644 src/StardewModdingAPI/Mod.cs create mode 100644 src/StardewModdingAPI/ModItem.cs create mode 100644 src/StardewModdingAPI/Program.cs create mode 100644 src/StardewModdingAPI/Properties/AssemblyInfo.cs create mode 100644 src/StardewModdingAPI/StardewModdingAPI.csproj create mode 100644 src/StardewModdingAPI/Version.cs create mode 100644 src/StardewModdingAPI/icon.ico create mode 100644 src/StardewModdingAPI/packages.config create mode 100644 src/StardewModdingAPI/steam_appid.txt create mode 100644 src/TrainerMod/FodyWeavers.xml create mode 100644 src/TrainerMod/Properties/AssemblyInfo.cs create mode 100644 src/TrainerMod/TrainerMod.cs create mode 100644 src/TrainerMod/TrainerMod.csproj create mode 100644 src/TrainerMod/manifest.json create mode 100644 src/TrainerMod/packages.config create mode 100644 src/Vanilla Items List.txt (limited to 'src') diff --git a/src/BobberBar Private Fields.txt b/src/BobberBar Private Fields.txt new file mode 100644 index 00000000..2784f0e7 --- /dev/null +++ b/src/BobberBar Private Fields.txt @@ -0,0 +1,40 @@ +Single difficulty +Int32 motionType +Int32 whichFish +Single bobberPosition +Single bobberSpeed +Single bobberAcceleration +Single bobberTargetPosition +Single scale +Single everythingShakeTimer +Single floaterSinkerAcceleration +Single treasurePosition +Single treasureCatchLevel +Single treasureAppearTimer +Single treasureScale +Boolean bobberInBar +Boolean buttonPressed +Boolean flipBubble +Boolean fadeIn +Boolean fadeOut +Boolean treasure +Boolean treasureCaught +Boolean perfect +Boolean bossFish +Int32 bobberBarHeight +Int32 fishSize +Int32 fishQuality +Int32 minFishSize +Int32 maxFishSize +Int32 fishSizeReductionTimer +Int32 whichBobber +Microsoft.Xna.Framework.Vector2 barShake +Microsoft.Xna.Framework.Vector2 fishShake +Microsoft.Xna.Framework.Vector2 everythingShake +Microsoft.Xna.Framework.Vector2 treasureShake +Single reelRotation +StardewValley.BellsAndWhistles.SparklingText sparkleText +Single bobberBarPos +Single bobberBarSpeed +Single bobberBarAcceleration +Single distanceFromCatching \ No newline at end of file diff --git a/src/Game1 Static Fields.txt b/src/Game1 Static Fields.txt new file mode 100644 index 00000000..4580f95f --- /dev/null +++ b/src/Game1 Static Fields.txt @@ -0,0 +1,329 @@ +Int32 pixelZoom +Int32 tileSize +System.String version +Microsoft.Xna.Framework.GraphicsDeviceManager graphics +Microsoft.Xna.Framework.Content.ContentManager content +Microsoft.Xna.Framework.Content.ContentManager temporaryContent +Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch +Microsoft.Xna.Framework.Input.GamePadState oldPadState +Single thumbStickSensitivity +Single runThreshold +Microsoft.Xna.Framework.Input.KeyboardState oldKBState +Microsoft.Xna.Framework.Input.MouseState oldMouseState +System.Collections.Generic.List`1[StardewValley.GameLocation] locations +StardewValley.GameLocation currentLocation +StardewValley.GameLocation locationAfterWarp +xTile.Display.IDisplayDevice mapDisplayDevice +StardewValley.Farmer player +StardewValley.Farmer serverHost +xTile.Dimensions.Rectangle viewport +Microsoft.Xna.Framework.Graphics.Texture2D objectSpriteSheet +Microsoft.Xna.Framework.Graphics.Texture2D toolSpriteSheet +Microsoft.Xna.Framework.Graphics.Texture2D cropSpriteSheet +Microsoft.Xna.Framework.Graphics.Texture2D mailboxTexture +Microsoft.Xna.Framework.Graphics.Texture2D emoteSpriteSheet +Microsoft.Xna.Framework.Graphics.Texture2D debrisSpriteSheet +Microsoft.Xna.Framework.Graphics.Texture2D toolIconBox +Microsoft.Xna.Framework.Graphics.Texture2D rainTexture +Microsoft.Xna.Framework.Graphics.Texture2D bigCraftableSpriteSheet +Microsoft.Xna.Framework.Graphics.Texture2D swordSwipe +Microsoft.Xna.Framework.Graphics.Texture2D swordSwipeDark +Microsoft.Xna.Framework.Graphics.Texture2D buffsIcons +Microsoft.Xna.Framework.Graphics.Texture2D daybg +Microsoft.Xna.Framework.Graphics.Texture2D nightbg +Microsoft.Xna.Framework.Graphics.Texture2D logoScreenTexture +Microsoft.Xna.Framework.Graphics.Texture2D tvStationTexture +Microsoft.Xna.Framework.Graphics.Texture2D cloud +Microsoft.Xna.Framework.Graphics.Texture2D menuTexture +Microsoft.Xna.Framework.Graphics.Texture2D lantern +Microsoft.Xna.Framework.Graphics.Texture2D windowLight +Microsoft.Xna.Framework.Graphics.Texture2D sconceLight +Microsoft.Xna.Framework.Graphics.Texture2D cauldronLight +Microsoft.Xna.Framework.Graphics.Texture2D shadowTexture +Microsoft.Xna.Framework.Graphics.Texture2D mouseCursors +Microsoft.Xna.Framework.Graphics.Texture2D indoorWindowLight +Microsoft.Xna.Framework.Graphics.Texture2D animations +Microsoft.Xna.Framework.Graphics.Texture2D titleScreenBG +Microsoft.Xna.Framework.Graphics.Texture2D logo +Microsoft.Xna.Framework.Graphics.RenderTarget2D lightmap +Microsoft.Xna.Framework.Graphics.Texture2D fadeToBlackRect +Microsoft.Xna.Framework.Graphics.Texture2D staminaRect +Microsoft.Xna.Framework.Graphics.Texture2D currentCoopTexture +Microsoft.Xna.Framework.Graphics.Texture2D currentBarnTexture +Microsoft.Xna.Framework.Graphics.Texture2D currentHouseTexture +Microsoft.Xna.Framework.Graphics.Texture2D greenhouseTexture +Microsoft.Xna.Framework.Graphics.Texture2D littleEffect +Microsoft.Xna.Framework.Graphics.SpriteFont dialogueFont +Microsoft.Xna.Framework.Graphics.SpriteFont smallFont +Microsoft.Xna.Framework.Graphics.SpriteFont borderFont +Microsoft.Xna.Framework.Graphics.SpriteFont tinyFont +Microsoft.Xna.Framework.Graphics.SpriteFont tinyFontBorder +Microsoft.Xna.Framework.Graphics.SpriteFont smoothFont +Single fadeToBlackAlpha +Single pickToolInterval +Single screenGlowAlpha +Single flashAlpha +Single starCropShimmerPause +Single noteBlockTimer +Single globalFadeSpeed +Boolean fadeToBlack +Boolean fadeIn +Boolean dialogueUp +Boolean dialogueTyping +Boolean pickingTool +Boolean isQuestion +Boolean nonWarpFade +Boolean particleRaining +Boolean newDay +Boolean inMine +Boolean isEating +Boolean menuUp +Boolean eventUp +Boolean viewportFreeze +Boolean eventOver +Boolean nameSelectUp +Boolean screenGlow +Boolean screenGlowHold +Boolean screenGlowUp +Boolean progressBar +Boolean isRaining +Boolean isSnowing +Boolean killScreen +Boolean coopDwellerBorn +Boolean messagePause +Boolean isDebrisWeather +Boolean boardingBus +Boolean listeningForKeyControlDefinitions +Boolean weddingToday +Boolean exitToTitle +Boolean debugMode +Boolean isLightning +Boolean displayHUD +Boolean displayFarmer +Boolean showKeyHelp +Boolean inputMode +Boolean shippingTax +Boolean dialogueButtonShrinking +Boolean jukeboxPlaying +Boolean drawLighting +Boolean bloomDay +Boolean quit +Boolean isChatting +Boolean globalFade +Boolean drawGrid +Boolean freezeControls +Boolean saveOnNewDay +Boolean panMode +Boolean showingEndOfNightStuff +Boolean wasRainingYesterday +Boolean hasLoadedGame +Boolean isActionAtCurrentCursorTile +Boolean isInspectionAtCurrentCursorTile +Boolean paused +Boolean lastCursorMotionWasMouse +System.String currentSeason +System.String debugOutput +System.String nextMusicTrack +System.String selectedItemsType +System.String nameSelectType +System.String messageAfterPause +System.String fertilizer +System.String samBandName +System.String elliottBookName +System.String slotResult +System.String keyHelpString +System.String debugInput +System.String loadingMessage +System.String errorMessage +System.Collections.Generic.Queue`1[System.String] currentObjectDialogue +System.Collections.Generic.Queue`1[System.String] mailbox +System.Collections.Generic.List`1[System.String] questionChoices +Int32 xLocationAfterWarp +Int32 yLocationAfterWarp +Int32 gameTimeInterval +Int32 currentQuestionChoice +Int32 currentDialogueCharacterIndex +Int32 dialogueTypingInterval +Int32 dayOfMonth +Int32 year +Int32 timeOfDay +Int32 numberOfSelectedItems +Int32 priceOfSelectedItem +Int32 currentWallpaper +Int32 farmerWallpaper +Int32 wallpaperPrice +Int32 currentFloor +Int32 FarmerFloor +Int32 floorPrice +Int32 dialogueWidth +Int32 countdownToWedding +Int32 menuChoice +Int32 tvStation +Int32 currentBillboard +Int32 facingDirectionAfterWarp +Int32 tmpTimeOfDay +Int32 percentageToWinStardewHero +Int32 mouseClickPolling +Int32 weatherIcon +Int32 hitShakeTimer +Int32 staminaShakeTimer +Int32 pauseThenDoFunctionTimer +Int32 weatherForTomorrow +Int32 currentSongIndex +Int32 cursorTileHintCheckTimer +Int32 timerUntilMouseFade +Int32 minecartHighScore +System.Collections.Generic.List`1[System.Int32] dealerCalicoJackTotal +Microsoft.Xna.Framework.Color morningColor +Microsoft.Xna.Framework.Color eveningColor +Microsoft.Xna.Framework.Color unselectedOptionColor +Microsoft.Xna.Framework.Color screenGlowColor +StardewValley.NPC currentSpeaker +System.Random random +System.Random recentMultiplayerRandom +System.Collections.Generic.Dictionary`2[System.Int32,System.String] objectInformation +System.Collections.Generic.Dictionary`2[System.Int32,System.String] bigCraftablesInformation +System.Collections.Generic.List`1[StardewValley.Object] shippingBin +StardewValley.Locations.MineShaft mine +System.Collections.Generic.List`1[StardewValley.HUDMessage] hudMessages +System.Collections.Generic.Dictionary`2[System.String,System.Boolean] eventConditions +System.Collections.Generic.Dictionary`2[System.String,System.String] NPCGiftTastes +Single musicPlayerVolume +Single pauseAccumulator +Single pauseTime +Single upPolling +Single downPolling +Single rightPolling +Single leftPolling +Single debrisSoundInterval +Single toolHold +Single windGust +Single dialogueButtonScale +Single creditsTimer +Single globalOutdoorLighting +Microsoft.Xna.Framework.Audio.Cue currentSong +Microsoft.Xna.Framework.Audio.AudioCategory musicCategory +Microsoft.Xna.Framework.Audio.AudioCategory soundCategory +Microsoft.Xna.Framework.PlayerIndex playerOneIndex +Microsoft.Xna.Framework.Audio.AudioEngine audioEngine +Microsoft.Xna.Framework.Audio.WaveBank waveBank +Microsoft.Xna.Framework.Audio.SoundBank soundBank +Microsoft.Xna.Framework.Vector2 shiny +Microsoft.Xna.Framework.Vector2 previousViewportPosition +Microsoft.Xna.Framework.Vector2 currentCursorTile +Microsoft.Xna.Framework.Vector2 lastCursorTile +StardewValley.RainDrop[] rainDrops +Double chanceToRainTomorrow +Microsoft.Xna.Framework.Audio.Cue fuseSound +Microsoft.Xna.Framework.Audio.Cue chargeUpSound +Microsoft.Xna.Framework.Audio.Cue wind +Double dailyLuck +System.Collections.Generic.List`1[StardewValley.WeatherDebris] debrisWeather +System.Collections.Generic.List`1[StardewValley.TemporaryAnimatedSprite] screenOverlayTempSprites +Byte gameMode +Byte multiplayerMode +System.Collections.Generic.IEnumerator`1[System.Int32] currentLoader +UInt64 uniqueIDForThisGame +StardewValley.LoadGameScreen loadGameScreen +StardewValley.Stats stats +Int32[] cropsOfTheWeek +StardewValley.Quests.Quest questOfTheDay +StardewValley.MoneyMadeScreen moneyMadeScreen +System.Collections.Generic.HashSet`1[StardewValley.LightSource] currentLightSources +Microsoft.Xna.Framework.Color ambientLight +Microsoft.Xna.Framework.Color outdoorLight +Microsoft.Xna.Framework.Color textColor +Microsoft.Xna.Framework.Color textShadowColor +StardewValley.Menus.IClickableMenu activeClickableMenu +StardewValley.Minigames.IMinigame currentMinigame +System.Collections.Generic.List`1[StardewValley.Menus.IClickableMenu] onScreenMenus +StardewValley.BloomComponent bloom +System.Collections.Generic.Dictionary`2[System.Int32,System.String] achievements +StardewValley.Object dishOfTheDay +StardewValley.Menus.BuffsDisplay buffsDisplay +StardewValley.Menus.DayTimeMoneyBox dayTimeMoneyBox +System.Collections.Generic.Dictionary`2[System.Int64,StardewValley.Farmer] otherFarmers +StardewValley.Network.Server server +StardewValley.Network.Client client +StardewValley.KeyboardDispatcher keyboardDispatcher +StardewValley.Background background +StardewValley.Events.FarmEvent farmEvent +afterFadeFunction afterFade +afterFadeFunction afterDialogues +afterFadeFunction afterViewport +afterFadeFunction viewportReachedTarget +afterFadeFunction afterPause +Microsoft.Xna.Framework.GameTime currentGameTime +System.Collections.Generic.List`1[StardewValley.DelayedAction] delayedActions +System.Collections.Generic.Stack`1[StardewValley.Menus.IClickableMenu] endOfNightMenus +StardewValley.Options options +StardewValley.Game1 game1 +Microsoft.Xna.Framework.Point lastMousePositionBeforeFade +Microsoft.Xna.Framework.Point viewportCenter +Microsoft.Xna.Framework.Vector2 viewportTarget +Single viewportSpeed +Int32 viewportHold +Boolean toggleFullScreen +Boolean isFullscreen +Boolean setToWindowedMode +Boolean setToFullscreen +System.String whereIsTodaysFest +Boolean farmerShouldPassOut +Microsoft.Xna.Framework.Vector2 currentViewportTarget +Microsoft.Xna.Framework.Vector2 viewportPositionLerp +Single screenGlowRate +Single screenGlowMax +Boolean haltAfterCheck +Int32 mouseCursor +Single mouseCursorTransparency +StardewValley.NPC objectDialoguePortraitPerson +Int32 defaultResolutionX +Int32 defaultResolutionY +Int32 smallestTileSize +Int32 up +Int32 right +Int32 down +Int32 left +Int32 spriteIndexForOveralls +Int32 colorToleranceForOveralls +Int32 spriteIndexForOverallsBorder +Int32 colorToloranceForOverallsBorder +Int32 dialogueBoxTileHeight +Int32 realMilliSecondsPerGameTenMinutes +Int32 rainDensity +Int32 millisecondsPerDialogueLetterType +Single pickToolDelay +Int32 defaultMinFishingBiteTime +Int32 defaultMaxFishingBiteTime +Int32 defaultMinFishingNibbleTime +Int32 defaultMaxFishingNibbleTime +Int32 minWallpaperPrice +Int32 maxWallpaperPrice +Int32 rainLoopLength +Int32 weather_sunny +Int32 weather_rain +Int32 weather_debris +Int32 weather_lightning +Int32 weather_festival +Int32 weather_snow +Int32 weather_wedding +Byte singlePlayer +Byte multiplayerClient +Byte multiplayerServer +Byte logoScreenGameMode +Byte titleScreenGameMode +Byte loadScreenGameMode +Byte newGameMode +Byte playingGameMode +Byte characterSelectMode +Byte loadingMode +Byte saveMode +Byte saveCompleteMode +Byte selectGameScreen +Byte creditsMode +Byte errorLogMode +Single keyPollingThreshold +Single toolHoldPerPowerupLevel +Single startingMusicVolume +Single thumbstickToMouseModifier +System.String NO_LETTER_MAIL \ No newline at end of file diff --git a/src/StardewModdingAPI.sln b/src/StardewModdingAPI.sln new file mode 100644 index 00000000..087d0b36 --- /dev/null +++ b/src/StardewModdingAPI.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25123.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrainerMod", "TrainerMod\TrainerMod.csproj", "{28480467-1A48-46A7-99F8-236D95225359}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StardewModdingAPI", "StardewModdingAPI\StardewModdingAPI.csproj", "{F1A573B0-F436-472C-AE29-0B91EA6B9F8F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {28480467-1A48-46A7-99F8-236D95225359}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {28480467-1A48-46A7-99F8-236D95225359}.Debug|Any CPU.Build.0 = Debug|Any CPU + {28480467-1A48-46A7-99F8-236D95225359}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {28480467-1A48-46A7-99F8-236D95225359}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {28480467-1A48-46A7-99F8-236D95225359}.Debug|x86.ActiveCfg = Debug|Any CPU + {28480467-1A48-46A7-99F8-236D95225359}.Release|Any CPU.ActiveCfg = Release|Any CPU + {28480467-1A48-46A7-99F8-236D95225359}.Release|Any CPU.Build.0 = Release|Any CPU + {28480467-1A48-46A7-99F8-236D95225359}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {28480467-1A48-46A7-99F8-236D95225359}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {28480467-1A48-46A7-99F8-236D95225359}.Release|x86.ActiveCfg = Release|Any CPU + {F1A573B0-F436-472C-AE29-0B91EA6B9F8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1A573B0-F436-472C-AE29-0B91EA6B9F8F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1A573B0-F436-472C-AE29-0B91EA6B9F8F}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {F1A573B0-F436-472C-AE29-0B91EA6B9F8F}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {F1A573B0-F436-472C-AE29-0B91EA6B9F8F}.Debug|x86.ActiveCfg = Debug|x86 + {F1A573B0-F436-472C-AE29-0B91EA6B9F8F}.Debug|x86.Build.0 = Debug|x86 + {F1A573B0-F436-472C-AE29-0B91EA6B9F8F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1A573B0-F436-472C-AE29-0B91EA6B9F8F}.Release|Any CPU.Build.0 = Release|Any CPU + {F1A573B0-F436-472C-AE29-0B91EA6B9F8F}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {F1A573B0-F436-472C-AE29-0B91EA6B9F8F}.Release|Mixed Platforms.Build.0 = Release|x86 + {F1A573B0-F436-472C-AE29-0B91EA6B9F8F}.Release|x86.ActiveCfg = Release|x86 + {F1A573B0-F436-472C-AE29-0B91EA6B9F8F}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/StardewModdingAPI/App.config b/src/StardewModdingAPI/App.config new file mode 100644 index 00000000..6664f1ed --- /dev/null +++ b/src/StardewModdingAPI/App.config @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/StardewModdingAPI/Command.cs b/src/StardewModdingAPI/Command.cs new file mode 100644 index 00000000..4214b1a7 --- /dev/null +++ b/src/StardewModdingAPI/Command.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using StardewModdingAPI.Events; + +namespace StardewModdingAPI +{ + public class Command + { + internal static List RegisteredCommands = new List(); + public string[] CalledArgs; + public string[] CommandArgs; + public string CommandDesc; + + public string CommandName; + + /// + /// Creates a Command from a Name, Description, and Arguments + /// + /// Name + /// Description + /// Arguments + public Command(string cname, string cdesc, string[] args = null) + { + CommandName = cname; + CommandDesc = cdesc; + if (args == null) + args = new string[0]; + CommandArgs = args; + } + + public event EventHandler CommandFired; + + /// + /// Calls the specified command. (It runs the command) + /// + /// The command to run + public static void CallCommand(string input) + { + input = input.TrimEnd(' '); + var args = new string[0]; + Command fnd; + if (input.Contains(" ")) + { + args = input.Split(new[] {" "}, 2, StringSplitOptions.RemoveEmptyEntries); + fnd = FindCommand(args[0]); + args = args[1].Split(new[] {" "}, StringSplitOptions.RemoveEmptyEntries); + } + else + { + fnd = FindCommand(input); + } + + if (fnd != null) + { + fnd.CalledArgs = args; + fnd.Fire(); + } + else + { + Log.AsyncR("Unknown Command"); + } + } + + /// + /// Registers a command to the list of commands properly + /// + /// Name of the command to register + /// Description + /// Arguments (these are purely for viewing so that a user can see what an argument needs to be) + /// + public static Command RegisterCommand(string command, string cdesc, string[] args = null) + { + var c = new Command(command, cdesc, args); + if (RegisteredCommands.Contains(c)) + { + Log.AsyncR($"Command already registered! [{c.CommandName}]"); + return RegisteredCommands.Find(x => x.Equals(c)); + } + + RegisteredCommands.Add(c); + Log.Async("Registered command: " + command); + + return c; + } + + /// + /// Looks up a command in the list of registered commands. Returns null if it doesn't exist (I think) + /// + /// Name of command to find + /// + public static Command FindCommand(string name) + { + return RegisteredCommands.Find(x => x.CommandName.Equals(name)); + } + + /// + /// Runs a command. Fires it. Calls it. Any of those. + /// + public void Fire() + { + if (CommandFired == null) + { + Log.AsyncR("Command failed to fire because it's fire event is null: " + CommandName); + return; + } + CommandFired.Invoke(this, new EventArgsCommand(this)); + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Config.cs b/src/StardewModdingAPI/Config.cs new file mode 100644 index 00000000..c5b7beca --- /dev/null +++ b/src/StardewModdingAPI/Config.cs @@ -0,0 +1,176 @@ +/* + Copyright 2016 Zoey (Zoryn) +*/ + +using System; +using System.IO; +using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace StardewModdingAPI +{ + public class Config + { + [JsonIgnore] + public virtual string ConfigLocation { get; protected internal set; } + + [JsonIgnore] + public virtual string ConfigDir => Path.GetDirectoryName(ConfigLocation); + + public virtual Config Instance() where T : Config => Activator.CreateInstance(); + + /// + /// Loads the config from the json blob on disk, updating and re-writing to the disk if needed. + /// + /// + /// + public virtual T LoadConfig() where T : Config + { + if (string.IsNullOrEmpty(ConfigLocation)) + { + Log.AsyncR("A config tried to load without specifying a location on the disk."); + return null; + } + + T ret = null; + + if (!File.Exists(ConfigLocation)) + { + //no config exists, generate default values + var c = GenerateDefaultConfig(); + c.ConfigLocation = ConfigLocation; + ret = c; + } + else + { + try + { + //try to load the config from a json blob on disk + var c = JsonConvert.DeserializeObject(File.ReadAllText(ConfigLocation), new JsonSerializerSettings {ContractResolver = new JsonResolver()}); + + c.ConfigLocation = ConfigLocation; + + //update the config with default values if needed + ret = c.UpdateConfig(); + + c = null; + } + catch (Exception ex) + { + Log.AsyncR($"Invalid JSON ({GetType().Name}): {ConfigLocation} \n{ex}"); + return GenerateDefaultConfig(); + } + } + + ret.WriteConfig(); + return ret; + } + + /// + /// MUST be implemented in inheriting class! + /// + public virtual T GenerateDefaultConfig() where T : Config + { + return null; + } + + /// + /// Merges a default-value config with the user-config on disk. + /// + /// + /// + public virtual T UpdateConfig() where T : Config + { + try + { + //default config + var b = JObject.FromObject(Instance().GenerateDefaultConfig(), new JsonSerializer {ContractResolver = new JsonResolver()}); + + //user config + var u = JObject.FromObject(this, new JsonSerializer {ContractResolver = new JsonResolver()}); + + //overwrite default values with user values + b.Merge(u, new JsonMergeSettings {MergeArrayHandling = MergeArrayHandling.Replace}); + + //cast json object to config + var c = b.ToObject(); + + //re-write the location on disk to the object + c.ConfigLocation = ConfigLocation; + + return c; + } + catch (Exception ex) + { + Log.AsyncR("An error occured when updating a config: " + ex); + return this as T; + } + } + } + + public static class ConfigExtensions + { + /// + /// Initializes an instance of any class that inherits from Config. + /// This method performs the loading, saving, and merging of the config on the disk and in memory at a default state. + /// This method should not be used to re-load or to re-save a config. + /// NOTE: You MUST set your config EQUAL to the return of this method! + /// + /// + /// + /// + /// + public static T InitializeConfig(this T baseConfig, string configLocation) where T : Config + { + if (baseConfig == null) + { + baseConfig = Activator.CreateInstance(); + /* + Log.AsyncR("A config tried to initialize whilst being null."); + return null; + */ + } + + if (string.IsNullOrEmpty(configLocation)) + { + Log.AsyncR("A config tried to initialize without specifying a location on the disk."); + return null; + } + + baseConfig.ConfigLocation = configLocation; + var c = baseConfig.LoadConfig(); + + return c; + } + + /// + /// Writes a config to a json blob on the disk specified in the config's properties. + /// + public static void WriteConfig(this T baseConfig) where T : Config + { + if (string.IsNullOrEmpty(baseConfig?.ConfigLocation) || string.IsNullOrEmpty(baseConfig.ConfigDir)) + { + Log.AsyncR("A config attempted to save when it itself or it's location were null."); + return; + } + + var s = JsonConvert.SerializeObject(baseConfig, typeof(T), Formatting.Indented, new JsonSerializerSettings {ContractResolver = new JsonResolver()}); + + if (!Directory.Exists(baseConfig.ConfigDir)) + Directory.CreateDirectory(baseConfig.ConfigDir); + + if (!File.Exists(baseConfig.ConfigLocation) || !File.ReadAllText(baseConfig.ConfigLocation).SequenceEqual(s)) + File.WriteAllText(baseConfig.ConfigLocation, s); + } + + /// + /// Re-reads the json blob on the disk and merges its values with a default config. + /// NOTE: You MUST set your config EQUAL to the return of this method! + /// + public static T ReloadConfig(this T baseConfig) where T : Config + { + return baseConfig.LoadConfig(); + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs new file mode 100644 index 00000000..0dd387b3 --- /dev/null +++ b/src/StardewModdingAPI/Constants.cs @@ -0,0 +1,67 @@ +using System; +using System.IO; +using System.Reflection; +using StardewValley; + +namespace StardewModdingAPI +{ + /// + /// Static class containing readonly values. + /// + public static class Constants + { + public static readonly Version Version = new Version(0, 40, 0, "Alpha"); + + /// + /// Not quite "constant", but it makes more sense for it to be here, at least for now + /// + public static int ModsLoaded = 0; + + /// + /// Stardew Valley's roaming app data location. + /// %AppData%//StardewValley + /// + public static string DataPath => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StardewValley"); + + public static string SavesPath => Path.Combine(DataPath, "Saves"); + + private static string saveFolderName => PlayerNull ? string.Empty : Game1.player.name.RemoveNumerics() + "_" + Game1.uniqueIDForThisGame; + public static string SaveFolderName => CurrentSavePathExists ? saveFolderName : ""; + + private static string currentSavePath => PlayerNull ? string.Empty : Path.Combine(SavesPath, saveFolderName); + public static string CurrentSavePath => CurrentSavePathExists ? currentSavePath : ""; + + public static bool CurrentSavePathExists => Directory.Exists(currentSavePath); + + public static bool PlayerNull => !Game1.hasLoadedGame || Game1.player == null || string.IsNullOrEmpty(Game1.player.name); + + /// + /// Execution path to execute the code. + /// + public static string ExecutionPath => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + + /// + /// Title for the API console + /// + public static string ConsoleTitle => $"Stardew Modding API Console - Version {Version.VersionString} - Mods Loaded: {ModsLoaded}"; + + /// + /// Path for log files to be output to. + /// %LocalAppData%//StardewValley//ErrorLogs + /// + public static string LogDir => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StardewValley", "ErrorLogs"); + + public static string LogPath => Path.Combine(LogDir, "MODDED_ProgramLog.Log_LATEST.txt"); + + /// + /// Whether or not to enable the Render Target drawing code offered by ClxS + /// Do not mark as 'const' or else 'if' checks will complain that the expression is always true in ReSharper + /// + public static bool EnableDrawingIntoRenderTarget => true; + + /// + /// Completely overrides the base game's draw call to the one is SGame + /// + public static bool EnableCompletelyOverridingBaseCalls => true; + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Entities/SCharacter.cs b/src/StardewModdingAPI/Entities/SCharacter.cs new file mode 100644 index 00000000..2d941d55 --- /dev/null +++ b/src/StardewModdingAPI/Entities/SCharacter.cs @@ -0,0 +1,6 @@ +namespace StardewModdingAPI.Entities +{ + internal class SCharacter + { + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Entities/SFarm.cs b/src/StardewModdingAPI/Entities/SFarm.cs new file mode 100644 index 00000000..c6c64681 --- /dev/null +++ b/src/StardewModdingAPI/Entities/SFarm.cs @@ -0,0 +1,6 @@ +namespace StardewModdingAPI.Entities +{ + internal class SFarm + { + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Entities/SFarmAnimal.cs b/src/StardewModdingAPI/Entities/SFarmAnimal.cs new file mode 100644 index 00000000..fb8ee267 --- /dev/null +++ b/src/StardewModdingAPI/Entities/SFarmAnimal.cs @@ -0,0 +1,6 @@ +namespace StardewModdingAPI.Entities +{ + internal class SFarmAnimal + { + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Entities/SNpc.cs b/src/StardewModdingAPI/Entities/SNpc.cs new file mode 100644 index 00000000..727dcff7 --- /dev/null +++ b/src/StardewModdingAPI/Entities/SNpc.cs @@ -0,0 +1,6 @@ +namespace StardewModdingAPI.Entities +{ + internal class SNpc + { + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Entities/SPlayer.cs b/src/StardewModdingAPI/Entities/SPlayer.cs new file mode 100644 index 00000000..d464cded --- /dev/null +++ b/src/StardewModdingAPI/Entities/SPlayer.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using StardewValley; + +namespace StardewModdingAPI.Entities +{ + /// + /// Static class for intergrating with the player + /// + public class SPlayer + { + /// + /// Calls 'getAllFarmers' in Game1 + /// + public static List AllFarmers => Game1.getAllFarmers(); + + /// + /// Do not use. + /// + [Obsolete("Use 'Player' instead.")] + public static Farmer CurrentFarmer => Game1.player; + + /// + /// Gets the current player from Game1 + /// + public static Farmer Player => Game1.player; + + /// + /// Gets the player's current location from Game1 + /// + public static GameLocation CurrentFarmerLocation => Player.currentLocation; + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Events/Controls.cs b/src/StardewModdingAPI/Events/Controls.cs new file mode 100644 index 00000000..6415561a --- /dev/null +++ b/src/StardewModdingAPI/Events/Controls.cs @@ -0,0 +1,58 @@ +using System; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; + +namespace StardewModdingAPI.Events +{ + public static class ControlEvents + { + public static event EventHandler KeyboardChanged = delegate { }; + public static event EventHandler KeyPressed = delegate { }; + public static event EventHandler KeyReleased = delegate { }; + public static event EventHandler MouseChanged = delegate { }; + public static event EventHandler ControllerButtonPressed = delegate { }; + public static event EventHandler ControllerButtonReleased = delegate { }; + public static event EventHandler ControllerTriggerPressed = delegate { }; + public static event EventHandler ControllerTriggerReleased = delegate { }; + + internal static void InvokeKeyboardChanged(KeyboardState priorState, KeyboardState newState) + { + KeyboardChanged.Invoke(null, new EventArgsKeyboardStateChanged(priorState, newState)); + } + + internal static void InvokeMouseChanged(MouseState priorState, MouseState newState) + { + MouseChanged.Invoke(null, new EventArgsMouseStateChanged(priorState, newState)); + } + + internal static void InvokeKeyPressed(Keys key) + { + KeyPressed.Invoke(null, new EventArgsKeyPressed(key)); + } + + internal static void InvokeKeyReleased(Keys key) + { + KeyReleased.Invoke(null, new EventArgsKeyPressed(key)); + } + + internal static void InvokeButtonPressed(PlayerIndex playerIndex, Buttons buttons) + { + ControllerButtonPressed.Invoke(null, new EventArgsControllerButtonPressed(playerIndex, buttons)); + } + + internal static void InvokeButtonReleased(PlayerIndex playerIndex, Buttons buttons) + { + ControllerButtonReleased.Invoke(null, new EventArgsControllerButtonReleased(playerIndex, buttons)); + } + + internal static void InvokeTriggerPressed(PlayerIndex playerIndex, Buttons buttons, float value) + { + ControllerTriggerPressed.Invoke(null, new EventArgsControllerTriggerPressed(playerIndex, buttons, value)); + } + + internal static void InvokeTriggerReleased(PlayerIndex playerIndex, Buttons buttons, float value) + { + ControllerTriggerReleased.Invoke(null, new EventArgsControllerTriggerReleased(playerIndex, buttons, value)); + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Events/EventArgs.cs b/src/StardewModdingAPI/Events/EventArgs.cs new file mode 100644 index 00000000..2bce964e --- /dev/null +++ b/src/StardewModdingAPI/Events/EventArgs.cs @@ -0,0 +1,272 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; +using StardewModdingAPI.Inheritance; +using StardewValley; +using StardewValley.Menus; +using Object = StardewValley.Object; + +namespace StardewModdingAPI.Events +{ + public class EventArgsKeyboardStateChanged : EventArgs + { + public EventArgsKeyboardStateChanged(KeyboardState priorState, KeyboardState newState) + { + NewState = newState; + NewState = newState; + } + + public KeyboardState NewState { get; private set; } + public KeyboardState PriorState { get; private set; } + } + + public class EventArgsKeyPressed : EventArgs + { + public EventArgsKeyPressed(Keys keyPressed) + { + KeyPressed = keyPressed; + } + + public Keys KeyPressed { get; private set; } + } + + public class EventArgsControllerButtonPressed : EventArgs + { + public EventArgsControllerButtonPressed(PlayerIndex playerIndex, Buttons buttonPressed) + { + PlayerIndex = playerIndex; + ButtonPressed = buttonPressed; + } + + public PlayerIndex PlayerIndex { get; private set; } + public Buttons ButtonPressed { get; private set; } + } + + public class EventArgsControllerButtonReleased : EventArgs + { + public EventArgsControllerButtonReleased(PlayerIndex playerIndex, Buttons buttonReleased) + { + PlayerIndex = playerIndex; + ButtonReleased = buttonReleased; + } + + public PlayerIndex PlayerIndex { get; private set; } + public Buttons ButtonReleased { get; private set; } + } + + public class EventArgsControllerTriggerPressed : EventArgs + { + public EventArgsControllerTriggerPressed(PlayerIndex playerIndex, Buttons buttonPressed, float value) + { + PlayerIndex = playerIndex; + ButtonPressed = buttonPressed; + Value = value; + } + + public PlayerIndex PlayerIndex { get; private set; } + public Buttons ButtonPressed { get; private set; } + public float Value { get; private set; } + } + + public class EventArgsControllerTriggerReleased : EventArgs + { + public EventArgsControllerTriggerReleased(PlayerIndex playerIndex, Buttons buttonReleased, float value) + { + PlayerIndex = playerIndex; + ButtonReleased = buttonReleased; + Value = value; + } + + public PlayerIndex PlayerIndex { get; private set; } + public Buttons ButtonReleased { get; private set; } + public float Value { get; private set; } + } + + public class EventArgsMouseStateChanged : EventArgs + { + public EventArgsMouseStateChanged(MouseState priorState, MouseState newState) + { + NewState = newState; + NewState = newState; + } + + public MouseState NewState { get; private set; } + public MouseState PriorState { get; private set; } + } + + public class EventArgsClickableMenuChanged : EventArgs + { + public EventArgsClickableMenuChanged(IClickableMenu priorMenu, IClickableMenu newMenu) + { + NewMenu = newMenu; + PriorMenu = priorMenu; + } + + public IClickableMenu NewMenu { get; private set; } + public IClickableMenu PriorMenu { get; private set; } + } + + public class EventArgsClickableMenuClosed : EventArgs + { + public EventArgsClickableMenuClosed(IClickableMenu priorMenu) + { + PriorMenu = priorMenu; + } + + public IClickableMenu PriorMenu { get; private set; } + } + + public class EventArgsGameLocationsChanged : EventArgs + { + public EventArgsGameLocationsChanged(List newLocations) + { + NewLocations = newLocations; + } + + public List NewLocations { get; private set; } + } + + public class EventArgsMineLevelChanged : EventArgs + { + public EventArgsMineLevelChanged(int previousMineLevel, int currentMineLevel) + { + PreviousMineLevel = previousMineLevel; + CurrentMineLevel = currentMineLevel; + } + + public int PreviousMineLevel { get; private set; } + public int CurrentMineLevel { get; private set; } + } + + public class EventArgsLocationObjectsChanged : EventArgs + { + public EventArgsLocationObjectsChanged(SerializableDictionary newObjects) + { + NewObjects = newObjects; + } + + public SerializableDictionary NewObjects { get; private set; } + } + + public class EventArgsCurrentLocationChanged : EventArgs + { + public EventArgsCurrentLocationChanged(GameLocation priorLocation, GameLocation newLocation) + { + NewLocation = newLocation; + PriorLocation = priorLocation; + } + + public GameLocation NewLocation { get; private set; } + public GameLocation PriorLocation { get; private set; } + } + + public class EventArgsFarmerChanged : EventArgs + { + public EventArgsFarmerChanged(Farmer priorFarmer, Farmer newFarmer) + { + NewFarmer = NewFarmer; + PriorFarmer = PriorFarmer; + } + + public Farmer NewFarmer { get; } + public Farmer PriorFarmer { get; } + } + + public class EventArgsInventoryChanged : EventArgs + { + public EventArgsInventoryChanged(List inventory, List changedItems) + { + Inventory = inventory; + Added = changedItems.Where(n => n.ChangeType == ChangeType.Added).ToList(); + Removed = changedItems.Where(n => n.ChangeType == ChangeType.Removed).ToList(); + QuantityChanged = changedItems.Where(n => n.ChangeType == ChangeType.StackChange).ToList(); + } + + public List Inventory { get; private set; } + public List Added { get; private set; } + public List Removed { get; private set; } + public List QuantityChanged { get; private set; } + } + + public class EventArgsLevelUp : EventArgs + { + public enum LevelType + { + Combat, + Farming, + Fishing, + Foraging, + Mining, + Luck + } + + public EventArgsLevelUp(LevelType type, int newLevel) + { + Type = type; + NewLevel = newLevel; + } + + public LevelType Type { get; private set; } + public int NewLevel { get; private set; } + } + + public class EventArgsIntChanged : EventArgs + { + public EventArgsIntChanged(int priorInt, int newInt) + { + NewInt = NewInt; + PriorInt = PriorInt; + } + + public int NewInt { get; } + public int PriorInt { get; } + } + + public class EventArgsStringChanged : EventArgs + { + public EventArgsStringChanged(string priorString, string newString) + { + NewString = newString; + PriorString = priorString; + } + + public string NewString { get; private set; } + public string PriorString { get; private set; } + } + + public class EventArgsLoadedGameChanged : EventArgs + { + public EventArgsLoadedGameChanged(bool loadedGame) + { + LoadedGame = loadedGame; + } + + public bool LoadedGame { get; private set; } + } + + public class EventArgsNewDay : EventArgs + { + public EventArgsNewDay(int prevDay, int curDay, bool newDay) + { + PreviousDay = prevDay; + CurrentDay = curDay; + IsNewDay = newDay; + } + + public int PreviousDay { get; private set; } + public int CurrentDay { get; private set; } + public bool IsNewDay { get; private set; } + } + + public class EventArgsCommand : EventArgs + { + public EventArgsCommand(Command command) + { + Command = command; + } + + public Command Command { get; private set; } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Events/Game.cs b/src/StardewModdingAPI/Events/Game.cs new file mode 100644 index 00000000..8b8042ed --- /dev/null +++ b/src/StardewModdingAPI/Events/Game.cs @@ -0,0 +1,123 @@ +using System; + +namespace StardewModdingAPI.Events +{ + public static class GameEvents + { + public static event EventHandler GameLoaded = delegate { }; + public static event EventHandler Initialize = delegate { }; + public static event EventHandler LoadContent = delegate { }; + public static event EventHandler FirstUpdateTick = delegate { }; + + /// + /// Fires every update (1/60 of a second) + /// + public static event EventHandler UpdateTick = delegate { }; + + /// + /// Fires every other update (1/30 of a second) + /// + public static event EventHandler SecondUpdateTick = delegate { }; + + /// + /// Fires every fourth update (1/15 of a second) + /// + public static event EventHandler FourthUpdateTick = delegate { }; + + /// + /// Fires every eighth update (roughly 1/8 of a second) + /// + public static event EventHandler EighthUpdateTick = delegate { }; + + /// + /// Fires every fifthteenth update (1/4 of a second) + /// + public static event EventHandler QuarterSecondTick = delegate { }; + + /// + /// Fires every thirtieth update (1/2 of a second) + /// + public static event EventHandler HalfSecondTick = delegate { }; + + /// + /// Fires every sixtieth update (a second) + /// + public static event EventHandler OneSecondTick = delegate { }; + + internal static void InvokeGameLoaded() + { + GameLoaded.Invoke(null, EventArgs.Empty); + } + + internal static void InvokeInitialize() + { + try + { + Initialize.Invoke(null, EventArgs.Empty); + } + catch (Exception ex) + { + Log.AsyncR("An exception occured in XNA Initialize: " + ex); + } + } + + internal static void InvokeLoadContent() + { + try + { + LoadContent.Invoke(null, EventArgs.Empty); + } + catch (Exception ex) + { + Log.AsyncR("An exception occured in XNA LoadContent: " + ex); + } + } + + internal static void InvokeUpdateTick() + { + try + { + UpdateTick.Invoke(null, EventArgs.Empty); + } + catch (Exception ex) + { + Log.AsyncR("An exception occured in XNA UpdateTick: " + ex); + } + } + + internal static void InvokeSecondUpdateTick() + { + SecondUpdateTick.Invoke(null, EventArgs.Empty); + } + + internal static void InvokeFourthUpdateTick() + { + FourthUpdateTick.Invoke(null, EventArgs.Empty); + } + + internal static void InvokeEighthUpdateTick() + { + EighthUpdateTick.Invoke(null, EventArgs.Empty); + } + + internal static void InvokeQuarterSecondTick() + { + QuarterSecondTick.Invoke(null, EventArgs.Empty); + } + + internal static void InvokeHalfSecondTick() + { + HalfSecondTick.Invoke(null, EventArgs.Empty); + } + + internal static void InvokeOneSecondTick() + { + OneSecondTick.Invoke(null, EventArgs.Empty); + } + + internal static void InvokeFirstUpdateTick() + { + FirstUpdateTick.Invoke(null, EventArgs.Empty); + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Events/Graphics.cs b/src/StardewModdingAPI/Events/Graphics.cs new file mode 100644 index 00000000..331d3e3a --- /dev/null +++ b/src/StardewModdingAPI/Events/Graphics.cs @@ -0,0 +1,162 @@ +using System; + +namespace StardewModdingAPI.Events +{ + /// + /// + /// + public static class GraphicsEvents + { + /// + /// Occurs when the form (game) is resized. + /// + public static event EventHandler Resize = delegate { }; + + /// + /// Occurs before anything is drawn. + /// + public static event EventHandler OnPreRenderEvent = delegate { }; + + /// + /// Occurs before the GUI is drawn. + /// + public static event EventHandler OnPreRenderGuiEvent = delegate { }; + + /// + /// Occurs after the GUI is drawn. + /// + public static event EventHandler OnPostRenderGuiEvent = delegate { }; + + /// + /// Occurs before the HUD is drawn. + /// + public static event EventHandler OnPreRenderHudEvent = delegate { }; + + /// + /// Occurs after the HUD is drawn. + /// + public static event EventHandler OnPostRenderHudEvent = delegate { }; + + /// + /// Occurs after everything is drawn. + /// + public static event EventHandler OnPostRenderEvent = delegate { }; + + /// + /// Occurs before the GUI is drawn. Does not check for conditional statements. + /// + public static event EventHandler OnPreRenderGuiEventNoCheck = delegate { }; + + /// + /// Occurs after the GUI is drawn. Does not check for conditional statements. + /// + public static event EventHandler OnPostRenderGuiEventNoCheck = delegate { }; + + /// + /// Occurs before the HUD is drawn. Does not check for conditional statements. + /// + public static event EventHandler OnPreRenderHudEventNoCheck = delegate { }; + + /// + /// Occurs after the HUD is drawn. Does not check for conditional statements. + /// + public static event EventHandler OnPostRenderHudEventNoCheck = delegate { }; + + /// + /// Draws when SGame.Debug is true. F3 toggles this. + /// Game1.spriteBatch.Begin() is pre-called. + /// Do not make end or begin calls to the spritebatch. + /// If you are only trying to add debug information, use SGame.DebugMessageQueue in your Update loop. + /// + public static event EventHandler DrawDebug = delegate { }; + + internal static void InvokeDrawDebug(object sender, EventArgs e) + { + DrawDebug.Invoke(sender, e); + } + + internal static void InvokeOnPreRenderEvent(object sender, EventArgs e) + { + OnPreRenderEvent.Invoke(sender, e); + } + + internal static void InvokeOnPreRenderGuiEvent(object sender, EventArgs e) + { + OnPreRenderGuiEvent.Invoke(sender, e); + } + + internal static void InvokeOnPostRenderGuiEvent(object sender, EventArgs e) + { + OnPostRenderGuiEvent.Invoke(sender, e); + } + + internal static void InvokeOnPreRenderHudEvent(object sender, EventArgs e) + { + OnPreRenderHudEvent.Invoke(sender, e); + } + + internal static void InvokeOnPostRenderHudEvent(object sender, EventArgs e) + { + OnPostRenderHudEvent.Invoke(sender, e); + } + + internal static void InvokeOnPostRenderEvent(object sender, EventArgs e) + { + OnPostRenderEvent.Invoke(sender, e); + } + + internal static void InvokeOnPreRenderGuiEventNoCheck(object sender, EventArgs e) + { + OnPreRenderGuiEventNoCheck.Invoke(sender, e); + } + + internal static void InvokeOnPostRenderGuiEventNoCheck(object sender, EventArgs e) + { + OnPostRenderGuiEventNoCheck.Invoke(sender, e); + } + + internal static void InvokeOnPreRenderHudEventNoCheck(object sender, EventArgs e) + { + OnPreRenderHudEventNoCheck.Invoke(sender, e); + } + + internal static void InvokeOnPostRenderHudEventNoCheck(object sender, EventArgs e) + { + OnPostRenderHudEventNoCheck.Invoke(sender, e); + } + + internal static void InvokeResize(object sender, EventArgs e) + { + Resize.Invoke(sender, e); + } + + #region To Remove + + [Obsolete("Use the other Pre/Post render events instead.")] + public static event EventHandler DrawTick = delegate { }; + + [Obsolete("Use the other Pre/Post render events instead. All of them will automatically be drawn into the render target if needed.")] + public static event EventHandler DrawInRenderTargetTick = delegate { }; + + [Obsolete("Should not be used.")] + public static void InvokeDrawTick() + { + try + { + DrawTick.Invoke(null, EventArgs.Empty); + } + catch (Exception ex) + { + Log.AsyncR("An exception occured in a Mod's DrawTick: " + ex); + } + } + + [Obsolete("Should not be used.")] + public static void InvokeDrawInRenderTargetTick() + { + DrawInRenderTargetTick.Invoke(null, EventArgs.Empty); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Events/Location.cs b/src/StardewModdingAPI/Events/Location.cs new file mode 100644 index 00000000..f951ab95 --- /dev/null +++ b/src/StardewModdingAPI/Events/Location.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using Microsoft.Xna.Framework; +using StardewValley; +using Object = StardewValley.Object; + +namespace StardewModdingAPI.Events +{ + public static class LocationEvents + { + public static event EventHandler LocationsChanged = delegate { }; + public static event EventHandler LocationObjectsChanged = delegate { }; + public static event EventHandler CurrentLocationChanged = delegate { }; + + internal static void InvokeLocationsChanged(List newLocations) + { + LocationsChanged.Invoke(null, new EventArgsGameLocationsChanged(newLocations)); + } + + internal static void InvokeCurrentLocationChanged(GameLocation priorLocation, GameLocation newLocation) + { + CurrentLocationChanged.Invoke(null, new EventArgsCurrentLocationChanged(priorLocation, newLocation)); + } + + internal static void InvokeOnNewLocationObject(SerializableDictionary newObjects) + { + LocationObjectsChanged.Invoke(null, new EventArgsLocationObjectsChanged(newObjects)); + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Events/Menu.cs b/src/StardewModdingAPI/Events/Menu.cs new file mode 100644 index 00000000..0e043780 --- /dev/null +++ b/src/StardewModdingAPI/Events/Menu.cs @@ -0,0 +1,21 @@ +using System; +using StardewValley.Menus; + +namespace StardewModdingAPI.Events +{ + public static class MenuEvents + { + public static event EventHandler MenuChanged = delegate { }; + public static event EventHandler MenuClosed = delegate { }; + + internal static void InvokeMenuChanged(IClickableMenu priorMenu, IClickableMenu newMenu) + { + MenuChanged.Invoke(null, new EventArgsClickableMenuChanged(priorMenu, newMenu)); + } + + internal static void InvokeMenuClosed(IClickableMenu priorMenu) + { + MenuClosed.Invoke(null, new EventArgsClickableMenuClosed(priorMenu)); + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Events/Mine.cs b/src/StardewModdingAPI/Events/Mine.cs new file mode 100644 index 00000000..55514d42 --- /dev/null +++ b/src/StardewModdingAPI/Events/Mine.cs @@ -0,0 +1,14 @@ +using System; + +namespace StardewModdingAPI.Events +{ + public static class MineEvents + { + public static event EventHandler MineLevelChanged = delegate { }; + + internal static void InvokeMineLevelChanged(int previousMinelevel, int currentMineLevel) + { + MineLevelChanged.Invoke(null, new EventArgsMineLevelChanged(previousMinelevel, currentMineLevel)); + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Events/Player.cs b/src/StardewModdingAPI/Events/Player.cs new file mode 100644 index 00000000..22f572b7 --- /dev/null +++ b/src/StardewModdingAPI/Events/Player.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using StardewModdingAPI.Inheritance; +using StardewValley; + +namespace StardewModdingAPI.Events +{ + public static class PlayerEvents + { + public static event EventHandler FarmerChanged = delegate { }; + public static event EventHandler InventoryChanged = delegate { }; + public static event EventHandler LeveledUp = delegate { }; + public static event EventHandler LoadedGame = delegate { }; + + internal static void InvokeFarmerChanged(Farmer priorFarmer, Farmer newFarmer) + { + FarmerChanged.Invoke(null, new EventArgsFarmerChanged(priorFarmer, newFarmer)); + } + + internal static void InvokeInventoryChanged(List inventory, List changedItems) + { + InventoryChanged.Invoke(null, new EventArgsInventoryChanged(inventory, changedItems)); + } + + internal static void InvokeLeveledUp(EventArgsLevelUp.LevelType type, int newLevel) + { + LeveledUp.Invoke(null, new EventArgsLevelUp(type, newLevel)); + } + + internal static void InvokeLoadedGame(EventArgsLoadedGameChanged loaded) + { + LoadedGame.Invoke(null, loaded); + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Events/Time.cs b/src/StardewModdingAPI/Events/Time.cs new file mode 100644 index 00000000..39ca642a --- /dev/null +++ b/src/StardewModdingAPI/Events/Time.cs @@ -0,0 +1,42 @@ +using System; + +namespace StardewModdingAPI.Events +{ + public static class TimeEvents + { + public static event EventHandler TimeOfDayChanged = delegate { }; + public static event EventHandler DayOfMonthChanged = delegate { }; + public static event EventHandler YearOfGameChanged = delegate { }; + public static event EventHandler SeasonOfYearChanged = delegate { }; + + /// + /// Occurs when Game1.newDay changes. True directly before saving, and False directly after. + /// + public static event EventHandler OnNewDay = delegate { }; + + internal static void InvokeTimeOfDayChanged(int priorInt, int newInt) + { + TimeOfDayChanged.Invoke(null, new EventArgsIntChanged(priorInt, newInt)); + } + + internal static void InvokeDayOfMonthChanged(int priorInt, int newInt) + { + DayOfMonthChanged.Invoke(null, new EventArgsIntChanged(priorInt, newInt)); + } + + internal static void InvokeYearOfGameChanged(int priorInt, int newInt) + { + YearOfGameChanged.Invoke(null, new EventArgsIntChanged(priorInt, newInt)); + } + + internal static void InvokeSeasonOfYearChanged(string priorString, string newString) + { + SeasonOfYearChanged.Invoke(null, new EventArgsStringChanged(priorString, newString)); + } + + internal static void InvokeOnNewDay(int priorInt, int newInt, bool newDay) + { + OnNewDay.Invoke(null, new EventArgsNewDay(priorInt, newInt, newDay)); + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Extensions.cs b/src/StardewModdingAPI/Extensions.cs new file mode 100644 index 00000000..abad6ce2 --- /dev/null +++ b/src/StardewModdingAPI/Extensions.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; + +namespace StardewModdingAPI +{ + public static class Extensions + { + public static Random Random = new Random(); + + public static bool IsKeyDown(this Keys key) + { + return Keyboard.GetState().IsKeyDown(key); + } + + public static Color RandomColour() + { + return new Color(Random.Next(0, 255), Random.Next(0, 255), Random.Next(0, 255)); + } + + [Obsolete("The usage of ToSingular has changed. Please update your call to use ToSingular")] + public static string ToSingular(this IEnumerable ienum, string split = ", ") + { + Log.AsyncR("The usage of ToSingular has changed. Please update your call to use ToSingular"); + return ""; + } + + public static string ToSingular(this IEnumerable ienum, string split = ", ") // where T : class + { + //Apparently Keys[] won't split normally :l + if (typeof(T) == typeof(Keys)) + { + return string.Join(split, ienum.ToArray()); + } + return string.Join(split, ienum); + } + + /*public static string ToSingular(this IEnumerable ienum, string split = ", ") + { + return string.Join(split, ienum); + }*/ + + public static bool IsInt32(this object o) + { + int i; + return int.TryParse(o.ToString(), out i); + } + + public static int AsInt32(this object o) + { + return int.Parse(o.ToString()); + } + + public static bool IsBool(this object o) + { + bool b; + return bool.TryParse(o.ToString(), out b); + } + + public static bool AsBool(this object o) + { + return bool.Parse(o.ToString()); + } + + public static int GetHash(this IEnumerable enumerable) + { + var hash = 0; + foreach (var v in enumerable) + { + hash ^= v.GetHashCode(); + } + return hash; + } + + public static T Cast(this object o) where T : class + { + return o as T; + } + + public static FieldInfo[] GetPrivateFields(this object o) + { + return o.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic); + } + + public static FieldInfo GetBaseFieldInfo(this Type t, string name) + { + return t.GetField(name, BindingFlags.Instance | BindingFlags.NonPublic); + } + + public static T GetBaseFieldValue(this Type t, object o, string name) where T : class + { + return t.GetBaseFieldInfo(name).GetValue(o) as T; + } + + public static void SetBaseFieldValue(this Type t, object o, string name, object newValue) where T : class + { + t.GetBaseFieldInfo(name).SetValue(o, newValue as T); + } + + /* + public static T GetBaseFieldValue(this object o, string name) where T : class + { + return o.GetType().GetBaseFieldInfo(name).GetValue(o) as T; + }*/ + + /* + public static object GetBaseFieldValue(this object o, string name) + { + return o.GetType().GetBaseFieldInfo(name).GetValue(o); + } + + public static void SetBaseFieldValue (this object o, string name, object newValue) + { + o.GetType().GetBaseFieldInfo(name).SetValue(o, newValue); + } + */ + + public static string RemoveNumerics(this string st) + { + var s = st; + foreach (var c in s) + { + if (!char.IsLetterOrDigit(c)) + { + s = s.Replace(c.ToString(), ""); + } + } + return s; + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/FodyWeavers.xml b/src/StardewModdingAPI/FodyWeavers.xml new file mode 100644 index 00000000..2e6d4a7a --- /dev/null +++ b/src/StardewModdingAPI/FodyWeavers.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/StardewModdingAPI/Inheritance/ItemStackChange.cs b/src/StardewModdingAPI/Inheritance/ItemStackChange.cs new file mode 100644 index 00000000..88fc002e --- /dev/null +++ b/src/StardewModdingAPI/Inheritance/ItemStackChange.cs @@ -0,0 +1,18 @@ +using StardewValley; + +namespace StardewModdingAPI.Inheritance +{ + public enum ChangeType + { + Removed, + Added, + StackChange + } + + public class ItemStackChange + { + public Item Item { get; set; } + public int StackChange { get; set; } + public ChangeType ChangeType { get; set; } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Inheritance/Menus/SBobberBar.cs b/src/StardewModdingAPI/Inheritance/Menus/SBobberBar.cs new file mode 100644 index 00000000..1e424f73 --- /dev/null +++ b/src/StardewModdingAPI/Inheritance/Menus/SBobberBar.cs @@ -0,0 +1,288 @@ +using System.Reflection; +using Microsoft.Xna.Framework; +using StardewValley.BellsAndWhistles; +using StardewValley.Menus; + +namespace StardewModdingAPI.Inheritance.Menus +{ + public class SBobberBar : BobberBar + { + /// + /// DO NOT CONSTRUCT THIS CLASS + /// To retrieve an instance of SBobberBar, use SBobberBar.ConstructFromBaseClass() + /// + public SBobberBar(int whichFish, float fishSize, bool treasure, int bobber) : base(whichFish, fishSize, treasure, bobber) + { + } + + public BobberBar BaseBobberBar { get; private set; } + + /// + /// The green rectangle bar that moves up and down + /// + public float bobberPosition + { + get { return (float) GetBaseFieldInfo("bobberPosition").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("bobberPosition").SetValue(BaseBobberBar, value); } + } + + /// + /// The green bar on the right. How close to catching the fish you are + /// Range: 0 - 1 | 1 = catch, 0 = fail + /// + public float distanceFromCatching + { + get { return (float) GetBaseFieldInfo("distanceFromCatching").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("distanceFromCatching").SetValue(BaseBobberBar, value); } + } + + public float difficulty + { + get { return (float) GetBaseFieldInfo("difficulty").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("difficulty").SetValue(BaseBobberBar, value); } + } + + public int motionType + { + get { return (int) GetBaseFieldInfo("motionType").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("motionType").SetValue(BaseBobberBar, value); } + } + + public int whichFish + { + get { return (int) GetBaseFieldInfo("whichFish").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("whichFish").SetValue(BaseBobberBar, value); } + } + + public float bobberSpeed + { + get { return (float) GetBaseFieldInfo("bobberSpeed").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("bobberSpeed").SetValue(BaseBobberBar, value); } + } + + public float bobberAcceleration + { + get { return (float) GetBaseFieldInfo("bobberAcceleration").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("bobberAcceleration").SetValue(BaseBobberBar, value); } + } + + public float bobberTargetPosition + { + get { return (float) GetBaseFieldInfo("bobberTargetPosition").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("bobberTargetPosition").SetValue(BaseBobberBar, value); } + } + + public float scale + { + get { return (float) GetBaseFieldInfo("scale").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("scale").SetValue(BaseBobberBar, value); } + } + + public float everythingShakeTimer + { + get { return (float) GetBaseFieldInfo("everythingShakeTimer").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("everythingShakeTimer").SetValue(BaseBobberBar, value); } + } + + public float floaterSinkerAcceleration + { + get { return (float) GetBaseFieldInfo("floaterSinkerAcceleration").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("floaterSinkerAcceleration").SetValue(BaseBobberBar, value); } + } + + public float treasurePosition + { + get { return (float) GetBaseFieldInfo("treasurePosition").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("treasurePosition").SetValue(BaseBobberBar, value); } + } + + public float treasureCatchLevel + { + get { return (float) GetBaseFieldInfo("treasureCatchLevel").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("treasureCatchLevel").SetValue(BaseBobberBar, value); } + } + + public float treasureAppearTimer + { + get { return (float) GetBaseFieldInfo("treasureAppearTimer").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("treasureAppearTimer").SetValue(BaseBobberBar, value); } + } + + public float treasureScale + { + get { return (float) GetBaseFieldInfo("treasureScale").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("treasureScale").SetValue(BaseBobberBar, value); } + } + + public bool bobberInBar + { + get { return (bool) GetBaseFieldInfo("bobberInBar").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("bobberInBar").SetValue(BaseBobberBar, value); } + } + + public bool buttonPressed + { + get { return (bool) GetBaseFieldInfo("buttonPressed").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("buttonPressed").SetValue(BaseBobberBar, value); } + } + + public bool flipBubble + { + get { return (bool) GetBaseFieldInfo("flipBubble").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("flipBubble").SetValue(BaseBobberBar, value); } + } + + public bool fadeIn + { + get { return (bool) GetBaseFieldInfo("fadeIn").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("fadeIn").SetValue(BaseBobberBar, value); } + } + + public bool fadeOut + { + get { return (bool) GetBaseFieldInfo("fadeOut").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("bobberPfadeOutosition").SetValue(BaseBobberBar, value); } + } + + /// + /// Whether or not a treasure chest appears + /// + public bool treasure + { + get { return (bool) GetBaseFieldInfo("treasure").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("treasure").SetValue(BaseBobberBar, value); } + } + + public bool treasureCaught + { + get { return (bool) GetBaseFieldInfo("treasureCaught").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("treasureCaught").SetValue(BaseBobberBar, value); } + } + + public bool perfect + { + get { return (bool) GetBaseFieldInfo("perfect").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("perfect").SetValue(BaseBobberBar, value); } + } + + public bool bossFish + { + get { return (bool) GetBaseFieldInfo("bossFish").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("bossFish").SetValue(BaseBobberBar, value); } + } + + public int bobberBarHeight + { + get { return (int) GetBaseFieldInfo("bobberBarHeight").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("bobberBarHeight").SetValue(BaseBobberBar, value); } + } + + public int fishSize + { + get { return (int) GetBaseFieldInfo("fishSize").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("fishSize").SetValue(BaseBobberBar, value); } + } + + public int fishQuality + { + get { return (int) GetBaseFieldInfo("fishQuality").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("fishQuality").SetValue(BaseBobberBar, value); } + } + + public int minFishSize + { + get { return (int) GetBaseFieldInfo("minFishSize").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("minFishSize").SetValue(BaseBobberBar, value); } + } + + public int maxFishSize + { + get { return (int) GetBaseFieldInfo("maxFishSize").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("maxFishSize").SetValue(BaseBobberBar, value); } + } + + public int fishSizeReductionTimer + { + get { return (int) GetBaseFieldInfo("fishSizeReductionTimer").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("fishSizeReductionTimer").SetValue(BaseBobberBar, value); } + } + + public int whichBobber + { + get { return (int) GetBaseFieldInfo("whichBobber").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("whichBobber").SetValue(BaseBobberBar, value); } + } + + public Vector2 barShake + { + get { return (Vector2) GetBaseFieldInfo("barShake").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("barShake").SetValue(BaseBobberBar, value); } + } + + public Vector2 fishShake + { + get { return (Vector2) GetBaseFieldInfo("fishShake").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("fishShake").SetValue(BaseBobberBar, value); } + } + + public Vector2 everythingShake + { + get { return (Vector2) GetBaseFieldInfo("everythingShake").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("everythingShake").SetValue(BaseBobberBar, value); } + } + + public Vector2 treasureShake + { + get { return (Vector2) GetBaseFieldInfo("treasureShake").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("treasureShake").SetValue(BaseBobberBar, value); } + } + + public float reelRotation + { + get { return (float) GetBaseFieldInfo("reelRotation").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("reelRotation").SetValue(BaseBobberBar, value); } + } + + public SparklingText sparkleText + { + get { return (SparklingText) GetBaseFieldInfo("sparkleText").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("sparkleText").SetValue(BaseBobberBar, value); } + } + + public float bobberBarPos + { + get { return (float) GetBaseFieldInfo("bobberBarPos").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("bobberBarPos").SetValue(BaseBobberBar, value); } + } + + public float bobberBarSpeed + { + get { return (float) GetBaseFieldInfo("bobberBarSpeed").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("bobberBarSpeed").SetValue(BaseBobberBar, value); } + } + + public float bobberBarAcceleration + { + get { return (float) GetBaseFieldInfo("bobberBarAcceleration").GetValue(BaseBobberBar); } + set { GetBaseFieldInfo("bobberBarAcceleration").SetValue(BaseBobberBar, value); } + } + + public static FieldInfo[] PrivateFields => GetPrivateFields(); + + public static SBobberBar ConstructFromBaseClass(BobberBar baseClass) + { + var b = new SBobberBar(0, 0, false, 0) {BaseBobberBar = baseClass}; + return b; + } + + public static FieldInfo[] GetPrivateFields() + { + return typeof(BobberBar).GetFields(BindingFlags.Instance | BindingFlags.NonPublic); + } + + public static FieldInfo GetBaseFieldInfo(string name) + { + return typeof(BobberBar).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic); + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Inheritance/Menus/SGameMenu.cs b/src/StardewModdingAPI/Inheritance/Menus/SGameMenu.cs new file mode 100644 index 00000000..a4d3d8d0 --- /dev/null +++ b/src/StardewModdingAPI/Inheritance/Menus/SGameMenu.cs @@ -0,0 +1,48 @@ +using System.Collections.Generic; +using System.Reflection; +using StardewValley.Menus; + +namespace StardewModdingAPI.Inheritance.Menus +{ + public class SGameMenu : GameMenu + { + public GameMenu BaseGameMenu { get; private set; } + + public List tabs + { + get { return (List) GetBaseFieldInfo("tabs").GetValue(BaseGameMenu); } + set { GetBaseFieldInfo("tabs").SetValue(BaseGameMenu, value); } + } + + public List pages + { + get { return (List) GetBaseFieldInfo("pages").GetValue(BaseGameMenu); } + set { GetBaseFieldInfo("pages").SetValue(BaseGameMenu, value); } + } + + public static SGameMenu ConstructFromBaseClass(GameMenu baseClass) + { + var s = new SGameMenu {BaseGameMenu = baseClass}; + return s; + } + + public override void receiveRightClick(int x, int y, bool playSound = true) + { + if (pages[currentTab] is InventoryPage) + { + Log.AsyncY("INV SCREEN"); + } + base.receiveRightClick(x, y, playSound); + } + + public static FieldInfo[] GetPrivateFields() + { + return typeof(GameMenu).GetFields(BindingFlags.Instance | BindingFlags.NonPublic); + } + + public static FieldInfo GetBaseFieldInfo(string name) + { + return typeof(GameMenu).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic); + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Inheritance/Menus/SInventoryPage.cs b/src/StardewModdingAPI/Inheritance/Menus/SInventoryPage.cs new file mode 100644 index 00000000..436b834d --- /dev/null +++ b/src/StardewModdingAPI/Inheritance/Menus/SInventoryPage.cs @@ -0,0 +1,19 @@ +using StardewValley.Menus; + +namespace StardewModdingAPI.Inheritance.Menus +{ + public class SInventoryPage : InventoryPage + { + public SInventoryPage(int x, int y, int width, int height) : base(x, y, width, height) + { + } + + public InventoryPage BaseInventoryPage { get; private set; } + + public static SInventoryPage ConstructFromBaseClass(InventoryPage baseClass) + { + var s = new SInventoryPage(0, 0, 0, 0) {BaseInventoryPage = baseClass}; + return s; + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Inheritance/Minigames/SMinigameBase.cs b/src/StardewModdingAPI/Inheritance/Minigames/SMinigameBase.cs new file mode 100644 index 00000000..f30021de --- /dev/null +++ b/src/StardewModdingAPI/Inheritance/Minigames/SMinigameBase.cs @@ -0,0 +1,34 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using StardewValley.Minigames; + +namespace StardewModdingAPI.Inheritance.Minigames +{ + internal abstract class SMinigameBase : IMinigame + { + public abstract bool tick(GameTime time); + + public abstract void receiveLeftClick(int x, int y, bool playSound = true); + + public abstract void leftClickHeld(int x, int y); + + public abstract void receiveRightClick(int x, int y, bool playSound = true); + + public abstract void releaseLeftClick(int x, int y); + + public abstract void releaseRightClick(int x, int y); + + public abstract void receiveKeyPress(Keys k); + + public abstract void receiveKeyRelease(Keys k); + + public abstract void draw(SpriteBatch b); + + public abstract void changeScreenSize(); + + public abstract void unload(); + + public abstract void receiveEventPoke(int data); + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Inheritance/SBareObject.cs b/src/StardewModdingAPI/Inheritance/SBareObject.cs new file mode 100644 index 00000000..5bef7b3e --- /dev/null +++ b/src/StardewModdingAPI/Inheritance/SBareObject.cs @@ -0,0 +1,20 @@ +namespace StardewModdingAPI.Inheritance +{ + public struct SBareObject + { + public int parentSheetIndex { get; set; } + public int stack { get; set; } + public bool isRecipe { get; set; } + public int price { get; set; } + public int quality { get; set; } + + public SBareObject(int psi, int sta, bool ir, int pri, int qua) + { + parentSheetIndex = psi; + stack = sta; + isRecipe = ir; + price = pri; + quality = qua; + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Inheritance/SGame.cs b/src/StardewModdingAPI/Inheritance/SGame.cs new file mode 100644 index 00000000..9b69434a --- /dev/null +++ b/src/StardewModdingAPI/Inheritance/SGame.cs @@ -0,0 +1,1726 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using StardewModdingAPI.Events; +using StardewValley; +using StardewValley.BellsAndWhistles; +using StardewValley.Locations; +using StardewValley.Menus; +using StardewValley.Tools; +using xTile.Dimensions; +using Rectangle = Microsoft.Xna.Framework.Rectangle; + +namespace StardewModdingAPI.Inheritance +{ + /// + /// The 'SGame' class. + /// This summary, and many others, only exists because XML doc tags. + /// + public class SGame : Game1 + { + /// + /// Useless right now. + /// + public const int LowestModItemID = 1000; + + private bool FireLoadedGameEvent; + + /// + /// Gets a jagged array of all buttons pressed on the gamepad the prior frame. + /// + public Buttons[][] PreviouslyPressedButtons; + + internal SGame() + { + Instance = this; + FirstUpdate = true; + } + + /// + /// Useless at this time. + /// + [Obsolete] + // ReSharper disable once UnusedAutoPropertyAccessor.Local + public static Dictionary ModItems { get; private set; } + + /// + /// The current KeyboardState + /// + public KeyboardState KStateNow { get; private set; } + + /// + /// The prior KeyboardState + /// + public KeyboardState KStatePrior { get; private set; } + + /// + /// The current MouseState + /// + public MouseState MStateNow { get; private set; } + + /// + /// The prior MouseState + /// + public MouseState MStatePrior { get; private set; } + + /// + /// All keys pressed on the current frame + /// + public Keys[] CurrentlyPressedKeys => KStateNow.GetPressedKeys(); + + /// + /// All keys pressed on the prior frame + /// + public Keys[] PreviouslyPressedKeys => KStatePrior.GetPressedKeys(); + + /// + /// All keys pressed on this frame except for the ones pressed on the prior frame + /// + public Keys[] FramePressedKeys => CurrentlyPressedKeys.Except(PreviouslyPressedKeys).ToArray(); + + /// + /// All keys pressed on the prior frame except for the ones pressed on the current frame + /// + public Keys[] FrameReleasedKeys => PreviouslyPressedKeys.Except(CurrentlyPressedKeys).ToArray(); + + /// + /// Whether or not a save was tagged as 'Loaded' the prior frame. + /// + public bool PreviouslyLoadedGame { get; private set; } + + /// + /// The list of GameLocations on the prior frame + /// + public int PreviousGameLocations { get; private set; } + + /// + /// The list of GameObjects on the prior frame + /// + public int PreviousLocationObjects { get; private set; } + + /// + /// The list of Items in the player's inventory on the prior frame + /// + public Dictionary PreviousItems { get; private set; } + + /// + /// The player's Combat level on the prior frame + /// + public int PreviousCombatLevel { get; private set; } + + /// + /// The player's Farming level on the prior frame + /// + public int PreviousFarmingLevel { get; private set; } + + /// + /// The player's Fishing level on the prior frame + /// + public int PreviousFishingLevel { get; private set; } + + /// + /// The player's Foraging level on the prior frame + /// + public int PreviousForagingLevel { get; private set; } + + /// + /// The player's Mining level on the prior frame + /// + public int PreviousMiningLevel { get; private set; } + + /// + /// The player's Luck level on the prior frame + /// + public int PreviousLuckLevel { get; private set; } + + //Kill me now comments are so boring + + /// + /// The player's previous game location + /// + public GameLocation PreviousGameLocation { get; private set; } + + /// + /// The previous ActiveGameMenu in Game1 + /// + public IClickableMenu PreviousActiveMenu { get; private set; } + + /// + /// Indicates if the MenuClosed event was fired to prevent it from re-firing. + /// + internal bool WasMenuClosedInvoked = false; + + /// + /// The previous mine level + /// + public int PreviousMineLevel { get; private set; } + + /// + /// The previous TimeOfDay (Int32 between 600 and 2400?) + /// + public int PreviousTimeOfDay { get; private set; } + + /// + /// The previous DayOfMonth (Int32 between 1 and 28?) + /// + public int PreviousDayOfMonth { get; private set; } + + /// + /// The previous Season (String as follows: "winter", "spring", "summer", "fall") + /// + public string PreviousSeasonOfYear { get; private set; } + + /// + /// The previous Year + /// + public int PreviousYearOfGame { get; private set; } + + /// + /// The previous result of Game1.newDay + /// + public bool PreviousIsNewDay { get; private set; } + + /// + /// The previous 'Farmer' (Player) + /// + public Farmer PreviousFarmer { get; private set; } + + /// + /// The current index of the update tick. Recycles every 60th tick to 0. (Int32 between 0 and 59) + /// + public int CurrentUpdateTick { get; private set; } + + /// + /// Whether or not this update frame is the very first of the entire game + /// + public bool FirstUpdate { get; private set; } + + /// + /// The current RenderTarget in Game1 (Private field, uses reflection) + /// + public RenderTarget2D Screen + { + get { return typeof(Game1).GetBaseFieldValue(Program.gamePtr, "screen"); } + set { typeof(Game1).SetBaseFieldValue(this, "screen", value); } + } + + /// + /// The current Colour in Game1 (Private field, uses reflection) + /// + public Color BgColour + { + get { return (Color) typeof(Game1).GetBaseFieldValue(Program.gamePtr, "bgColor"); } + set { typeof(Game1).SetBaseFieldValue(this, "bgColor", value); } + } + + /// + /// Static accessor for an Instance of the class SGame + /// + public static SGame Instance { get; private set; } + + /// + /// The game's FPS. Re-determined every Draw update. + /// + public static float FramesPerSecond { get; private set; } + + /// + /// Whether or not we're in a pseudo 'debug' mode. Mostly for displaying information like FPS. + /// + public static bool Debug { get; private set; } + + internal static Queue DebugMessageQueue { get; private set; } + + /// + /// The current player (equal to Farmer.Player) + /// + [Obsolete("Use Farmer.Player instead")] + public Farmer CurrentFarmer => player; + + /// + /// Gets ALL static fields that belong to 'Game1' + /// + public static FieldInfo[] GetStaticFields => typeof(Game1).GetFields(); + + /// + /// Whether or not a button was just pressed on the controller + /// + /// + /// + /// + /// + private bool WasButtonJustPressed(Buttons button, ButtonState buttonState, PlayerIndex stateIndex) + { + return buttonState == ButtonState.Pressed && !PreviouslyPressedButtons[(int) stateIndex].Contains(button); + } + + /// + /// Whether or not a button was just released on the controller + /// + /// + /// + /// + /// + private bool WasButtonJustReleased(Buttons button, ButtonState buttonState, PlayerIndex stateIndex) + { + return buttonState == ButtonState.Released && PreviouslyPressedButtons[(int) stateIndex].Contains(button); + } + + /// + /// Whether or not an analog button was just pressed on the controller + /// + /// + /// + /// + /// + private bool WasButtonJustPressed(Buttons button, float value, PlayerIndex stateIndex) + { + return WasButtonJustPressed(button, value > 0.2f ? ButtonState.Pressed : ButtonState.Released, stateIndex); + } + + /// + /// Whether or not an analog button was just released on the controller + /// + /// + /// + /// + /// + private bool WasButtonJustReleased(Buttons button, float value, PlayerIndex stateIndex) + { + return WasButtonJustReleased(button, value > 0.2f ? ButtonState.Pressed : ButtonState.Released, stateIndex); + } + + /// + /// Gets an array of all Buttons pressed on a joystick + /// + /// + /// + public Buttons[] GetButtonsDown(PlayerIndex index) + { + var state = GamePad.GetState(index); + var buttons = new List(); + if (state.IsConnected) + { + if (state.Buttons.A == ButtonState.Pressed) buttons.Add(Buttons.A); + if (state.Buttons.B == ButtonState.Pressed) buttons.Add(Buttons.B); + if (state.Buttons.Back == ButtonState.Pressed) buttons.Add(Buttons.Back); + if (state.Buttons.BigButton == ButtonState.Pressed) buttons.Add(Buttons.BigButton); + if (state.Buttons.LeftShoulder == ButtonState.Pressed) buttons.Add(Buttons.LeftShoulder); + if (state.Buttons.LeftStick == ButtonState.Pressed) buttons.Add(Buttons.LeftStick); + if (state.Buttons.RightShoulder == ButtonState.Pressed) buttons.Add(Buttons.RightShoulder); + if (state.Buttons.RightStick == ButtonState.Pressed) buttons.Add(Buttons.RightStick); + if (state.Buttons.Start == ButtonState.Pressed) buttons.Add(Buttons.Start); + if (state.Buttons.X == ButtonState.Pressed) buttons.Add(Buttons.X); + if (state.Buttons.Y == ButtonState.Pressed) buttons.Add(Buttons.Y); + if (state.DPad.Up == ButtonState.Pressed) buttons.Add(Buttons.DPadUp); + if (state.DPad.Down == ButtonState.Pressed) buttons.Add(Buttons.DPadDown); + if (state.DPad.Left == ButtonState.Pressed) buttons.Add(Buttons.DPadLeft); + if (state.DPad.Right == ButtonState.Pressed) buttons.Add(Buttons.DPadRight); + if (state.Triggers.Left > 0.2f) buttons.Add(Buttons.LeftTrigger); + if (state.Triggers.Right > 0.2f) buttons.Add(Buttons.RightTrigger); + } + return buttons.ToArray(); + } + + /// + /// Gets all buttons that were pressed on the current frame of a joystick + /// + /// + /// + public Buttons[] GetFramePressedButtons(PlayerIndex index) + { + var state = GamePad.GetState(index); + var buttons = new List(); + if (state.IsConnected) + { + if (WasButtonJustPressed(Buttons.A, state.Buttons.A, index)) buttons.Add(Buttons.A); + if (WasButtonJustPressed(Buttons.B, state.Buttons.B, index)) buttons.Add(Buttons.B); + if (WasButtonJustPressed(Buttons.Back, state.Buttons.Back, index)) buttons.Add(Buttons.Back); + if (WasButtonJustPressed(Buttons.BigButton, state.Buttons.BigButton, index)) buttons.Add(Buttons.BigButton); + if (WasButtonJustPressed(Buttons.LeftShoulder, state.Buttons.LeftShoulder, index)) buttons.Add(Buttons.LeftShoulder); + if (WasButtonJustPressed(Buttons.LeftStick, state.Buttons.LeftStick, index)) buttons.Add(Buttons.LeftStick); + if (WasButtonJustPressed(Buttons.RightShoulder, state.Buttons.RightShoulder, index)) buttons.Add(Buttons.RightShoulder); + if (WasButtonJustPressed(Buttons.RightStick, state.Buttons.RightStick, index)) buttons.Add(Buttons.RightStick); + if (WasButtonJustPressed(Buttons.Start, state.Buttons.Start, index)) buttons.Add(Buttons.Start); + if (WasButtonJustPressed(Buttons.X, state.Buttons.X, index)) buttons.Add(Buttons.X); + if (WasButtonJustPressed(Buttons.Y, state.Buttons.Y, index)) buttons.Add(Buttons.Y); + if (WasButtonJustPressed(Buttons.DPadUp, state.DPad.Up, index)) buttons.Add(Buttons.DPadUp); + if (WasButtonJustPressed(Buttons.DPadDown, state.DPad.Down, index)) buttons.Add(Buttons.DPadDown); + if (WasButtonJustPressed(Buttons.DPadLeft, state.DPad.Left, index)) buttons.Add(Buttons.DPadLeft); + if (WasButtonJustPressed(Buttons.DPadRight, state.DPad.Right, index)) buttons.Add(Buttons.DPadRight); + if (WasButtonJustPressed(Buttons.LeftTrigger, state.Triggers.Left, index)) buttons.Add(Buttons.LeftTrigger); + if (WasButtonJustPressed(Buttons.RightTrigger, state.Triggers.Right, index)) buttons.Add(Buttons.RightTrigger); + } + return buttons.ToArray(); + } + + /// + /// Gets all buttons that were released on the current frame of a joystick + /// + /// + /// + public Buttons[] GetFrameReleasedButtons(PlayerIndex index) + { + var state = GamePad.GetState(index); + var buttons = new List(); + if (state.IsConnected) + { + if (WasButtonJustReleased(Buttons.A, state.Buttons.A, index)) buttons.Add(Buttons.A); + if (WasButtonJustReleased(Buttons.B, state.Buttons.B, index)) buttons.Add(Buttons.B); + if (WasButtonJustReleased(Buttons.Back, state.Buttons.Back, index)) buttons.Add(Buttons.Back); + if (WasButtonJustReleased(Buttons.BigButton, state.Buttons.BigButton, index)) buttons.Add(Buttons.BigButton); + if (WasButtonJustReleased(Buttons.LeftShoulder, state.Buttons.LeftShoulder, index)) buttons.Add(Buttons.LeftShoulder); + if (WasButtonJustReleased(Buttons.LeftStick, state.Buttons.LeftStick, index)) buttons.Add(Buttons.LeftStick); + if (WasButtonJustReleased(Buttons.RightShoulder, state.Buttons.RightShoulder, index)) buttons.Add(Buttons.RightShoulder); + if (WasButtonJustReleased(Buttons.RightStick, state.Buttons.RightStick, index)) buttons.Add(Buttons.RightStick); + if (WasButtonJustReleased(Buttons.Start, state.Buttons.Start, index)) buttons.Add(Buttons.Start); + if (WasButtonJustReleased(Buttons.X, state.Buttons.X, index)) buttons.Add(Buttons.X); + if (WasButtonJustReleased(Buttons.Y, state.Buttons.Y, index)) buttons.Add(Buttons.Y); + if (WasButtonJustReleased(Buttons.DPadUp, state.DPad.Up, index)) buttons.Add(Buttons.DPadUp); + if (WasButtonJustReleased(Buttons.DPadDown, state.DPad.Down, index)) buttons.Add(Buttons.DPadDown); + if (WasButtonJustReleased(Buttons.DPadLeft, state.DPad.Left, index)) buttons.Add(Buttons.DPadLeft); + if (WasButtonJustReleased(Buttons.DPadRight, state.DPad.Right, index)) buttons.Add(Buttons.DPadRight); + if (WasButtonJustReleased(Buttons.LeftTrigger, state.Triggers.Left, index)) buttons.Add(Buttons.LeftTrigger); + if (WasButtonJustReleased(Buttons.RightTrigger, state.Triggers.Right, index)) buttons.Add(Buttons.RightTrigger); + } + return buttons.ToArray(); + } + + /// + /// + /// + public static MethodInfo DrawFarmBuildings = typeof(Game1).GetMethod("drawFarmBuildings", BindingFlags.NonPublic | BindingFlags.Instance); + + /// + /// + /// + public static MethodInfo DrawHUD = typeof(Game1).GetMethod("drawHUD", BindingFlags.NonPublic | BindingFlags.Instance); + + /// + /// + /// + public static MethodInfo DrawDialogueBox = typeof(Game1).GetMethod("drawDialogueBox", BindingFlags.NonPublic | BindingFlags.Instance); + + public static MethodInfo CheckForEscapeKeys = typeof(Game1).GetMethod("checkForEscapeKeys", BindingFlags.NonPublic | BindingFlags.Instance); + + public static MethodInfo UpdateControlInput = typeof(Game1).GetMethod("UpdateControlInput", BindingFlags.NonPublic | BindingFlags.Instance); + + public static MethodInfo UpdateCharacters = typeof(Game1).GetMethod("UpdateCharacters", BindingFlags.NonPublic | BindingFlags.Instance); + + public static MethodInfo UpdateLocations = typeof(Game1).GetMethod("UpdateLocations", BindingFlags.NonPublic | BindingFlags.Instance); + + public static MethodInfo getViewportCenter = typeof(Game1).GetMethod("getViewportCenter", BindingFlags.NonPublic | BindingFlags.Instance); + + public static MethodInfo UpdateTitleScreen = typeof(Game1).GetMethod("UpdateTitleScreen", BindingFlags.NonPublic | BindingFlags.Instance); + + public delegate void BaseBaseDraw(); + + /// + /// Whether or not the game's zoom level is 1.0f + /// + public bool ZoomLevelIsOne => options.zoomLevel.Equals(1.0f); + + /// + /// XNA Init Method + /// + protected override void Initialize() + { + Log.AsyncY("XNA Initialize"); + //ModItems = new Dictionary(); + DebugMessageQueue = new Queue(); + PreviouslyPressedButtons = new Buttons[4][]; + for (var i = 0; i < 4; ++i) PreviouslyPressedButtons[i] = new Buttons[0]; + + base.Initialize(); + GameEvents.InvokeInitialize(); + } + + /// + /// XNA LC Method + /// + protected override void LoadContent() + { + Log.AsyncY("XNA LoadContent"); + base.LoadContent(); + GameEvents.InvokeLoadContent(); + } + + /// + /// XNA Update Method + /// + /// + protected override void Update(GameTime gameTime) + { + QueueDebugMessage("FPS: " + FramesPerSecond); + UpdateEventCalls(); + + /* + if (ZoomLevelIsOne) + { + options.zoomLevel = 0.99f; + InvokeBasePrivateInstancedMethod("Window_ClientSizeChanged", null, null); + } + */ + + if (FramePressedKeys.Contains(Keys.F3)) + { + Debug = !Debug; + } + + if (FramePressedKeys.Contains(Keys.F2)) + { + //Built-in debug mode + debugMode = !debugMode; + } + + if (Constants.EnableCompletelyOverridingBaseCalls) + { + #region Overridden Update Call + + try + { + if (Program.BuildType == 0) + SteamHelper.update(); + if ((paused /*|| !this.IsActive*/) && (options == null || options.pauseWhenOutOfFocus || paused)) + return; + if (quit) + Exit(); + currentGameTime = gameTime; + if (gameMode != 11) + { + if (IsMultiplayer && gameMode == 3) + { + if (multiplayerMode == 2) + server.receiveMessages(); + else + client.receiveMessages(); + } + if (IsActive) + InvokeMethodInfo(CheckForEscapeKeys); + //InvokeBasePrivateInstancedMethod("checkForEscapeKeys"); + + //this.checkForEscapeKeys(); + updateMusic(); + updateRaindropPosition(); + bloom?.tick(gameTime); + if (globalFade) + { + if (!dialogueUp) + { + if (fadeIn) + { + fadeToBlackAlpha = Math.Max(0.0f, fadeToBlackAlpha - globalFadeSpeed); + if (fadeToBlackAlpha <= 0.0) + { + globalFade = false; + if (afterFade != null) + { + afterFadeFunction afterFadeFunction = afterFade; + afterFade(); + if (afterFade != null && afterFade.Equals(afterFadeFunction)) + afterFade = null; + if (nonWarpFade) + fadeToBlack = false; + } + } + } + else + { + fadeToBlackAlpha = Math.Min(1f, fadeToBlackAlpha + globalFadeSpeed); + if (fadeToBlackAlpha >= 1.0) + { + globalFade = false; + if (afterFade != null) + { + afterFadeFunction afterFadeFunction = afterFade; + afterFade(); + if (afterFade != null && afterFade.Equals(afterFadeFunction)) + afterFade = null; + if (nonWarpFade) + fadeToBlack = false; + } + } + } + } + else + InvokeMethodInfo(UpdateControlInput, gameTime); + //InvokeBasePrivateInstancedMethod("UpdateControlInput", gameTime); + //this.UpdateControlInput(gameTime); + } + else if (pauseThenDoFunctionTimer > 0) + { + freezeControls = true; + pauseThenDoFunctionTimer -= gameTime.ElapsedGameTime.Milliseconds; + if (pauseThenDoFunctionTimer <= 0) + { + freezeControls = false; + afterPause?.Invoke(); + } + } + if (gameMode == 3 || gameMode == 2) + { + player.millisecondsPlayed += (uint) gameTime.ElapsedGameTime.Milliseconds; + bool flag = true; + if (currentMinigame != null) + { + if (pauseTime > 0.0) + updatePause(gameTime); + if (fadeToBlack) + { + updateScreenFade(gameTime); + if (fadeToBlackAlpha >= 1.0) + fadeToBlack = false; + } + else + { + if (thumbstickMotionMargin > 0) + thumbstickMotionMargin -= gameTime.ElapsedGameTime.Milliseconds; + if (IsActive) + { + KeyboardState state1 = Keyboard.GetState(); + MouseState state2 = Mouse.GetState(); + GamePadState state3 = GamePad.GetState(PlayerIndex.One); + foreach (Keys keys in state1.GetPressedKeys()) + { + if (!oldKBState.IsKeyDown(keys)) + currentMinigame.receiveKeyPress(keys); + } + if (options.gamepadControls) + { + if (currentMinigame == null) + { + oldMouseState = state2; + oldKBState = state1; + oldPadState = state3; + return; + } + foreach (Buttons b in Utility.getPressedButtons(state3, oldPadState)) + currentMinigame.receiveKeyPress(Utility.mapGamePadButtonToKey(b)); + if (currentMinigame == null) + { + oldMouseState = state2; + oldKBState = state1; + oldPadState = state3; + return; + } + if (state3.ThumbSticks.Right.Y < -0.200000002980232 && oldPadState.ThumbSticks.Right.Y >= -0.200000002980232) + currentMinigame.receiveKeyPress(Keys.Down); + if (state3.ThumbSticks.Right.Y > 0.200000002980232 && oldPadState.ThumbSticks.Right.Y <= 0.200000002980232) + currentMinigame.receiveKeyPress(Keys.Up); + if (state3.ThumbSticks.Right.X < -0.200000002980232 && oldPadState.ThumbSticks.Right.X >= -0.200000002980232) + currentMinigame.receiveKeyPress(Keys.Left); + if (state3.ThumbSticks.Right.X > 0.200000002980232 && oldPadState.ThumbSticks.Right.X <= 0.200000002980232) + currentMinigame.receiveKeyPress(Keys.Right); + if (oldPadState.ThumbSticks.Right.Y < -0.200000002980232 && state3.ThumbSticks.Right.Y >= -0.200000002980232) + currentMinigame.receiveKeyRelease(Keys.Down); + if (oldPadState.ThumbSticks.Right.Y > 0.200000002980232 && state3.ThumbSticks.Right.Y <= 0.200000002980232) + currentMinigame.receiveKeyRelease(Keys.Up); + if (oldPadState.ThumbSticks.Right.X < -0.200000002980232 && state3.ThumbSticks.Right.X >= -0.200000002980232) + currentMinigame.receiveKeyRelease(Keys.Left); + if (oldPadState.ThumbSticks.Right.X > 0.200000002980232 && state3.ThumbSticks.Right.X <= 0.200000002980232) + currentMinigame.receiveKeyRelease(Keys.Right); + if (isGamePadThumbstickInMotion()) + { + setMousePosition(getMouseX() + (int) (state3.ThumbSticks.Left.X * 16.0), getMouseY() - (int) (state3.ThumbSticks.Left.Y * 16.0)); + lastCursorMotionWasMouse = false; + } + else if (getMousePosition().X != getOldMouseX() || getMousePosition().Y != getOldMouseY()) + lastCursorMotionWasMouse = true; + } + foreach (Keys keys in oldKBState.GetPressedKeys()) + { + if (!state1.IsKeyDown(keys)) + currentMinigame.receiveKeyRelease(keys); + } + if (options.gamepadControls) + { + if (currentMinigame == null) + { + oldMouseState = state2; + oldKBState = state1; + oldPadState = state3; + return; + } + if (state3.IsConnected && state3.IsButtonDown(Buttons.X) && !oldPadState.IsButtonDown(Buttons.X)) + currentMinigame.receiveRightClick(getMouseX(), getMouseY(), true); + else if (state3.IsConnected && state3.IsButtonDown(Buttons.A) && !oldPadState.IsButtonDown(Buttons.A)) + currentMinigame.receiveLeftClick(getMouseX(), getMouseY(), true); + else if (state3.IsConnected && !state3.IsButtonDown(Buttons.X) && oldPadState.IsButtonDown(Buttons.X)) + currentMinigame.releaseRightClick(getMouseX(), getMouseY()); + else if (state3.IsConnected && !state3.IsButtonDown(Buttons.A) && oldPadState.IsButtonDown(Buttons.A)) + currentMinigame.releaseLeftClick(getMouseX(), getMouseY()); + foreach (Buttons b in Utility.getPressedButtons(oldPadState, state3)) + currentMinigame.receiveKeyRelease(Utility.mapGamePadButtonToKey(b)); + if (state3.IsConnected && state3.IsButtonDown(Buttons.A)) + currentMinigame?.leftClickHeld(0, 0); + } + if (currentMinigame == null) + { + oldMouseState = state2; + oldKBState = state1; + oldPadState = state3; + return; + } + if (state2.LeftButton == ButtonState.Pressed && oldMouseState.LeftButton != ButtonState.Pressed) + currentMinigame.receiveLeftClick(getMouseX(), getMouseY(), true); + if (state2.RightButton == ButtonState.Pressed && oldMouseState.RightButton != ButtonState.Pressed) + currentMinigame.receiveRightClick(getMouseX(), getMouseY(), true); + if (state2.LeftButton == ButtonState.Released && oldMouseState.LeftButton == ButtonState.Pressed) + currentMinigame.releaseLeftClick(getMouseX(), getMouseY()); + if (state2.RightButton == ButtonState.Released && oldMouseState.RightButton == ButtonState.Pressed) + currentMinigame.releaseLeftClick(getMouseX(), getMouseY()); + if (state2.LeftButton == ButtonState.Pressed && oldMouseState.LeftButton == ButtonState.Pressed) + currentMinigame.leftClickHeld(getMouseX(), getMouseY()); + oldMouseState = state2; + oldKBState = state1; + oldPadState = state3; + } + if (currentMinigame != null && currentMinigame.tick(gameTime)) + { + currentMinigame.unload(); + currentMinigame = null; + fadeIn = true; + fadeToBlackAlpha = 1f; + return; + } + } + flag = IsMultiplayer; + } + else if (farmEvent != null && farmEvent.tickUpdate(gameTime)) + { + farmEvent.makeChangesToLocation(); + timeOfDay = 600; + UpdateOther(gameTime); + displayHUD = true; + farmEvent = null; + currentLocation = getLocationFromName("FarmHouse"); + player.position = Utility.PointToVector2(Utility.getHomeOfFarmer(player).getBedSpot()) * tileSize; + player.position.X -= tileSize; + changeMusicTrack("none"); + currentLocation.resetForPlayerEntry(); + player.forceCanMove(); + freezeControls = false; + displayFarmer = true; + outdoorLight = Color.White; + viewportFreeze = false; + fadeToBlackAlpha = 0.0f; + fadeToBlack = false; + globalFadeToClear(null, 0.02f); + player.mailForTomorrow.Clear(); + showEndOfNightStuff(); + } + if (flag) + { + if (endOfNightMenus.Count() > 0 && activeClickableMenu == null) + activeClickableMenu = endOfNightMenus.Pop(); + if (activeClickableMenu != null) + { + updateActiveMenu(gameTime); + } + else + { + if (pauseTime > 0.0) + updatePause(gameTime); + if (!globalFade && !freezeControls && (activeClickableMenu == null && IsActive)) + InvokeMethodInfo(UpdateControlInput, gameTime); + //InvokeBasePrivateInstancedMethod("UpdateControlInput", gameTime); + //this.UpdateControlInput(gameTime); + } + if (showingEndOfNightStuff && endOfNightMenus.Count() == 0 && activeClickableMenu == null) + { + showingEndOfNightStuff = false; + globalFadeToClear(playMorningSong, 0.02f); + } + if (!showingEndOfNightStuff) + { + if (IsMultiplayer || activeClickableMenu == null && currentMinigame == null) + UpdateGameClock(gameTime); + //this.UpdateCharacters(gameTime); + //this.UpdateLocations(gameTime); + //InvokeBasePrivateInstancedMethod("UpdateCharacters", gameTime); + //InvokeBasePrivateInstancedMethod("UpdateLocations", gameTime); + //UpdateViewPort(false, (Point)InvokeBasePrivateInstancedMethod("getViewportCenter")); + + InvokeMethodInfo(UpdateCharacters, gameTime); + InvokeMethodInfo(UpdateLocations, gameTime); + UpdateViewPort(false, (Point) InvokeMethodInfo(getViewportCenter)); + } + UpdateOther(gameTime); + if (messagePause) + { + KeyboardState state1 = Keyboard.GetState(); + MouseState state2 = Mouse.GetState(); + GamePadState state3 = GamePad.GetState(PlayerIndex.One); + if (isOneOfTheseKeysDown(state1, options.actionButton) && !isOneOfTheseKeysDown(oldKBState, options.actionButton)) + pressActionButton(state1, state2, state3); + oldKBState = state1; + oldPadState = state3; + } + } + } + else + { + //InvokeBasePrivateInstancedMethod("UpdateTitleScreen", gameTime); + InvokeMethodInfo(UpdateTitleScreen, gameTime); + //this.UpdateTitleScreen(gameTime); + if (activeClickableMenu != null) + updateActiveMenu(gameTime); + if (gameMode == 10) + UpdateOther(gameTime); + } + audioEngine?.Update(); + if (multiplayerMode == 2 && gameMode == 3) + server.sendMessages(gameTime); + } + } + catch (Exception ex) + { + Log.Error("An error occurred in the overridden update loop: " + ex); + } + + //typeof (Game).GetMethod("Update", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(this, new object[] {gameTime}); + //base.Update(gameTime); + + #endregion + } + else + { + try + { + base.Update(gameTime); + } + catch (Exception ex) + { + Log.AsyncR("An error occured in the base update loop: " + ex); + Console.ReadKey(); + } + } + + GameEvents.InvokeUpdateTick(); + if (FirstUpdate) + { + GameEvents.InvokeFirstUpdateTick(); + FirstUpdate = false; + } + + if (CurrentUpdateTick % 2 == 0) + GameEvents.InvokeSecondUpdateTick(); + + if (CurrentUpdateTick % 4 == 0) + GameEvents.InvokeFourthUpdateTick(); + + if (CurrentUpdateTick % 8 == 0) + GameEvents.InvokeEighthUpdateTick(); + + if (CurrentUpdateTick % 15 == 0) + GameEvents.InvokeQuarterSecondTick(); + + if (CurrentUpdateTick % 30 == 0) + GameEvents.InvokeHalfSecondTick(); + + if (CurrentUpdateTick % 60 == 0) + GameEvents.InvokeOneSecondTick(); + + CurrentUpdateTick += 1; + if (CurrentUpdateTick >= 60) + CurrentUpdateTick = 0; + + if (KStatePrior != KStateNow) + KStatePrior = KStateNow; + + for (var i = PlayerIndex.One; i <= PlayerIndex.Four; i++) + { + PreviouslyPressedButtons[(int) i] = GetButtonsDown(i); + } + } + + /// + /// XNA Draw Method + /// + /// + protected override void Draw(GameTime gameTime) + { + FramesPerSecond = 1 / (float) gameTime.ElapsedGameTime.TotalSeconds; + + if (Constants.EnableCompletelyOverridingBaseCalls) + { + #region Overridden Draw + + try + { + if (!ZoomLevelIsOne) + { + GraphicsDevice.SetRenderTarget(Screen); + } + + GraphicsDevice.Clear(BgColour); + if (options.showMenuBackground && activeClickableMenu != null && activeClickableMenu.showWithoutTransparencyIfOptionIsSet()) + { + spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); + activeClickableMenu.drawBackground(spriteBatch); + GraphicsEvents.InvokeOnPreRenderGuiEvent(null, EventArgs.Empty); + activeClickableMenu.draw(spriteBatch); + GraphicsEvents.InvokeOnPostRenderGuiEvent(null, EventArgs.Empty); + spriteBatch.End(); + if (!ZoomLevelIsOne) + { + GraphicsDevice.SetRenderTarget(null); + GraphicsDevice.Clear(BgColour); + spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullNone); + spriteBatch.Draw(Screen, Vector2.Zero, Screen.Bounds, Color.White, 0f, Vector2.Zero, options.zoomLevel, SpriteEffects.None, 1f); + spriteBatch.End(); + } + return; + } + if (gameMode == 11) + { + spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); + spriteBatch.DrawString(smoothFont, "Stardew Valley has crashed...", new Vector2(16f, 16f), Color.HotPink); + spriteBatch.DrawString(smoothFont, "Please send the error report or a screenshot of this message to @ConcernedApe. (http://stardewvalley.net/contact/)", new Vector2(16f, 32f), new Color(0, 255, 0)); + spriteBatch.DrawString(smoothFont, parseText(errorMessage, smoothFont, graphics.GraphicsDevice.Viewport.Width), new Vector2(16f, 48f), Color.White); + spriteBatch.End(); + return; + } + if (currentMinigame != null) + { + currentMinigame.draw(spriteBatch); + if (globalFade && !menuUp && (!nameSelectUp || messagePause)) + { + spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); + spriteBatch.Draw(fadeToBlackRect, graphics.GraphicsDevice.Viewport.Bounds, Color.Black * ((gameMode == 0) ? (1f - fadeToBlackAlpha) : fadeToBlackAlpha)); + spriteBatch.End(); + } + if (!ZoomLevelIsOne) + { + GraphicsDevice.SetRenderTarget(null); + GraphicsDevice.Clear(BgColour); + spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullNone); + spriteBatch.Draw(Screen, Vector2.Zero, Screen.Bounds, Color.White, 0f, Vector2.Zero, options.zoomLevel, SpriteEffects.None, 1f); + spriteBatch.End(); + } + return; + } + if (showingEndOfNightStuff) + { + spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); + activeClickableMenu?.draw(spriteBatch); + spriteBatch.End(); + if (!ZoomLevelIsOne) + { + GraphicsDevice.SetRenderTarget(null); + GraphicsDevice.Clear(BgColour); + spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullNone); + spriteBatch.Draw(Screen, Vector2.Zero, Screen.Bounds, Color.White, 0f, Vector2.Zero, options.zoomLevel, SpriteEffects.None, 1f); + spriteBatch.End(); + } + return; + } + if (gameMode == 6) + { + spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); + string text = ""; + int num = 0; + while (num < gameTime.TotalGameTime.TotalMilliseconds % 999.0 / 333.0) + { + text += "."; + num++; + } + SpriteText.drawString(spriteBatch, "Loading" + text, 64, graphics.GraphicsDevice.Viewport.Height - 64, 999, -1, 999, 1f, 1f, false, 0, "Loading..."); + spriteBatch.End(); + if (!ZoomLevelIsOne) + { + GraphicsDevice.SetRenderTarget(null); + GraphicsDevice.Clear(BgColour); + spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullNone); + spriteBatch.Draw(Screen, Vector2.Zero, Screen.Bounds, Color.White, 0f, Vector2.Zero, options.zoomLevel, SpriteEffects.None, 1f); + spriteBatch.End(); + } + return; + } + if (gameMode == 0) + { + spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); + } + else + { + if (drawLighting) + { + GraphicsDevice.SetRenderTarget(lightmap); + GraphicsDevice.Clear(Color.White * 0f); + spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointClamp, null, null); + spriteBatch.Draw(staminaRect, lightmap.Bounds, currentLocation.name.Equals("UndergroundMine") ? mine.getLightingColor(gameTime) : ((!ambientLight.Equals(Color.White) && (!isRaining || !currentLocation.isOutdoors)) ? ambientLight : outdoorLight)); + for (int i = 0; i < currentLightSources.Count; i++) + { + if (Utility.isOnScreen(currentLightSources.ElementAt(i).position, (int) (currentLightSources.ElementAt(i).radius * tileSize * 4f))) + { + spriteBatch.Draw(currentLightSources.ElementAt(i).lightTexture, GlobalToLocal(viewport, currentLightSources.ElementAt(i).position) / options.lightingQuality, currentLightSources.ElementAt(i).lightTexture.Bounds, currentLightSources.ElementAt(i).color, 0f, new Vector2(currentLightSources.ElementAt(i).lightTexture.Bounds.Center.X, currentLightSources.ElementAt(i).lightTexture.Bounds.Center.Y), currentLightSources.ElementAt(i).radius / options.lightingQuality, SpriteEffects.None, 0.9f); + } + } + spriteBatch.End(); + GraphicsDevice.SetRenderTarget(ZoomLevelIsOne ? null : Screen); + } + if (bloomDay) + { + bloom?.BeginDraw(); + } + GraphicsDevice.Clear(BgColour); + spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); + GraphicsEvents.InvokeOnPreRenderEvent(null, EventArgs.Empty); + background?.draw(spriteBatch); + mapDisplayDevice.BeginScene(spriteBatch); + currentLocation.Map.GetLayer("Back").Draw(mapDisplayDevice, viewport, Location.Origin, false, pixelZoom); + currentLocation.drawWater(spriteBatch); + if (CurrentEvent == null) + { + using (List.Enumerator enumerator = currentLocation.characters.GetEnumerator()) + { + while (enumerator.MoveNext()) + { + NPC current = enumerator.Current; + if (current != null && !current.swimming && !current.hideShadow && !current.IsMonster && !currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(current.getTileLocation())) + { + spriteBatch.Draw(shadowTexture, GlobalToLocal(viewport, current.position + new Vector2(current.sprite.spriteWidth * pixelZoom / 2f, current.GetBoundingBox().Height + (current.IsMonster ? 0 : (pixelZoom * 3)))), shadowTexture.Bounds, Color.White, 0f, new Vector2(shadowTexture.Bounds.Center.X, shadowTexture.Bounds.Center.Y), (pixelZoom + current.yJumpOffset / 40f) * current.scale, SpriteEffects.None, Math.Max(0f, current.getStandingY() / 10000f) - 1E-06f); + } + } + goto IL_B30; + } + } + foreach (NPC current2 in CurrentEvent.actors) + { + if (!current2.swimming && !current2.hideShadow && !currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(current2.getTileLocation())) + { + spriteBatch.Draw(shadowTexture, GlobalToLocal(viewport, current2.position + new Vector2(current2.sprite.spriteWidth * pixelZoom / 2f, current2.GetBoundingBox().Height + (current2.IsMonster ? 0 : (pixelZoom * 3)))), shadowTexture.Bounds, Color.White, 0f, new Vector2(shadowTexture.Bounds.Center.X, shadowTexture.Bounds.Center.Y), (pixelZoom + current2.yJumpOffset / 40f) * current2.scale, SpriteEffects.None, Math.Max(0f, current2.getStandingY() / 10000f) - 1E-06f); + } + } + IL_B30: + if (!player.swimming && !player.isRidingHorse() && !currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(player.getTileLocation())) + { + spriteBatch.Draw(shadowTexture, GlobalToLocal(player.position + new Vector2(32f, 24f)), shadowTexture.Bounds, Color.White, 0f, new Vector2(shadowTexture.Bounds.Center.X, shadowTexture.Bounds.Center.Y), 4f - (((player.running || player.usingTool) && player.FarmerSprite.indexInCurrentAnimation > 1) ? (Math.Abs(FarmerRenderer.featureYOffsetPerFrame[player.FarmerSprite.CurrentFrame]) * 0.5f) : 0f), SpriteEffects.None, 0f); + } + currentLocation.Map.GetLayer("Buildings").Draw(mapDisplayDevice, viewport, Location.Origin, false, pixelZoom); + mapDisplayDevice.EndScene(); + spriteBatch.End(); + spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); + if (CurrentEvent == null) + { + using (List.Enumerator enumerator3 = currentLocation.characters.GetEnumerator()) + { + while (enumerator3.MoveNext()) + { + NPC current3 = enumerator3.Current; + if (current3 != null && !current3.swimming && !current3.hideShadow && currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(current3.getTileLocation())) + { + spriteBatch.Draw(shadowTexture, GlobalToLocal(viewport, current3.position + new Vector2(current3.sprite.spriteWidth * pixelZoom / 2f, current3.GetBoundingBox().Height + (current3.IsMonster ? 0 : (pixelZoom * 3)))), shadowTexture.Bounds, Color.White, 0f, new Vector2(shadowTexture.Bounds.Center.X, shadowTexture.Bounds.Center.Y), (pixelZoom + current3.yJumpOffset / 40f) * current3.scale, SpriteEffects.None, Math.Max(0f, current3.getStandingY() / 10000f) - 1E-06f); + } + } + goto IL_F5F; + } + } + foreach (NPC current4 in CurrentEvent.actors) + { + if (!current4.swimming && !current4.hideShadow && currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(current4.getTileLocation())) + { + spriteBatch.Draw(shadowTexture, GlobalToLocal(viewport, current4.position + new Vector2(current4.sprite.spriteWidth * pixelZoom / 2f, current4.GetBoundingBox().Height + (current4.IsMonster ? 0 : (pixelZoom * 3)))), shadowTexture.Bounds, Color.White, 0f, new Vector2(shadowTexture.Bounds.Center.X, shadowTexture.Bounds.Center.Y), (pixelZoom + current4.yJumpOffset / 40f) * current4.scale, SpriteEffects.None, Math.Max(0f, current4.getStandingY() / 10000f) - 1E-06f); + } + } + IL_F5F: + if (!player.swimming && !player.isRidingHorse() && currentLocation.shouldShadowBeDrawnAboveBuildingsLayer(player.getTileLocation())) + { + spriteBatch.Draw(shadowTexture, GlobalToLocal(player.position + new Vector2(32f, 24f)), shadowTexture.Bounds, Color.White, 0f, new Vector2(shadowTexture.Bounds.Center.X, shadowTexture.Bounds.Center.Y), 4f - (((player.running || player.usingTool) && player.FarmerSprite.indexInCurrentAnimation > 1) ? (Math.Abs(FarmerRenderer.featureYOffsetPerFrame[player.FarmerSprite.CurrentFrame]) * 0.5f) : 0f), SpriteEffects.None, Math.Max(0.0001f, player.getStandingY() / 10000f + 0.00011f) - 0.0001f); + } + if (displayFarmer) + { + player.draw(spriteBatch); + } + if ((eventUp || killScreen) && !killScreen) + { + currentLocation.currentEvent?.draw(spriteBatch); + } + if (player.currentUpgrade != null && player.currentUpgrade.daysLeftTillUpgradeDone <= 3 && currentLocation.Name.Equals("Farm")) + { + spriteBatch.Draw(player.currentUpgrade.workerTexture, GlobalToLocal(viewport, player.currentUpgrade.positionOfCarpenter), player.currentUpgrade.getSourceRectangle(), Color.White, 0f, Vector2.Zero, 1f, SpriteEffects.None, (player.currentUpgrade.positionOfCarpenter.Y + tileSize * 3 / 4) / 10000f); + } + currentLocation.draw(spriteBatch); + if (eventUp && currentLocation.currentEvent?.messageToScreen != null) + { + drawWithBorder(currentLocation.currentEvent.messageToScreen, Color.Black, Color.White, new Vector2(graphics.GraphicsDevice.Viewport.TitleSafeArea.Width / 2 - borderFont.MeasureString(currentLocation.currentEvent.messageToScreen).X / 2f, graphics.GraphicsDevice.Viewport.TitleSafeArea.Height - tileSize), 0f, 1f, 0.999f); + } + if (player.ActiveObject == null && (player.UsingTool || pickingTool) && player.CurrentTool != null && (!player.CurrentTool.Name.Equals("Seeds") || pickingTool)) + { + drawTool(player); + } + if (currentLocation.Name.Equals("Farm")) + { + //typeof (Game1).GetMethod("drawFarmBuildings", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(Program.gamePtr, null); + DrawFarmBuildings.Invoke(Program.gamePtr, null); + //this.drawFarmBuildings(); + } + if (tvStation >= 0) + { + spriteBatch.Draw(tvStationTexture, GlobalToLocal(viewport, new Vector2(6 * tileSize + tileSize / 4, 2 * tileSize + tileSize / 2)), new Rectangle(tvStation * 24, 0, 24, 15), Color.White, 0f, Vector2.Zero, 4f, SpriteEffects.None, 1E-08f); + } + if (panMode) + { + spriteBatch.Draw(fadeToBlackRect, new Rectangle((int) Math.Floor((getOldMouseX() + viewport.X) / (double) tileSize) * tileSize - viewport.X, (int) Math.Floor((getOldMouseY() + viewport.Y) / (double) tileSize) * tileSize - viewport.Y, tileSize, tileSize), Color.Lime * 0.75f); + foreach (Warp current5 in currentLocation.warps) + { + spriteBatch.Draw(fadeToBlackRect, new Rectangle(current5.X * tileSize - viewport.X, current5.Y * tileSize - viewport.Y, tileSize, tileSize), Color.Red * 0.75f); + } + } + mapDisplayDevice.BeginScene(spriteBatch); + currentLocation.Map.GetLayer("Front").Draw(mapDisplayDevice, viewport, Location.Origin, false, pixelZoom); + mapDisplayDevice.EndScene(); + currentLocation.drawAboveFrontLayer(spriteBatch); + spriteBatch.End(); + spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); + if (currentLocation.Name.Equals("Farm") && stats.SeedsSown >= 200u) + { + spriteBatch.Draw(debrisSpriteSheet, GlobalToLocal(viewport, new Vector2(3 * tileSize + tileSize / 4, tileSize + tileSize / 3)), getSourceRectForStandardTileSheet(debrisSpriteSheet, 16), Color.White); + spriteBatch.Draw(debrisSpriteSheet, GlobalToLocal(viewport, new Vector2(4 * tileSize + tileSize, 2 * tileSize + tileSize)), getSourceRectForStandardTileSheet(debrisSpriteSheet, 16), Color.White); + spriteBatch.Draw(debrisSpriteSheet, GlobalToLocal(viewport, new Vector2(5 * tileSize, 2 * tileSize)), getSourceRectForStandardTileSheet(debrisSpriteSheet, 16), Color.White); + spriteBatch.Draw(debrisSpriteSheet, GlobalToLocal(viewport, new Vector2(3 * tileSize + tileSize / 2, 3 * tileSize)), getSourceRectForStandardTileSheet(debrisSpriteSheet, 16), Color.White); + spriteBatch.Draw(debrisSpriteSheet, GlobalToLocal(viewport, new Vector2(5 * tileSize - tileSize / 4, tileSize)), getSourceRectForStandardTileSheet(debrisSpriteSheet, 16), Color.White); + spriteBatch.Draw(debrisSpriteSheet, GlobalToLocal(viewport, new Vector2(4 * tileSize, 3 * tileSize + tileSize / 6)), getSourceRectForStandardTileSheet(debrisSpriteSheet, 16), Color.White); + spriteBatch.Draw(debrisSpriteSheet, GlobalToLocal(viewport, new Vector2(4 * tileSize + tileSize / 5, 2 * tileSize + tileSize / 3)), getSourceRectForStandardTileSheet(debrisSpriteSheet, 16), Color.White); + } + if (displayFarmer && player.ActiveObject != null && player.ActiveObject.bigCraftable && checkBigCraftableBoundariesForFrontLayer() && currentLocation.Map.GetLayer("Front").PickTile(new Location(player.getStandingX(), player.getStandingY()), viewport.Size) == null) + { + drawPlayerHeldObject(player); + } + else if (displayFarmer && player.ActiveObject != null && ((currentLocation.Map.GetLayer("Front").PickTile(new Location((int) player.position.X, (int) player.position.Y - tileSize * 3 / 5), viewport.Size) != null && !currentLocation.Map.GetLayer("Front").PickTile(new Location((int) player.position.X, (int) player.position.Y - tileSize * 3 / 5), viewport.Size).TileIndexProperties.ContainsKey("FrontAlways")) || (currentLocation.Map.GetLayer("Front").PickTile(new Location(player.GetBoundingBox().Right, (int) player.position.Y - tileSize * 3 / 5), viewport.Size) != null && !currentLocation.Map.GetLayer("Front").PickTile(new Location(player.GetBoundingBox().Right, (int) player.position.Y - tileSize * 3 / 5), viewport.Size).TileIndexProperties.ContainsKey("FrontAlways")))) + { + drawPlayerHeldObject(player); + } + if ((player.UsingTool || pickingTool) && player.CurrentTool != null && (!player.CurrentTool.Name.Equals("Seeds") || pickingTool) && currentLocation.Map.GetLayer("Front").PickTile(new Location(player.getStandingX(), (int) player.position.Y - tileSize * 3 / 5), viewport.Size) != null && currentLocation.Map.GetLayer("Front").PickTile(new Location(player.getStandingX(), player.getStandingY()), viewport.Size) == null) + { + drawTool(player); + } + if (currentLocation.Map.GetLayer("AlwaysFront") != null) + { + mapDisplayDevice.BeginScene(spriteBatch); + currentLocation.Map.GetLayer("AlwaysFront").Draw(mapDisplayDevice, viewport, Location.Origin, false, pixelZoom); + mapDisplayDevice.EndScene(); + } + if (toolHold > 400f && player.CurrentTool.UpgradeLevel >= 1 && player.canReleaseTool) + { + Color color = Color.White; + switch ((int) (toolHold / 600f) + 2) + { + case 1: + color = Tool.copperColor; + break; + case 2: + color = Tool.steelColor; + break; + case 3: + color = Tool.goldColor; + break; + case 4: + color = Tool.iridiumColor; + break; + } + spriteBatch.Draw(littleEffect, new Rectangle((int) player.getLocalPosition(viewport).X - 2, (int) player.getLocalPosition(viewport).Y - (player.CurrentTool.Name.Equals("Watering Can") ? 0 : tileSize) - 2, (int) (toolHold % 600f * 0.08f) + 4, tileSize / 8 + 4), Color.Black); + spriteBatch.Draw(littleEffect, new Rectangle((int) player.getLocalPosition(viewport).X, (int) player.getLocalPosition(viewport).Y - (player.CurrentTool.Name.Equals("Watering Can") ? 0 : tileSize), (int) (toolHold % 600f * 0.08f), tileSize / 8), color); + } + if (isDebrisWeather && currentLocation.IsOutdoors && !currentLocation.ignoreDebrisWeather && !currentLocation.Name.Equals("Desert") && viewport.X > -10) + { + foreach (WeatherDebris current6 in debrisWeather) + { + current6.draw(spriteBatch); + } + } + farmEvent?.draw(spriteBatch); + if (currentLocation.LightLevel > 0f && timeOfDay < 2000) + { + spriteBatch.Draw(fadeToBlackRect, graphics.GraphicsDevice.Viewport.Bounds, Color.Black * currentLocation.LightLevel); + } + if (screenGlow) + { + spriteBatch.Draw(fadeToBlackRect, graphics.GraphicsDevice.Viewport.Bounds, screenGlowColor * screenGlowAlpha); + } + currentLocation.drawAboveAlwaysFrontLayer(spriteBatch); + if (player.CurrentTool is FishingRod && ((player.CurrentTool as FishingRod).isTimingCast || (player.CurrentTool as FishingRod).castingChosenCountdown > 0f || (player.CurrentTool as FishingRod).fishCaught || (player.CurrentTool as FishingRod).showingTreasure)) + { + player.CurrentTool.draw(spriteBatch); + } + if (isRaining && currentLocation.IsOutdoors && !currentLocation.Name.Equals("Desert") && !(currentLocation is Summit) && (!eventUp || currentLocation.isTileOnMap(new Vector2(viewport.X / tileSize, viewport.Y / tileSize)))) + { + for (int j = 0; j < rainDrops.Length; j++) + { + spriteBatch.Draw(rainTexture, rainDrops[j].position, getSourceRectForStandardTileSheet(rainTexture, rainDrops[j].frame), Color.White); + } + } + + spriteBatch.End(); + + //base.Draw(gameTime); + + spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); + if (eventUp && currentLocation.currentEvent != null) + { + foreach (NPC current7 in currentLocation.currentEvent.actors) + { + if (current7.isEmoting) + { + Vector2 localPosition = current7.getLocalPosition(viewport); + localPosition.Y -= tileSize * 2 + pixelZoom * 3; + if (current7.age == 2) + { + localPosition.Y += tileSize / 2; + } + else if (current7.gender == 1) + { + localPosition.Y += tileSize / 6; + } + spriteBatch.Draw(emoteSpriteSheet, localPosition, new Rectangle(current7.CurrentEmoteIndex * (tileSize / 4) % emoteSpriteSheet.Width, current7.CurrentEmoteIndex * (tileSize / 4) / emoteSpriteSheet.Width * (tileSize / 4), tileSize / 4, tileSize / 4), Color.White, 0f, Vector2.Zero, 4f, SpriteEffects.None, current7.getStandingY() / 10000f); + } + } + } + spriteBatch.End(); + if (drawLighting) + { + spriteBatch.Begin(SpriteSortMode.Deferred, new BlendState + { + ColorBlendFunction = BlendFunction.ReverseSubtract, + ColorDestinationBlend = Blend.One, + ColorSourceBlend = Blend.SourceColor + }, SamplerState.LinearClamp, null, null); + spriteBatch.Draw(lightmap, Vector2.Zero, lightmap.Bounds, Color.White, 0f, Vector2.Zero, options.lightingQuality, SpriteEffects.None, 1f); + if (isRaining && currentLocation.isOutdoors && !(currentLocation is Desert)) + { + spriteBatch.Draw(staminaRect, graphics.GraphicsDevice.Viewport.Bounds, Color.OrangeRed * 0.45f); + } + spriteBatch.End(); + } + spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); + if (drawGrid) + { + int num2 = -viewport.X % tileSize; + float num3 = -(float) viewport.Y % tileSize; + for (int k = num2; k < graphics.GraphicsDevice.Viewport.Width; k += tileSize) + { + spriteBatch.Draw(staminaRect, new Rectangle(k, (int) num3, 1, graphics.GraphicsDevice.Viewport.Height), Color.Red * 0.5f); + } + for (float num4 = num3; num4 < (float) graphics.GraphicsDevice.Viewport.Height; num4 += (float) tileSize) + { + spriteBatch.Draw(staminaRect, new Rectangle(num2, (int) num4, graphics.GraphicsDevice.Viewport.Width, 1), Color.Red * 0.5f); + } + } + if (currentBillboard != 0) + { + drawBillboard(); + } + + GraphicsEvents.InvokeOnPreRenderHudEventNoCheck(null, EventArgs.Empty); + if ((displayHUD || eventUp) && currentBillboard == 0 && gameMode == 3 && !freezeControls && !panMode) + { + GraphicsEvents.InvokeOnPreRenderHudEvent(null, EventArgs.Empty); + //typeof (Game1).GetMethod("drawHUD", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(Program.gamePtr, null); + DrawHUD.Invoke(Program.gamePtr, null); + GraphicsEvents.InvokeOnPostRenderHudEvent(null, EventArgs.Empty); + //this.drawHUD(); + } + else if (activeClickableMenu == null && farmEvent == null) + { + spriteBatch.Draw(mouseCursors, new Vector2(getOldMouseX(), getOldMouseY()), getSourceRectForStandardTileSheet(mouseCursors, 0, 16, 16), Color.White, 0f, Vector2.Zero, 4f + dialogueButtonScale / 150f, SpriteEffects.None, 1f); + } + GraphicsEvents.InvokeOnPostRenderHudEventNoCheck(null, EventArgs.Empty); + + if (hudMessages.Any() && (!eventUp || isFestival())) + { + for (int l = hudMessages.Count - 1; l >= 0; l--) + { + hudMessages[l].draw(spriteBatch, l); + } + } + } + farmEvent?.draw(spriteBatch); + if (dialogueUp && !nameSelectUp && !messagePause && !(activeClickableMenu is DialogueBox)) + { + //typeof (Game1).GetMethod("drawDialogueBox", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(Program.gamePtr, null); + DrawDialogueBox.Invoke(Program.gamePtr, null); + //this.drawDialogueBox(); + } + if (progressBar) + { + spriteBatch.Draw(fadeToBlackRect, new Rectangle((graphics.GraphicsDevice.Viewport.TitleSafeArea.Width - dialogueWidth) / 2, graphics.GraphicsDevice.Viewport.TitleSafeArea.Bottom - tileSize * 2, dialogueWidth, tileSize / 2), Color.LightGray); + spriteBatch.Draw(staminaRect, new Rectangle((graphics.GraphicsDevice.Viewport.TitleSafeArea.Width - dialogueWidth) / 2, graphics.GraphicsDevice.Viewport.TitleSafeArea.Bottom - tileSize * 2, (int) (pauseAccumulator / pauseTime * dialogueWidth), tileSize / 2), Color.DimGray); + } + if (eventUp) + { + currentLocation.currentEvent?.drawAfterMap(spriteBatch); + } + if (isRaining && currentLocation.isOutdoors && !(currentLocation is Desert)) + { + spriteBatch.Draw(staminaRect, graphics.GraphicsDevice.Viewport.Bounds, Color.Blue * 0.2f); + } + if ((fadeToBlack || globalFade) && !menuUp && (!nameSelectUp || messagePause)) + { + spriteBatch.Draw(fadeToBlackRect, graphics.GraphicsDevice.Viewport.Bounds, Color.Black * ((gameMode == 0) ? (1f - fadeToBlackAlpha) : fadeToBlackAlpha)); + } + else if (flashAlpha > 0f) + { + if (options.screenFlash) + { + spriteBatch.Draw(fadeToBlackRect, graphics.GraphicsDevice.Viewport.Bounds, Color.White * Math.Min(1f, flashAlpha)); + } + flashAlpha -= 0.1f; + } + if ((messagePause || globalFade) && dialogueUp) + { + //typeof (Game1).GetMethod("drawDialogueBox", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(Program.gamePtr, null); + DrawDialogueBox.Invoke(Program.gamePtr, null); + //this.drawDialogueBox(); + } + foreach (TemporaryAnimatedSprite current8 in screenOverlayTempSprites) + { + current8.draw(spriteBatch, true); + } + if (debugMode) + { + spriteBatch.DrawString(smallFont, string.Concat(new object[] + { + panMode ? ((getOldMouseX() + viewport.X) / tileSize + "," + (getOldMouseY() + viewport.Y) / tileSize) : string.Concat("aplayer: ", player.getStandingX() / tileSize, ", ", player.getStandingY() / tileSize), + Environment.NewLine, + "debugOutput: ", + debugOutput + }), new Vector2(GraphicsDevice.Viewport.TitleSafeArea.X, GraphicsDevice.Viewport.TitleSafeArea.Y), Color.Red, 0f, Vector2.Zero, 1f, SpriteEffects.None, 0.9999999f); + } + if (inputMode) + { + spriteBatch.DrawString(smallFont, "Input: " + debugInput, new Vector2(tileSize, tileSize * 3), Color.Purple); + } + if (showKeyHelp) + { + spriteBatch.DrawString(smallFont, keyHelpString, new Vector2(tileSize, viewport.Height - tileSize - (dialogueUp ? (tileSize * 3 + (isQuestion ? (questionChoices.Count * tileSize) : 0)) : 0) - smallFont.MeasureString(keyHelpString).Y), Color.LightGray, 0f, Vector2.Zero, 1f, SpriteEffects.None, 0.9999999f); + } + + GraphicsEvents.InvokeOnPreRenderGuiEventNoCheck(null, EventArgs.Empty); + if (activeClickableMenu != null) + { + GraphicsEvents.InvokeOnPreRenderGuiEvent(null, EventArgs.Empty); + activeClickableMenu.draw(spriteBatch); + GraphicsEvents.InvokeOnPostRenderGuiEvent(null, EventArgs.Empty); + } + else + { + farmEvent?.drawAboveEverything(spriteBatch); + } + GraphicsEvents.InvokeOnPostRenderGuiEventNoCheck(null, EventArgs.Empty); + + GraphicsEvents.InvokeOnPostRenderEvent(null, EventArgs.Empty); + spriteBatch.End(); + + GraphicsEvents.InvokeDrawInRenderTargetTick(); + + if (!ZoomLevelIsOne) + { + GraphicsDevice.SetRenderTarget(null); + GraphicsDevice.Clear(BgColour); + spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullNone); + spriteBatch.Draw(Screen, Vector2.Zero, Screen.Bounds, Color.White, 0f, Vector2.Zero, options.zoomLevel, SpriteEffects.None, 1f); + spriteBatch.End(); + } + + GraphicsEvents.InvokeDrawTick(); + } + catch (Exception ex) + { + Log.Error("An error occured in the overridden draw loop: " + ex); + } + + #endregion + } + else + { + #region Base Draw Call + + try + { + base.Draw(gameTime); + } + catch (Exception ex) + { + Log.AsyncR("An error occured in the base draw loop: " + ex); + Console.ReadKey(); + } + + GraphicsEvents.InvokeDrawTick(); + + if (Constants.EnableDrawingIntoRenderTarget) + { + if (!options.zoomLevel.Equals(1.0f)) + { + if (Screen.RenderTargetUsage == RenderTargetUsage.DiscardContents) + { + Screen = new RenderTarget2D(graphics.GraphicsDevice, Math.Min(4096, (int) (Window.ClientBounds.Width * (1.0 / options.zoomLevel))), + Math.Min(4096, (int) (Window.ClientBounds.Height * (1.0 / options.zoomLevel))), + false, SurfaceFormat.Color, DepthFormat.Depth16, 1, RenderTargetUsage.PreserveContents); + } + GraphicsDevice.SetRenderTarget(Screen); + } + + // Not beginning the batch due to inconsistancies with the standard draw tick... + //spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); + + GraphicsEvents.InvokeDrawInRenderTargetTick(); + + //spriteBatch.End(); + + //Re-draw the HUD + spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.PointClamp, null, null); + activeClickableMenu?.draw(spriteBatch); + /* + if ((displayHUD || eventUp) && currentBillboard == 0 && gameMode == 3 && !freezeControls && !panMode) + typeof (Game1).GetMethod("drawHUD", BindingFlags.NonPublic | BindingFlags.Instance)?.Invoke(Program.gamePtr, null); + */ + spriteBatch.Draw(mouseCursors, new Vector2(getOldMouseX(), getOldMouseY()), getSourceRectForStandardTileSheet(mouseCursors, options.gamepadControls ? 44 : 0, 16, 16), Color.White, 0.0f, Vector2.Zero, pixelZoom + dialogueButtonScale / 150f, SpriteEffects.None, 1f); + + spriteBatch.End(); + + if (!options.zoomLevel.Equals(1.0f)) + { + GraphicsDevice.SetRenderTarget(null); + spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullNone); + spriteBatch.Draw(Screen, Vector2.Zero, Screen.Bounds, Color.White, 0.0f, Vector2.Zero, options.zoomLevel, SpriteEffects.None, 1f); + spriteBatch.End(); + } + } + + #endregion + } + + if (Debug) + { + spriteBatch.Begin(); + + int i = 0; + while (DebugMessageQueue.Any()) + { + string s = DebugMessageQueue.Dequeue(); + spriteBatch.DrawString(smoothFont, s, new Vector2(0, i * 14), Color.CornflowerBlue); + i++; + } + GraphicsEvents.InvokeDrawDebug(null, EventArgs.Empty); + + spriteBatch.End(); + } + else + { + DebugMessageQueue.Clear(); + } + } + + [Obsolete("Do not use at this time.")] + // ReSharper disable once UnusedMember.Local + private static int RegisterModItem(SObject modItem) + { + if (modItem.HasBeenRegistered) + { + Log.AsyncR($"The item {modItem.Name} has already been registered with ID {modItem.RegisteredId}"); + return modItem.RegisteredId; + } + var newId = LowestModItemID; + if (ModItems.Count > 0) + newId = Math.Max(LowestModItemID, ModItems.OrderBy(x => x.Key).First().Key + 1); + ModItems.Add(newId, modItem); + modItem.HasBeenRegistered = true; + modItem.RegisteredId = newId; + return newId; + } + + [Obsolete("Do not use at this time.")] + // ReSharper disable once UnusedMember.Local + private static SObject PullModItemFromDict(int id, bool isIndex) + { + if (isIndex) + { + if (ModItems.ElementAtOrDefault(id).Value != null) + { + return ModItems.ElementAt(id).Value.Clone(); + } + Log.AsyncR("ModItem Dictionary does not contain index: " + id); + return null; + } + if (ModItems.ContainsKey(id)) + { + return ModItems[id].Clone(); + } + Log.AsyncR("ModItem Dictionary does not contain ID: " + id); + return null; + } + + private void UpdateEventCalls() + { + KStateNow = Keyboard.GetState(); + + MStateNow = Mouse.GetState(); + + foreach (var k in FramePressedKeys) + ControlEvents.InvokeKeyPressed(k); + + foreach (var k in FrameReleasedKeys) + ControlEvents.InvokeKeyReleased(k); + + for (var i = PlayerIndex.One; i <= PlayerIndex.Four; i++) + { + var buttons = GetFramePressedButtons(i); + foreach (var b in buttons) + { + if (b == Buttons.LeftTrigger || b == Buttons.RightTrigger) + { + ControlEvents.InvokeTriggerPressed(i, b, b == Buttons.LeftTrigger ? GamePad.GetState(i).Triggers.Left : GamePad.GetState(i).Triggers.Right); + } + else + { + ControlEvents.InvokeButtonPressed(i, b); + } + } + } + + for (var i = PlayerIndex.One; i <= PlayerIndex.Four; i++) + { + foreach (var b in GetFrameReleasedButtons(i)) + { + if (b == Buttons.LeftTrigger || b == Buttons.RightTrigger) + { + ControlEvents.InvokeTriggerReleased(i, b, b == Buttons.LeftTrigger ? GamePad.GetState(i).Triggers.Left : GamePad.GetState(i).Triggers.Right); + } + else + { + ControlEvents.InvokeButtonReleased(i, b); + } + } + } + + + if (KStateNow != KStatePrior) + { + ControlEvents.InvokeKeyboardChanged(KStatePrior, KStateNow); + } + + if (MStateNow != MStatePrior) + { + ControlEvents.InvokeMouseChanged(MStatePrior, MStateNow); + MStatePrior = MStateNow; + } + + if (activeClickableMenu != null && activeClickableMenu != PreviousActiveMenu) + { + MenuEvents.InvokeMenuChanged(PreviousActiveMenu, activeClickableMenu); + PreviousActiveMenu = activeClickableMenu; + WasMenuClosedInvoked = false; + } + + if (!WasMenuClosedInvoked && PreviousActiveMenu != null && activeClickableMenu == null) + { + MenuEvents.InvokeMenuClosed(PreviousActiveMenu); + WasMenuClosedInvoked = true; + } + + if (locations.GetHash() != PreviousGameLocations) + { + LocationEvents.InvokeLocationsChanged(locations); + PreviousGameLocations = locations.GetHash(); + } + + if (currentLocation != PreviousGameLocation) + { + LocationEvents.InvokeCurrentLocationChanged(PreviousGameLocation, currentLocation); + PreviousGameLocation = currentLocation; + } + + if (player != null && player != PreviousFarmer) + { + PlayerEvents.InvokeFarmerChanged(PreviousFarmer, player); + PreviousFarmer = player; + } + + if (player != null && player.combatLevel != PreviousCombatLevel) + { + PlayerEvents.InvokeLeveledUp(EventArgsLevelUp.LevelType.Combat, player.combatLevel); + PreviousCombatLevel = player.combatLevel; + } + + if (player != null && player.farmingLevel != PreviousFarmingLevel) + { + PlayerEvents.InvokeLeveledUp(EventArgsLevelUp.LevelType.Farming, player.farmingLevel); + PreviousFarmingLevel = player.farmingLevel; + } + + if (player != null && player.fishingLevel != PreviousFishingLevel) + { + PlayerEvents.InvokeLeveledUp(EventArgsLevelUp.LevelType.Fishing, player.fishingLevel); + PreviousFishingLevel = player.fishingLevel; + } + + if (player != null && player.foragingLevel != PreviousForagingLevel) + { + PlayerEvents.InvokeLeveledUp(EventArgsLevelUp.LevelType.Foraging, player.foragingLevel); + PreviousForagingLevel = player.foragingLevel; + } + + if (player != null && player.miningLevel != PreviousMiningLevel) + { + PlayerEvents.InvokeLeveledUp(EventArgsLevelUp.LevelType.Mining, player.miningLevel); + PreviousMiningLevel = player.miningLevel; + } + + if (player != null && player.luckLevel != PreviousLuckLevel) + { + PlayerEvents.InvokeLeveledUp(EventArgsLevelUp.LevelType.Luck, player.luckLevel); + PreviousLuckLevel = player.luckLevel; + } + + List changedItems; + if (player != null && HasInventoryChanged(player.items, out changedItems)) + { + PlayerEvents.InvokeInventoryChanged(player.items, changedItems); + PreviousItems = player.items.Where(n => n != null).ToDictionary(n => n, n => n.Stack); + } + + var objectHash = currentLocation?.objects?.GetHash(); + if (objectHash != null && PreviousLocationObjects != objectHash) + { + LocationEvents.InvokeOnNewLocationObject(currentLocation.objects); + PreviousLocationObjects = objectHash ?? -1; + } + + if (timeOfDay != PreviousTimeOfDay) + { + TimeEvents.InvokeTimeOfDayChanged(PreviousTimeOfDay, timeOfDay); + PreviousTimeOfDay = timeOfDay; + } + + if (dayOfMonth != PreviousDayOfMonth) + { + TimeEvents.InvokeDayOfMonthChanged(PreviousDayOfMonth, dayOfMonth); + PreviousDayOfMonth = dayOfMonth; + } + + if (currentSeason != PreviousSeasonOfYear) + { + TimeEvents.InvokeSeasonOfYearChanged(PreviousSeasonOfYear, currentSeason); + PreviousSeasonOfYear = currentSeason; + } + + if (year != PreviousYearOfGame) + { + TimeEvents.InvokeYearOfGameChanged(PreviousYearOfGame, year); + PreviousYearOfGame = year; + } + + //NOTE THAT THIS MUST CHECK BEFORE SETTING IT TO TRUE BECAUSE OF SOME SILLY ISSUES + if (FireLoadedGameEvent) + { + PlayerEvents.InvokeLoadedGame(new EventArgsLoadedGameChanged(hasLoadedGame)); + FireLoadedGameEvent = false; + } + + if (hasLoadedGame != PreviouslyLoadedGame) + { + FireLoadedGameEvent = true; + PreviouslyLoadedGame = hasLoadedGame; + } + + if (mine != null && PreviousMineLevel != mine.mineLevel) + { + MineEvents.InvokeMineLevelChanged(PreviousMineLevel, mine.mineLevel); + PreviousMineLevel = mine.mineLevel; + } + + if (PreviousIsNewDay != newDay) + { + TimeEvents.InvokeOnNewDay(PreviousDayOfMonth, dayOfMonth, newDay); + PreviousIsNewDay = newDay; + } + } + + private bool HasInventoryChanged(List items, out List changedItems) + { + changedItems = new List(); + IEnumerable actualItems = items.Where(n => n != null).ToArray(); + foreach (var item in actualItems) + { + if (PreviousItems != null && PreviousItems.ContainsKey(item)) + { + if (PreviousItems[item] != item.Stack) + { + changedItems.Add(new ItemStackChange {Item = item, StackChange = item.Stack - PreviousItems[item], ChangeType = ChangeType.StackChange}); + } + } + else + { + changedItems.Add(new ItemStackChange {Item = item, StackChange = item.Stack, ChangeType = ChangeType.Added}); + } + } + + if (PreviousItems != null) + { + changedItems.AddRange(PreviousItems.Where(n => actualItems.All(i => i != n.Key)).Select(n => + new ItemStackChange {Item = n.Key, StackChange = -n.Key.Stack, ChangeType = ChangeType.Removed})); + } + + return changedItems.Any(); + } + + /// + /// Invokes a private, non-static method in Game1 via Reflection + /// + /// The name of the method + /// Any parameters needed + /// Whatever the method normally returns. Null if void. + [Obsolete("This is very slow. Cache the method info and then invoke it with InvokeMethodInfo().")] + public static object InvokeBasePrivateInstancedMethod(string name, params object[] parameters) + { + try + { + return typeof(Game1).GetMethod(name, BindingFlags.NonPublic | BindingFlags.Instance).Invoke(Program.gamePtr, parameters); + } + catch + { + Log.AsyncR("Failed to call base method: " + name); + return null; + } + } + + /// + /// Invokes a given method info with the supplied parameters + /// + /// + /// + /// + public static object InvokeMethodInfo(MethodInfo mi, params object[] parameters) + { + try + { + return mi.Invoke(Program.gamePtr, parameters); + } + catch + { + Log.AsyncR("Failed to call base method: " + mi.Name); + return null; + } + } + + /// + /// Queue's a message to be drawn in Debug mode (F3) + /// + /// + public static bool QueueDebugMessage(string message) + { + if (!Debug) + return false; + + if (DebugMessageQueue.Count > 32) + return false; + + DebugMessageQueue.Enqueue(message); + return true; + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Inheritance/SObject.cs b/src/StardewModdingAPI/Inheritance/SObject.cs new file mode 100644 index 00000000..639b85b1 --- /dev/null +++ b/src/StardewModdingAPI/Inheritance/SObject.cs @@ -0,0 +1,277 @@ +using System; +using System.Xml.Serialization; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using StardewValley; +using Object = StardewValley.Object; + +#pragma warning disable 1591 + +namespace StardewModdingAPI.Inheritance +{ + [Obsolete("Do not use at this time.")] + public class SObject : Object + { + public SObject() + { + name = "Modded Item Name"; + Description = "Modded Item Description"; + CategoryName = "Modded Item Category"; + Category = 4163; + CategoryColour = Color.White; + IsPassable = false; + IsPlaceable = false; + boundingBox = new Rectangle(0, 0, 64, 64); + MaxStackSize = 999; + + type = "interactive"; + } + + public override string Name + { + get { return name; } + set { name = value; } + } + + public string Description { get; set; } + public Texture2D Texture { get; set; } + public string CategoryName { get; set; } + public Color CategoryColour { get; set; } + public bool IsPassable { get; set; } + public bool IsPlaceable { get; set; } + public bool HasBeenRegistered { get; set; } + public int RegisteredId { get; set; } + + public int MaxStackSize { get; set; } + + public bool WallMounted { get; set; } + public Vector2 DrawPosition { get; set; } + + public bool FlaggedForPickup { get; set; } + + [XmlIgnore] + public Vector2 CurrentMouse { get; protected set; } + + [XmlIgnore] + public Vector2 PlacedAt { get; protected set; } + + public override int Stack + { + get { return stack; } + set { stack = value; } + } + + public override string getDescription() + { + return Description; + } + + public override void draw(SpriteBatch spriteBatch, int x, int y, float alpha = 1) + { + if (Texture != null) + { + spriteBatch.Draw(Texture, Game1.GlobalToLocal(Game1.viewport, new Vector2(x * Game1.tileSize + Game1.tileSize / 2 + (shakeTimer > 0 ? Game1.random.Next(-1, 2) : 0), y * Game1.tileSize + Game1.tileSize / 2 + (shakeTimer > 0 ? Game1.random.Next(-1, 2) : 0))), Game1.currentLocation.getSourceRectForObject(ParentSheetIndex), Color.White * alpha, 0f, new Vector2(8f, 8f), scale.Y > 1f ? getScale().Y : Game1.pixelZoom, flipped ? SpriteEffects.FlipHorizontally : SpriteEffects.None, (isPassable() ? getBoundingBox(new Vector2(x, y)).Top : getBoundingBox(new Vector2(x, y)).Bottom) / 10000f); + } + } + + public new void drawAsProp(SpriteBatch b) + { + } + + public override void draw(SpriteBatch spriteBatch, int xNonTile, int yNonTile, float layerDepth, float alpha = 1) + { + Log.Debug("THIS DRAW FUNCTION IS NOT IMPLEMENTED I WANT TO KNOW WHERE IT IS CALLED"); + //try + //{ + // if (Texture != null) + // { + // int targSize = Game1.tileSize; + // int midX = (xNonTile) + 32; + // int midY = (yNonTile) + 32; + + // int targX = midX - targSize / 2; + // int targY = midY - targSize / 2; + + // Rectangle targ = new Rectangle(targX, targY, targSize, targSize); + // spriteBatch.Draw(Texture, targ, null, new Color(255, 255, 255, 255f * alpha), 0, Vector2.Zero, SpriteEffects.None, layerDepth); + // //spriteBatch.Draw(Program.DebugPixel, targ, null, Color.Red, 0, Vector2.Zero, SpriteEffects.None, layerDepth); + // /* + // spriteBatch.DrawString(Game1.dialogueFont, "TARG: " + targ, new Vector2(128, 0), Color.Red); + // spriteBatch.DrawString(Game1.dialogueFont, ".", new Vector2(targX * 0.5f, targY), Color.Orange); + // spriteBatch.DrawString(Game1.dialogueFont, ".", new Vector2(targX, targY), Color.Red); + // spriteBatch.DrawString(Game1.dialogueFont, ".", new Vector2(targX * 1.5f, targY), Color.Yellow); + // spriteBatch.DrawString(Game1.dialogueFont, ".", new Vector2(targX * 2f, targY), Color.Green); + // */ + // } + //} + //catch (Exception ex) + //{ + // Log.AsyncR(ex.ToString()); + // Console.ReadKey(); + //} + } + + public override void drawInMenu(SpriteBatch spriteBatch, Vector2 location, float scaleSize, float transparency, float layerDepth, bool drawStackNumber) + { + if (isRecipe) + { + transparency = 0.5f; + scaleSize *= 0.75f; + } + + if (Texture != null) + { + var targSize = (int) (64 * scaleSize * 0.9f); + var midX = (int) (location.X + 32); + var midY = (int) (location.Y + 32); + + var targX = midX - targSize / 2; + var targY = midY - targSize / 2; + + spriteBatch.Draw(Texture, new Rectangle(targX, targY, targSize, targSize), null, new Color(255, 255, 255, transparency), 0, Vector2.Zero, SpriteEffects.None, layerDepth); + } + if (drawStackNumber) + { + var _scale = 0.5f + scaleSize; + Game1.drawWithBorder(stack.ToString(), Color.Black, Color.White, location + new Vector2(Game1.tileSize - Game1.tinyFont.MeasureString(string.Concat(stack.ToString())).X * _scale, Game1.tileSize - (float) ((double) Game1.tinyFont.MeasureString(string.Concat(stack.ToString())).Y * 3.0f / 4.0f) * _scale), 0.0f, _scale, 1f, true); + } + } + + public override void drawWhenHeld(SpriteBatch spriteBatch, Vector2 objectPosition, Farmer f) + { + if (Texture != null) + { + var targSize = 64; + var midX = (int) (objectPosition.X + 32); + var midY = (int) (objectPosition.Y + 32); + + var targX = midX - targSize / 2; + var targY = midY - targSize / 2; + + spriteBatch.Draw(Texture, new Rectangle(targX, targY, targSize, targSize), null, Color.White, 0, Vector2.Zero, SpriteEffects.None, (f.getStandingY() + 2) / 10000f); + } + } + + public override Color getCategoryColor() + { + return CategoryColour; + } + + public override string getCategoryName() + { + if (string.IsNullOrEmpty(CategoryName)) + return "Modded Item"; + return CategoryName; + } + + public override bool isPassable() + { + return IsPassable; + } + + public override bool isPlaceable() + { + return IsPlaceable; + } + + public override int maximumStackSize() + { + return MaxStackSize; + } + + public SObject Clone() + { + var toRet = new SObject + { + Name = Name, + CategoryName = CategoryName, + Description = Description, + Texture = Texture, + IsPassable = IsPassable, + IsPlaceable = IsPlaceable, + quality = quality, + scale = scale, + isSpawnedObject = isSpawnedObject, + isRecipe = isRecipe, + questItem = questItem, + stack = 1, + HasBeenRegistered = HasBeenRegistered, + RegisteredId = RegisteredId + }; + + + return toRet; + } + + public override Item getOne() + { + return Clone(); + } + + public override void actionWhenBeingHeld(Farmer who) + { + var x = Game1.oldMouseState.X + Game1.viewport.X; + var y = Game1.oldMouseState.Y + Game1.viewport.Y; + + x = x / Game1.tileSize; + y = y / Game1.tileSize; + + CurrentMouse = new Vector2(x, y); + //Program.LogDebug(canBePlacedHere(Game1.currentLocation, CurrentMouse)); + base.actionWhenBeingHeld(who); + } + + public override bool canBePlacedHere(GameLocation l, Vector2 tile) + { + //Program.LogDebug(CurrentMouse.ToString().Replace("{", "").Replace("}", "")); + if (!l.objects.ContainsKey(tile)) + return true; + + return false; + } + + public override bool placementAction(GameLocation location, int x, int y, Farmer who = null) + { + if (Game1.didPlayerJustRightClick()) + return false; + + x = x / Game1.tileSize; + y = y / Game1.tileSize; + + //Program.LogDebug(x + " - " + y); + //Console.ReadKey(); + + var key = new Vector2(x, y); + + if (!canBePlacedHere(location, key)) + return false; + + var s = Clone(); + + s.PlacedAt = key; + s.boundingBox = new Rectangle(x / Game1.tileSize * Game1.tileSize, y / Game1.tileSize * Game1.tileSize, boundingBox.Width, boundingBox.Height); + + location.objects.Add(key, s); + Log.Async($"{GetHashCode()} - {s.GetHashCode()}"); + + return true; + } + + public override void actionOnPlayerEntry() + { + //base.actionOnPlayerEntry(); + } + + public override void drawPlacementBounds(SpriteBatch spriteBatch, GameLocation location) + { + if (canBePlacedHere(location, CurrentMouse)) + { + var targSize = Game1.tileSize; + + var x = Game1.oldMouseState.X + Game1.viewport.X; + var y = Game1.oldMouseState.Y + Game1.viewport.Y; + spriteBatch.Draw(Game1.mouseCursors, new Vector2(x / Game1.tileSize * Game1.tileSize - Game1.viewport.X, y / Game1.tileSize * Game1.tileSize - Game1.viewport.Y), new Rectangle(Utility.playerCanPlaceItemHere(location, this, x, y, Game1.player) ? 194 : 210, 388, 16, 16), Color.White, 0.0f, Vector2.Zero, Game1.pixelZoom, SpriteEffects.None, 0.01f); + } + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/JsonResolver.cs b/src/StardewModdingAPI/JsonResolver.cs new file mode 100644 index 00000000..b6e763f7 --- /dev/null +++ b/src/StardewModdingAPI/JsonResolver.cs @@ -0,0 +1,212 @@ +using System; +using System.Collections.Generic; +using Microsoft.Xna.Framework; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; +using StardewModdingAPI.Inheritance; +using Object = StardewValley.Object; + +namespace StardewModdingAPI +{ + internal class JsonResolver : DefaultContractResolver + { + protected override JsonContract CreateContract(Type objectType) + { + if (objectType == typeof(Rectangle) || objectType == typeof(Rectangle?)) + { + Console.WriteLine("FOUND A RECT"); + JsonContract contract = CreateObjectContract(objectType); + contract.Converter = new RectangleConverter(); + return contract; + } + if (objectType == typeof(Object)) + { + Log.AsyncY("FOUND AN OBJECT"); + JsonContract contract = CreateObjectContract(objectType); + contract.Converter = new ObjectConverter(); + return contract; + } + return base.CreateContract(objectType); + } + } + + public class ObjectConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + Log.AsyncY("TRYING TO WRITE"); + var obj = (Object) value; + Log.AsyncY("TRYING TO WRITE"); + + var jObject = GetObject(obj); + Log.AsyncY("TRYING TO WRITE"); + + try + { + Log.AsyncY(jObject.ToString()); + } + catch (Exception ex) + { + Log.AsyncR(ex); + } + + Console.ReadKey(); + + jObject.WriteTo(writer); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var jObject = JObject.Load(reader); + + return GetObject(jObject); + } + + public override bool CanConvert(Type objectType) + { + throw new NotImplementedException(); + } + + protected static JObject GetObject(Object o) + { + try + { + var parentSheetIndex = o.parentSheetIndex; + var stack = o.stack; + var isRecipe = o.isRecipe; + var price = o.price; + var quality = o.quality; + + var oo = new SBareObject(parentSheetIndex, stack, isRecipe, price, quality); + Log.AsyncG(JsonConvert.SerializeObject(oo)); + return JObject.FromObject(oo); + } + catch (Exception ex) + { + Log.AsyncR(ex); + Console.ReadKey(); + } + return null; + } + + protected static Object GetObject(JObject jObject) + { + var parentSheetIndex = GetTokenValue(jObject, "parentSheetIndex") as int?; + var stack = GetTokenValue(jObject, "parentSheetIndex") as int?; + var isRecipe = GetTokenValue(jObject, "parentSheetIndex") as bool?; + var price = GetTokenValue(jObject, "parentSheetIndex") as int?; + var quality = GetTokenValue(jObject, "parentSheetIndex") as int?; + + return new Object(parentSheetIndex ?? 0, stack ?? 0, isRecipe ?? false, price ?? -1, quality ?? 0); + } + + protected static Object GetObject(JToken jToken) + { + var jObject = JObject.FromObject(jToken); + + return GetObject(jObject); + } + + protected static T GetTokenValue(JObject jObject, string tokenName) where T : class + { + JToken jToken; + jObject.TryGetValue(tokenName, StringComparison.InvariantCultureIgnoreCase, out jToken); + return jToken as T; + } + } + + public class RectangleConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var rectangle = (Rectangle) value; + + var jObject = GetObject(rectangle); + + jObject.WriteTo(writer); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + Console.WriteLine(reader.ReadAsString()); + var jObject = JObject.Load(reader); + + return GetRectangle(jObject); + } + + public override bool CanConvert(Type objectType) + { + throw new NotImplementedException(); + } + + protected static JObject GetObject(Rectangle rectangle) + { + var x = rectangle.X; + var y = rectangle.Y; + var width = rectangle.Width; + var height = rectangle.Height; + + return JObject.FromObject(new {x, y, width, height}); + } + + protected static Rectangle GetRectangle(JObject jObject) + { + var x = GetTokenValue(jObject, "x") ?? 0; + var y = GetTokenValue(jObject, "y") ?? 0; + var width = GetTokenValue(jObject, "width") ?? 0; + var height = GetTokenValue(jObject, "height") ?? 0; + + return new Rectangle(x, y, width, height); + } + + protected static Rectangle GetRectangle(JToken jToken) + { + var jObject = JObject.FromObject(jToken); + + return GetRectangle(jObject); + } + + protected static int? GetTokenValue(JObject jObject, string tokenName) + { + JToken jToken; + return jObject.TryGetValue(tokenName, StringComparison.InvariantCultureIgnoreCase, out jToken) ? (int) jToken : (int?) null; + } + } + + public class RectangleListConverter : RectangleConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var rectangleList = (IList) value; + + var jArray = new JArray(); + + foreach (var rectangle in rectangleList) + { + jArray.Add(GetObject(rectangle)); + } + + jArray.WriteTo(writer); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var rectangleList = new List(); + + var jArray = JArray.Load(reader); + + foreach (var jToken in jArray) + { + rectangleList.Add(GetRectangle(jToken)); + } + + return rectangleList; + } + + public override bool CanConvert(Type objectType) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Logger.cs b/src/StardewModdingAPI/Logger.cs new file mode 100644 index 00000000..0d69b6ec --- /dev/null +++ b/src/StardewModdingAPI/Logger.cs @@ -0,0 +1,326 @@ +using System; +using System.Collections.Concurrent; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace StardewModdingAPI +{ + public static class Log + { + private static readonly LogWriter _writer; + + static Log() + { + _writer = LogWriter.Instance; + } + + private static void PrintLog(LogInfo li) + { + _writer.WriteToLog(li); + } + + #region Exception Logging + + /// + /// Catch unhandled exception from the application + /// + /// Should be moved out of here if we do more than just log the exception. + public static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) + { + Console.WriteLine("An exception has been caught"); + File.WriteAllText(Constants.LogDir + "\\MODDED_ErrorLog.Log_" + DateTime.UtcNow.Ticks + ".txt", e.ExceptionObject.ToString()); + } + + /// + /// Catch thread exception from the application + /// + /// Should be moved out of here if we do more than just log the exception. + public static void Application_ThreadException(object sender, ThreadExceptionEventArgs e) + { + Console.WriteLine("A thread exception has been caught"); + File.WriteAllText(Constants.LogDir + "\\MODDED_ErrorLog.Log_" + Extensions.Random.Next(100000000, 999999999) + ".txt", e.Exception.ToString()); + } + + #endregion + + #region Sync Logging + + /// + /// NOTICE: Sync logging is discouraged. Please use Async instead. + /// + /// Message to log + /// Colour of message + public static void SyncColour(object message, ConsoleColor colour) + { + PrintLog(new LogInfo(message?.ToString(), colour)); + } + + #endregion + + #region Async Logging + + public static void AsyncColour(object message, ConsoleColor colour) + { + Task.Run(() => { PrintLog(new LogInfo(message?.ToString(), colour)); }); + } + + public static void Async(object message) + { + AsyncColour(message?.ToString(), ConsoleColor.Gray); + } + + public static void AsyncR(object message) + { + AsyncColour(message?.ToString(), ConsoleColor.Red); + } + + public static void AsyncO(object message) + { + AsyncColour(message.ToString(), ConsoleColor.DarkYellow); + } + + public static void AsyncY(object message) + { + AsyncColour(message?.ToString(), ConsoleColor.Yellow); + } + + public static void AsyncG(object message) + { + AsyncColour(message?.ToString(), ConsoleColor.Green); + } + + public static void AsyncC(object message) + { + AsyncColour(message?.ToString(), ConsoleColor.Cyan); + } + + public static void AsyncM(object message) + { + AsyncColour(message?.ToString(), ConsoleColor.Magenta); + } + + public static void Error(object message) + { + AsyncR("[ERROR] " + message); + } + + public static void Success(object message) + { + AsyncG("[SUCCESS] " + message); + } + + public static void Info(object message) + { + AsyncY("[INFO] " + message); + } + + public static void Out(object message) + { + Async("[OUT] " + message); + } + + public static void Debug(object message) + { + AsyncO("[DEBUG] " + message); + } + + #endregion + + #region ToRemove + + public static void LogValueNotSpecified() + { + AsyncR(" must be specified"); + } + + public static void LogObjectValueNotSpecified() + { + AsyncR(" and must be specified"); + } + + public static void LogValueInvalid() + { + AsyncR(" is invalid"); + } + + public static void LogObjectInvalid() + { + AsyncR(" is invalid"); + } + + public static void LogValueNotInt32() + { + AsyncR(" must be a whole number (Int32)"); + } + + [Obsolete("Parameter 'values' is no longer supported. Format before logging.")] + private static void PrintLog(object message, bool disableLogging, params object[] values) + { + PrintLog(new LogInfo(message?.ToString())); + } + + [Obsolete("Parameter 'values' is no longer supported. Format before logging.")] + public static void Success(object message, params object[] values) + { + Success(message); + } + + [Obsolete("Parameter 'values' is no longer supported. Format before logging.")] + public static void Verbose(object message, params object[] values) + { + Out(message); + } + + [Obsolete("Parameter 'values' is no longer supported. Format before logging.")] + public static void Comment(object message, params object[] values) + { + AsyncC(message); + } + + [Obsolete("Parameter 'values' is no longer supported. Format before logging.")] + public static void Info(object message, params object[] values) + { + Info(message); + } + + [Obsolete("Parameter 'values' is no longer supported. Format before logging.")] + public static void Error(object message, params object[] values) + { + Error(message); + } + + [Obsolete("Parameter 'values' is no longer supported. Format before logging.")] + public static void Debug(object message, params object[] values) + { + Debug(message); + } + + #endregion + } + + /// + /// A Logging class implementing the Singleton pattern and an internal Queue to be flushed perdiodically + /// + public class LogWriter + { + private static LogWriter _instance; + private static ConcurrentQueue _logQueue; + private static DateTime _lastFlushTime = DateTime.Now; + private static StreamWriter _stream; + + /// + /// Private to prevent creation of other instances + /// + private LogWriter() + { + } + + /// + /// Exposes _instace and creates a new one if it is null + /// + internal static LogWriter Instance + { + get + { + if (_instance == null) + { + _instance = new LogWriter(); + // Field cannot be used by anything else regardless, do not surround with lock { } + // ReSharper disable once InconsistentlySynchronizedField + _logQueue = new ConcurrentQueue(); + Console.WriteLine(Constants.LogPath); + + // If the ErrorLogs dir doesn't exist StreamWriter will throw an exception. + if (!Directory.Exists(Constants.LogDir)) + { + Directory.CreateDirectory(Constants.LogDir); + } + + _stream = new StreamWriter(Constants.LogPath, false); + Console.WriteLine("Created log instance"); + } + return _instance; + } + } + + /// + /// Writes into the ConcurrentQueue the Message specified + /// + /// The message to write to the log + public void WriteToLog(string message) + { + lock (_logQueue) + { + var logEntry = new LogInfo(message); + _logQueue.Enqueue(logEntry); + + if (_logQueue.Any()) + { + FlushLog(); + } + } + } + + /// + /// Writes into the ConcurrentQueue the Entry specified + /// + /// The logEntry to write to the log + public void WriteToLog(LogInfo logEntry) + { + lock (_logQueue) + { + _logQueue.Enqueue(logEntry); + + if (_logQueue.Any()) + { + FlushLog(); + } + } + } + + /// + /// Flushes the ConcurrentQueue to the log file specified in Constants + /// + private void FlushLog() + { + lock (_stream) + { + LogInfo entry; + while (_logQueue.TryDequeue(out entry)) + { + string m = $"[{entry.LogTime}] {entry.Message}"; + + Console.ForegroundColor = entry.Colour; + Console.WriteLine(m); + Console.ForegroundColor = ConsoleColor.Gray; + + _stream.WriteLine(m); + } + _stream.Flush(); + } + } + } + + /// + /// A struct to store the message and the Date and Time the log entry was created + /// + public struct LogInfo + { + public string Message { get; set; } + public string LogTime { get; set; } + public string LogDate { get; set; } + public ConsoleColor Colour { get; set; } + + public LogInfo(string message, ConsoleColor colour = ConsoleColor.Gray) + { + if (string.IsNullOrEmpty(message)) + message = "[null]"; + Message = message; + LogDate = DateTime.Now.ToString("yyyy-MM-dd"); + LogTime = DateTime.Now.ToString("hh:mm:ss.fff tt"); + Colour = colour; + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Manifest.cs b/src/StardewModdingAPI/Manifest.cs new file mode 100644 index 00000000..5eabc01b --- /dev/null +++ b/src/StardewModdingAPI/Manifest.cs @@ -0,0 +1,89 @@ +using System; +using System.IO; +using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace StardewModdingAPI +{ + public class Manifest : Config + { + /// + /// The name of your mod. + /// + public virtual string Name { get; set; } + + /// + /// The name of the mod's authour. + /// + public virtual string Authour { get; set; } + + /// + /// The version of the mod. + /// + public virtual Version Version { get; set; } + + /// + /// A description of the mod. + /// + public virtual string Description { get; set; } + + /// + /// The unique ID of the mod. It doesn't *need* to be anything. + /// + public virtual string UniqueID { get; set; } + + /// + /// Whether or not the mod uses per-save-config files. + /// + public virtual bool PerSaveConfigs { get; set; } + + /// + /// The name of the DLL in the directory that has the Entry() method. + /// + public virtual string EntryDll { get; set; } + + public override T GenerateDefaultConfig() + { + Name = ""; + Authour = ""; + Version = new Version(0, 0, 0, ""); + Description = ""; + UniqueID = Guid.NewGuid().ToString(); + PerSaveConfigs = false; + EntryDll = ""; + return this as T; + } + + public override T LoadConfig() + { + if (File.Exists(ConfigLocation)) + { + try + { + Manifest m = JsonConvert.DeserializeObject(File.ReadAllText(ConfigLocation)); + } + catch + { + //Invalid json blob. Try to remove version? + try + { + JObject j = JObject.Parse(File.ReadAllText(ConfigLocation)); + if (!j.GetValue("Version").Contains("{")) + { + Log.AsyncC("INVALID JSON VERSION. TRYING TO REMOVE SO A NEW CAN BE AUTO-GENERATED"); + j.Remove("Version"); + File.WriteAllText(ConfigLocation, j.ToString()); + } + } + catch (Exception) + { + // ignored + } + } + } + + return base.LoadConfig(); + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Mod.cs b/src/StardewModdingAPI/Mod.cs new file mode 100644 index 00000000..8edfcf7e --- /dev/null +++ b/src/StardewModdingAPI/Mod.cs @@ -0,0 +1,51 @@ +using System.IO; + +namespace StardewModdingAPI +{ + public class Mod + { + /// + /// The mod's manifest + /// + public Manifest Manifest { get; internal set; } + + /// + /// Where the mod is located on the disk. + /// + public string PathOnDisk { get; internal set; } + + /// + /// A basic path to store your mod's config at. + /// + public string BaseConfigPath => PathOnDisk + "\\config.json"; + + /// + /// A basic path to where per-save configs are stored + /// + public string PerSaveConfigFolder => GetPerSaveConfigFolder(); + + /// + /// A basic path to store your mod's config at, dependent on the current save. + /// The Manifest must allow for per-save configs. This is to keep from having an + /// empty directory in every mod folder. + /// + public string PerSaveConfigPath => Constants.CurrentSavePathExists ? Path.Combine(PerSaveConfigFolder, Constants.SaveFolderName + ".json") : ""; + + /// + /// A basic method that is the entry-point of your mod. It will always be called once when the mod loads. + /// + public virtual void Entry(params object[] objects) + { + } + + private string GetPerSaveConfigFolder() + { + if (Manifest.PerSaveConfigs) + { + return Path.Combine(PathOnDisk, "psconfigs"); + } + Log.AsyncR($"The mod [{Manifest.Name}] is not configured to use per-save configs."); + return ""; + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/ModItem.cs b/src/StardewModdingAPI/ModItem.cs new file mode 100644 index 00000000..cf2e10b2 --- /dev/null +++ b/src/StardewModdingAPI/ModItem.cs @@ -0,0 +1,15 @@ +using Microsoft.Xna.Framework.Graphics; +using StardewValley; + +namespace StardewModdingAPI +{ + internal class ModItem : Object + { + public Item AsItem => this; + + public override string Name { get; set; } + public string Description { get; set; } + public int ID { get; set; } + public Texture2D Texture { get; set; } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs new file mode 100644 index 00000000..81e48c7d --- /dev/null +++ b/src/StardewModdingAPI/Program.cs @@ -0,0 +1,451 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Windows.Forms; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using StardewModdingAPI.Events; +using StardewModdingAPI.Inheritance; +using StardewModdingAPI.Inheritance.Menus; +using StardewValley; +using StardewValley.Menus; + +namespace StardewModdingAPI +{ + public class Program + { + private static List _modPaths; + + public static SGame gamePtr; + public static bool ready; + + public static Assembly StardewAssembly; + public static Type StardewProgramType; + public static FieldInfo StardewGameInfo; + public static Form StardewForm; + + public static Thread gameThread; + public static Thread consoleInputThread; + //private static List _modContentPaths; + + public static Texture2D DebugPixel { get; private set; } + + // ReSharper disable once PossibleNullReferenceException + public static int BuildType => (int) StardewProgramType.GetField("buildType", BindingFlags.Public | BindingFlags.Static).GetValue(null); + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /// + /// Main method holding the API execution + /// + /// + private static void Main(string[] args) + { + Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB"); + + try + { + Log.AsyncY("SDV Version: " + Game1.version); + Log.AsyncY("SMAPI Version: " + Constants.Version.VersionString); + ConfigureUI(); + ConfigurePaths(); + ConfigureSDV(); + + GameRunInvoker(); + } + catch (Exception e) + { + // Catch and display all exceptions. + Console.WriteLine(e); + Console.ReadKey(); + Log.AsyncR("Critical error: " + e); + } + + Log.AsyncY("The API will now terminate. Press any key to continue..."); + Console.ReadKey(); + } + + /// + /// Set up the console properties + /// + private static void ConfigureUI() + { + Console.Title = Constants.ConsoleTitle; + +#if DEBUG + Console.Title += " - DEBUG IS NOT FALSE, AUTHOUR NEEDS TO REUPLOAD THIS VERSION"; +#endif + } + + /// + /// Setup the required paths and logging + /// + private static void ConfigurePaths() + { + Log.AsyncY("Validating api paths..."); + + _modPaths = new List {Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StardewValley", "Mods"), Path.Combine(Constants.ExecutionPath, "Mods")}; + //_modContentPaths = new List(); + + //TODO: Have an app.config and put the paths inside it so users can define locations to load mods from + + //Mods need to make their own content paths, since we're doing a different, manifest-driven, approach. + //_modContentPaths.Add(Path.Combine(Constants.ExecutionPath, "Mods", "Content")); + //_modContentPaths.Add(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StardewValley", "Mods", "Content")); + + //Checks that all defined modpaths exist as directories + _modPaths.ForEach(VerifyPath); + //_modContentPaths.ForEach(path => VerifyPath(path)); + VerifyPath(Constants.LogDir); + + if (!File.Exists(Constants.ExecutionPath + "\\Stardew Valley.exe")) + { + throw new FileNotFoundException($"Could not found: {Constants.ExecutionPath}\\Stardew Valley.exe"); + } + } + + /// + /// Load Stardev Valley and control features + /// + private static void ConfigureSDV() + { + Log.AsyncY("Initializing SDV Assembly..."); + + // Load in the assembly - ignores security + StardewAssembly = Assembly.UnsafeLoadFrom(Constants.ExecutionPath + "\\Stardew Valley.exe"); + StardewProgramType = StardewAssembly.GetType("StardewValley.Program", true); + StardewGameInfo = StardewProgramType.GetField("gamePtr"); + + // Change the game's version + Log.AsyncY("Injecting New SDV Version..."); + Game1.version += $"-Z_MODDED | SMAPI {Constants.Version.VersionString}"; + + // Create the thread for the game to run in. + gameThread = new Thread(RunGame); + Log.AsyncY("Starting SDV..."); + gameThread.Start(); + + // Wait for the game to load up + while (!ready) + { + } + + //SDV is running + Log.AsyncY("SDV Loaded Into Memory"); + + //Create definition to listen for input + Log.AsyncY("Initializing Console Input Thread..."); + consoleInputThread = new Thread(ConsoleInputThread); + + // The only command in the API (at least it should be, for now) + Command.RegisterCommand("help", "Lists all commands | 'help ' returns command description").CommandFired += help_CommandFired; + //Command.RegisterCommand("crash", "crashes sdv").CommandFired += delegate { Game1.player.draw(null); }; + + //Subscribe to events + ControlEvents.KeyPressed += Events_KeyPressed; + GameEvents.LoadContent += Events_LoadContent; + //Events.MenuChanged += Events_MenuChanged; //Idk right now + + Log.AsyncY("Applying Final SDV Tweaks..."); + StardewInvoke(() => + { + gamePtr.IsMouseVisible = false; + gamePtr.Window.Title = "Stardew Valley - Version " + Game1.version; + StardewForm.Resize += GraphicsEvents.InvokeResize; + }); + } + + /// + /// Wrap the 'RunGame' method for console output + /// + private static void GameRunInvoker() + { + //Game's in memory now, send the event + Log.AsyncY("Game Loaded"); + GameEvents.InvokeGameLoaded(); + + Log.AsyncY("Type 'help' for help, or 'help ' for a command's usage"); + //Begin listening to input + consoleInputThread.Start(); + + + while (ready) + { + //Check if the game is still running 10 times a second + Thread.Sleep(1000 / 10); + } + + //abort the thread, we're closing + if (consoleInputThread != null && consoleInputThread.ThreadState == ThreadState.Running) + consoleInputThread.Abort(); + + Log.AsyncY("Game Execution Finished"); + Log.AsyncY("Shutting Down..."); + Thread.Sleep(100); + Environment.Exit(0); + } + + /// + /// Create the given directory path if it does not exist + /// + /// Desired directory path + private static void VerifyPath(string path) + { + try + { + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + } + catch (Exception ex) + { + Log.AsyncR("Could not create a path: " + path + "\n\n" + ex); + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public static void RunGame() + { + Application.ThreadException += Log.Application_ThreadException; + Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); + AppDomain.CurrentDomain.UnhandledException += Log.CurrentDomain_UnhandledException; + + try + { + gamePtr = new SGame(); + Log.AsyncY("Patching SDV Graphics Profile..."); + Game1.graphics.GraphicsProfile = GraphicsProfile.HiDef; + LoadMods(); + + StardewForm = Control.FromHandle(gamePtr.Window.Handle).FindForm(); + if (StardewForm != null) StardewForm.Closing += StardewForm_Closing; + + ready = true; + + StardewGameInfo.SetValue(StardewProgramType, gamePtr); + gamePtr.Run(); + } + catch (Exception ex) + { + Log.AsyncR("Game failed to start: " + ex); + } + } + + private static void StardewForm_Closing(object sender, CancelEventArgs e) + { + e.Cancel = true; + + if (true || MessageBox.Show("Are you sure you would like to quit Stardew Valley?\nUnsaved progress will be lost!", "Confirm Exit", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) == DialogResult.Yes) + { + gamePtr.Exit(); + gamePtr.Dispose(); + StardewForm.Hide(); + ready = false; + } + } + + public static void LoadMods() + { + Log.AsyncY("LOADING MODS"); + foreach (var ModPath in _modPaths) + { + foreach (var d in Directory.GetDirectories(ModPath)) + { + foreach (var s in Directory.GetFiles(d, "manifest.json")) + { + if (s.Contains("StardewInjector")) + continue; + Log.AsyncG("Found Manifest: " + s); + var manifest = new Manifest(); + try + { + var t = File.ReadAllText(s); + if (string.IsNullOrEmpty(t)) + { + Log.AsyncR($"Failed to read mod manifest '{s}'. Manifest is empty!"); + continue; + } + + manifest = manifest.InitializeConfig(s); + + if (string.IsNullOrEmpty(manifest.EntryDll)) + { + Log.AsyncR($"Failed to read mod manifest '{s}'. EntryDll is empty!"); + continue; + } + } + catch (Exception ex) + { + Log.AsyncR($"Failed to read mod manifest '{s}'. Exception details:\n" + ex); + continue; + } + var targDir = Path.GetDirectoryName(s); + var psDir = Path.Combine(targDir, "psconfigs"); + Log.AsyncY($"Created psconfigs directory @{psDir}"); + try + { + if (manifest.PerSaveConfigs) + { + if (!Directory.Exists(psDir)) + { + Directory.CreateDirectory(psDir); + Log.AsyncY($"Created psconfigs directory @{psDir}"); + } + + if (!Directory.Exists(psDir)) + { + Log.AsyncR($"Failed to create psconfigs directory '{psDir}'. No exception occured."); + continue; + } + } + } + catch (Exception ex) + { + Log.AsyncR($"Failed to create psconfigs directory '{targDir}'. Exception details:\n" + ex); + continue; + } + var targDll = string.Empty; + try + { + targDll = Path.Combine(targDir, manifest.EntryDll); + if (!File.Exists(targDll)) + { + Log.AsyncR($"Failed to load mod '{manifest.EntryDll}'. File {targDll} does not exist!"); + continue; + } + + var mod = Assembly.UnsafeLoadFrom(targDll); + + if (mod.DefinedTypes.Count(x => x.BaseType == typeof(Mod)) > 0) + { + Log.AsyncY("Loading Mod DLL..."); + var tar = mod.DefinedTypes.First(x => x.BaseType == typeof(Mod)); + var m = (Mod) mod.CreateInstance(tar.ToString()); + if (m != null) + { + m.PathOnDisk = targDir; + m.Manifest = manifest; + Log.AsyncG($"LOADED MOD: {m.Manifest.Name} by {m.Manifest.Authour} - Version {m.Manifest.Version} | Description: {m.Manifest.Description} (@ {targDll})"); + Constants.ModsLoaded += 1; + m.Entry(); + } + } + else + { + Log.AsyncR("Invalid Mod DLL"); + } + } + catch (Exception ex) + { + Log.AsyncR($"Failed to load mod '{targDll}'. Exception details:\n" + ex); + } + } + } + } + Log.AsyncG($"LOADED {Constants.ModsLoaded} MODS"); + Console.Title = Constants.ConsoleTitle; + } + + public static void ConsoleInputThread() + { + var input = string.Empty; + + while (true) + { + Command.CallCommand(Console.ReadLine()); + } + } + + private static void Events_LoadContent(object o, EventArgs e) + { + Log.AsyncY("Initializing Debug Assets..."); + DebugPixel = new Texture2D(Game1.graphics.GraphicsDevice, 1, 1); + DebugPixel.SetData(new[] {Color.White}); + +#if DEBUG + StardewModdingAPI.Log.Async("REGISTERING BASE CUSTOM ITEM"); + SObject so = new SObject(); + so.Name = "Mario Block"; + so.CategoryName = "SMAPI Test Mod"; + so.Description = "It's a block from Mario!\nLoaded in realtime by SMAPI."; + so.Texture = Texture2D.FromStream(Game1.graphics.GraphicsDevice, new FileStream(_modContentPaths[0] + "\\Test.png", FileMode.Open)); + so.IsPassable = true; + so.IsPlaceable = true; + StardewModdingAPI.Log.Async("REGISTERED WITH ID OF: " + SGame.RegisterModItem(so)); + + //StardewModdingAPI.Log.Async("REGISTERING SECOND CUSTOM ITEM"); + //SObject so2 = new SObject(); + //so2.Name = "Mario Painting"; + //so2.CategoryName = "SMAPI Test Mod"; + //so2.Description = "It's a painting of a creature from Mario!\nLoaded in realtime by SMAPI."; + //so2.Texture = Texture2D.FromStream(Game1.graphics.GraphicsDevice, new FileStream(_modContentPaths[0] + "\\PaintingTest.png", FileMode.Open)); + //so2.IsPassable = true; + //so2.IsPlaceable = true; + //StardewModdingAPI.Log.Async("REGISTERED WITH ID OF: " + SGame.RegisterModItem(so2)); + + Command.CallCommand("load"); +#endif + } + + private static void Events_KeyPressed(object o, EventArgsKeyPressed e) + { + } + + private static void Events_MenuChanged(IClickableMenu newMenu) + { + Log.AsyncY("NEW MENU: " + newMenu.GetType()); + if (newMenu is GameMenu) + { + Game1.activeClickableMenu = SGameMenu.ConstructFromBaseClass(Game1.activeClickableMenu as GameMenu); + } + } + + private static void Events_LocationsChanged(List newLocations) + { +#if DEBUG + SGame.ModLocations = SGameLocation.ConstructFromBaseClasses(Game1.locations); +#endif + } + + private static void Events_CurrentLocationChanged(GameLocation newLocation) + { + //SGame.CurrentLocation = null; + //System.Threading.Thread.Sleep(10); +#if DEBUG + Console.WriteLine(newLocation.name); + SGame.CurrentLocation = SGame.LoadOrCreateSGameLocationFromName(newLocation.name); +#endif + //Game1.currentLocation = SGame.CurrentLocation; + //Log.LogComment(((SGameLocation) newLocation).name); + //Log.LogComment("LOC CHANGED: " + SGame.currentLocation.name); + } + + public static void StardewInvoke(Action a) + { + StardewForm.Invoke(a); + } + + private static void help_CommandFired(object o, EventArgsCommand e) + { + if (e.Command.CalledArgs.Length > 0) + { + var fnd = Command.FindCommand(e.Command.CalledArgs[0]); + if (fnd == null) + Log.AsyncR("The command specified could not be found"); + else + { + Log.AsyncY(fnd.CommandArgs.Length > 0 ? $"{fnd.CommandName}: {fnd.CommandDesc} - {fnd.CommandArgs.ToSingular()}" : $"{fnd.CommandName}: {fnd.CommandDesc}"); + } + } + else + Log.AsyncY("Commands: " + Command.RegisteredCommands.Select(x => x.CommandName).ToSingular()); + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Properties/AssemblyInfo.cs b/src/StardewModdingAPI/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..f5435644 --- /dev/null +++ b/src/StardewModdingAPI/Properties/AssemblyInfo.cs @@ -0,0 +1,39 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. + +[assembly: AssemblyTitle("Stardew Modding API (SMAPI)")] +[assembly: AssemblyDescription("Stardew Valley modding API.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Stardew Modding API (SMAPI)")] +[assembly: AssemblyCopyright("Copyright © SMAPI Dev Team 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. + +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM + +[assembly: Guid("5c3f7f42-fefd-43db-aaea-92ea3bcad531")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] + +[assembly: AssemblyVersion("0.40.0.0")] +[assembly: AssemblyFileVersion("0.40.0.0")] \ No newline at end of file diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj new file mode 100644 index 00000000..106f5bcd --- /dev/null +++ b/src/StardewModdingAPI/StardewModdingAPI.csproj @@ -0,0 +1,216 @@ + + + + + Debug + AnyCPU + {F1A573B0-F436-472C-AE29-0B91EA6B9F8F} + Exe + Properties + StardewModdingAPI + StardewModdingAPI + v4.6.1 + 512 + + + + + + + + + false + + + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + + + + + $(SteamInstallPath) + + + + + ..\ + + + + + AnyCPU + true + full + true + bin\Debug\ + TRACE;DEBUG + prompt + 4 + false + true + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + true + + + x86 + bin\Debug\ + false + TRACE + true + true + bin\Debug\StardewModdingAPI.XML + true + 6 + + + x86 + bin\Release\ + false + bin\Release\StardewModdingAPI.XML + TRACE + true + true + 6 + + + icon.ico + + + StardewModdingAPI.Program + + + + False + + + False + + + False + + + False + + + ..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll + True + + + False + $(SteamPath)\steamapps\common\Stardew Valley\Stardew Valley.exe + False + + + + + + + + + + + + False + $(SteamPath)\steamapps\common\Stardew Valley\xTile.dll + False + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + Microsoft .NET Framework 4.5 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/src/StardewModdingAPI/Version.cs b/src/StardewModdingAPI/Version.cs new file mode 100644 index 00000000..5e47a703 --- /dev/null +++ b/src/StardewModdingAPI/Version.cs @@ -0,0 +1,23 @@ +using Newtonsoft.Json; + +namespace StardewModdingAPI +{ + public struct Version + { + public int MajorVersion { get; set; } + public int MinorVersion { get; set; } + public int PatchVersion { get; set; } + public string Build { get; set; } + + [JsonIgnore] + public string VersionString => $"{MajorVersion}.{MinorVersion}.{PatchVersion} {Build}"; + + public Version(int major, int minor, int patch, string build) + { + MajorVersion = major; + MinorVersion = minor; + PatchVersion = patch; + Build = build; + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/icon.ico b/src/StardewModdingAPI/icon.ico new file mode 100644 index 00000000..2985c5cd Binary files /dev/null and b/src/StardewModdingAPI/icon.ico differ diff --git a/src/StardewModdingAPI/packages.config b/src/StardewModdingAPI/packages.config new file mode 100644 index 00000000..0196f5b3 --- /dev/null +++ b/src/StardewModdingAPI/packages.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/StardewModdingAPI/steam_appid.txt b/src/StardewModdingAPI/steam_appid.txt new file mode 100644 index 00000000..9fe92b96 --- /dev/null +++ b/src/StardewModdingAPI/steam_appid.txt @@ -0,0 +1 @@ +413150 \ No newline at end of file diff --git a/src/TrainerMod/FodyWeavers.xml b/src/TrainerMod/FodyWeavers.xml new file mode 100644 index 00000000..dc708fcb --- /dev/null +++ b/src/TrainerMod/FodyWeavers.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/TrainerMod/Properties/AssemblyInfo.cs b/src/TrainerMod/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..521f5a79 --- /dev/null +++ b/src/TrainerMod/Properties/AssemblyInfo.cs @@ -0,0 +1,39 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. + +[assembly: AssemblyTitle("TrainerMod")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("TrainerMod")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. + +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM + +[assembly: Guid("76791e28-b1b5-407c-82d6-50c3e5b7e037")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] + +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/src/TrainerMod/TrainerMod.cs b/src/TrainerMod/TrainerMod.cs new file mode 100644 index 00000000..da5365cb --- /dev/null +++ b/src/TrainerMod/TrainerMod.cs @@ -0,0 +1,768 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Xna.Framework; +using StardewModdingAPI; +using StardewModdingAPI.Events; +using StardewValley; +using StardewValley.Menus; +using StardewValley.Objects; +using StardewValley.Tools; +using Object = StardewValley.Object; + +namespace TrainerMod +{ + public class TrainerMod : Mod + { + /* + public override string Name + { + get { return "Trainer Mod"; } + } + + public override string Authour + { + get { return "Zoryn Aaron"; } + } + + public override string Version + { + get { return "1.0"; } + } + + public override string Description + { + get { return "Registers several commands to use. Most commands are trainer-like in that they offer forms of cheating."; } + } + */ + + public static int frozenTime; + public static bool infHealth, infStamina, infMoney, freezeTime; + + public override void Entry(params object[] objects) + { + RegisterCommands(); + GameEvents.UpdateTick += Events_UpdateTick; + } + + private static void Events_UpdateTick(object sender, EventArgs e) + { + if (Game1.player == null) + return; + + if (infHealth) + { + Game1.player.health = Game1.player.maxHealth; + } + if (infStamina) + { + Game1.player.stamina = Game1.player.MaxStamina; + } + if (infMoney) + { + Game1.player.money = 999999; + } + if (freezeTime) + { + Game1.timeOfDay = frozenTime; + } + } + + public static void RegisterCommands() + { + Command.RegisterCommand("types", "Lists all value types | types").CommandFired += types_CommandFired; + + Command.RegisterCommand("hide", "Hides the game form | hide").CommandFired += hide_CommandFired; + Command.RegisterCommand("show", "Shows the game form | show").CommandFired += show_CommandFired; + + Command.RegisterCommand("save", "Saves the game? Doesn't seem to work. | save").CommandFired += save_CommandFired; + Command.RegisterCommand("load", "Shows the load screen | load").CommandFired += load_CommandFired; + + Command.RegisterCommand("exit", "Closes the game | exit").CommandFired += exit_CommandFired; + Command.RegisterCommand("stop", "Closes the game | stop").CommandFired += exit_CommandFired; + + Command.RegisterCommand("player_setname", "Sets the player's name | player_setname ", new[] {"(player, pet, farm) (String) The target name"}).CommandFired += player_setName; + Command.RegisterCommand("player_setmoney", "Sets the player's money | player_setmoney |inf", new[] {"(Int32) The target money"}).CommandFired += player_setMoney; + Command.RegisterCommand("player_setstamina", "Sets the player's stamina | player_setstamina |inf", new[] {"(Int32) The target stamina"}).CommandFired += player_setStamina; + Command.RegisterCommand("player_setmaxstamina", "Sets the player's max stamina | player_setmaxstamina ", new[] {"(Int32) The target max stamina"}).CommandFired += player_setMaxStamina; + Command.RegisterCommand("player_sethealth", "Sets the player's health | player_sethealth |inf", new[] {"(Int32) The target health"}).CommandFired += player_setHealth; + Command.RegisterCommand("player_setmaxhealth", "Sets the player's max health | player_setmaxhealth ", new[] {"(Int32) The target max health"}).CommandFired += player_setMaxHealth; + Command.RegisterCommand("player_setimmunity", "Sets the player's immunity | player_setimmunity ", new[] {"(Int32) The target immunity"}).CommandFired += player_setImmunity; + + Command.RegisterCommand("player_setlevel", "Sets the player's specified skill to the specified value | player_setlevel ", new[] {"(luck, mining, combat, farming, fishing, foraging) (1-10) The target level"}).CommandFired += player_setLevel; + Command.RegisterCommand("player_setspeed", "Sets the player's speed to the specified value?", new[] {"(Int32) The target speed [0 is normal]"}).CommandFired += player_setSpeed; + Command.RegisterCommand("player_changecolour", "Sets the player's colour of the specified object | player_changecolor ", new[] {"(hair, eyes, pants) (r,g,b)"}).CommandFired += player_changeColour; + Command.RegisterCommand("player_changestyle", "Sets the player's style of the specified object | player_changecolor ", new[] {"(hair, shirt, skin, acc, shoe, swim, gender) (Int32)"}).CommandFired += player_changeStyle; + + Command.RegisterCommand("player_additem", "Gives the player an item | player_additem [count] [quality]", new[] {"(Int32) (Int32)[count] (Int32)[quality]"}).CommandFired += player_addItem; + Command.RegisterCommand("player_addmelee", "Gives the player a melee item | player_addmelee ", new[] {"?"}).CommandFired += player_addMelee; + Command.RegisterCommand("player_addring", "Gives the player a ring | player_addring ", new[] {"?"}).CommandFired += player_addRing; + + Command.RegisterCommand("out_items", "Outputs a list of items | out_items", new[] {""}).CommandFired += out_items; + Command.RegisterCommand("out_melee", "Outputs a list of melee weapons | out_melee", new[] {""}).CommandFired += out_melee; + Command.RegisterCommand("out_rings", "Outputs a list of rings | out_rings", new[] {""}).CommandFired += out_rings; + Command.RegisterCommand("newitem", "not to be used | newitem", new[] {""}).CommandFired += RegisterNewItem; + + Command.RegisterCommand("world_settime", "Sets the time to the specified value | world_settime ", new[] {"(Int32) The target time [06:00 AM is 600]"}).CommandFired += world_setTime; + Command.RegisterCommand("world_freezetime", "Freezes or thaws time | world_freezetime ", new[] {"(0 - 1) Whether or not to freeze time. 0 is thawed, 1 is frozen"}).CommandFired += world_freezeTime; + Command.RegisterCommand("world_setday", "Sets the day to the specified value | world_setday ", new[] {"(Int32) The target day [1-28]"}).CommandFired += world_setDay; + Command.RegisterCommand("world_setseason", "Sets the season to the specified value | world_setseason ", new[] {"(winter, spring, summer, fall) The target season"}).CommandFired += world_setSeason; + Command.RegisterCommand("world_downminelevel", "Goes down one mine level? | world_downminelevel", new[] {""}).CommandFired += world_downMineLevel; + Command.RegisterCommand("world_setminelevel", "Sets mine level? | world_setminelevel", new[] {"(Int32) The target level"}).CommandFired += world_setMineLevel; + } + + private static void types_CommandFired(object sender, EventArgsCommand e) + { + Log.AsyncY($"[Int32: {int.MinValue} - {int.MaxValue}], [Int64: {long.MinValue} - {long.MaxValue}], [String: \"raw text\"], [Colour: r,g,b (EG: 128, 32, 255)]"); + } + + private static void hide_CommandFired(object sender, EventArgsCommand e) + { + Program.StardewInvoke(() => { Program.StardewForm.Hide(); }); + } + + private static void show_CommandFired(object sender, EventArgsCommand e) + { + Program.StardewInvoke(() => { Program.StardewForm.Show(); }); + } + + private static void save_CommandFired(object sender, EventArgsCommand e) + { + SaveGame.Save(); + } + + private static void load_CommandFired(object sender, EventArgsCommand e) + { + Game1.hasLoadedGame = false; + Game1.activeClickableMenu = new LoadGameMenu(); + } + + private static void exit_CommandFired(object sender, EventArgsCommand e) + { + Program.gamePtr.Exit(); + Environment.Exit(0); + } + + private static void player_setName(object sender, EventArgsCommand e) + { + if (e.Command.CalledArgs.Length > 1) + { + var obj = e.Command.CalledArgs[0]; + var objs = "player,pet,farm".Split(','); + if (objs.Contains(obj)) + { + switch (obj) + { + case "player": + Game1.player.Name = e.Command.CalledArgs[1]; + break; + case "pet": + Log.AsyncR("Pets cannot currently be renamed."); + break; + case "farm": + Game1.player.farmName = e.Command.CalledArgs[1]; + break; + } + } + else + { + Log.LogObjectInvalid(); + } + } + else + { + Log.LogObjectValueNotSpecified(); + } + } + + private static void player_setMoney(object sender, EventArgsCommand e) + { + if (e.Command.CalledArgs.Length > 0) + { + if (e.Command.CalledArgs[0] == "inf") + { + infMoney = true; + } + else + { + infMoney = false; + var ou = 0; + if (int.TryParse(e.Command.CalledArgs[0], out ou)) + { + Game1.player.Money = ou; + Log.Async($"Set {Game1.player.Name}'s money to {Game1.player.Money}"); + } + else + { + Log.LogValueNotInt32(); + } + } + } + else + { + Log.LogValueNotSpecified(); + } + } + + private static void player_setStamina(object sender, EventArgsCommand e) + { + if (e.Command.CalledArgs.Length > 0) + { + if (e.Command.CalledArgs[0] == "inf") + { + infStamina = true; + } + else + { + infStamina = false; + var ou = 0; + if (int.TryParse(e.Command.CalledArgs[0], out ou)) + { + Game1.player.Stamina = ou; + Log.Async($"Set {Game1.player.Name}'s stamina to {Game1.player.Stamina}"); + } + else + { + Log.LogValueNotInt32(); + } + } + } + else + { + Log.LogValueNotSpecified(); + } + } + + private static void player_setMaxStamina(object sender, EventArgsCommand e) + { + if (e.Command.CalledArgs.Length > 0) + { + var ou = 0; + if (int.TryParse(e.Command.CalledArgs[0], out ou)) + { + Game1.player.MaxStamina = ou; + Log.Async($"Set {Game1.player.Name}'s max stamina to {Game1.player.MaxStamina}"); + } + else + { + Log.LogValueNotInt32(); + } + } + else + { + Log.LogValueNotSpecified(); + } + } + + private static void player_setLevel(object sender, EventArgsCommand e) + { + if (e.Command.CalledArgs.Length > 1) + { + var skill = e.Command.CalledArgs[0]; + var skills = "luck,mining,combat,farming,fishing,foraging".Split(','); + if (skills.Contains(skill)) + { + var ou = 0; + if (int.TryParse(e.Command.CalledArgs[1], out ou)) + { + switch (skill) + { + case "luck": + Game1.player.LuckLevel = ou; + break; + case "mining": + Game1.player.MiningLevel = ou; + break; + case "combat": + Game1.player.CombatLevel = ou; + break; + case "farming": + Game1.player.FarmingLevel = ou; + break; + case "fishing": + Game1.player.FishingLevel = ou; + break; + case "foraging": + Game1.player.ForagingLevel = ou; + break; + } + } + else + { + Log.LogValueNotInt32(); + } + } + else + { + Log.AsyncR(" is invalid"); + } + } + else + { + Log.AsyncR(" and must be specified"); + } + } + + private static void player_setSpeed(object sender, EventArgsCommand e) + { + if (e.Command.CalledArgs.Length > 0) + { + if (e.Command.CalledArgs[0].IsInt32()) + { + Game1.player.addedSpeed = e.Command.CalledArgs[0].AsInt32(); + Log.Async($"Set {Game1.player.Name}'s added speed to {Game1.player.addedSpeed}"); + } + else + { + Log.LogValueNotInt32(); + } + } + else + { + Log.LogValueNotSpecified(); + } + } + + private static void player_changeColour(object sender, EventArgsCommand e) + { + if (e.Command.CalledArgs.Length > 1) + { + var obj = e.Command.CalledArgs[0]; + var objs = "hair,eyes,pants".Split(','); + if (objs.Contains(obj)) + { + var cs = e.Command.CalledArgs[1].Split(new[] {','}, 3); + if (cs[0].IsInt32() && cs[1].IsInt32() && cs[2].IsInt32()) + { + var c = new Color(cs[0].AsInt32(), cs[1].AsInt32(), cs[2].AsInt32()); + switch (obj) + { + case "hair": + Game1.player.hairstyleColor = c; + break; + case "eyes": + Game1.player.changeEyeColor(c); + break; + case "pants": + Game1.player.pantsColor = c; + break; + } + } + else + { + Log.AsyncR(" is invalid"); + } + } + else + { + Log.LogObjectInvalid(); + } + } + else + { + Log.AsyncR(" and must be specified"); + } + } + + private static void player_changeStyle(object sender, EventArgsCommand e) + { + if (e.Command.CalledArgs.Length > 1) + { + var obj = e.Command.CalledArgs[0]; + var objs = "hair,shirt,skin,acc,shoe,swim,gender".Split(','); + if (objs.Contains(obj)) + { + if (e.Command.CalledArgs[1].IsInt32()) + { + var i = e.Command.CalledArgs[1].AsInt32(); + switch (obj) + { + case "hair": + Game1.player.changeHairStyle(i); + break; + case "shirt": + Game1.player.changeShirt(i); + break; + case "acc": + Game1.player.changeAccessory(i); + break; + case "skin": + Game1.player.changeSkinColor(i); + break; + case "shoe": + Game1.player.changeShoeColor(i); + break; + case "swim": + if (i == 0) + Game1.player.changeOutOfSwimSuit(); + else if (i == 1) + Game1.player.changeIntoSwimsuit(); + else + Log.AsyncR(" must be 0 or 1 for this "); + break; + case "gender": + if (i == 0) + Game1.player.changeGender(true); + else if (i == 1) + Game1.player.changeGender(false); + else + Log.AsyncR(" must be 0 or 1 for this "); + break; + } + } + else + { + Log.LogValueInvalid(); + } + } + else + { + Log.LogObjectInvalid(); + } + } + else + { + Log.LogObjectValueNotSpecified(); + } + } + + private static void world_freezeTime(object sender, EventArgsCommand e) + { + if (e.Command.CalledArgs.Length > 0) + { + if (e.Command.CalledArgs[0].IsInt32()) + { + if (e.Command.CalledArgs[0].AsInt32() == 0 || e.Command.CalledArgs[0].AsInt32() == 1) + { + freezeTime = e.Command.CalledArgs[0].AsInt32() == 1; + frozenTime = freezeTime ? Game1.timeOfDay : 0; + Log.AsyncY("Time is now " + (freezeTime ? "frozen" : "thawed")); + } + else + { + Log.AsyncR(" should be 0 or 1"); + } + } + else + { + Log.LogValueNotInt32(); + } + } + else + { + Log.LogValueNotSpecified(); + } + } + + private static void world_setTime(object sender, EventArgsCommand e) + { + if (e.Command.CalledArgs.Length > 0) + { + if (e.Command.CalledArgs[0].IsInt32()) + { + if (e.Command.CalledArgs[0].AsInt32() <= 2600 && e.Command.CalledArgs[0].AsInt32() >= 600) + { + Game1.timeOfDay = e.Command.CalledArgs[0].AsInt32(); + frozenTime = freezeTime ? Game1.timeOfDay : 0; + Log.AsyncY("Time set to: " + Game1.timeOfDay); + } + else + { + Log.AsyncR(" should be between 600 and 2600 (06:00 AM - 02:00 AM [NEXT DAY])"); + } + } + else + { + Log.LogValueNotInt32(); + } + } + else + { + Log.LogValueNotSpecified(); + } + } + + private static void world_setDay(object sender, EventArgsCommand e) + { + if (e.Command.CalledArgs.Length > 0) + { + if (e.Command.CalledArgs[0].IsInt32()) + { + if (e.Command.CalledArgs[0].AsInt32() <= 28 && e.Command.CalledArgs[0].AsInt32() > 0) + { + Game1.dayOfMonth = e.Command.CalledArgs[0].AsInt32(); + } + else + { + Log.AsyncY(" must be between 1 and 28"); + } + } + else + { + Log.LogValueNotInt32(); + } + } + else + { + Log.LogValueNotSpecified(); + } + } + + private static void world_setSeason(object sender, EventArgsCommand e) + { + if (e.Command.CalledArgs.Length > 0) + { + var obj = e.Command.CalledArgs[0]; + var objs = "winter,spring,summer,fall".Split(','); + if (objs.Contains(obj)) + { + Game1.currentSeason = obj; + } + else + { + Log.LogValueInvalid(); + } + } + else + { + Log.LogValueNotSpecified(); + } + } + + private static void player_setHealth(object sender, EventArgsCommand e) + { + if (e.Command.CalledArgs.Length > 0) + { + if (e.Command.CalledArgs[0] == "inf") + { + infHealth = true; + } + else + { + infHealth = false; + if (e.Command.CalledArgs[0].IsInt32()) + { + Game1.player.health = e.Command.CalledArgs[0].AsInt32(); + } + else + { + Log.LogValueNotInt32(); + } + } + } + else + { + Log.LogValueNotSpecified(); + } + } + + private static void player_setMaxHealth(object sender, EventArgsCommand e) + { + if (e.Command.CalledArgs.Length > 0) + { + if (e.Command.CalledArgs[0].IsInt32()) + { + Game1.player.maxHealth = e.Command.CalledArgs[0].AsInt32(); + } + else + { + Log.LogValueNotInt32(); + } + } + else + { + Log.LogValueNotSpecified(); + } + } + + private static void player_setImmunity(object sender, EventArgsCommand e) + { + if (e.Command.CalledArgs.Length > 0) + { + if (e.Command.CalledArgs[0].IsInt32()) + { + Game1.player.immunity = e.Command.CalledArgs[0].AsInt32(); + } + else + { + Log.LogValueNotInt32(); + } + } + else + { + Log.LogValueNotSpecified(); + } + } + + private static void player_addItem(object sender, EventArgsCommand e) + { + if (e.Command.CalledArgs.Length > 0) + { + if (e.Command.CalledArgs[0].IsInt32()) + { + var count = 1; + var quality = 0; + if (e.Command.CalledArgs.Length > 1) + { + Console.WriteLine(e.Command.CalledArgs[1]); + if (e.Command.CalledArgs[1].IsInt32()) + { + count = e.Command.CalledArgs[1].AsInt32(); + } + else + { + Log.AsyncR("[count] is invalid"); + return; + } + + if (e.Command.CalledArgs.Length > 2) + { + if (e.Command.CalledArgs[2].IsInt32()) + { + quality = e.Command.CalledArgs[2].AsInt32(); + } + else + { + Log.AsyncR("[quality] is invalid"); + return; + } + } + } + + var o = new Object(e.Command.CalledArgs[0].AsInt32(), count) {quality = quality}; + + Game1.player.addItemByMenuIfNecessary(o); + } + else + { + Log.AsyncR(" is invalid"); + } + } + else + { + Log.LogObjectValueNotSpecified(); + } + } + + private static void player_addMelee(object sender, EventArgsCommand e) + { + if (e.Command.CalledArgs.Length > 0) + { + if (e.Command.CalledArgs[0].IsInt32()) + { + var toAdd = new MeleeWeapon(e.Command.CalledArgs[0].AsInt32()); + Game1.player.addItemByMenuIfNecessary(toAdd); + Log.Async($"Given {toAdd.Name} to {Game1.player.Name}"); + } + else + { + Log.AsyncR(" is invalid"); + } + } + else + { + Log.LogObjectValueNotSpecified(); + } + } + + private static void player_addRing(object sender, EventArgsCommand e) + { + if (e.Command.CalledArgs.Length > 0) + { + if (e.Command.CalledArgs[0].IsInt32()) + { + var toAdd = new Ring(e.Command.CalledArgs[0].AsInt32()); + Game1.player.addItemByMenuIfNecessary(toAdd); + Log.Async($"Given {toAdd.Name} to {Game1.player.Name}"); + } + else + { + Log.AsyncR(" is invalid"); + } + } + else + { + Log.LogObjectValueNotSpecified(); + } + } + + private static void out_items(object sender, EventArgsCommand e) + { + for (var i = 0; i < 1000; i++) + { + try + { + Item it = new Object(i, 1); + if (it.Name != "Error Item") + Console.WriteLine(i + "| " + it.Name); + } + catch + { + } + } + } + + private static void out_melee(object sender, EventArgsCommand e) + { + var d = Game1.content.Load>("Data\\weapons"); + Console.Write("DATA\\WEAPONS: "); + foreach (var v in d) + { + Console.WriteLine(v.Key + " | " + v.Value); + } + } + + private static void out_rings(object sender, EventArgsCommand e) + { + for (var i = 0; i < 100; i++) + { + try + { + Item it = new Ring(i); + if (it.Name != "Error Item") + Console.WriteLine(i + "| " + it.Name); + } + catch + { + } + } + } + + private static void world_downMineLevel(object sender, EventArgsCommand e) + { + Game1.nextMineLevel(); + } + + private static void world_setMineLevel(object sender, EventArgsCommand e) + { + if (e.Command.CalledArgs.Length > 0) + { + if (e.Command.CalledArgs[0].IsInt32()) + { + Game1.enterMine(true, e.Command.CalledArgs[0].AsInt32(), ""); + } + else + { + Log.LogValueNotInt32(); + } + } + else + { + Log.LogValueNotSpecified(); + } + } + + private static void blank_command(object sender, EventArgsCommand e) + { + } + + private static void RegisterNewItem(object sender, EventArgsCommand e) + { +#if DEBUG + SObject s = SGame.PullModItemFromDict(0, true); + s.Stack = 999; + Game1.player.addItemToInventory(s); +#endif + } + } +} \ No newline at end of file diff --git a/src/TrainerMod/TrainerMod.csproj b/src/TrainerMod/TrainerMod.csproj new file mode 100644 index 00000000..6de2a20d --- /dev/null +++ b/src/TrainerMod/TrainerMod.csproj @@ -0,0 +1,115 @@ + + + + + Debug + AnyCPU + {28480467-1A48-46A7-99F8-236D95225359} + Library + Properties + TrainerMod + TrainerMod + v4.6.1 + 512 + + + + + + true + full + true + ..\StardewModdingAPI\bin\Debug\Mods\TrainerMod\ + TRACE + prompt + 4 + x86 + false + 6 + true + + + pdbonly + true + ..\StardewModdingAPI\bin\Release\Mods\TrainerMod\ + TRACE + prompt + 4 + false + 6 + true + x86 + + + + + $(SteamInstallPath) + + + + + ..\ + + + + + + False + + + False + + + ..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll + True + + + $(SteamPath)\steamapps\common\Stardew Valley\Stardew Valley.exe + False + + + + + + + + + + + $(SteamPath)\steamapps\common\Stardew Valley\xTile.dll + False + + + + + + + + + {f1a573b0-f436-472c-ae29-0b91ea6b9f8f} + StardewModdingAPI + False + + + + + PreserveNewest + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/TrainerMod/manifest.json b/src/TrainerMod/manifest.json new file mode 100644 index 00000000..0fb9fa65 --- /dev/null +++ b/src/TrainerMod/manifest.json @@ -0,0 +1,6 @@ +{ + "Name": "Trainer Mod", + "Authour": "Zoryn", + "Description": "Registers several commands to use. Most commands are trainer-like in that they offer forms of cheating.", + "EntryDll": "TrainerMod.dll" +} \ No newline at end of file diff --git a/src/TrainerMod/packages.config b/src/TrainerMod/packages.config new file mode 100644 index 00000000..c064c58d --- /dev/null +++ b/src/TrainerMod/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/Vanilla Items List.txt b/src/Vanilla Items List.txt new file mode 100644 index 00000000..1fb18802 --- /dev/null +++ b/src/Vanilla Items List.txt @@ -0,0 +1,541 @@ +0| Weeds +2| Stone +4| Stone +16| Wild Horseradish +18| Daffodil +20| Leek +22| Dandelion +24| Parsnip +30| Lumber +60| Emerald +62| Aquamarine +64| Ruby +66| Amethyst +68| Topaz +70| Jade +72| Diamond +74| Prismatic Shard +78| Cave Carrot +80| Quartz +82| Fire Quartz +84| Frozen Tear +86| Earth Crystal +88| Coconut +90| Cactus Fruit +92| Sap +93| Torch +94| Spirit Torch +96| Dwarf Scroll I +97| Dwarf Scroll II +98| Dwarf Scroll III +99| Dwarf Scroll IV +100| Chipped Amphora +101| Arrowhead +102| Lost Book +103| Ancient Doll +104| Elvish Jewelry +105| Chewing Stick +106| Ornamental Fan +107| Dinosaur Egg +108| Rare Disc +109| Ancient Sword +110| Rusty Spoon +111| Rusty Spur +112| Rusty Cog +113| Chicken Statue +114| Ancient Seed +115| Prehistoric Tool +116| Dried Starfish +117| Anchor +118| Glass Shards +119| Bone Flute +120| Prehistoric Handaxe +121| Dwarvish Helm +122| Dwarf Gadget +123| Ancient Drum +124| Golden Mask +125| Golden Relic +126| Strange Doll +127| Strange Doll +128| Pufferfish +129| Anchovy +130| Tuna +131| Sardine +132| Bream +136| Largemouth Bass +137| Smallmouth Bass +138| Rainbow Trout +139| Salmon +140| Walleye +141| Perch +142| Carp +143| Catfish +144| Pike +145| Sunfish +146| Red Mullet +147| Herring +148| Eel +149| Octopus +150| Red Snapper +151| Squid +152| Seaweed +153| Green Algae +154| Sea Cucumber +155| Super Cucumber +156| Ghostfish +157| White Algae +158| Stonefish +159| Crimsonfish +160| Angler +161| Ice Pip +162| Lava Eel +163| Legend +164| Sandfish +165| Scorpion Carp +166| Treasure Chest +167| Joja Cola +168| Trash +169| Driftwood +170| Broken Glasses +171| Broken CD +172| Soggy Newspaper +174| Large Egg +176| Egg +178| Hay +180| Egg +182| Large Egg +184| Milk +186| Large Milk +188| Green Bean +190| Cauliflower +192| Potato +194| Fried Egg +195| Omelet +196| Salad +197| Cheese Cauliflower +198| Baked Fish +199| Parsnip Soup +200| Vegetable Medley +201| Complete Breakfast +202| Fried Calamari +203| Strange Bun +204| Lucky Lunch +205| Fried Mushroom +206| Pizza +207| Bean Hotpot +208| Glazed Yams +209| Carp Surprise +210| Hashbrowns +211| Pancakes +212| Salmon Dinner +213| Fish Taco +214| Crispy Bass +215| Pepper Poppers +216| Bread +218| Tom Kha Soup +219| Trout Soup +220| Chocolate Cake +221| Pink Cake +222| Rhubarb Pie +223| Cookie +224| Spaghetti +225| Fried Eel +226| Spicy Eel +227| Sashimi +228| Maki Roll +229| Tortilla +230| Red Plate +231| Eggplant Parmesan +232| Rice Pudding +233| Ice Cream +234| Blueberry Tart +235| Autumn's Bounty +236| Pumpkin Soup +237| Super Meal +238| Cranberry Sauce +239| Stuffing +240| Farmer's Lunch +241| Survival Burger +242| Dish O' The Sea +243| Miner's Treat +244| Roots Platter +245| Sugar +246| Wheat Flour +247| Oil +248| Garlic +250| Kale +252| Rhubarb +254| Melon +256| Tomato +257| Morel +258| Blueberry +259| Fiddlehead Fern +260| Hot Pepper +262| Wheat +264| Radish +266| Red Cabbage +268| Starfruit +270| Corn +272| Eggplant +274| Artichoke +276| Pumpkin +278| Bok Choy +280| Yam +281| Chanterelle +282| Cranberries +283| Holly +284| Beet +286| Cherry Bomb +287| Bomb +288| Mega Bomb +290| Stone +294| Twig +295| Twig +296| Salmonberry +297| Grass Starter +298| Hardwood Fence +299| Amaranth Seeds +300| Amaranth +301| Grape Starter +302| Hops Starter +303| Pale Ale +304| Hops +305| Void Egg +306| Mayonnaise +307| Duck Mayonnaise +309| Acorn +310| Maple Seed +311| Pine Cone +313| Weeds +314| Weeds +315| Weeds +316| Weeds +317| Weeds +318| Weeds +319| Weeds +320| Weeds +321| Weeds +322| Wood Fence +323| Stone Fence +324| Iron Fence +325| Gate +326| Dwarvish Translation Guide +328| Wood Floor +329| Stone Floor +330| Clay +331| Weathered Floor +333| Crystal Floor +334| Copper Bar +335| Iron Bar +336| Gold Bar +337| Iridium Bar +338| Refined Quartz +340| Honey +341| Tea Set +342| Pickles +343| Stone +344| Jelly +346| Beer +347| Rare Seed +348| Wine +349| Energy Tonic +350| Juice +351| Muscle Remedy +368| Basic Fertilizer +369| Quality Fertilizer +370| Basic Retaining Soil +371| Quality Retaining Soil +372| Clam +373| Golden Pumpkin +376| Poppy +378| Copper Ore +380| Iron Ore +382| Coal +384| Gold Ore +386| Iridium Ore +388| Wood +390| Stone +392| Nautilus Shell +393| Coral +394| Rainbow Shell +395| Coffee +396| Spice Berry +397| Sea Urchin +398| Grape +399| Spring Onion +400| Strawberry +401| Straw Floor +402| Sweet Pea +403| Field Snack +404| Common Mushroom +405| Wood Path +406| Wild Plum +407| Gravel Path +408| Hazelnut +409| Crystal Path +410| Blackberry +411| Cobblestone Path +412| Winter Root +413| Blue Slime Egg +414| Crystal Fruit +415| Stepping Stone Path +416| Snow Yam +417| Sweet Gem Berry +418| Crocus +419| Vinegar +420| Red Mushroom +421| Sunflower +422| Purple Mushroom +423| Rice +424| Cheese +425| Fairy Seeds +426| Goat Cheese +427| Tulip Bulb +428| Cloth +429| Jazz Seeds +430| Truffle +431| Sunflower Seeds +432| Truffle Oil +434| Stardrop +436| Goat Milk +437| Red Slime Egg +438| L. Goat Milk +439| Purple Slime Egg +440| Wool +441| Explosive Ammo +442| Duck Egg +444| Duck Feather +446| Rabbit's Foot +449| Stone Base +450| Stone +452| Weeds +453| Poppy Seeds +454| Ancient Fruit +455| Spangle Seeds +456| Algae Soup +457| Pale Broth +458| Bouquet +460| Mermaid's Pendant +461| Decorative Pot +463| Drum Block +464| Flute Block +465| Speed-Gro +466| Deluxe Speed-Gro +472| Parsnip Seeds +473| Bean Starter +474| Cauliflower Seeds +475| Potato Seeds +476| Garlic Seeds +477| Kale Seeds +478| Rhubarb Seeds +479| Melon Seeds +480| Tomato Seeds +481| Blueberry Seeds +482| Pepper Seeds +483| Wheat Seeds +484| Radish Seeds +485| Red Cabbage Seeds +486| Starfruit Seeds +487| Corn Seeds +488| Eggplant Seeds +489| Artichoke Seeds +490| Pumpkin Seeds +491| Bok Choy Seeds +492| Yam Seeds +493| Cranberry Seeds +494| Beet Seeds +495| Spring Seeds +496| Summer Seeds +497| Fall Seeds +498| Winter Seeds +499| Ancient Seeds +516| Small Glow Ring +517| Glow Ring +518| Small Magnet Ring +519| Magnet Ring +520| Slime Charmer Ring +521| Warrior Ring +522| Vampire Ring +523| Savage Ring +524| Ring of Yoba +525| Sturdy Ring +526| Burglar's Ring +527| Iridium Band +528| Jukebox Ring +529| Amethyst Ring +530| Topaz Ring +531| Aquamarine Ring +532| Jade Ring +533| Emerald Ring +534| Ruby Ring +535| Geode +536| Frozen Geode +537| Magma Geode +538| Alamite +539| Bixite +540| Baryte +541| Aerinite +542| Calcite +543| Dolomite +544| Esperite +545| Fluorapatite +546| Geminite +547| Helvite +548| Jamborite +549| Jagoite +550| Kyanite +551| Lunarite +552| Malachite +553| Neptunite +554| Lemon Stone +555| Nekoite +556| Orpiment +557| Petrified Slime +558| Thunder Egg +559| Pyrite +560| Ocean Stone +561| Ghost Crystal +562| Tigerseye +563| Jasper +564| Opal +565| Fire Opal +566| Celestine +567| Marble +568| Sandstone +569| Granite +570| Basalt +571| Limestone +572| Soapstone +573| Hematite +574| Mudstone +575| Obsidian +576| Slate +577| Fairy Stone +578| Star Shards +579| Prehistoric Scapula +580| Prehistoric Tibia +581| Prehistoric Skull +582| Skeletal Hand +583| Prehistoric Rib +584| Prehistoric Vertebra +585| Skeletal Tail +586| Nautilus Fossil +587| Amphibian Fossil +588| Palm Fossil +589| Trilobite +590| Artifact Spot +591| Tulip +593| Summer Spangle +595| Fairy Rose +597| Blue Jazz +599| Sprinkler +604| Plum Pudding +605| Artichoke Dip +606| Stir Fry +607| Roasted Hazelnuts +608| Pumpkin Pie +609| Radish Salad +610| Fruit Salad +611| Blackberry Cobbler +612| Cranberry Candy +613| Apple +618| Bruschetta +621| Quality Sprinkler +628| Cherry Sapling +629| Apricot Sapling +630| Orange Sapling +631| Peach Sapling +632| Pomegranate Sapling +633| Apple Sapling +634| Apricot +635| Orange +636| Peach +637| Pomegranate +638| Cherry +645| Iridium Sprinkler +648| Coleslaw +649| Fiddlehead Risotto +651| Poppyseed Muffin +668| Stone +670| Stone +674| Weeds +675| Weeds +676| Weeds +677| Weeds +678| Weeds +679| Weeds +680| Green Slime Egg +681| Rain Totem +682| Mutant Carp +684| Bug Meat +685| Bait +686| Spinner +687| Dressed Spinner +688| Warp Totem: Farm +689| Warp Totem: Mountains +690| Warp Totem: Beach +691| Barbed Hook +692| Lead Bobber +693| Treasure Hunter +694| Trap Bobber +695| Cork Bobber +698| Sturgeon +699| Tiger Trout +700| Bullhead +701| Tilapia +702| Chub +703| Magnet +704| Dorado +705| Albacore +706| Shad +707| Lingcod +708| Halibut +709| Hardwood +710| Crab Pot +715| Lobster +716| Crayfish +717| Crab +718| Cockle +719| Mussel +720| Shrimp +721| Snail +722| Periwinkle +723| Oyster +724| Maple Syrup +725| Oak Resin +726| Pine Tar +727| Chowder +728| Fish Stew +729| Escargot +730| Lobster Bisque +731| Maple Bar +732| Crab Cakes +734| Woodskip +745| Strawberry Seeds +746| Jack-O-Lantern +747| Rotten Plant +748| Rotten Plant +749| Omni Geode +750| Weeds +751| Stone +760| Stone +762| Stone +764| Stone +765| Stone +766| Slime +767| Bat Wing +768| Solar Essence +769| Void Essence +770| Mixed Seeds +771| Fiber +772| Oil of Garlic +773| Life Elixir +774| Wild Bait +775| Glacierfish +784| Weeds +785| Weeds +786| Weeds +787| Battery Pack +788| Lost Axe +789| Lucky Purple Shorts +790| Berry Basket -- cgit