summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/BobberBar Private Fields.txt40
-rw-r--r--src/Game1 Static Fields.txt329
-rw-r--r--src/StardewModdingAPI.sln46
-rw-r--r--src/StardewModdingAPI/App.config10
-rw-r--r--src/StardewModdingAPI/Command.cs109
-rw-r--r--src/StardewModdingAPI/Config.cs176
-rw-r--r--src/StardewModdingAPI/Constants.cs67
-rw-r--r--src/StardewModdingAPI/Entities/SCharacter.cs6
-rw-r--r--src/StardewModdingAPI/Entities/SFarm.cs6
-rw-r--r--src/StardewModdingAPI/Entities/SFarmAnimal.cs6
-rw-r--r--src/StardewModdingAPI/Entities/SNpc.cs6
-rw-r--r--src/StardewModdingAPI/Entities/SPlayer.cs33
-rw-r--r--src/StardewModdingAPI/Events/Controls.cs58
-rw-r--r--src/StardewModdingAPI/Events/EventArgs.cs272
-rw-r--r--src/StardewModdingAPI/Events/Game.cs123
-rw-r--r--src/StardewModdingAPI/Events/Graphics.cs162
-rw-r--r--src/StardewModdingAPI/Events/Location.cs30
-rw-r--r--src/StardewModdingAPI/Events/Menu.cs21
-rw-r--r--src/StardewModdingAPI/Events/Mine.cs14
-rw-r--r--src/StardewModdingAPI/Events/Player.cs35
-rw-r--r--src/StardewModdingAPI/Events/Time.cs42
-rw-r--r--src/StardewModdingAPI/Extensions.cs135
-rw-r--r--src/StardewModdingAPI/FodyWeavers.xml5
-rw-r--r--src/StardewModdingAPI/Inheritance/ItemStackChange.cs18
-rw-r--r--src/StardewModdingAPI/Inheritance/Menus/SBobberBar.cs288
-rw-r--r--src/StardewModdingAPI/Inheritance/Menus/SGameMenu.cs48
-rw-r--r--src/StardewModdingAPI/Inheritance/Menus/SInventoryPage.cs19
-rw-r--r--src/StardewModdingAPI/Inheritance/Minigames/SMinigameBase.cs34
-rw-r--r--src/StardewModdingAPI/Inheritance/SBareObject.cs20
-rw-r--r--src/StardewModdingAPI/Inheritance/SGame.cs1726
-rw-r--r--src/StardewModdingAPI/Inheritance/SObject.cs277
-rw-r--r--src/StardewModdingAPI/JsonResolver.cs212
-rw-r--r--src/StardewModdingAPI/Logger.cs326
-rw-r--r--src/StardewModdingAPI/Manifest.cs89
-rw-r--r--src/StardewModdingAPI/Mod.cs51
-rw-r--r--src/StardewModdingAPI/ModItem.cs15
-rw-r--r--src/StardewModdingAPI/Program.cs451
-rw-r--r--src/StardewModdingAPI/Properties/AssemblyInfo.cs39
-rw-r--r--src/StardewModdingAPI/StardewModdingAPI.csproj216
-rw-r--r--src/StardewModdingAPI/Version.cs23
-rw-r--r--src/StardewModdingAPI/icon.icobin0 -> 4286 bytes
-rw-r--r--src/StardewModdingAPI/packages.config7
-rw-r--r--src/StardewModdingAPI/steam_appid.txt1
-rw-r--r--src/TrainerMod/FodyWeavers.xml5
-rw-r--r--src/TrainerMod/Properties/AssemblyInfo.cs39
-rw-r--r--src/TrainerMod/TrainerMod.cs768
-rw-r--r--src/TrainerMod/TrainerMod.csproj115
-rw-r--r--src/TrainerMod/manifest.json6
-rw-r--r--src/TrainerMod/packages.config5
-rw-r--r--src/Vanilla Items List.txt541
50 files changed, 7070 insertions, 0 deletions
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 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<configuration>
+ <startup>
+ <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
+ </startup>
+ <runtime>
+ <loadFromRemoteSources enabled="true" />
+ </runtime>
+</configuration> \ 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<Command> RegisteredCommands = new List<Command>();
+ public string[] CalledArgs;
+ public string[] CommandArgs;
+ public string CommandDesc;
+
+ public string CommandName;
+
+ /// <summary>
+ /// Creates a Command from a Name, Description, and Arguments
+ /// </summary>
+ /// <param name="cname">Name</param>
+ /// <param name="cdesc">Description</param>
+ /// <param name="args">Arguments</param>
+ 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<EventArgsCommand> CommandFired;
+
+ /// <summary>
+ /// Calls the specified command. (It runs the command)
+ /// </summary>
+ /// <param name="input">The command to run</param>
+ 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");
+ }
+ }
+
+ /// <summary>
+ /// Registers a command to the list of commands properly
+ /// </summary>
+ /// <param name="command">Name of the command to register</param>
+ /// <param name="cdesc">Description</param>
+ /// <param name="args">Arguments (these are purely for viewing so that a user can see what an argument needs to be)</param>
+ /// <returns></returns>
+ 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;
+ }
+
+ /// <summary>
+ /// Looks up a command in the list of registered commands. Returns null if it doesn't exist (I think)
+ /// </summary>
+ /// <param name="name">Name of command to find</param>
+ /// <returns></returns>
+ public static Command FindCommand(string name)
+ {
+ return RegisteredCommands.Find(x => x.CommandName.Equals(name));
+ }
+
+ /// <summary>
+ /// Runs a command. Fires it. Calls it. Any of those.
+ /// </summary>
+ 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<T>() where T : Config => Activator.CreateInstance<T>();
+
+ /// <summary>
+ /// Loads the config from the json blob on disk, updating and re-writing to the disk if needed.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <returns></returns>
+ public virtual T LoadConfig<T>() 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<T>();
+ c.ConfigLocation = ConfigLocation;
+ ret = c;
+ }
+ else
+ {
+ try
+ {
+ //try to load the config from a json blob on disk
+ var c = JsonConvert.DeserializeObject<T>(File.ReadAllText(ConfigLocation), new JsonSerializerSettings {ContractResolver = new JsonResolver()});
+
+ c.ConfigLocation = ConfigLocation;
+
+ //update the config with default values if needed
+ ret = c.UpdateConfig<T>();
+
+ c = null;
+ }
+ catch (Exception ex)
+ {
+ Log.AsyncR($"Invalid JSON ({GetType().Name}): {ConfigLocation} \n{ex}");
+ return GenerateDefaultConfig<T>();
+ }
+ }
+
+ ret.WriteConfig();
+ return ret;
+ }
+
+ /// <summary>
+ /// MUST be implemented in inheriting class!
+ /// </summary>
+ public virtual T GenerateDefaultConfig<T>() where T : Config
+ {
+ return null;
+ }
+
+ /// <summary>
+ /// Merges a default-value config with the user-config on disk.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <returns></returns>
+ public virtual T UpdateConfig<T>() where T : Config
+ {
+ try
+ {
+ //default config
+ var b = JObject.FromObject(Instance<T>().GenerateDefaultConfig<T>(), 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<T>();
+
+ //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
+ {
+ /// <summary>
+ /// 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!
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="baseConfig"></param>
+ /// <param name="configLocation"></param>
+ /// <returns></returns>
+ public static T InitializeConfig<T>(this T baseConfig, string configLocation) where T : Config
+ {
+ if (baseConfig == null)
+ {
+ baseConfig = Activator.CreateInstance<T>();
+ /*
+ 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<T>();
+
+ return c;
+ }
+
+ /// <summary>
+ /// Writes a config to a json blob on the disk specified in the config's properties.
+ /// </summary>
+ public static void WriteConfig<T>(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);
+ }
+
+ /// <summary>
+ /// 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!
+ /// </summary>
+ public static T ReloadConfig<T>(this T baseConfig) where T : Config
+ {
+ return baseConfig.LoadConfig<T>();
+ }
+ }
+} \ 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
+{
+ /// <summary>
+ /// Static class containing readonly values.
+ /// </summary>
+ public static class Constants
+ {
+ public static readonly Version Version = new Version(0, 40, 0, "Alpha");
+
+ /// <summary>
+ /// Not quite "constant", but it makes more sense for it to be here, at least for now
+ /// </summary>
+ public static int ModsLoaded = 0;
+
+ /// <summary>
+ /// Stardew Valley's roaming app data location.
+ /// %AppData%//StardewValley
+ /// </summary>
+ 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);
+
+ /// <summary>
+ /// Execution path to execute the code.
+ /// </summary>
+ public static string ExecutionPath => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+
+ /// <summary>
+ /// Title for the API console
+ /// </summary>
+ public static string ConsoleTitle => $"Stardew Modding API Console - Version {Version.VersionString} - Mods Loaded: {ModsLoaded}";
+
+ /// <summary>
+ /// Path for log files to be output to.
+ /// %LocalAppData%//StardewValley//ErrorLogs
+ /// </summary>
+ 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");
+
+ /// <summary>
+ /// 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
+ /// </summary>
+ public static bool EnableDrawingIntoRenderTarget => true;
+
+ /// <summary>
+ /// Completely overrides the base game's draw call to the one is SGame
+ /// </summary>
+ 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
+{
+ /// <summary>
+ /// Static class for intergrating with the player
+ /// </summary>
+ public class SPlayer
+ {
+ /// <summary>
+ /// Calls 'getAllFarmers' in Game1
+ /// </summary>
+ public static List<Farmer> AllFarmers => Game1.getAllFarmers();
+
+ /// <summary>
+ /// Do not use.
+ /// </summary>
+ [Obsolete("Use 'Player' instead.")]
+ public static Farmer CurrentFarmer => Game1.player;
+
+ /// <summary>
+ /// Gets the current player from Game1
+ /// </summary>
+ public static Farmer Player => Game1.player;
+
+ /// <summary>
+ /// Gets the player's current location from Game1
+ /// </summary>
+ 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<EventArgsKeyboardStateChanged> KeyboardChanged = delegate { };
+ public static event EventHandler<EventArgsKeyPressed> KeyPressed = delegate { };
+ public static event EventHandler<EventArgsKeyPressed> KeyReleased = delegate { };
+ public static event EventHandler<EventArgsMouseStateChanged> MouseChanged = delegate { };
+ public static event EventHandler<EventArgsControllerButtonPressed> ControllerButtonPressed = delegate { };
+ public static event EventHandler<EventArgsControllerButtonReleased> ControllerButtonReleased = delegate { };
+ public static event EventHandler<EventArgsControllerTriggerPressed> ControllerTriggerPressed = delegate { };
+ public static event EventHandler<EventArgsControllerTriggerReleased> 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<GameLocation> newLocations)
+ {
+ NewLocations = newLocations;
+ }
+
+ public List<GameLocation> 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<Vector2, Object> newObjects)
+ {
+ NewObjects = newObjects;
+ }
+
+ public SerializableDictionary<Vector2, Object> 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<Item> inventory, List<ItemStackChange> 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<Item> Inventory { get; private set; }
+ public List<ItemStackChange> Added { get; private set; }
+ public List<ItemStackChange> Removed { get; private set; }
+ public List<ItemStackChange> 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 { };
+
+ /// <summary>
+ /// Fires every update (1/60 of a second)
+ /// </summary>
+ public static event EventHandler UpdateTick = delegate { };
+
+ /// <summary>
+ /// Fires every other update (1/30 of a second)
+ /// </summary>
+ public static event EventHandler SecondUpdateTick = delegate { };
+
+ /// <summary>
+ /// Fires every fourth update (1/15 of a second)
+ /// </summary>
+ public static event EventHandler FourthUpdateTick = delegate { };
+
+ /// <summary>
+ /// Fires every eighth update (roughly 1/8 of a second)
+ /// </summary>
+ public static event EventHandler EighthUpdateTick = delegate { };
+
+ /// <summary>
+ /// Fires every fifthteenth update (1/4 of a second)
+ /// </summary>
+ public static event EventHandler QuarterSecondTick = delegate { };
+
+ /// <summary>
+ /// Fires every thirtieth update (1/2 of a second)
+ /// </summary>
+ public static event EventHandler HalfSecondTick = delegate { };
+
+ /// <summary>
+ /// Fires every sixtieth update (a second)
+ /// </summary>
+ 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
+{
+ /// <summary>
+ ///
+ /// </summary>
+ public static class GraphicsEvents
+ {
+ /// <summary>
+ /// Occurs when the form (game) is resized.
+ /// </summary>
+ public static event EventHandler Resize = delegate { };
+
+ /// <summary>
+ /// Occurs before anything is drawn.
+ /// </summary>
+ public static event EventHandler OnPreRenderEvent = delegate { };
+
+ /// <summary>
+ /// Occurs before the GUI is drawn.
+ /// </summary>
+ public static event EventHandler OnPreRenderGuiEvent = delegate { };
+
+ /// <summary>
+ /// Occurs after the GUI is drawn.
+ /// </summary>
+ public static event EventHandler OnPostRenderGuiEvent = delegate { };
+
+ /// <summary>
+ /// Occurs before the HUD is drawn.
+ /// </summary>
+ public static event EventHandler OnPreRenderHudEvent = delegate { };
+
+ /// <summary>
+ /// Occurs after the HUD is drawn.
+ /// </summary>
+ public static event EventHandler OnPostRenderHudEvent = delegate { };
+
+ /// <summary>
+ /// Occurs after everything is drawn.
+ /// </summary>
+ public static event EventHandler OnPostRenderEvent = delegate { };
+
+ /// <summary>
+ /// Occurs before the GUI is drawn. Does not check for conditional statements.
+ /// </summary>
+ public static event EventHandler OnPreRenderGuiEventNoCheck = delegate { };
+
+ /// <summary>
+ /// Occurs after the GUI is drawn. Does not check for conditional statements.
+ /// </summary>
+ public static event EventHandler OnPostRenderGuiEventNoCheck = delegate { };
+
+ /// <summary>
+ /// Occurs before the HUD is drawn. Does not check for conditional statements.
+ /// </summary>
+ public static event EventHandler OnPreRenderHudEventNoCheck = delegate { };
+
+ /// <summary>
+ /// Occurs after the HUD is drawn. Does not check for conditional statements.
+ /// </summary>
+ public static event EventHandler OnPostRenderHudEventNoCheck = delegate { };
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ 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<EventArgsGameLocationsChanged> LocationsChanged = delegate { };
+ public static event EventHandler<EventArgsLocationObjectsChanged> LocationObjectsChanged = delegate { };
+ public static event EventHandler<EventArgsCurrentLocationChanged> CurrentLocationChanged = delegate { };
+
+ internal static void InvokeLocationsChanged(List<GameLocation> 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<Vector2, Object> 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<EventArgsClickableMenuChanged> MenuChanged = delegate { };
+ public static event EventHandler<EventArgsClickableMenuClosed> 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<EventArgsMineLevelChanged> 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<EventArgsFarmerChanged> FarmerChanged = delegate { };
+ public static event EventHandler<EventArgsInventoryChanged> InventoryChanged = delegate { };
+ public static event EventHandler<EventArgsLevelUp> LeveledUp = delegate { };
+ public static event EventHandler<EventArgsLoadedGameChanged> LoadedGame = delegate { };
+
+ internal static void InvokeFarmerChanged(Farmer priorFarmer, Farmer newFarmer)
+ {
+ FarmerChanged.Invoke(null, new EventArgsFarmerChanged(priorFarmer, newFarmer));
+ }
+
+ internal static void InvokeInventoryChanged(List<Item> inventory, List<ItemStackChange> 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<EventArgsIntChanged> TimeOfDayChanged = delegate { };
+ public static event EventHandler<EventArgsIntChanged> DayOfMonthChanged = delegate { };
+ public static event EventHandler<EventArgsIntChanged> YearOfGameChanged = delegate { };
+ public static event EventHandler<EventArgsStringChanged> SeasonOfYearChanged = delegate { };
+
+ /// <summary>
+ /// Occurs when Game1.newDay changes. True directly before saving, and False directly after.
+ /// </summary>
+ public static event EventHandler<EventArgsNewDay> 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<T>")]
+ public static string ToSingular(this IEnumerable ienum, string split = ", ")
+ {
+ Log.AsyncR("The usage of ToSingular has changed. Please update your call to use ToSingular<T>");
+ return "";
+ }
+
+ public static string ToSingular<T>(this IEnumerable<T> 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<T>(this IEnumerable<T> 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<T>(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<T>(this Type t, object o, string name) where T : class
+ {
+ return t.GetBaseFieldInfo(name).GetValue(o) as T;
+ }
+
+ public static void SetBaseFieldValue<T>(this Type t, object o, string name, object newValue) where T : class
+ {
+ t.GetBaseFieldInfo(name).SetValue(o, newValue as T);
+ }
+
+ /*
+ public static T GetBaseFieldValue<T>(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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Weavers>
+ <Costura/>
+
+</Weavers> \ 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
+ {
+ /// <summary>
+ /// DO NOT CONSTRUCT THIS CLASS
+ /// To retrieve an instance of SBobberBar, use SBobberBar.ConstructFromBaseClass()
+ /// </summary>
+ public SBobberBar(int whichFish, float fishSize, bool treasure, int bobber) : base(whichFish, fishSize, treasure, bobber)
+ {
+ }
+
+ public BobberBar BaseBobberBar { get; private set; }
+
+ /// <summary>
+ /// The green rectangle bar that moves up and down
+ /// </summary>
+ public float bobberPosition
+ {
+ get { return (float) GetBaseFieldInfo("bobberPosition").GetValue(BaseBobberBar); }
+ set { GetBaseFieldInfo("bobberPosition").SetValue(BaseBobberBar, value); }
+ }
+
+ /// <summary>
+ /// The green bar on the right. How close to catching the fish you are
+ /// Range: 0 - 1 | 1 = catch, 0 = fail
+ /// </summary>
+ 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); }
+ }
+
+ /// <summary>
+ /// Whether or not a treasure chest appears
+ /// </summary>
+ 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<ClickableComponent> tabs
+ {
+ get { return (List<ClickableComponent>) GetBaseFieldInfo("tabs").GetValue(BaseGameMenu); }
+ set { GetBaseFieldInfo("tabs").SetValue(BaseGameMenu, value); }
+ }
+
+ public List<IClickableMenu> pages
+ {
+ get { return (List<IClickableMenu>) 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
+{
+ /// <summary>
+ /// The 'SGame' class.
+ /// This summary, and many others, only exists because XML doc tags.
+ /// </summary>
+ public class SGame : Game1
+ {
+ /// <summary>
+ /// Useless right now.
+ /// </summary>
+ public const int LowestModItemID = 1000;
+
+ private bool FireLoadedGameEvent;
+
+ /// <summary>
+ /// Gets a jagged array of all buttons pressed on the gamepad the prior frame.
+ /// </summary>
+ public Buttons[][] PreviouslyPressedButtons;
+
+ internal SGame()
+ {
+ Instance = this;
+ FirstUpdate = true;
+ }
+
+ /// <summary>
+ /// Useless at this time.
+ /// </summary>
+ [Obsolete]
+ // ReSharper disable once UnusedAutoPropertyAccessor.Local
+ public static Dictionary<int, SObject> ModItems { get; private set; }
+
+ /// <summary>
+ /// The current KeyboardState
+ /// </summary>
+ public KeyboardState KStateNow { get; private set; }
+
+ /// <summary>
+ /// The prior KeyboardState
+ /// </summary>
+ public KeyboardState KStatePrior { get; private set; }
+
+ /// <summary>
+ /// The current MouseState
+ /// </summary>
+ public MouseState MStateNow { get; private set; }
+
+ /// <summary>
+ /// The prior MouseState
+ /// </summary>
+ public MouseState MStatePrior { get; private set; }
+
+ /// <summary>
+ /// All keys pressed on the current frame
+ /// </summary>
+ public Keys[] CurrentlyPressedKeys => KStateNow.GetPressedKeys();
+
+ /// <summary>
+ /// All keys pressed on the prior frame
+ /// </summary>
+ public Keys[] PreviouslyPressedKeys => KStatePrior.GetPressedKeys();
+
+ /// <summary>
+ /// All keys pressed on this frame except for the ones pressed on the prior frame
+ /// </summary>
+ public Keys[] FramePressedKeys => CurrentlyPressedKeys.Except(PreviouslyPressedKeys).ToArray();
+
+ /// <summary>
+ /// All keys pressed on the prior frame except for the ones pressed on the current frame
+ /// </summary>
+ public Keys[] FrameReleasedKeys => PreviouslyPressedKeys.Except(CurrentlyPressedKeys).ToArray();
+
+ /// <summary>
+ /// Whether or not a save was tagged as 'Loaded' the prior frame.
+ /// </summary>
+ public bool PreviouslyLoadedGame { get; private set; }
+
+ /// <summary>
+ /// The list of GameLocations on the prior frame
+ /// </summary>
+ public int PreviousGameLocations { get; private set; }
+
+ /// <summary>
+ /// The list of GameObjects on the prior frame
+ /// </summary>
+ public int PreviousLocationObjects { get; private set; }
+
+ /// <summary>
+ /// The list of Items in the player's inventory on the prior frame
+ /// </summary>
+ public Dictionary<Item, int> PreviousItems { get; private set; }
+
+ /// <summary>
+ /// The player's Combat level on the prior frame
+ /// </summary>
+ public int PreviousCombatLevel { get; private set; }
+
+ /// <summary>
+ /// The player's Farming level on the prior frame
+ /// </summary>
+ public int PreviousFarmingLevel { get; private set; }
+
+ /// <summary>
+ /// The player's Fishing level on the prior frame
+ /// </summary>
+ public int PreviousFishingLevel { get; private set; }
+
+ /// <summary>
+ /// The player's Foraging level on the prior frame
+ /// </summary>
+ public int PreviousForagingLevel { get; private set; }
+
+ /// <summary>
+ /// The player's Mining level on the prior frame
+ /// </summary>
+ public int PreviousMiningLevel { get; private set; }
+
+ /// <summary>
+ /// The player's Luck level on the prior frame
+ /// </summary>
+ public int PreviousLuckLevel { get; private set; }
+
+ //Kill me now comments are so boring
+
+ /// <summary>
+ /// The player's previous game location
+ /// </summary>
+ public GameLocation PreviousGameLocation { get; private set; }
+
+ /// <summary>
+ /// The previous ActiveGameMenu in Game1
+ /// </summary>
+ public IClickableMenu PreviousActiveMenu { get; private set; }
+
+ /// <summary>
+ /// Indicates if the MenuClosed event was fired to prevent it from re-firing.
+ /// </summary>
+ internal bool WasMenuClosedInvoked = false;
+
+ /// <summary>
+ /// The previous mine level
+ /// </summary>
+ public int PreviousMineLevel { get; private set; }
+
+ /// <summary>
+ /// The previous TimeOfDay (Int32 between 600 and 2400?)
+ /// </summary>
+ public int PreviousTimeOfDay { get; private set; }
+
+ /// <summary>
+ /// The previous DayOfMonth (Int32 between 1 and 28?)
+ /// </summary>
+ public int PreviousDayOfMonth { get; private set; }
+
+ /// <summary>
+ /// The previous Season (String as follows: "winter", "spring", "summer", "fall")
+ /// </summary>
+ public string PreviousSeasonOfYear { get; private set; }
+
+ /// <summary>
+ /// The previous Year
+ /// </summary>
+ public int PreviousYearOfGame { get; private set; }
+
+ /// <summary>
+ /// The previous result of Game1.newDay
+ /// </summary>
+ public bool PreviousIsNewDay { get; private set; }
+
+ /// <summary>
+ /// The previous 'Farmer' (Player)
+ /// </summary>
+ public Farmer PreviousFarmer { get; private set; }
+
+ /// <summary>
+ /// The current index of the update tick. Recycles every 60th tick to 0. (Int32 between 0 and 59)
+ /// </summary>
+ public int CurrentUpdateTick { get; private set; }
+
+ /// <summary>
+ /// Whether or not this update frame is the very first of the entire game
+ /// </summary>
+ public bool FirstUpdate { get; private set; }
+
+ /// <summary>
+ /// The current RenderTarget in Game1 (Private field, uses reflection)
+ /// </summary>
+ public RenderTarget2D Screen
+ {
+ get { return typeof(Game1).GetBaseFieldValue<RenderTarget2D>(Program.gamePtr, "screen"); }
+ set { typeof(Game1).SetBaseFieldValue<RenderTarget2D>(this, "screen", value); }
+ }
+
+ /// <summary>
+ /// The current Colour in Game1 (Private field, uses reflection)
+ /// </summary>
+ public Color BgColour
+ {
+ get { return (Color) typeof(Game1).GetBaseFieldValue<object>(Program.gamePtr, "bgColor"); }
+ set { typeof(Game1).SetBaseFieldValue<object>(this, "bgColor", value); }
+ }
+
+ /// <summary>
+ /// Static accessor for an Instance of the class SGame
+ /// </summary>
+ public static SGame Instance { get; private set; }
+
+ /// <summary>
+ /// The game's FPS. Re-determined every Draw update.
+ /// </summary>
+ public static float FramesPerSecond { get; private set; }
+
+ /// <summary>
+ /// Whether or not we're in a pseudo 'debug' mode. Mostly for displaying information like FPS.
+ /// </summary>
+ public static bool Debug { get; private set; }
+
+ internal static Queue<String> DebugMessageQueue { get; private set; }
+
+ /// <summary>
+ /// The current player (equal to Farmer.Player)
+ /// </summary>
+ [Obsolete("Use Farmer.Player instead")]
+ public Farmer CurrentFarmer => player;
+
+ /// <summary>
+ /// Gets ALL static fields that belong to 'Game1'
+ /// </summary>
+ public static FieldInfo[] GetStaticFields => typeof(Game1).GetFields();
+
+ /// <summary>
+ /// Whether or not a button was just pressed on the controller
+ /// </summary>
+ /// <param name="button"></param>
+ /// <param name="buttonState"></param>
+ /// <param name="stateIndex"></param>
+ /// <returns></returns>
+ private bool WasButtonJustPressed(Buttons button, ButtonState buttonState, PlayerIndex stateIndex)
+ {
+ return buttonState == ButtonState.Pressed && !PreviouslyPressedButtons[(int) stateIndex].Contains(button);
+ }
+
+ /// <summary>
+ /// Whether or not a button was just released on the controller
+ /// </summary>
+ /// <param name="button"></param>
+ /// <param name="buttonState"></param>
+ /// <param name="stateIndex"></param>
+ /// <returns></returns>
+ private bool WasButtonJustReleased(Buttons button, ButtonState buttonState, PlayerIndex stateIndex)
+ {
+ return buttonState == ButtonState.Released && PreviouslyPressedButtons[(int) stateIndex].Contains(button);
+ }
+
+ /// <summary>
+ /// Whether or not an analog button was just pressed on the controller
+ /// </summary>
+ /// <param name="button"></param>
+ /// <param name="value"></param>
+ /// <param name="stateIndex"></param>
+ /// <returns></returns>
+ private bool WasButtonJustPressed(Buttons button, float value, PlayerIndex stateIndex)
+ {
+ return WasButtonJustPressed(button, value > 0.2f ? ButtonState.Pressed : ButtonState.Released, stateIndex);
+ }
+
+ /// <summary>
+ /// Whether or not an analog button was just released on the controller
+ /// </summary>
+ /// <param name="button"></param>
+ /// <param name="value"></param>
+ /// <param name="stateIndex"></param>
+ /// <returns></returns>
+ private bool WasButtonJustReleased(Buttons button, float value, PlayerIndex stateIndex)
+ {
+ return WasButtonJustReleased(button, value > 0.2f ? ButtonState.Pressed : ButtonState.Released, stateIndex);
+ }
+
+ /// <summary>
+ /// Gets an array of all Buttons pressed on a joystick
+ /// </summary>
+ /// <param name="index"></param>
+ /// <returns></returns>
+ public Buttons[] GetButtonsDown(PlayerIndex index)
+ {
+ var state = GamePad.GetState(index);
+ var buttons = new List<Buttons>();
+ 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();
+ }
+
+ /// <summary>
+ /// Gets all buttons that were pressed on the current frame of a joystick
+ /// </summary>
+ /// <param name="index"></param>
+ /// <returns></returns>
+ public Buttons[] GetFramePressedButtons(PlayerIndex index)
+ {
+ var state = GamePad.GetState(index);
+ var buttons = new List<Buttons>();
+ 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();
+ }
+
+ /// <summary>
+ /// Gets all buttons that were released on the current frame of a joystick
+ /// </summary>
+ /// <param name="index"></param>
+ /// <returns></returns>
+ public Buttons[] GetFrameReleasedButtons(PlayerIndex index)
+ {
+ var state = GamePad.GetState(index);
+ var buttons = new List<Buttons>();
+ 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();
+ }
+
+ /// <summary>
+ ///
+ /// </summary>
+ public static MethodInfo DrawFarmBuildings = typeof(Game1).GetMethod("drawFarmBuildings", BindingFlags.NonPublic | BindingFlags.Instance);
+
+ /// <summary>
+ ///
+ /// </summary>
+ public static MethodInfo DrawHUD = typeof(Game1).GetMethod("drawHUD", BindingFlags.NonPublic | BindingFlags.Instance);
+
+ /// <summary>
+ ///
+ /// </summary>
+ 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();
+
+ /// <summary>
+ /// Whether or not the game's zoom level is 1.0f
+ /// </summary>
+ public bool ZoomLevelIsOne => options.zoomLevel.Equals(1.0f);
+
+ /// <summary>
+ /// XNA Init Method
+ /// </summary>
+ protected override void Initialize()
+ {
+ Log.AsyncY("XNA Initialize");
+ //ModItems = new Dictionary<int, SObject>();
+ DebugMessageQueue = new Queue<string>();
+ PreviouslyPressedButtons = new Buttons[4][];
+ for (var i = 0; i < 4; ++i) PreviouslyPressedButtons[i] = new Buttons[0];
+
+ base.Initialize();
+ GameEvents.InvokeInitialize();
+ }
+
+ /// <summary>
+ /// XNA LC Method
+ /// </summary>
+ protected override void LoadContent()
+ {
+ Log.AsyncY("XNA LoadContent");
+ base.LoadContent();
+ GameEvents.InvokeLoadContent();
+ }
+
+ /// <summary>
+ /// XNA Update Method
+ /// </summary>
+ /// <param name="gameTime"></param>
+ 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);
+ }
+ }
+
+ /// <summary>
+ /// XNA Draw Method
+ /// </summary>
+ /// <param name="gameTime"></param>
+ 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<NPC>.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<NPC>.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<ItemStackChange> 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<Item> items, out List<ItemStackChange> changedItems)
+ {
+ changedItems = new List<ItemStackChange>();
+ IEnumerable<Item> 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();
+ }
+
+ /// <summary>
+ /// Invokes a private, non-static method in Game1 via Reflection
+ /// </summary>
+ /// <param name="name">The name of the method</param>
+ /// <param name="parameters">Any parameters needed</param>
+ /// <returns>Whatever the method normally returns. Null if void.</returns>
+ [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;
+ }
+ }
+
+ /// <summary>
+ /// Invokes a given method info with the supplied parameters
+ /// </summary>
+ /// <param name="mi"></param>
+ /// <param name="parameters"></param>
+ /// <returns></returns>
+ 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;
+ }
+ }
+
+ /// <summary>
+ /// Queue's a message to be drawn in Debug mode (F3)
+ /// </summary>
+ /// <returns></returns>
+ 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<object>(jObject, "parentSheetIndex") as int?;
+ var stack = GetTokenValue<object>(jObject, "parentSheetIndex") as int?;
+ var isRecipe = GetTokenValue<object>(jObject, "parentSheetIndex") as bool?;
+ var price = GetTokenValue<object>(jObject, "parentSheetIndex") as int?;
+ var quality = GetTokenValue<object>(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<T>(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<Rectangle>) 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<Rectangle>();
+
+ 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
+
+ /// <summary>
+ /// Catch unhandled exception from the application
+ /// </summary>
+ /// <remarks>Should be moved out of here if we do more than just log the exception.</remarks>
+ 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());
+ }
+
+ /// <summary>
+ /// Catch thread exception from the application
+ /// </summary>
+ /// <remarks>Should be moved out of here if we do more than just log the exception.</remarks>
+ 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
+
+ /// <summary>
+ /// NOTICE: Sync logging is discouraged. Please use Async instead.
+ /// </summary>
+ /// <param name="message">Message to log</param>
+ /// <param name="colour">Colour of message</param>
+ 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("<value> must be specified");
+ }
+
+ public static void LogObjectValueNotSpecified()
+ {
+ AsyncR("<object> and <value> must be specified");
+ }
+
+ public static void LogValueInvalid()
+ {
+ AsyncR("<value> is invalid");
+ }
+
+ public static void LogObjectInvalid()
+ {
+ AsyncR("<object> is invalid");
+ }
+
+ public static void LogValueNotInt32()
+ {
+ AsyncR("<value> 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
+ }
+
+ /// <summary>
+ /// A Logging class implementing the Singleton pattern and an internal Queue to be flushed perdiodically
+ /// </summary>
+ public class LogWriter
+ {
+ private static LogWriter _instance;
+ private static ConcurrentQueue<LogInfo> _logQueue;
+ private static DateTime _lastFlushTime = DateTime.Now;
+ private static StreamWriter _stream;
+
+ /// <summary>
+ /// Private to prevent creation of other instances
+ /// </summary>
+ private LogWriter()
+ {
+ }
+
+ /// <summary>
+ /// Exposes _instace and creates a new one if it is null
+ /// </summary>
+ 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<LogInfo>();
+ 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;
+ }
+ }
+
+ /// <summary>
+ /// Writes into the ConcurrentQueue the Message specified
+ /// </summary>
+ /// <param name="message">The message to write to the log</param>
+ public void WriteToLog(string message)
+ {
+ lock (_logQueue)
+ {
+ var logEntry = new LogInfo(message);
+ _logQueue.Enqueue(logEntry);
+
+ if (_logQueue.Any())
+ {
+ FlushLog();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Writes into the ConcurrentQueue the Entry specified
+ /// </summary>
+ /// <param name="logEntry">The logEntry to write to the log</param>
+ public void WriteToLog(LogInfo logEntry)
+ {
+ lock (_logQueue)
+ {
+ _logQueue.Enqueue(logEntry);
+
+ if (_logQueue.Any())
+ {
+ FlushLog();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Flushes the ConcurrentQueue to the log file specified in Constants
+ /// </summary>
+ 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();
+ }
+ }
+ }
+
+ /// <summary>
+ /// A struct to store the message and the Date and Time the log entry was created
+ /// </summary>
+ 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
+ {
+ /// <summary>
+ /// The name of your mod.
+ /// </summary>
+ public virtual string Name { get; set; }
+
+ /// <summary>
+ /// The name of the mod's authour.
+ /// </summary>
+ public virtual string Authour { get; set; }
+
+ /// <summary>
+ /// The version of the mod.
+ /// </summary>
+ public virtual Version Version { get; set; }
+
+ /// <summary>
+ /// A description of the mod.
+ /// </summary>
+ public virtual string Description { get; set; }
+
+ /// <summary>
+ /// The unique ID of the mod. It doesn't *need* to be anything.
+ /// </summary>
+ public virtual string UniqueID { get; set; }
+
+ /// <summary>
+ /// Whether or not the mod uses per-save-config files.
+ /// </summary>
+ public virtual bool PerSaveConfigs { get; set; }
+
+ /// <summary>
+ /// The name of the DLL in the directory that has the Entry() method.
+ /// </summary>
+ public virtual string EntryDll { get; set; }
+
+ public override T GenerateDefaultConfig<T>()
+ {
+ Name = "";
+ Authour = "";
+ Version = new Version(0, 0, 0, "");
+ Description = "";
+ UniqueID = Guid.NewGuid().ToString();
+ PerSaveConfigs = false;
+ EntryDll = "";
+ return this as T;
+ }
+
+ public override T LoadConfig<T>()
+ {
+ if (File.Exists(ConfigLocation))
+ {
+ try
+ {
+ Manifest m = JsonConvert.DeserializeObject<Manifest>(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<T>();
+ }
+ }
+} \ 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
+ {
+ /// <summary>
+ /// The mod's manifest
+ /// </summary>
+ public Manifest Manifest { get; internal set; }
+
+ /// <summary>
+ /// Where the mod is located on the disk.
+ /// </summary>
+ public string PathOnDisk { get; internal set; }
+
+ /// <summary>
+ /// A basic path to store your mod's config at.
+ /// </summary>
+ public string BaseConfigPath => PathOnDisk + "\\config.json";
+
+ /// <summary>
+ /// A basic path to where per-save configs are stored
+ /// </summary>
+ public string PerSaveConfigFolder => GetPerSaveConfigFolder();
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ public string PerSaveConfigPath => Constants.CurrentSavePathExists ? Path.Combine(PerSaveConfigFolder, Constants.SaveFolderName + ".json") : "";
+
+ /// <summary>
+ /// A basic method that is the entry-point of your mod. It will always be called once when the mod loads.
+ /// </summary>
+ 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<string> _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<string> _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);
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /// <summary>
+ /// Main method holding the API execution
+ /// </summary>
+ /// <param name="args"></param>
+ 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();
+ }
+
+ /// <summary>
+ /// Set up the console properties
+ /// </summary>
+ private static void ConfigureUI()
+ {
+ Console.Title = Constants.ConsoleTitle;
+
+#if DEBUG
+ Console.Title += " - DEBUG IS NOT FALSE, AUTHOUR NEEDS TO REUPLOAD THIS VERSION";
+#endif
+ }
+
+ /// <summary>
+ /// Setup the required paths and logging
+ /// </summary>
+ private static void ConfigurePaths()
+ {
+ Log.AsyncY("Validating api paths...");
+
+ _modPaths = new List<string> {Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StardewValley", "Mods"), Path.Combine(Constants.ExecutionPath, "Mods")};
+ //_modContentPaths = new List<string>();
+
+ //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");
+ }
+ }
+
+ /// <summary>
+ /// Load Stardev Valley and control features
+ /// </summary>
+ 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 <cmd>' 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;
+ });
+ }
+
+ /// <summary>
+ /// Wrap the 'RunGame' method for console output
+ /// </summary>
+ 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 <cmd>' 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);
+ }
+
+ /// <summary>
+ /// Create the given directory path if it does not exist
+ /// </summary>
+ /// <param name="path">Desired directory path</param>
+ 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<GameLocation> 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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{F1A573B0-F436-472C-AE29-0B91EA6B9F8F}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>StardewModdingAPI</RootNamespace>
+ <AssemblyName>StardewModdingAPI</AssemblyName>
+ <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <SccProjectName>
+ </SccProjectName>
+ <SccLocalPath>
+ </SccLocalPath>
+ <SccAuxPath>
+ </SccAuxPath>
+ <SccProvider>
+ </SccProvider>
+ <IsWebBootstrapper>false</IsWebBootstrapper>
+ <TargetFrameworkProfile />
+ <NuGetPackageImportStamp>
+ </NuGetPackageImportStamp>
+ <PublishUrl>publish\</PublishUrl>
+ <Install>true</Install>
+ <InstallFrom>Disk</InstallFrom>
+ <UpdateEnabled>false</UpdateEnabled>
+ <UpdateMode>Foreground</UpdateMode>
+ <UpdateInterval>7</UpdateInterval>
+ <UpdateIntervalUnits>Days</UpdateIntervalUnits>
+ <UpdatePeriodically>false</UpdatePeriodically>
+ <UpdateRequired>false</UpdateRequired>
+ <MapFileExtensions>true</MapFileExtensions>
+ <ApplicationRevision>0</ApplicationRevision>
+ <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
+ <UseApplicationTrust>false</UseApplicationTrust>
+ <BootstrapperEnabled>true</BootstrapperEnabled>
+ </PropertyGroup>
+ <Choose>
+ <When Condition="'$(SteamInstallPath)' != ''">
+ <PropertyGroup>
+ <SteamPath>$(SteamInstallPath)</SteamPath>
+ </PropertyGroup>
+ </When>
+ <Otherwise>
+ <PropertyGroup>
+ <SteamPath>..\</SteamPath>
+ </PropertyGroup>
+ </Otherwise>
+ </Choose>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>TRACE;DEBUG</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <Prefer32Bit>false</Prefer32Bit>
+ <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <Prefer32Bit>false</Prefer32Bit>
+ <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
+ <PlatformTarget>x86</PlatformTarget>
+ <OutputPath>bin\Debug\</OutputPath>
+ <Prefer32Bit>false</Prefer32Bit>
+ <DefineConstants>TRACE</DefineConstants>
+ <UseVSHostingProcess>true</UseVSHostingProcess>
+ <Optimize>true</Optimize>
+ <DocumentationFile>bin\Debug\StardewModdingAPI.XML</DocumentationFile>
+ <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
+ <LangVersion>6</LangVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
+ <PlatformTarget>x86</PlatformTarget>
+ <OutputPath>bin\Release\</OutputPath>
+ <Prefer32Bit>false</Prefer32Bit>
+ <DocumentationFile>bin\Release\StardewModdingAPI.XML</DocumentationFile>
+ <DefineConstants>TRACE</DefineConstants>
+ <Optimize>true</Optimize>
+ <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
+ <LangVersion>6</LangVersion>
+ </PropertyGroup>
+ <PropertyGroup>
+ <ApplicationIcon>icon.ico</ApplicationIcon>
+ </PropertyGroup>
+ <PropertyGroup>
+ <StartupObject>StardewModdingAPI.Program</StartupObject>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.Xna.Framework.Game, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.Xna.Framework.Xact, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+ <HintPath>..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
+ <Reference Include="Stardew Valley, Version=1.0.5905.5747, Culture=neutral, processorArchitecture=x86">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>$(SteamPath)\steamapps\common\Stardew Valley\Stardew Valley.exe</HintPath>
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Drawing" />
+ <Reference Include="System.Windows.Forms" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ <Reference Include="xTile, Version=2.0.4.0, Culture=neutral, processorArchitecture=x86">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>$(SteamPath)\steamapps\common\Stardew Valley\xTile.dll</HintPath>
+ <Private>False</Private>
+ </Reference>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Command.cs" />
+ <Compile Include="Config.cs" />
+ <Compile Include="Constants.cs" />
+ <Compile Include="Entities\SCharacter.cs" />
+ <Compile Include="Entities\SFarm.cs" />
+ <Compile Include="Entities\SFarmAnimal.cs" />
+ <Compile Include="Entities\SNpc.cs" />
+ <Compile Include="Entities\SPlayer.cs" />
+ <Compile Include="Events\Controls.cs" />
+ <Compile Include="Events\EventArgs.cs" />
+ <Compile Include="Events\Game.cs" />
+ <Compile Include="Events\Graphics.cs" />
+ <Compile Include="Events\Location.cs" />
+ <Compile Include="Events\Menu.cs" />
+ <Compile Include="Events\Mine.cs" />
+ <Compile Include="Events\Player.cs" />
+ <Compile Include="Events\Time.cs" />
+ <Compile Include="Extensions.cs" />
+ <Compile Include="Inheritance\ItemStackChange.cs" />
+ <Compile Include="Inheritance\Menus\SBobberBar.cs" />
+ <Compile Include="Inheritance\Menus\SGameMenu.cs" />
+ <Compile Include="Inheritance\Menus\SInventoryPage.cs" />
+ <Compile Include="Inheritance\Minigames\SMinigameBase.cs" />
+ <Compile Include="Inheritance\SBareObject.cs" />
+ <Compile Include="Inheritance\SObject.cs" />
+ <Compile Include="JsonResolver.cs" />
+ <Compile Include="Logger.cs" />
+ <Compile Include="Manifest.cs" />
+ <Compile Include="Mod.cs" />
+ <Compile Include="ModItem.cs" />
+ <Compile Include="Program.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="Inheritance\SGame.cs" />
+ <Compile Include="Version.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="App.config" />
+ <None Include="packages.config" />
+ </ItemGroup>
+ <ItemGroup>
+ <Content Include="FodyWeavers.xml" />
+ <Content Include="icon.ico" />
+ <Content Include="steam_appid.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <BootstrapperPackage Include=".NETFramework,Version=v4.5">
+ <Visible>False</Visible>
+ <ProductName>Microsoft .NET Framework 4.5 %28x86 and x64%29</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5 SP1</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <PropertyGroup>
+ <PostBuildEvent>
+ </PostBuildEvent>
+ </PropertyGroup>
+ <Import Project="..\packages\Fody.1.29.4\build\dotnet\Fody.targets" Condition="Exists('..\packages\Fody.1.29.4\build\dotnet\Fody.targets')" />
+ <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+ <PropertyGroup>
+ <ErrorText>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}.</ErrorText>
+ </PropertyGroup>
+ <Error Condition="!Exists('..\packages\Fody.1.29.4\build\dotnet\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.1.29.4\build\dotnet\Fody.targets'))" />
+ </Target>
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project> \ 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
--- /dev/null
+++ b/src/StardewModdingAPI/icon.ico
Binary files 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 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<packages>
+ <package id="Costura.Fody" version="1.3.3.0" targetFramework="net461" developmentDependency="true" />
+ <package id="Fody" version="1.29.4" targetFramework="net461" developmentDependency="true" />
+ <package id="Newtonsoft.Json" version="8.0.3" targetFramework="net461" />
+</packages> \ 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 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<Weavers>
+
+</Weavers> \ 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 <object> <value>", new[] {"(player, pet, farm)<object> (String)<value> The target name"}).CommandFired += player_setName;
+ Command.RegisterCommand("player_setmoney", "Sets the player's money | player_setmoney <value>|inf", new[] {"(Int32)<value> The target money"}).CommandFired += player_setMoney;
+ Command.RegisterCommand("player_setstamina", "Sets the player's stamina | player_setstamina <value>|inf", new[] {"(Int32)<value> The target stamina"}).CommandFired += player_setStamina;
+ Command.RegisterCommand("player_setmaxstamina", "Sets the player's max stamina | player_setmaxstamina <value>", new[] {"(Int32)<value> The target max stamina"}).CommandFired += player_setMaxStamina;
+ Command.RegisterCommand("player_sethealth", "Sets the player's health | player_sethealth <value>|inf", new[] {"(Int32)<value> The target health"}).CommandFired += player_setHealth;
+ Command.RegisterCommand("player_setmaxhealth", "Sets the player's max health | player_setmaxhealth <value>", new[] {"(Int32)<value> The target max health"}).CommandFired += player_setMaxHealth;
+ Command.RegisterCommand("player_setimmunity", "Sets the player's immunity | player_setimmunity <value>", new[] {"(Int32)<value> The target immunity"}).CommandFired += player_setImmunity;
+
+ Command.RegisterCommand("player_setlevel", "Sets the player's specified skill to the specified value | player_setlevel <skill> <value>", new[] {"(luck, mining, combat, farming, fishing, foraging)<skill> (1-10)<value> The target level"}).CommandFired += player_setLevel;
+ Command.RegisterCommand("player_setspeed", "Sets the player's speed to the specified value?", new[] {"(Int32)<value> The target speed [0 is normal]"}).CommandFired += player_setSpeed;
+ Command.RegisterCommand("player_changecolour", "Sets the player's colour of the specified object | player_changecolor <object> <colour>", new[] {"(hair, eyes, pants)<object> (r,g,b)<colour>"}).CommandFired += player_changeColour;
+ Command.RegisterCommand("player_changestyle", "Sets the player's style of the specified object | player_changecolor <object> <value>", new[] {"(hair, shirt, skin, acc, shoe, swim, gender)<object> (Int32)<value>"}).CommandFired += player_changeStyle;
+
+ Command.RegisterCommand("player_additem", "Gives the player an item | player_additem <item> [count] [quality]", new[] {"(Int32)<id> (Int32)[count] (Int32)[quality]"}).CommandFired += player_addItem;
+ Command.RegisterCommand("player_addmelee", "Gives the player a melee item | player_addmelee <item>", new[] {"?<item>"}).CommandFired += player_addMelee;
+ Command.RegisterCommand("player_addring", "Gives the player a ring | player_addring <item>", new[] {"?<item>"}).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 <value>", new[] {"(Int32)<value> The target time [06:00 AM is 600]"}).CommandFired += world_setTime;
+ Command.RegisterCommand("world_freezetime", "Freezes or thaws time | world_freezetime <value>", new[] {"(0 - 1)<value> 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 <value>", new[] {"(Int32)<value> The target day [1-28]"}).CommandFired += world_setDay;
+ Command.RegisterCommand("world_setseason", "Sets the season to the specified value | world_setseason <value>", new[] {"(winter, spring, summer, fall)<value> 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)<value> 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("<skill> is invalid");
+ }
+ }
+ else
+ {
+ Log.AsyncR("<skill> and <value> 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("<colour> is invalid");
+ }
+ }
+ else
+ {
+ Log.LogObjectInvalid();
+ }
+ }
+ else
+ {
+ Log.AsyncR("<object> and <colour> 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("<value> must be 0 or 1 for this <object>");
+ break;
+ case "gender":
+ if (i == 0)
+ Game1.player.changeGender(true);
+ else if (i == 1)
+ Game1.player.changeGender(false);
+ else
+ Log.AsyncR("<value> must be 0 or 1 for this <object>");
+ 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("<value> 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("<value> 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("<value> 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("<item> 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("<item> 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("<item> 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<Dictionary<int, string>>("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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{28480467-1A48-46A7-99F8-236D95225359}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>TrainerMod</RootNamespace>
+ <AssemblyName>TrainerMod</AssemblyName>
+ <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <TargetFrameworkProfile />
+ <NuGetPackageImportStamp>
+ </NuGetPackageImportStamp>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>..\StardewModdingAPI\bin\Debug\Mods\TrainerMod\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <PlatformTarget>x86</PlatformTarget>
+ <Prefer32Bit>false</Prefer32Bit>
+ <LangVersion>6</LangVersion>
+ <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>..\StardewModdingAPI\bin\Release\Mods\TrainerMod\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <Prefer32Bit>false</Prefer32Bit>
+ <LangVersion>6</LangVersion>
+ <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
+ <PlatformTarget>x86</PlatformTarget>
+ </PropertyGroup>
+ <Choose>
+ <When Condition="'$(SteamInstallPath)' != ''">
+ <PropertyGroup>
+ <SteamPath>$(SteamInstallPath)</SteamPath>
+ </PropertyGroup>
+ </When>
+ <Otherwise>
+ <PropertyGroup>
+ <SteamPath>..\</SteamPath>
+ </PropertyGroup>
+ </Otherwise>
+ </Choose>
+ <ItemGroup>
+ <Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.Xna.Framework.Game, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+ <HintPath>..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
+ <Reference Include="Stardew Valley">
+ <HintPath>$(SteamPath)\steamapps\common\Stardew Valley\Stardew Valley.exe</HintPath>
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Windows.Forms" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ <Reference Include="xTile">
+ <HintPath>$(SteamPath)\steamapps\common\Stardew Valley\xTile.dll</HintPath>
+ <Private>False</Private>
+ </Reference>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="TrainerMod.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\StardewModdingAPI\StardewModdingAPI.csproj">
+ <Project>{f1a573b0-f436-472c-ae29-0b91ea6b9f8f}</Project>
+ <Name>StardewModdingAPI</Name>
+ <Private>False</Private>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="manifest.json">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
+ <None Include="packages.config" />
+ </ItemGroup>
+ <ItemGroup>
+ <Content Include="FodyWeavers.xml" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <PropertyGroup>
+ <PostBuildEvent>
+ </PostBuildEvent>
+ </PropertyGroup>
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project> \ 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 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<packages>
+ <package id="Newtonsoft.Json" version="8.0.3" targetFramework="net461" />
+</packages> \ 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