Compare commits

..

9 Commits

837 changed files with 6270 additions and 70671 deletions

View File

@ -1,81 +0,0 @@
# topmost editorconfig
root: true
##########
## General formatting
## documentation: https://editorconfig.org
##########
[*]
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
[*.{csproj,nuspec,targets}]
indent_size = 2
[*.csproj]
charset = utf-8-bom
insert_final_newline = false
[README.txt]
end_of_line=crlf
[*.{command,sh}]
end_of_line=lf
##########
## C# formatting
## documentation: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference
##########
[*.cs]
#sort 'system' usings first
dotnet_sort_system_directives_first = true
# use 'this.' qualifier
dotnet_style_qualification_for_field = true:error
dotnet_style_qualification_for_property = true:error
dotnet_style_qualification_for_method = true:error
dotnet_style_qualification_for_event = true:error
# use language keywords (like int) instead of type (like Int32)
dotnet_style_predefined_type_for_locals_parameters_members = true:error
dotnet_style_predefined_type_for_member_access = true:error
# don't use 'var' for language keywords
csharp_style_var_for_built_in_types = false:error
# suggest modern C# features where simpler
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_conditional_delegate_call = true:suggestion
csharp_prefer_simple_default_expression = true:suggestion
# prefer method block bodies
csharp_style_expression_bodied_methods = false:suggestion
csharp_style_expression_bodied_constructors = false:suggestion
# prefer property expression bodies
csharp_style_expression_bodied_properties = true:suggestion
csharp_style_expression_bodied_indexers = true:suggestion
csharp_style_expression_bodied_accessors = true:suggestion
# prefer inline out variables
csharp_style_inlined_variable_declaration = true:warning
# avoid superfluous braces
csharp_prefer_braces = false:hint
##########
## Column guidelines
## documentation: https://marketplace.visualstudio.com/items?itemName=PaulHarrington.EditorGuidelines
##########
[*.md]
guidelines = 100

6
.gitattributes vendored
View File

@ -1,6 +0,0 @@
# normalize line endings
* text=auto
README.txt text eol=crlf
*.command text eol=lf
*.sh text eol=lf

View File

@ -1,12 +0,0 @@
Do you want to...
* **Ask for help or report a bug?**
Please see 'get help' on [the SMAPI website](https://smapi.io) instead, don't create a GitHub
issue.
* **Submit a pull request?**
Pull requests are welcome! If you're submitting a new feature, it's best to discuss first to make
sure it'll be accepted. Feel free to come chat [on Discord](https://smapi.io/community).
Documenting your code and using the same formatting conventions is appreciated, but don't worry too
much about it. We'll fix up the code after we accept the pull request if needed.

3
.github/FUNDING.yml vendored
View File

@ -1,3 +0,0 @@
patreon: pathoschild
ko_fi: pathoschild
custom: https://www.paypal.me/pathoschild

View File

@ -1,8 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Troubleshooting guide for players
url: https://smapi.io/troubleshoot
about: See if your question is already answered first!
- name: Get help or discuss
url: https://smapi.io/help
about: Ask for help from the community, or join the Stardew Valley Discord to ask questions, report issues, or discuss with the SMAPI developer, players, and mod authors. The SMAPI developer is @Pathoschild#0001 on Discord.

View File

@ -1,37 +0,0 @@
---
name: Create a development task
about: DON'T DO THIS BEFORE READING. This is for specific changes to the code or technical bug reports. See below if something isn't working, you have questions or ideas, or you want to discuss something.
---
<!--
STOP!
Is this a specific development task? Don't create an issue if not!
See https://smapi.io/community if something isn't working, you have questions or ideas, or you want
to discuss something.
If you're absolutely sure it's a specific development task (e.g. a specific bug, not just
'something went wrong on my computer'), edit the template below.
-->
**Describe the bug**
A clear and concise description of what the bug is. Provide any other details you think might be relevant here.
**To Reproduce**
Exact steps which reproduce the bug, if possible. For example:
1. Load save '...'.
2. Walk to '....'.
3. Click '....'.
4. Error occurs.
**Log file**
Upload your SMAPI log to https://smapi.io/log and post a link here.
**Screenshots**
If applicable, add screenshots to help explain your problem.

3
.github/SUPPORT.md vendored
View File

@ -1,3 +0,0 @@
GitHub issues are only used for SMAPI development tasks.
To get help with SMAPI problems, see 'get help' on [the SMAPI website](https://smapi.io/) instead.

256
.gitignore vendored
View File

@ -1,47 +1,261 @@
# user-specific files # SMAPI Specific Ignores
StardewModdingAPI/bin/
StardewModdingAPI/obj/
TrainerMod/bin/
TrainerMod/obj/
StardewInjector/bin/
StardewInjector/obj/
packages/
*.symlink
*.lnk
!*.exe
!*.dll
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo *.suo
*.user *.user
*.userosscache *.userosscache
*.sln.docstates *.sln.docstates
# build results # User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/ [Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/ [Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/ [Bb]in/
[Oo]bj/ [Oo]bj/
[Ll]og/
# Visual Studio cache/options # Visual Studio 2015 cache/options directory
.config/
.vs/ .vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# ReSharper # MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/ _ReSharper*/
*.[Rr]e[Ss]harper *.[Rr]e[Ss]harper
*.DotSettings.user *.DotSettings.user
# Rider # JustCode is a .NET coding add-in
.idea/ .JustCode
# NuGet packages # TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# NuGet Packages
*.nupkg *.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/* **/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props *.nuget.props
*.nuget.targets *.nuget.targets
# sensitive files # Microsoft Azure Build Output
appsettings.Development.json csx/
*.build.csdef
# Azure generated files # Microsoft Azure Emulator
src/SMAPI.Web/Properties/PublishProfiles/*.pubxml ecf/
src/SMAPI.Web/Properties/ServiceDependencies/* - Web Deploy/ rcf/
# macOS # Microsoft Azure ApplicationInsights config file
.DS_Store ApplicationInsights.config
# Loader Game Asserts # Windows Store app package directories and files
src/Loader/Assets/Content/ AppPackages/
src/Loader/libs/ BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# VSHistory # Visual Studio cache files
**/.vshistory/ # files ending in .cache can be ignored
/build/StardewValleyAndroidStuff/ *.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml

View File

@ -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

329
Game1 Static Fields.txt Normal file
View File

@ -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

674
LICENSE Normal file
View File

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
{one line to give the program's name and a brief idea of what it does.}
Copyright (C) {year} {name of author}
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
{project} Copyright (C) {year} {fullname}
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

View File

@ -1,165 +0,0 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

33
README.md Normal file
View File

@ -0,0 +1,33 @@
## NOTICE: THIS PROJECT IS STILL IN ALPHA
# What is SMAPI
SMAPI (Stardew Mapping Application Programming Interface) is a tool to help modders make changes to Stardew. It is a standalone executable which goes alongside your Stardew.exe.
## Latest Version: 0.37.2
- Added KeyReleased event
- Added event handlers for GamePads
- Internal Cleanup
Tested and works with current versions of: SmartMod, StardewCJB , FreezeInside
Download: https://github.com/ClxS/SMAPI/releases/tag/0.37.2
## Installation
To install SMAPI:
- Firstly, make sure you have .NET 4.5. You can get it here: https://www.microsoft.com/en-gb/download/details.aspx?id=30653
- Download the the latest release binary here: https://github.com/ClxS/SMAPI/releases/latest
- Extract the zip file alongside your Stardew.exe, for example, if using Steam this would be somewhere like C:/ProgramFiles/Steam/steamapps/common/StardewValley
- To launch SMAPI, launch StardewValleyModdingAPI.exe
To install mods:
- To install mods just download the mod's .DLL file, and place it in %appdata%\StardewValley\Mods\. SMAPI will take care of the rest!
## Future Plans
- Content only mods
- Support for a wide range of events
- Enable the addition of new custom content such as locations, NPCs, and items.
## Mod Developers!
Mod developers would work off the release branch. The master branch will contain mid-version updates which could make your mods incompatable with both the current release and the upcoming releases.

46
StardewModdingAPI.sln Normal file
View File

@ -0,0 +1,46 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.24720.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

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StardewModdingAPI.API
{
class Game
{
}
}

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
</startup>
<runtime>
<loadFromRemoteSources enabled="true"/>
</runtime>
</configuration>

View File

@ -0,0 +1,108 @@
using StardewModdingAPI.Events;
using System;
using System.Collections.Generic;
namespace StardewModdingAPI
{
public class Command
{
internal static List<Command> RegisteredCommands = new List<Command>();
public String CommandName;
public String CommandDesc;
public String[] CommandArgs;
public String[] CalledArgs;
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(new[] {' '});
string[] 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.Error("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)
{
Command c = new Command(command, cdesc, args);
if (RegisteredCommands.Contains(c))
{
Log.Error("Command already registered! [{0}]", c.CommandName);
return RegisteredCommands.Find(x => x.Equals(c));
}
RegisteredCommands.Add(c);
Log.Verbose("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>
/// 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;
}
/// <summary>
/// Runs a command. Fires it. Calls it. Any of those.
/// </summary>
public void Fire()
{
if (CommandFired == null)
{
Log.Error("Command failed to fire because it's fire event is null: " + CommandName);
return;
}
CommandFired.Invoke(this, new EventArgsCommand(this));
}
}
}

View File

@ -0,0 +1,49 @@
using System;
using System.IO;
using System.Reflection;
namespace StardewModdingAPI
{
/// <summary>
/// Static class containing readonly values.
/// </summary>
public static class Constants
{
/// <summary>
/// Stardew Valley's local app data location.
/// %LocalAppData%//StardewValley
/// </summary>
public static string DataPath => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StardewValley");
/// <summary>
/// Execution path to execute the code.
/// </summary>
public static string ExecutionPath => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
/// <summary>
/// Execution path to execute the code.
/// </summary>
public static string StardewExePath => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\Stardew Valley.exe";
/// <summary>
/// Title for the API console
/// </summary>
public static string ConsoleTitle => string.Format("Stardew Modding API Console - Version {0}", VersionString);
/// <summary>
/// Path for log files to be output to.
/// %LocalAppData%//StardewValley//ErrorLogs
/// </summary>
public static string LogPath => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StardewValley", "ErrorLogs");
public const int MajorVersion = 0;
public const int MinorVersion = 37;
public const int PatchVersion = 2;
public const string Build = "Alpha";
public static string VersionString => string.Format("{0}.{1}.{2} {3}", MajorVersion, MinorVersion, PatchVersion, Build);
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StardewModdingAPI.Entities
{
class SCharacter
{
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StardewModdingAPI.Entities
{
class SFarm
{
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StardewModdingAPI.Entities
{
class SFarmAnimal
{
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StardewModdingAPI.Entities
{
class SNpc
{
}
}

View File

@ -0,0 +1,37 @@
using StardewModdingAPI.Inheritance;
using StardewValley;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StardewModdingAPI.Entities
{
public static class SPlayer
{
public static List<Farmer> AllFarmers
{
get
{
return SGame.getAllFarmers();
}
}
public static Farmer CurrentFarmer
{
get
{
return SGame.player;
}
}
public static GameLocation CurrentFarmerLocation
{
get
{
return SGame.player.currentLocation;
}
}
}
}

View File

@ -0,0 +1,62 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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 { };
public static void InvokeKeyboardChanged(KeyboardState priorState, KeyboardState newState)
{
KeyboardChanged.Invoke(null, new EventArgsKeyboardStateChanged(priorState, newState));
}
public static void InvokeMouseChanged(MouseState priorState, MouseState newState)
{
MouseChanged.Invoke(null, new EventArgsMouseStateChanged(priorState, newState));
}
public static void InvokeKeyPressed(Keys key)
{
KeyPressed.Invoke(null, new EventArgsKeyPressed(key));
}
public static void InvokeKeyReleased(Keys key)
{
KeyReleased.Invoke(null, new EventArgsKeyPressed(key));
}
public static void InvokeButtonPressed(PlayerIndex playerIndex, Buttons buttons)
{
ControllerButtonPressed.Invoke(null, new EventArgsControllerButtonPressed(playerIndex, buttons));
}
public static void InvokeButtonReleased(PlayerIndex playerIndex, Buttons buttons)
{
ControllerButtonReleased.Invoke(null, new EventArgsControllerButtonReleased(playerIndex, buttons));
}
public static void InvokeTriggerPressed(PlayerIndex playerIndex, Buttons buttons, float value)
{
ControllerTriggerPressed.Invoke(null, new EventArgsControllerTriggerPressed(playerIndex, buttons, value));
}
public static void InvokeTriggerReleased(PlayerIndex playerIndex, Buttons buttons, float value)
{
ControllerTriggerReleased.Invoke(null, new EventArgsControllerTriggerReleased(playerIndex, buttons, value));
}
}
}

View File

@ -0,0 +1,210 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using StardewModdingAPI.Inheritance;
using StardewValley;
using StardewValley.Menus;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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 EventArgsGameLocationsChanged : EventArgs
{
public EventArgsGameLocationsChanged(List<GameLocation> newLocations)
{
NewLocations = newLocations;
}
public List<GameLocation> NewLocations { get; private set; }
}
public class EventArgsLocationObjectsChanged : EventArgs
{
public EventArgsLocationObjectsChanged(SerializableDictionary<Vector2, StardewValley.Object> newObjects)
{
NewObjects = newObjects;
}
public SerializableDictionary<Vector2, StardewValley.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; private set; }
public Farmer PriorFarmer { get; private set; }
}
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, Int32 newLevel)
{
Type = type;
NewLevel = newLevel;
}
public LevelType Type { get; private set; }
public Int32 NewLevel { get; private set; }
}
public class EventArgsIntChanged : EventArgs
{
public EventArgsIntChanged(Int32 priorInt, Int32 newInt)
{
NewInt = NewInt;
PriorInt = PriorInt;
}
public Int32 NewInt { get; private set; }
public Int32 PriorInt { get; private set; }
}
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 EventArgsCommand : EventArgs
{
public EventArgsCommand(Command command)
{
Command = command;
}
public Command Command { get; private set; }
}
}

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StardewModdingAPI.Events
{
public class FarmAnimal
{
public void eatGrass_OnEnter()
{
}
}
}

View File

@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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 UpdateTick = delegate { };
public static void InvokeGameLoaded()
{
GameLoaded.Invoke(null, EventArgs.Empty);
}
public static void InvokeInitialize()
{
try
{
Initialize.Invoke(null, EventArgs.Empty);
}
catch (Exception ex)
{
Log.Error("An exception occured in XNA Initialize: " + ex.ToString());
}
}
public static void InvokeLoadContent()
{
try
{
LoadContent.Invoke(null, EventArgs.Empty);
}
catch (Exception ex)
{
Log.Error("An exception occured in XNA LoadContent: " + ex.ToString());
}
}
public static void InvokeUpdateTick()
{
try
{
Program.IsGameReferenceDirty = true;
var test = Program.gamePtr;
UpdateTick.Invoke(null, EventArgs.Empty);
}
catch (Exception ex)
{
Log.Error("An exception occured in XNA UpdateTick: " + ex.ToString());
}
}
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StardewModdingAPI.Events
{
public static class GraphicsEvents
{
public static event EventHandler Resize = delegate { };
public static event EventHandler DrawTick = delegate { };
public static void InvokeDrawTick()
{
try
{
DrawTick.Invoke(null, EventArgs.Empty);
}
catch (Exception ex)
{
Log.Error("An exception occured in XNA DrawTick: " + ex.ToString());
}
}
public static void InvokeResize(object sender, EventArgs e)
{
Resize.Invoke(sender, e);
}
}
}

View File

@ -0,0 +1,32 @@
using Microsoft.Xna.Framework;
using StardewValley;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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 { };
public static void InvokeLocationsChanged(List<GameLocation> newLocations)
{
LocationsChanged.Invoke(null, new EventArgsGameLocationsChanged(newLocations));
}
public static void InvokeCurrentLocationChanged(GameLocation priorLocation, GameLocation newLocation)
{
CurrentLocationChanged.Invoke(null, new EventArgsCurrentLocationChanged(priorLocation, newLocation));
}
internal static void InvokeOnNewLocationObject(SerializableDictionary<Vector2, StardewValley.Object> newObjects)
{
LocationObjectsChanged.Invoke(null, new EventArgsLocationObjectsChanged(newObjects));
}
}
}

View File

@ -0,0 +1,19 @@
using StardewValley.Menus;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StardewModdingAPI.Events
{
public static class MenuEvents
{
public static event EventHandler<EventArgsClickableMenuChanged> MenuChanged = delegate { };
public static void InvokeMenuChanged(IClickableMenu priorMenu, IClickableMenu newMenu)
{
MenuChanged.Invoke(null, new EventArgsClickableMenuChanged(priorMenu, newMenu));
}
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StardewModdingAPI.Events
{
public static class MineEvents
{
}
}

View File

@ -0,0 +1,32 @@
using StardewModdingAPI.Inheritance;
using StardewValley;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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 void InvokeFarmerChanged(Farmer priorFarmer, Farmer newFarmer)
{
FarmerChanged.Invoke(null, new EventArgsFarmerChanged(priorFarmer, newFarmer));
}
public static void InvokeInventoryChanged(List<Item> inventory, List<ItemStackChange> changedItems)
{
InventoryChanged.Invoke(null, new EventArgsInventoryChanged(inventory, changedItems));
}
public static void InvokeLeveledUp(EventArgsLevelUp.LevelType type, int newLevel)
{
LeveledUp.Invoke(null, new EventArgsLevelUp(type, newLevel));
}
}
}

View File

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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 { };
public static void InvokeTimeOfDayChanged(Int32 priorInt, Int32 newInt)
{
TimeOfDayChanged.Invoke(null, new EventArgsIntChanged(priorInt, newInt));
}
public static void InvokeDayOfMonthChanged(Int32 priorInt, Int32 newInt)
{
DayOfMonthChanged.Invoke(null, new EventArgsIntChanged(priorInt, newInt));
}
public static void InvokeYearOfGameChanged(Int32 priorInt, Int32 newInt)
{
YearOfGameChanged.Invoke(null, new EventArgsIntChanged(priorInt, newInt));
}
public static void InvokeSeasonOfYearChanged(String priorString, String newString)
{
SeasonOfYearChanged.Invoke(null, new EventArgsStringChanged(priorString, newString));
}
}
}

View File

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StardewModdingAPI.ExtensionMethods
{
public static class ArrayExtensions
{
public static void ForEach(this Array array, Action<Array, int[]> action)
{
if (array.LongLength == 0) return;
ArrayTraverse walker = new ArrayTraverse(array);
do action(array, walker.Position);
while (walker.Step());
}
//public static void ForEach(Array array, Action<Array, int[]> action)
//{
// if (array.LongLength == 0) return;
// ArrayTraverse walker = new ArrayTraverse(array);
// do action(array, walker.Position);
// while (walker.Step());
//}
}
internal class ArrayTraverse
{
public int[] Position;
private int[] maxLengths;
public ArrayTraverse(Array array)
{
maxLengths = new int[array.Rank];
for (int i = 0; i < array.Rank; ++i)
{
maxLengths[i] = array.GetLength(i) - 1;
}
Position = new int[array.Rank];
}
public bool Step()
{
for (int i = 0; i < Position.Length; ++i)
{
if (Position[i] < maxLengths[i])
{
Position[i]++;
for (int j = 0; j < i; j++)
{
Position[j] = 0;
}
return true;
}
}
return false;
}
}
}

View File

@ -0,0 +1,160 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace StardewModdingAPI.ExtensionMethods
{
//From https://github.com/jpmikkers/net-object-deep-copy/blob/master/ObjectExtensions.cs
public static class ObjectExtensions
{
public static T Copy<T>(this object original)
{
return (T)new DeepCopyContext().InternalCopy(original, true);
}
private class DeepCopyContext
{
private static readonly Func<object, object> CloneMethod;
private readonly Dictionary<Object, Object> m_Visited;
private readonly Dictionary<Type, FieldInfo[]> m_NonShallowFieldCache;
static DeepCopyContext()
{
MethodInfo cloneMethod = typeof(Object).GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance);
var p1 = Expression.Parameter(typeof(object));
var body = Expression.Call(p1, cloneMethod);
CloneMethod = Expression.Lambda<Func<object, object>>(body, p1).Compile();
//Console.WriteLine("typeof(object) contains {0} nonshallow fields", NonShallowFields(typeof(object)).Count());
}
public DeepCopyContext()
{
m_Visited = new Dictionary<object, object>(new ReferenceEqualityComparer());
m_NonShallowFieldCache = new Dictionary<Type, FieldInfo[]>();
}
private static bool IsPrimitive(Type type)
{
if (type.IsValueType && type.IsPrimitive) return true;
if (type == typeof(String)) return true;
if (type == typeof(Decimal)) return true;
if (type == typeof(DateTime)) return true;
return false;
}
public Object InternalCopy(Object originalObject, bool includeInObjectGraph)
{
if (originalObject == null) return null;
var typeToReflect = originalObject.GetType();
if (IsPrimitive(typeToReflect)) return originalObject;
if (typeof(XElement).IsAssignableFrom(typeToReflect)) return new XElement(originalObject as XElement);
if (typeof(Delegate).IsAssignableFrom(typeToReflect)) return null;
if (includeInObjectGraph)
{
object result;
if (m_Visited.TryGetValue(originalObject, out result)) return result;
}
var cloneObject = CloneMethod(originalObject);
if (includeInObjectGraph)
{
m_Visited.Add(originalObject, cloneObject);
}
if (typeToReflect.IsArray)
{
var arrayElementType = typeToReflect.GetElementType();
if (IsPrimitive(arrayElementType))
{
// for an array of primitives, do nothing. The shallow clone is enough.
}
else if (arrayElementType.IsValueType)
{
// if its an array of structs, there's no need to check and add the individual elements to 'visited', because in .NET it's impossible to create
// references to individual array elements.
Array clonedArray = (Array)cloneObject;
clonedArray.ForEach((array, indices) => array.SetValue(InternalCopy(clonedArray.GetValue(indices), false), indices));
}
else
{
// it's an array of ref types
Array clonedArray = (Array)cloneObject;
clonedArray.ForEach((array, indices) => array.SetValue(InternalCopy(clonedArray.GetValue(indices), true), indices));
}
}
else
{
foreach (var fieldInfo in CachedNonShallowFields(typeToReflect))
{
var originalFieldValue = fieldInfo.GetValue(originalObject);
// a valuetype field can never have a reference pointing to it, so don't check the object graph in that case
Log.Error("Replace this with a recurse-less version");
var clonedFieldValue = InternalCopy(originalFieldValue, !fieldInfo.FieldType.IsValueType);
fieldInfo.SetValue(cloneObject, clonedFieldValue);
}
}
return cloneObject;
}
private FieldInfo[] CachedNonShallowFields(Type typeToReflect)
{
FieldInfo[] result;
if (!m_NonShallowFieldCache.TryGetValue(typeToReflect, out result))
{
result = NonShallowFields(typeToReflect).ToArray();
m_NonShallowFieldCache[typeToReflect] = result;
}
return result;
}
/// <summary>
/// From the given type hierarchy (i.e. including all base types), return all fields that should be deep-copied
/// </summary>
/// <param name="typeToReflect"></param>
/// <returns></returns>
private static IEnumerable<FieldInfo> NonShallowFields(Type typeToReflect)
{
while (typeToReflect != typeof(object))
{
foreach (var fieldInfo in typeToReflect.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly))
{
if (IsPrimitive(fieldInfo.FieldType)) continue; // this is 5% faster than a where clause..
yield return fieldInfo;
}
typeToReflect = typeToReflect.BaseType;
}
}
}
}
public class ReferenceEqualityComparer : EqualityComparer<Object>
{
public override bool Equals(object x, object y)
{
return ReferenceEquals(x, y);
}
public override int GetHashCode(object obj)
{
if (obj == null) return 0;
// The RuntimeHelpers.GetHashCode method always calls the Object.GetHashCode method non-virtually,
// even if the object's type has overridden the Object.GetHashCode method.
return RuntimeHelpers.GetHashCode(obj);
}
}
}

View File

@ -0,0 +1,64 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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));
}
public static string ToSingular(this IEnumerable<Object> enumerable, string split = ", ")
{
string result = string.Join(split, enumerable);
return result;
}
public static bool IsInt32(this object o)
{
int i;
return Int32.TryParse(o.ToString(), out i);
}
public static Int32 AsInt32(this object o)
{
return Int32.Parse(o.ToString());
}
public static bool IsBool(this object o)
{
bool b;
return Boolean.TryParse(o.ToString(), out b);
}
public static bool AsBool(this object o)
{
return Boolean.Parse(o.ToString());
}
public static int GetHash(this IEnumerable enumerable)
{
int hash = 0;
foreach (var v in enumerable)
{
hash ^= v.GetHashCode();
}
return hash;
}
}
}

View File

@ -0,0 +1,163 @@
using Mono.Cecil;
using Mono.Cecil.Cil;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace StardewModdingAPI.Helpers
{
public enum CecilContextType
{
SMAPI,
Stardew
}
public class CecilContext
{
public CecilContextType ContextType { get; private set;}
private AssemblyDefinition _assemblyDefinition { get; set; }
private bool _isMemoryStreamDirty { get; set; }
private MemoryStream _modifiedAssembly;
public MemoryStream ModifiedAssembly
{
get
{
if(_modifiedAssembly == null)
{
_modifiedAssembly = new MemoryStream();
_assemblyDefinition.Write(_modifiedAssembly);
}
else
{
if(_isMemoryStreamDirty)
{
_modifiedAssembly.Dispose();
_modifiedAssembly = new MemoryStream();
_assemblyDefinition.Write(_modifiedAssembly);
}
}
return _modifiedAssembly;
}
}
public CecilContext(CecilContextType contextType)
{
ContextType = contextType;
if (ContextType == CecilContextType.SMAPI)
_assemblyDefinition = AssemblyDefinition.ReadAssembly(Assembly.GetExecutingAssembly().Location);
else
_assemblyDefinition = AssemblyDefinition.ReadAssembly(Constants.StardewExePath);
}
public ILProcessor GetMethodILProcessor(string type, string method)
{
if (_assemblyDefinition == null)
throw new Exception("ERROR Assembly not properly read. Cannot parse");
if (string.IsNullOrWhiteSpace(type) || string.IsNullOrWhiteSpace(method))
throw new ArgumentNullException("Both type and method must be set");
Mono.Cecil.Cil.ILProcessor ilProcessor = null;
TypeDefinition typeDef = GetTypeDefinition(type);
if (typeDef != null)
{
MethodDefinition methodDef = typeDef.Methods.FirstOrDefault(m => m.Name == method);
if (methodDef != null)
{
ilProcessor = methodDef.Body.GetILProcessor();
}
}
return ilProcessor;
}
public TypeDefinition GetTypeDefinition(string type)
{
if (_assemblyDefinition == null)
throw new Exception("ERROR Assembly not properly read. Cannot parse");
if (string.IsNullOrWhiteSpace(type))
throw new ArgumentNullException("Both type and method must be set");
TypeDefinition typeDef = _assemblyDefinition.MainModule.Types.FirstOrDefault(n => n.FullName == type);
return typeDef;
}
public MethodDefinition GetMethodDefinition(string type, string method)
{
MethodDefinition methodDef = null;
TypeDefinition typeDef = GetTypeDefinition(type);
if (typeDef != null)
{
methodDef = typeDef.Methods.FirstOrDefault(m => m.Name == method);
}
return methodDef;
}
public ConstructorInfo GetSMAPITypeContructor(string type)
{
if (_assemblyDefinition == null)
throw new Exception("ERROR Assembly not properly read. Cannot parse");
if (ContextType != CecilContextType.SMAPI)
throw new Exception("GetSMAPIMethodReference can only be called on the SMAPI context");
ConstructorInfo methodInfo = null;
var reflectionType = Assembly.GetExecutingAssembly().GetType(type);
if (reflectionType != null)
{
methodInfo = reflectionType.GetConstructor(Type.EmptyTypes);
}
return methodInfo;
}
public MethodInfo GetSMAPIMethodReference(string type, string method)
{
if (_assemblyDefinition == null)
throw new Exception("ERROR Assembly not properly read. Cannot parse");
if (ContextType != CecilContextType.SMAPI)
throw new Exception("GetSMAPIMethodReference can only be called on the SMAPI context");
MethodInfo methodInfo = null;
var reflectionType = Assembly.GetExecutingAssembly().GetType(type);
if (reflectionType != null)
{
methodInfo = reflectionType.GetMethod(method);
}
return methodInfo;
}
public MethodReference ImportSMAPIMethodInStardew(CecilContext destinationContext, MethodBase method)
{
if (_assemblyDefinition == null)
throw new Exception("ERROR Assembly not properly read. Cannot parse");
if (ContextType != CecilContextType.SMAPI)
throw new Exception("ImportSmapiMethodInStardew can only be called on the Stardew context");
MethodReference reference = null;
if (method != null)
{
reference = destinationContext._assemblyDefinition.MainModule.Import(method);
}
return reference;
}
internal void WriteAssembly(string file)
{
_assemblyDefinition.Write(file);
}
}
}

View File

@ -0,0 +1,97 @@
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace StardewModdingAPI.Helpers
{
public static class CecilHelper
{
//System.Void StardewValley.Game1::.ctor()
private static void InjectMethod(ILProcessor ilProcessor, Instruction target, MethodReference method)
{
Instruction callEnterInstruction = ilProcessor.Create(OpCodes.Call, method);
if(method.HasThis)
{
Instruction loadObjInstruction = ilProcessor.Create(OpCodes.Ldarg_0);
ilProcessor.InsertBefore(target, loadObjInstruction);
}
if (method.HasParameters)
{
Instruction loadObjInstruction = ilProcessor.Create(OpCodes.Ldarg_0);
ilProcessor.InsertBefore(target, loadObjInstruction);
ilProcessor.InsertAfter(loadObjInstruction, callEnterInstruction);
}
else
{
ilProcessor.InsertBefore(target, callEnterInstruction);
}
}
private static void InjectMethod(ILProcessor ilProcessor, IEnumerable<Instruction> targets, MethodReference method)
{
foreach(var target in targets.ToList())
{
InjectMethod(ilProcessor, target, method);
}
}
private static List<Instruction> GetMatchingInstructions(Collection<Instruction> instructions, OpCode opcode, object @object)
{
return instructions.Where(n => n.OpCode == opcode && n.Operand == @object).ToList();
}
public static void RedirectConstructor(CecilContext stardewContext, CecilContext smapiContext,
string typeToAlter, string methodToAlter,
string injecteeType, string injecteeMethod,
string injectedType, string injectedMethod)
{
var ilProcessor = stardewContext.GetMethodILProcessor(typeToAlter, methodToAlter);
var methodDefinition = stardewContext.GetMethodDefinition(injecteeType, injecteeMethod);
var methodInfo = smapiContext.GetSMAPITypeContructor(injectedType);
var reference = smapiContext.ImportSMAPIMethodInStardew(stardewContext, methodInfo);
var instructionsToAlter = GetMatchingInstructions(ilProcessor.Body.Instructions, OpCodes.Newobj, methodDefinition);
var newInstruction = ilProcessor.Create(OpCodes.Newobj, reference);
foreach(var instruction in instructionsToAlter)
{
ilProcessor.Replace(instruction, newInstruction);
}
}
// public void ReplaceInstruction(ILProcessor processor, OpCode opcode, string oldOperand, string newOperand)
//{
//var instructions = processor.Body.Instructions.Where(i => i.OpCode == opcode && i.Operand == oldOperand);
// processor.Create()
//}
public static void InjectEntryMethod(CecilContext stardewContext, CecilContext smapiContext, string injecteeType, string injecteeMethod,
string injectedType, string injectedMethod)
{
var methodInfo = smapiContext.GetSMAPIMethodReference(injectedType, injectedMethod);
var reference = smapiContext.ImportSMAPIMethodInStardew(stardewContext, methodInfo);
var ilProcessor = stardewContext.GetMethodILProcessor(injecteeType, injecteeMethod);
InjectMethod(ilProcessor, ilProcessor.Body.Instructions.First(), reference);
}
public static void InjectExitMethod(CecilContext stardewContext, CecilContext smapiContext, string injecteeType, string injecteeMethod,
string injectedType, string injectedMethod)
{
var methodInfo = smapiContext.GetSMAPIMethodReference(injectedType, injectedMethod);
var reference = smapiContext.ImportSMAPIMethodInStardew(stardewContext, methodInfo);
var ilProcessor = stardewContext.GetMethodILProcessor(injecteeType, injecteeMethod);
InjectMethod(ilProcessor, ilProcessor.Body.Instructions.Where(i => i.OpCode == OpCodes.Ret), reference);
}
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace StardewModdingAPI.Helpers
{
public static class ReflectionHelper
{
}
}

View File

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace StardewModdingAPI.Helpers
{
public static class StardewAssembly
{
private static Assembly ModifiedGameAssembly { get; set; }
private static CecilContext StardewContext { get; set; }
private static CecilContext SmapiContext { get; set; }
public static void ModifyStardewAssembly()
{
StardewContext = new CecilContext(CecilContextType.Stardew);
SmapiContext = new CecilContext(CecilContextType.SMAPI);
CecilHelper.InjectEntryMethod(StardewContext, SmapiContext, "StardewValley.Game1", ".ctor", "StardewModdingAPI.Program", "Test");
CecilHelper.InjectExitMethod(StardewContext, SmapiContext, "StardewValley.Game1", "Initialize", "StardewModdingAPI.Events.GameEvents", "InvokeInitialize");
CecilHelper.InjectExitMethod(StardewContext, SmapiContext, "StardewValley.Game1", "LoadContent", "StardewModdingAPI.Events.GameEvents", "InvokeLoadContent");
CecilHelper.InjectExitMethod(StardewContext, SmapiContext, "StardewValley.Game1", "Update", "StardewModdingAPI.Events.GameEvents", "InvokeUpdateTick");
CecilHelper.InjectExitMethod(StardewContext, SmapiContext, "StardewValley.Game1", "Draw", "StardewModdingAPI.Events.GraphicsEvents", "InvokeDrawTick");
}
public static void LoadStardewAssembly()
{
ModifiedGameAssembly = Assembly.Load(StardewContext.ModifiedAssembly.GetBuffer());
//ModifiedGameAssembly = Assembly.UnsafeLoadFrom(Constants.ExecutionPath + "\\Stardew Valley.exe");
}
internal static void Launch()
{
ModifiedGameAssembly.EntryPoint.Invoke(null, new object[] { new string[] { } });
}
internal static void WriteModifiedExe()
{
StardewContext.WriteAssembly("StardewValley-Modified.exe");
}
}
}

View File

@ -0,0 +1,23 @@
using StardewValley;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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; }
}
}

View File

@ -0,0 +1,298 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using StardewValley.BellsAndWhistles;
using StardewValley.Menus;
namespace StardewModdingAPI.Inheritance.Menus
{
public class SBobberBar : BobberBar
{
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
{
get { return GetPrivateFields(); }
}
public static SBobberBar ConstructFromBaseClass(BobberBar baseClass)
{
SBobberBar b = new SBobberBar(0, 0, false, 0);
b.BaseBobberBar = baseClass;
return b;
}
/// <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 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);
}
}
}

View File

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.Eventing.Reader;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework.Graphics;
using StardewValley;
using StardewValley.Menus;
namespace StardewModdingAPI.Inheritance.Menus
{
public class SGameMenu : StardewValley.Menus.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)
{
SGameMenu s = new SGameMenu();
s.BaseGameMenu = baseClass;
return s;
}
public override void receiveRightClick(int x, int y, bool playSound = true)
{
if (pages[currentTab] is InventoryPage)
{
Log.Verbose("INV SCREEN");
}
else
{
}
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);
}
}
}

View File

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using StardewValley;
using StardewValley.Menus;
namespace StardewModdingAPI.Inheritance.Menus
{
public class SInventoryPage : InventoryPage
{
public InventoryPage BaseInventoryPage { get; private set; }
public static SInventoryPage ConstructFromBaseClass(InventoryPage baseClass)
{
SInventoryPage s = new SInventoryPage(0,0,0,0);
s.BaseInventoryPage = baseClass;
return s;
}
public SInventoryPage(int x, int y, int width, int height) : base(x, y, width, height)
{
}
}
}

View File

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace StardewModdingAPI.Inheritance.Minigames
{
abstract class SMinigameBase : StardewValley.Minigames.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);
}
}

View File

@ -0,0 +1,497 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Xml.Serialization;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using StardewValley;
using StardewValley.Characters;
using StardewValley.Menus;
using StardewValley.Monsters;
using StardewValley.Quests;
using StardewValley.TerrainFeatures;
using StardewModdingAPI.Events;
namespace StardewModdingAPI.Inheritance
{
public class SGame : Game1
{
public static List<SGameLocation> ModLocations = new List<SGameLocation>();
public static SGameLocation CurrentLocation { get; internal set; }
public static Dictionary<Int32, SObject> ModItems { get; private set; }
public const Int32 LowestModItemID = 1000;
public static FieldInfo[] StaticFields { get { return GetStaticFields(); } }
public static FieldInfo[] GetStaticFields()
{
return typeof(Game1).GetFields();
}
public KeyboardState KStateNow { get; private set; }
public KeyboardState KStatePrior { get; private set; }
public MouseState MStateNow { get; private set; }
public MouseState MStatePrior { get; private set; }
public Keys[] CurrentlyPressedKeys { get; private set; }
public Keys[] PreviouslyPressedKeys { get; private set; }
public Keys[] FramePressedKeys
{
get { return CurrentlyPressedKeys.Where(x => !PreviouslyPressedKeys.Contains(x)).ToArray(); }
}
public Keys[] FrameReleasedKeys
{
get { return PreviouslyPressedKeys.Where(x => !CurrentlyPressedKeys.Contains(x)).ToArray(); }
}
public Buttons[][] PreviouslyPressedButtons;
private bool WasButtonJustPressed(Buttons button, ButtonState buttonState, PlayerIndex stateIndex)
{
return buttonState == ButtonState.Pressed && !PreviouslyPressedButtons[(int)stateIndex].Contains(button);
}
private bool WasButtonJustReleased(Buttons button, ButtonState buttonState, PlayerIndex stateIndex)
{
return buttonState == ButtonState.Released && PreviouslyPressedButtons[(int)stateIndex].Contains(button);
}
private bool WasButtonJustPressed(Buttons button, float value, PlayerIndex stateIndex)
{
return WasButtonJustPressed(button, value > 0.2f ? ButtonState.Pressed : ButtonState.Released, stateIndex);
}
private bool WasButtonJustReleased(Buttons button, float value, PlayerIndex stateIndex)
{
return WasButtonJustReleased(button, value > 0.2f ? ButtonState.Pressed : ButtonState.Released, stateIndex);
}
public Buttons[] GetButtonsDown(PlayerIndex index)
{
GamePadState state = GamePad.GetState((PlayerIndex)index);
List<Buttons> 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();
}
public Buttons[] GetFramePressedButtons(PlayerIndex index)
{
GamePadState state = GamePad.GetState((PlayerIndex)index);
List<Buttons> 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();
}
public Buttons[] GetFrameReleasedButtons(PlayerIndex index)
{
GamePadState state = GamePad.GetState((PlayerIndex)index);
List<Buttons> 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();
}
public int PreviousGameLocations { get; private set; }
public int PreviousLocationObjects { get; private set; }
public int PreviousItems_ { get; private set; }
public Dictionary<Item, int> PreviousItems { get; private set; }
public int PreviousCombatLevel { get; private set; }
public int PreviousFarmingLevel { get; private set; }
public int PreviousFishingLevel { get; private set; }
public int PreviousForagingLevel { get; private set; }
public int PreviousMiningLevel { get; private set; }
public int PreviousLuckLevel { get; private set; }
public GameLocation PreviousGameLocation { get; private set; }
public IClickableMenu PreviousActiveMenu { get; private set; }
public Int32 PreviousTimeOfDay { get; private set; }
public Int32 PreviousDayOfMonth { get; private set; }
public String PreviousSeasonOfYear { get; private set; }
public Int32 PreviousYearOfGame { get; private set; }
public Farmer PreviousFarmer { get; private set; }
private static SGame instance;
public static SGame Instance { get { return instance; } }
public Farmer CurrentFarmer { get { return player; } }
public SGame()
{
instance = this;
graphics.GraphicsProfile = GraphicsProfile.HiDef;
}
protected override void Initialize()
{
Log.Verbose("XNA Initialize");
ModItems = new Dictionary<Int32, SObject>();
PreviouslyPressedKeys = new Keys[0];
PreviouslyPressedButtons = new Buttons[4][];
for (int i = 0; i < 4; ++i) PreviouslyPressedButtons[i] = new Buttons[0];
base.Initialize();
Events.GameEvents.InvokeInitialize();
}
protected override void LoadContent()
{
Log.Verbose("XNA LoadContent");
base.LoadContent();
Events.GameEvents.InvokeLoadContent();
}
protected override void Update(GameTime gameTime)
{
UpdateEventCalls();
try
{
base.Update(gameTime);
}
catch (Exception ex)
{
Log.Error("An error occured in the base update loop: " + ex);
Console.ReadKey();
}
Events.GameEvents.InvokeUpdateTick();
PreviouslyPressedKeys = CurrentlyPressedKeys;
for(PlayerIndex i = PlayerIndex.One; i <= PlayerIndex.Four; i++)
{
PreviouslyPressedButtons[(int)i] = GetButtonsDown(i);
}
}
protected override void Draw(GameTime gameTime)
{
base.Draw(gameTime);
Events.GraphicsEvents.InvokeDrawTick();
if (false)
{
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
if (CurrentLocation != null)
CurrentLocation.draw(spriteBatch);
if (player != null && player.position != null)
spriteBatch.DrawString(dialogueFont, player.position.ToString(), new Vector2(0, 180), Color.Orange);
spriteBatch.End();
}
}
public static Int32 RegisterModItem(SObject modItem)
{
if (modItem.HasBeenRegistered)
{
Log.Error("The item {0} has already been registered with ID {1}", modItem.Name, modItem.RegisteredId);
return modItem.RegisteredId;
}
Int32 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;
}
public static SObject PullModItemFromDict(Int32 id, bool isIndex)
{
if (isIndex)
{
if (ModItems.ElementAtOrDefault(id).Value != null)
{
return ModItems.ElementAt(id).Value.Clone();
}
Log.Error("ModItem Dictionary does not contain index: " + id.ToString());
return null;
}
if (ModItems.ContainsKey(id))
{
return ModItems[id].Clone();
}
Log.Error("ModItem Dictionary does not contain ID: " + id.ToString());
return null;
}
public static SGameLocation GetLocationFromName(String name)
{
return ModLocations.FirstOrDefault(n => n.name == name);
}
public static SGameLocation LoadOrCreateSGameLocationFromName(String name)
{
if (GetLocationFromName(name) != null)
return GetLocationFromName(name);
GameLocation gl = locations.FirstOrDefault(x => x.name == name);
if (gl != null)
{
Log.Debug("A custom location was created for the new name: " + name);
SGameLocation s = SGameLocation.ConstructFromBaseClass(gl);
ModLocations.Add(s);
return s;
}
if (currentLocation != null && currentLocation.name == name)
{
gl = currentLocation;
Log.Debug("A custom location was created from the current location for the new name: " + name);
SGameLocation s = SGameLocation.ConstructFromBaseClass(gl);
ModLocations.Add(s);
return s;
}
Log.Debug("A custom location could not be created for: " + name);
return null;
}
public void UpdateEventCalls()
{
KStateNow = Keyboard.GetState();
CurrentlyPressedKeys = KStateNow.GetPressedKeys();
MStateNow = Mouse.GetState();
foreach (Keys k in FramePressedKeys)
Events.ControlEvents.InvokeKeyPressed(k);
foreach (Keys k in FrameReleasedKeys)
Events.ControlEvents.InvokeKeyReleased(k);
for (PlayerIndex i = PlayerIndex.One; i <= PlayerIndex.Four; i++)
{
Buttons[] buttons = GetFramePressedButtons(i);
foreach (Buttons b in buttons)
{
if(b == Buttons.LeftTrigger || b == Buttons.RightTrigger)
{
Events.ControlEvents.InvokeTriggerPressed(i, b, b == Buttons.LeftTrigger ? GamePad.GetState(i).Triggers.Left : GamePad.GetState(i).Triggers.Right);
}
else
{
Events.ControlEvents.InvokeButtonPressed(i, b);
}
}
}
for (PlayerIndex i = PlayerIndex.One; i <= PlayerIndex.Four; i++)
{
foreach (Buttons b in GetFrameReleasedButtons(i))
{
if (b == Buttons.LeftTrigger || b == Buttons.RightTrigger)
{
Events.ControlEvents.InvokeTriggerReleased(i, b, b == Buttons.LeftTrigger ? GamePad.GetState(i).Triggers.Left : GamePad.GetState(i).Triggers.Right);
}
else
{
Events.ControlEvents.InvokeButtonReleased(i, b);
}
}
}
if (KStateNow != KStatePrior)
{
Events.ControlEvents.InvokeKeyboardChanged(KStatePrior, KStateNow);
KStatePrior = KStateNow;
}
if (MStateNow != MStatePrior)
{
Events.ControlEvents.InvokeMouseChanged(MStatePrior, MStateNow);
MStatePrior = MStateNow;
}
if (activeClickableMenu != null && activeClickableMenu != PreviousActiveMenu)
{
Events.MenuEvents.InvokeMenuChanged(PreviousActiveMenu, activeClickableMenu);
PreviousActiveMenu = activeClickableMenu;
}
if (locations.GetHash() != PreviousGameLocations)
{
Events.LocationEvents.InvokeLocationsChanged(locations);
PreviousGameLocations = locations.GetHash();
}
if (currentLocation != PreviousGameLocation)
{
Events.LocationEvents.InvokeCurrentLocationChanged(PreviousGameLocation, currentLocation);
PreviousGameLocation = currentLocation;
}
if (player != null && player != PreviousFarmer)
{
Events.PlayerEvents.InvokeFarmerChanged(PreviousFarmer, player);
PreviousFarmer = player;
}
if (player != null && player.combatLevel != PreviousCombatLevel)
{
Events.PlayerEvents.InvokeLeveledUp(EventArgsLevelUp.LevelType.Combat, player.combatLevel);
PreviousCombatLevel = player.combatLevel;
}
if (player != null && player.farmingLevel != PreviousFarmingLevel)
{
Events.PlayerEvents.InvokeLeveledUp(EventArgsLevelUp.LevelType.Farming, player.farmingLevel);
PreviousFarmingLevel = player.farmingLevel;
}
if (player != null && player.fishingLevel != PreviousFishingLevel)
{
Events.PlayerEvents.InvokeLeveledUp(EventArgsLevelUp.LevelType.Fishing, player.fishingLevel);
PreviousFishingLevel = player.fishingLevel;
}
if (player != null && player.foragingLevel != PreviousForagingLevel)
{
Events.PlayerEvents.InvokeLeveledUp(EventArgsLevelUp.LevelType.Foraging, player.foragingLevel);
PreviousForagingLevel = player.foragingLevel;
}
if (player != null && player.miningLevel != PreviousMiningLevel)
{
Events.PlayerEvents.InvokeLeveledUp(EventArgsLevelUp.LevelType.Mining, player.miningLevel);
PreviousMiningLevel = player.miningLevel;
}
if (player != null && player.luckLevel != PreviousLuckLevel)
{
Events.PlayerEvents.InvokeLeveledUp(EventArgsLevelUp.LevelType.Luck, player.luckLevel);
PreviousLuckLevel = player.luckLevel;
}
List<ItemStackChange> changedItems;
if (player != null && HasInventoryChanged(player.items, out changedItems))
{
Events.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)
{
Events.LocationEvents.InvokeOnNewLocationObject(currentLocation.objects);
PreviousLocationObjects = objectHash ?? -1;
}
if (timeOfDay != PreviousTimeOfDay)
{
Events.TimeEvents.InvokeTimeOfDayChanged(PreviousTimeOfDay, timeOfDay);
PreviousTimeOfDay = timeOfDay;
}
if (dayOfMonth != PreviousDayOfMonth)
{
Events.TimeEvents.InvokeDayOfMonthChanged(PreviousDayOfMonth, dayOfMonth);
PreviousDayOfMonth = dayOfMonth;
}
if (currentSeason != PreviousSeasonOfYear)
{
Events.TimeEvents.InvokeSeasonOfYearChanged(PreviousSeasonOfYear, currentSeason);
PreviousSeasonOfYear = currentSeason;
}
if (year != PreviousYearOfGame)
{
Events.TimeEvents.InvokeYearOfGameChanged(PreviousYearOfGame, year);
PreviousYearOfGame = year;
}
}
private bool HasInventoryChanged(List<Item> items, out List<ItemStackChange> changedItems)
{
changedItems = new List<ItemStackChange>();
IEnumerable<Item> actualItems = items.Where(n => n != null);
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.Any(i => i == n.Key)).Select(n =>
new ItemStackChange() { Item = n.Key, StackChange = -n.Key.Stack, ChangeType = ChangeType.Removed }));
}
return (changedItems.Any());
}
}
}

View File

@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.Eventing.Reader;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewValley;
using StardewValley.BellsAndWhistles;
namespace StardewModdingAPI.Inheritance
{
[Obsolete]
public class SGameLocation : GameLocation
{
public GameLocation BaseGameLocation { get; private set; }
public SerializableDictionary<Vector2, SObject> ModObjects { get; set; }
public static SGameLocation ConstructFromBaseClass(GameLocation baseClass, bool copyAllData = false)
{
SGameLocation s = new SGameLocation();
s.BaseGameLocation = baseClass;
s.name = baseClass.name;
Log.Debug("CONSTRUCTED: " + s.name);
if (copyAllData)
{
foreach (var v in baseClass.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
{
try
{
var fi = s.GetType().GetField(v.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (fi != null && !fi.IsStatic)
{
fi.SetValue(s, v.GetValue(baseClass));
//Console.WriteLine("SET {0} ON {1} TO {2}", fi.Name, s.name, v.GetValue(baseClass));
}
}
catch (Exception ex)
{
Log.Error(ex);
}
}
}
return s;
}
public static List<SGameLocation> ConstructFromBaseClasses(List<GameLocation> baseGameLocations, bool copyAllData = false)
{
return baseGameLocations.Select(gl => ConstructFromBaseClass(gl, copyAllData)).ToList();
}
public virtual void update(GameTime gameTime)
{
}
public override void draw(SpriteBatch b)
{
foreach (var v in ModObjects)
{
v.Value.draw(b, (int)v.Key.X, (int)v.Key.Y, 0.999f, 1);
}
}
public SGameLocation()
{
ModObjects = new SerializableDictionary<Vector2, SObject>();
}
}
}

View File

@ -0,0 +1,275 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewValley;
using StardewValley.Locations;
namespace StardewModdingAPI.Inheritance
{
public class SObject : StardewValley.Object
{
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 Boolean IsPassable { get; set; }
public Boolean IsPlaceable { get; set; }
public Boolean HasBeenRegistered { get; set; }
public Int32 RegisteredId { get; set; }
public Int32 MaxStackSize { get; set; }
public Boolean WallMounted { get; set; }
public Vector2 DrawPosition { get; set; }
public Boolean 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 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 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((float)(((x * Game1.tileSize) + (Game1.tileSize / 2)) + ((this.shakeTimer > 0) ? Game1.random.Next(-1, 2) : 0)), (float)(((y * Game1.tileSize) + (Game1.tileSize / 2)) + ((this.shakeTimer > 0) ? Game1.random.Next(-1, 2) : 0)))), new Rectangle?(Game1.currentLocation.getSourceRectForObject(this.ParentSheetIndex)), (Color)(Color.White * alpha), 0f, new Vector2(8f, 8f), (this.scale.Y > 1f) ? this.getScale().Y : ((float)Game1.pixelZoom), this.flipped ? SpriteEffects.FlipHorizontally : SpriteEffects.None, (this.isPassable() ? ((float)this.getBoundingBox(new Vector2((float)x, (float)y)).Top) : ((float)this.getBoundingBox(new Vector2((float)x, (float)y)).Bottom)) / 10000f);
}
}
public 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");
return;
try
{
if (Texture != null)
{
int targSize = Game1.tileSize;
int midX = (int) ((xNonTile) + 32);
int midY = (int) ((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.Error(ex.ToString());
Console.ReadKey();
}
}
public override void drawInMenu(SpriteBatch spriteBatch, Vector2 location, float scaleSize, float transparency, float layerDepth, bool drawStackNumber)
{
if (this.isRecipe)
{
transparency = 0.5f;
scaleSize *= 0.75f;
}
if (Texture != null)
{
int targSize = (int) (64 * scaleSize * 0.9f);
int midX = (int) ((location.X) + 32);
int midY = (int) ((location.Y) + 32);
int targX = midX - targSize / 2;
int 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)
{
float scale = 0.5f + scaleSize;
Game1.drawWithBorder(string.Concat(this.stack.ToString()), Color.Black, Color.White, location + new Vector2((float) Game1.tileSize - Game1.tinyFont.MeasureString(string.Concat(this.stack.ToString())).X * scale, (float) Game1.tileSize - (float) ((double) Game1.tinyFont.MeasureString(string.Concat(this.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)
{
int targSize = 64;
int midX = (int) ((objectPosition.X) + 32);
int midY = (int) ((objectPosition.Y) + 32);
int targX = midX - targSize / 2;
int 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()
{
SObject toRet = new SObject();
toRet.Name = this.Name;
toRet.CategoryName = this.CategoryName;
toRet.Description = this.Description;
toRet.Texture = this.Texture;
toRet.IsPassable = this.IsPassable;
toRet.IsPlaceable = this.IsPlaceable;
toRet.quality = this.quality;
toRet.scale = this.scale;
toRet.isSpawnedObject = this.isSpawnedObject;
toRet.isRecipe = this.isRecipe;
toRet.questItem = this.questItem;
toRet.stack = 1;
toRet.HasBeenRegistered = this.HasBeenRegistered;
toRet.RegisteredId = this.RegisteredId;
return toRet;
}
public override Item getOne()
{
return this.Clone();
}
public override void actionWhenBeingHeld(Farmer who)
{
int x = Game1.oldMouseState.X + Game1.viewport.X;
int 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();
Vector2 key = new Vector2(x, y);
if (!canBePlacedHere(location, key))
return false;
SObject s = Clone();
s.PlacedAt = key;
s.boundingBox = new Rectangle(x / Game1.tileSize * Game1.tileSize, y / Game1.tileSize * Game1.tileSize, this.boundingBox.Width, this.boundingBox.Height);
location.objects.Add(key, s);
Log.Verbose("{0} - {1}", this.GetHashCode(), s.GetHashCode());
return true;
}
public override void actionOnPlayerEntry()
{
//base.actionOnPlayerEntry();
}
public override void drawPlacementBounds(SpriteBatch spriteBatch, GameLocation location)
{
if (canBePlacedHere(location, CurrentMouse))
{
int targSize = Game1.tileSize;
int x = Game1.oldMouseState.X + Game1.viewport.X;
int y = Game1.oldMouseState.Y + Game1.viewport.Y;
spriteBatch.Draw(Game1.mouseCursors, new Vector2((float)(x / Game1.tileSize * Game1.tileSize - Game1.viewport.X), (float)(y / Game1.tileSize * Game1.tileSize - Game1.viewport.Y)), new Microsoft.Xna.Framework.Rectangle?(new Microsoft.Xna.Framework.Rectangle(Utility.playerCanPlaceItemHere(location, (Item)this, x, y, Game1.player) ? 194 : 210, 388, 16, 16)), Color.White, 0.0f, Vector2.Zero, (float)Game1.pixelZoom, SpriteEffects.None, 0.01f);
}
}
}
}

182
StardewModdingAPI/Log.cs Normal file
View File

@ -0,0 +1,182 @@
using System;
using System.IO;
using System.Threading;
namespace StardewModdingAPI
{
/// <summary>
/// Class to organize logging calls.
/// </summary>
public class Log
{
private static StreamWriter _logStream;
private static string _logPath;
/// <summary>
/// Set up the logging stream
/// </summary>
/// <param name="logPath"></param>
public static void Initialize(string logPath)
{
_logPath = logPath;
var logFile = string.Format("{0}\\MODDED_ProgramLog.Log_LATEST.txt", logPath);
try
{
_logStream = new StreamWriter(logFile, false);
}
catch (Exception)
{
// TODO: not use general exception
Log.Error("Could not initialize LogStream - Logging is disabled");
}
}
/// <summary>
/// Print provided parameters to the console/file as applicable
/// </summary>
/// <param name="message">Desired message</param>
/// <param name="suppressMessage">When true, writes to ONLY console and not the log file.</param>
/// <param name="values">Additional params to be added to the message</param>
private static void PrintLog(object message, bool disableLogging, params object[] values)
{
string logOutput = string.Format("[{0}] {1}", System.DateTime.Now.ToLongTimeString(), String.Format(message.ToString(), values));
Console.WriteLine(logOutput);
if (_logStream != null && !disableLogging)
{
_logStream.WriteLine(logOutput);
_logStream.Flush();
}
}
/// <summary>
/// Successful message to display to console and logging.
/// </summary>
/// <param name="message"></param>
/// <param name="values"></param>
public static void Success(object message, params object[] values)
{
Console.ForegroundColor = ConsoleColor.Green;
Log.PrintLog(message?.ToString(), false, values);
Console.ForegroundColor = ConsoleColor.Gray;
}
/// <summary>
/// Generic comment to display to console and logging.
/// </summary>
/// <param name="message"></param>
/// <param name="values"></param>
public static void Verbose(object message, params object[] values)
{
Log.PrintLog(message?.ToString(), false, values);
}
/// <summary>
/// Additional comment to display to console and logging.
/// </summary>
/// <param name="message"></param>
/// <param name="values"></param>
public static void Comment(object message, params object[] values)
{
Log.PrintLog(message?.ToString(), false, values);
}
/// <summary>
/// Additional comment to display to console and logging.
/// </summary>
/// <param name="message"></param>
/// <param name="values"></param>
public static void Warning(object message, params object[] values)
{
Console.ForegroundColor = ConsoleColor.Yellow;
Log.PrintLog(message?.ToString(), false, values);
Console.ForegroundColor = ConsoleColor.Gray;
}
/// <summary>
/// Message for only console. Does not appear in logging.
/// </summary>
/// <param name="message"></param>
/// <param name="values"></param>
public static void Info(object message, params object[] values)
{
Log.PrintLog(message.ToString(), true, values);
}
/// <summary>
/// Important message indicating an error.
/// </summary>
/// <param name="message"></param>
/// <param name="values"></param>
public static void Error(object message, params object[] values)
{
Console.ForegroundColor = ConsoleColor.Red;
Log.PrintLog(message.ToString(), false, values);
Console.ForegroundColor = ConsoleColor.Gray;
}
/// <summary>
/// A message displayed only while in DEBUG mode
/// </summary>
/// <param name="message"></param>
/// <param name="values"></param>
public static void Debug(object message, params object[] values)
{
#if DEBUG
Console.ForegroundColor = ConsoleColor.Yellow;
Log.PrintLog(message.ToString(), false, values);
Console.ForegroundColor = ConsoleColor.Gray;
#endif
}
/// <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(_logPath + "\\MODDED_ErrorLog.Log_" + Extensions.Random.Next(100000000, 999999999) + ".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(_logPath + "\\MODDED_ErrorLog.Log_" + Extensions.Random.Next(100000000, 999999999) + ".txt", e.Exception.ToString());
}
// I'm including the following for now because they have a lot of references with different uses.
// They should be removed since they do not provide any insight into actual problems, and other log methods should be used.
public static void LogValueNotSpecified()
{
Error("<value> must be specified");
}
public static void LogObjectValueNotSpecified()
{
Error("<object> and <value> must be specified");
}
public static void LogValueInvalid()
{
Error("<value> is invalid");
}
public static void LogObjectInvalid()
{
Error("<object> is invalid");
}
public static void LogValueNotInt32()
{
Error("<value> must be a whole number (Int32)");
}
}
}

39
StardewModdingAPI/Mod.cs Normal file
View File

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StardewModdingAPI
{
public class Mod
{
/// <summary>
/// The name of your mod.
/// </summary>
public virtual string Name { get; protected set; }
/// <summary>
/// The name of the mod's authour.
/// </summary>
public virtual string Authour { get; protected set; }
/// <summary>
/// The version of the mod.
/// </summary>
public virtual string Version { get; protected set; }
/// <summary>
/// A description of the mod.
/// </summary>
public virtual string Description { get; protected set; }
/// <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)
{
}
}
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewValley;
namespace StardewModdingAPI
{
class ModItem : StardewValley.Object
{
public Item AsItem { get { return (Item) this; } }
public override string Name { get; set; }
public string Description { get; set; }
public int ID { get; set; }
public Texture2D Texture { get; set; }
public ModItem()
{
}
}
}

View File

@ -0,0 +1,373 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Mono.Cecil;
using Mono.Cecil.Cil;
using StardewModdingAPI.Events;
using StardewModdingAPI.ExtensionMethods;
using StardewModdingAPI.Helpers;
using StardewModdingAPI.Inheritance;
using StardewModdingAPI.Inheritance.Menus;
using StardewValley;
using StardewValley.Menus;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;
namespace StardewModdingAPI
{
public class Program
{
private static List<string> _modPaths;
private static List<string> _modContentPaths;
public static Texture2D DebugPixel { get; private set; }
public static bool IsGameReferenceDirty { get; set; }
public static object gameInst;
public static Game1 _gamePtr;
public static Game1 gamePtr
{
get
{
if(IsGameReferenceDirty && gameInst != null)
{
_gamePtr = gameInst.Copy<Game1>();
}
return _gamePtr;
}
}
public static bool ready;
public static Form StardewForm;
public static Thread gameThread;
public static Thread consoleInputThread;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Main method holding the API execution
/// </summary>
/// <param name="args"></param>
private static void Main(string[] args)
{
try
{
ConfigureUI();
ConfigurePaths();
ConfigureMethodInjection();
ConfigureSDV();
GameRunInvoker();
}
catch (Exception e)
{
// Catch and display all exceptions.
StardewModdingAPI.Log.Error("Critical error: " + e);
}
StardewModdingAPI.Log.Comment("The API will now terminate. Press any key to continue...");
Console.ReadKey();
}
/// <summary>
/// Configures Mono.Cecil injections
/// </summary>
private static void ConfigureMethodInjection()
{
StardewAssembly.ModifyStardewAssembly();
#if DEBUG
StardewAssembly.WriteModifiedExe();
#endif
}
public static void Test(object instance)
{
gameInst = instance;
IsGameReferenceDirty = true;
}
/// <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()
{
StardewModdingAPI.Log.Info("Validating api paths...");
_modPaths = new List<string>();
_modContentPaths = new List<string>();
//TODO: Have an app.config and put the paths inside it so users can define locations to load mods from
_modPaths.Add(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StardewValley", "Mods"));
_modPaths.Add(Path.Combine(Constants.ExecutionPath, "Mods"));
_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(path => VerifyPath(path));
_modContentPaths.ForEach(path => VerifyPath(path));
VerifyPath(Constants.LogPath);
StardewModdingAPI.Log.Initialize(Constants.LogPath);
if (!File.Exists(Constants.ExecutionPath + "\\Stardew Valley.exe"))
{
StardewModdingAPI.Log.Error("Replace this");
//throw new FileNotFoundException(string.Format("Could not found: {0}\\Stardew Valley.exe", Constants.ExecutionPath));
}
}
/// <summary>
/// Load Stardev Valley and control features
/// </summary>
private static void ConfigureSDV()
{
StardewModdingAPI.Log.Info("Initializing SDV Assembly...");
// Load in the assembly - ignores security
StardewAssembly.LoadStardewAssembly();
StardewModdingAPI.Log.Comment("SDV Loaded Into Memory");
// Change the game's version
StardewModdingAPI.Log.Verbose("Injecting New SDV Version...");
Game1.version += string.Format("-Z_MODDED | SMAPI {0}", Constants.VersionString);
// Create the thread for the game to run in.
Application.ThreadException += StardewModdingAPI.Log.Application_ThreadException;
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException += StardewModdingAPI.Log.CurrentDomain_UnhandledException;
//Create definition to listen for input
StardewModdingAPI.Log.Verbose("Initializing Console Input Thread...");
consoleInputThread = new Thread(ConsoleInputThread);
Command.RegisterCommand("help", "Lists all commands | 'help <cmd>' returns command description").CommandFired += help_CommandFired;
StardewAssembly.Launch();
}
/// <summary>
/// Wrap the 'RunGame' method for console output
/// </summary>
private static void GameRunInvoker()
{
//Game's in memory now, send the event
StardewModdingAPI.Log.Verbose("Game Loaded");
Events.GameEvents.InvokeGameLoaded();
StardewModdingAPI.Log.Comment("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();
StardewModdingAPI.Log.Verbose("Game Execution Finished");
StardewModdingAPI.Log.Verbose("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)
{
StardewModdingAPI.Log.Error("Could not create a path: " + path + "\n\n" + ex);
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
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()
{
StardewModdingAPI.Log.Verbose("LOADING MODS");
int loadedMods = 0;
foreach (string ModPath in _modPaths)
{
foreach (String s in Directory.GetFiles(ModPath, "*.dll"))
{
if (s.Contains("StardewInjector"))
continue;
StardewModdingAPI.Log.Success("Found DLL: " + s);
try
{
Assembly mod = Assembly.UnsafeLoadFrom(s); //to combat internet-downloaded DLLs
if (mod.DefinedTypes.Count(x => x.BaseType == typeof(Mod)) > 0)
{
StardewModdingAPI.Log.Verbose("Loading Mod DLL...");
TypeInfo tar = mod.DefinedTypes.First(x => x.BaseType == typeof(Mod));
Mod m = (Mod)mod.CreateInstance(tar.ToString());
Console.WriteLine("LOADED MOD: {0} by {1} - Version {2} | Description: {3}", m.Name, m.Authour, m.Version, m.Description);
loadedMods += 1;
m.Entry();
}
else
{
StardewModdingAPI.Log.Error("Invalid Mod DLL");
}
}
catch (Exception ex)
{
StardewModdingAPI.Log.Error("Failed to load mod '{0}'. Exception details:\n" + ex, s);
}
}
}
StardewModdingAPI.Log.Success("LOADED {0} MODS", loadedMods);
}
public static void ConsoleInputThread()
{
string input = string.Empty;
while (true)
{
Command.CallCommand(Console.ReadLine());
}
}
public static void StardewInvoke(Action a)
{
StardewForm.Invoke(a);
}
static void help_CommandFired(object o, EventArgsCommand e)
{
if (e.Command.CalledArgs.Length > 0)
{
Command fnd = Command.FindCommand(e.Command.CalledArgs[0]);
if (fnd == null)
StardewModdingAPI.Log.Error("The command specified could not be found");
else
{
if (fnd.CommandArgs.Length > 0)
StardewModdingAPI.Log.Info("{0}: {1} - {2}", fnd.CommandName, fnd.CommandDesc, fnd.CommandArgs.ToSingular());
else
StardewModdingAPI.Log.Info("{0}: {1}", fnd.CommandName, fnd.CommandDesc);
}
}
else
StardewModdingAPI.Log.Info("Commands: " + Command.RegisteredCommands.Select(x => x.CommandName).ToSingular());
}
#region Logging
[Obsolete("This method is obsolete and will be removed in v0.39, please use the appropriate methods in the Log class")]
public static void Log(object o, params object[] format)
{
StardewModdingAPI.Log.Info(o, format);
}
[Obsolete("This method is obsolete and will be removed in v0.39, please use the appropriate methods in the Log class")]
public static void LogColour(ConsoleColor c, object o, params object[] format)
{
StardewModdingAPI.Log.Info(o, format);
}
[Obsolete("This method is obsolete and will be removed in v0.39, please use the appropriate methods in the Log class")]
public static void LogInfo(object o, params object[] format)
{
StardewModdingAPI.Log.Info(o, format);
}
[Obsolete("This method is obsolete and will be removed in v0.39, please use the appropriate methods in the Log class")]
public static void LogError(object o, params object[] format)
{
StardewModdingAPI.Log.Error(o, format);
}
[Obsolete("This method is obsolete and will be removed in v0.39, please use the appropriate methods in the Log class")]
public static void LogDebug(object o, params object[] format)
{
StardewModdingAPI.Log.Debug(o, format);
}
[Obsolete("This method is obsolete and will be removed in v0.39, please use the appropriate methods in the Log class")]
public static void LogValueNotSpecified()
{
StardewModdingAPI.Log.Error("<value> must be specified");
}
[Obsolete("This method is obsolete and will be removed in v0.39, please use the appropriate methods in the Log class")]
public static void LogObjectValueNotSpecified()
{
StardewModdingAPI.Log.Error("<object> and <value> must be specified");
}
[Obsolete("This method is obsolete and will be removed in v0.39, please use the appropriate methods in the Log class")]
public static void LogValueInvalid()
{
StardewModdingAPI.Log.Error("<value> is invalid");
}
[Obsolete("This method is obsolete and will be removed in v0.39, please use the appropriate methods in the Log class")]
public static void LogObjectInvalid()
{
StardewModdingAPI.Log.Error("<object> is invalid");
}
[Obsolete("This method is obsolete and will be removed in v0.39, please use the appropriate methods in the Log class")]
public static void LogValueNotInt32()
{
StardewModdingAPI.Log.Error("<value> must be a whole number (Int32)");
}
#endregion
}
}

View File

@ -1,27 +1,35 @@
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyTitle("Loader")] [assembly: AssemblyTitle("StardewModdingAPI")]
[assembly: AssemblyDescription("")] [assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Loader")] [assembly: AssemblyProduct("StardewModdingAPI")]
[assembly: AssemblyCopyright("Copyright © 2018")] [assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [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)] [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: // Version information for an assembly consists of the following four values:
// //
// Major Version // Major Version
// Minor Version // Minor Version
// Build Number // Build Number
// Revision // Revision
// //
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyVersion("1.0.0.0")]

View File

@ -0,0 +1,198 @@
<?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.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SccProjectName>
</SccProjectName>
<SccLocalPath>
</SccLocalPath>
<SccAuxPath>
</SccAuxPath>
<SccProvider>
</SccProvider>
<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>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<TargetFrameworkProfile />
</PropertyGroup>
<Choose>
<When Condition="'$(SteamInstallPath)' != ''">
<PropertyGroup>
<SteamPath>$(SteamInstallPath)</SteamPath>
</PropertyGroup>
</When>
<Otherwise>
<PropertyGroup>
<SteamPath>..\..\..\..\Games\SteamLibrary</SteamPath>
</PropertyGroup>
</Otherwise>
</Choose>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</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>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<PlatformTarget>x86</PlatformTarget>
<OutputPath>bin\x86\Debug\</OutputPath>
<Prefer32Bit>false</Prefer32Bit>
<DefineConstants>DEBUG</DefineConstants>
<UseVSHostingProcess>true</UseVSHostingProcess>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<PlatformTarget>x86</PlatformTarget>
<OutputPath>bin\x86\Release\</OutputPath>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>icon.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
<Reference Include="Microsoft.Xna.Framework.Game, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
<Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
<Reference Include="Microsoft.Xna.Framework.Xact, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
<Reference Include="Mono.Cecil, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.9.6.1\lib\net45\Mono.Cecil.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.Cecil.Mdb, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.9.6.1\lib\net45\Mono.Cecil.Mdb.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.Cecil.Pdb, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.9.6.1\lib\net45\Mono.Cecil.Pdb.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.Cecil.Rocks, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.9.6.1\lib\net45\Mono.Cecil.Rocks.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Stardew Valley, Version=1.0.5912.41135, Culture=neutral, processorArchitecture=x86">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\Games\SteamLibrary\steamapps\common\Stardew Valley\Stardew Valley.exe</HintPath>
</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">
<HintPath>..\..\..\..\Games\SteamLibrary\steamapps\common\Stardew Valley\xTile.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="API\Game.cs" />
<Compile Include="Command.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\FarmAnimal.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="ExtensionMethods\Array.cs" />
<Compile Include="ExtensionMethods\Object.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="Helpers\CecilContext.cs" />
<Compile Include="Helpers\CecilHelper.cs" />
<Compile Include="Helpers\ReflectionHelper.cs" />
<Compile Include="Helpers\StardewAssembly.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\SGameLocation.cs" />
<Compile Include="Inheritance\SObject.cs" />
<Compile Include="Log.cs" />
<Compile Include="Mod.cs" />
<Compile Include="ModItem.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Inheritance\SGame.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<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>
<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>

BIN
StardewModdingAPI/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Mono.Cecil" version="0.9.6.1" targetFramework="net45" />
</packages>

View File

@ -1,21 +1,27 @@
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Android.App;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyTitle("MobilePatch")] [assembly: AssemblyTitle("TrainerMod")]
[assembly: AssemblyDescription("")] [assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("MobilePatch")] [assembly: AssemblyProduct("TrainerMod")]
[assembly: AssemblyCopyright("Copyright © 2023")] [assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [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)] [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: // Version information for an assembly consists of the following four values:
// //
// Major Version // Major Version

771
TrainerMod/TrainerMod.cs Normal file
View File

@ -0,0 +1,771 @@
using StardewModdingAPI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Mime;
using System.Text;
using System.Threading.Tasks;
using StardewModdingAPI.Inheritance;
using StardewValley;
using StardewValley.Tools;
using Microsoft.Xna.Framework;
using StardewValley.Objects;
using StardewModdingAPI.Events;
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;
}
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", "Outputs a list of melee weapons | out_melee", 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;
}
static void types_CommandFired(object sender, EventArgsCommand e)
{
Log.Verbose("[Int32: {0} - {1}], [Int64: {2} - {3}], [String: \"raw text\"], [Colour: r,g,b (EG: 128, 32, 255)]", Int32.MinValue, Int32.MaxValue, Int64.MinValue, Int64.MaxValue);
}
static void hide_CommandFired(object sender, EventArgsCommand e)
{
Program.StardewInvoke(() => { Program.StardewForm.Hide(); });
}
static void show_CommandFired(object sender, EventArgsCommand e)
{
Program.StardewInvoke(() => { Program.StardewForm.Show(); });
}
static void save_CommandFired(object sender, EventArgsCommand e)
{
StardewValley.SaveGame.Save();
}
static void load_CommandFired(object sender, EventArgsCommand e)
{
Game1.activeClickableMenu = new StardewValley.Menus.LoadGameMenu();
}
static void exit_CommandFired(object sender, EventArgsCommand e)
{
Program.gamePtr.Exit();
Environment.Exit(0);
}
static void player_setName(object sender, EventArgsCommand e)
{
if (e.Command.CalledArgs.Length > 1)
{
string obj = e.Command.CalledArgs[0];
string[] objs = "player,pet,farm".Split(new[] { ',' });
if (objs.Contains(obj))
{
switch (obj)
{
case "player":
Game1.player.Name = e.Command.CalledArgs[1];
break;
case "pet":
Log.Error("Pets cannot currently be renamed.");
break;
case "farm":
Game1.player.farmName = e.Command.CalledArgs[1];
break;
}
}
else
{
Log.LogObjectInvalid();
}
}
else
{
Log.LogObjectValueNotSpecified();
}
}
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;
int ou = 0;
if (Int32.TryParse(e.Command.CalledArgs[0], out ou))
{
Game1.player.Money = ou;
Log.Verbose("Set {0}'s money to {1}", Game1.player.Name, Game1.player.Money);
}
else
{
Log.LogValueNotInt32();
}
}
}
else
{
Log.LogValueNotSpecified();
}
}
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;
int ou = 0;
if (Int32.TryParse(e.Command.CalledArgs[0], out ou))
{
Game1.player.Stamina = ou;
Log.Verbose("Set {0}'s stamina to {1}", Game1.player.Name, Game1.player.Stamina);
}
else
{
Log.LogValueNotInt32();
}
}
}
else
{
Log.LogValueNotSpecified();
}
}
static void player_setMaxStamina(object sender, EventArgsCommand e)
{
if (e.Command.CalledArgs.Length > 0)
{
int ou = 0;
if (Int32.TryParse(e.Command.CalledArgs[0], out ou))
{
Game1.player.MaxStamina = ou;
Log.Verbose("Set {0}'s max stamina to {1}", Game1.player.Name, Game1.player.MaxStamina);
}
else
{
Log.LogValueNotInt32();
}
}
else
{
Log.LogValueNotSpecified();
}
}
static void player_setLevel(object sender, EventArgsCommand e)
{
if (e.Command.CalledArgs.Length > 1)
{
string skill = e.Command.CalledArgs[0];
string[] skills = "luck,mining,combat,farming,fishing,foraging".Split(new[] { ',' });
if (skills.Contains(skill))
{
int ou = 0;
if (Int32.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.Error("<skill> is invalid");
}
}
else
{
Log.Error("<skill> and <value> must be specified");
}
}
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.Verbose("Set {0}'s added speed to {1}", Game1.player.Name, Game1.player.addedSpeed);
}
else
{
Log.LogValueNotInt32();
}
}
else
{
Log.LogValueNotSpecified();
}
}
static void player_changeColour(object sender, EventArgsCommand e)
{
if (e.Command.CalledArgs.Length > 1)
{
string obj = e.Command.CalledArgs[0];
string[] objs = "hair,eyes,pants".Split(new[] { ',' });
if (objs.Contains(obj))
{
string[] cs = e.Command.CalledArgs[1].Split(new[] { ',' }, 3);
if (cs[0].IsInt32() && cs[1].IsInt32() && cs[2].IsInt32())
{
Color 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.Error("<colour> is invalid");
}
}
else
{
Log.LogObjectInvalid();
}
}
else
{
Log.Error("<object> and <colour> must be specified");
}
}
static void player_changeStyle(object sender, EventArgsCommand e)
{
if (e.Command.CalledArgs.Length > 1)
{
string obj = e.Command.CalledArgs[0];
string[] objs = "hair,shirt,skin,acc,shoe,swim,gender".Split(new[] { ',' });
if (objs.Contains(obj))
{
if (e.Command.CalledArgs[1].IsInt32())
{
int 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.Error("<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.Error("<value> must be 0 or 1 for this <object>");
break;
}
}
else
{
Log.LogValueInvalid();
}
}
else
{
Log.LogObjectInvalid();
}
}
else
{
Log.LogObjectValueNotSpecified();
}
}
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.Verbose("Time is now " + (freezeTime ? "frozen" : "thawed"));
}
else
{
Log.Error("<value> should be 0 or 1");
}
}
else
{
Log.LogValueNotInt32();
}
}
else
{
Log.LogValueNotSpecified();
}
}
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.Verbose("Time set to: " + Game1.timeOfDay);
}
else
{
Log.Error("<value> should be between 600 and 2600 (06:00 AM - 02:00 AM [NEXT DAY])");
}
}
else
{
Log.LogValueNotInt32();
}
}
else
{
Log.LogValueNotSpecified();
}
}
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.Verbose("<value> must be between 1 and 28");
}
}
else
{
Log.LogValueNotInt32();
}
}
else
{
Log.LogValueNotSpecified();
}
}
static void world_setSeason(object sender, EventArgsCommand e)
{
if (e.Command.CalledArgs.Length > 0)
{
string obj = e.Command.CalledArgs[0];
string[] objs = "winter,spring,summer,fall".Split(new[] { ',' });
if (objs.Contains(obj))
{
Game1.currentSeason = obj;
}
else
{
Log.LogValueInvalid();
}
}
else
{
Log.LogValueNotSpecified();
}
}
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();
}
}
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();
}
}
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();
}
}
static void player_addItem(object sender, EventArgsCommand e)
{
if (e.Command.CalledArgs.Length > 0)
{
if (e.Command.CalledArgs[0].IsInt32())
{
int count = 1;
int 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.Error("[count] is invalid");
return;
}
if (e.Command.CalledArgs.Length > 2)
{
if (e.Command.CalledArgs[2].IsInt32())
{
quality = e.Command.CalledArgs[2].AsInt32();
}
else
{
Log.Error("[quality] is invalid");
return;
}
}
}
StardewValley.Object o = new StardewValley.Object(e.Command.CalledArgs[0].AsInt32(), count);
o.quality = quality;
Game1.player.addItemByMenuIfNecessary((Item)o);
}
else
{
Log.Error("<item> is invalid");
}
}
else
{
Log.LogObjectValueNotSpecified();
}
}
static void player_addMelee(object sender, EventArgsCommand e)
{
if (e.Command.CalledArgs.Length > 0)
{
if (e.Command.CalledArgs[0].IsInt32())
{
MeleeWeapon toAdd = new MeleeWeapon(e.Command.CalledArgs[0].AsInt32());
Game1.player.addItemByMenuIfNecessary(toAdd);
Log.Verbose("Given {0} to {1}", toAdd.Name, Game1.player.Name);
}
else
{
Log.Error("<item> is invalid");
}
}
else
{
Log.LogObjectValueNotSpecified();
}
}
static void player_addRing(object sender, EventArgsCommand e)
{
if (e.Command.CalledArgs.Length > 0)
{
if (e.Command.CalledArgs[0].IsInt32())
{
Ring toAdd = new Ring(e.Command.CalledArgs[0].AsInt32());
Game1.player.addItemByMenuIfNecessary(toAdd);
Log.Verbose("Given {0} to {1}", toAdd.Name, Game1.player.Name);
}
else
{
Log.Error("<item> is invalid");
}
}
else
{
Log.LogObjectValueNotSpecified();
}
}
static void out_items(object sender, EventArgsCommand e)
{
for (int i = 0; i < 1000; i++)
{
try
{
Item it = new StardewValley.Object(i, 1);
if (it.Name != "Error Item")
Console.WriteLine(i + "| " + it.Name);
}
catch
{
}
}
}
static void out_melee(object sender, EventArgsCommand e)
{
Dictionary<int, string> d = Game1.content.Load<Dictionary<int, string>>("Data\\weapons");
Console.Write("DATA\\WEAPONS: ");
foreach (var v in d)
{
Console.WriteLine(v.Key + " | " + v.Value);
}
}
static void out_rings(object sender, EventArgsCommand e)
{
for (int i = 0; i < 100; i++)
{
try
{
Item it = new Ring(i);
if (it.Name != "Error Item")
Console.WriteLine(i + "| " + it.Name);
}
catch
{
}
}
}
static void world_downMineLevel(object sender, EventArgsCommand e)
{
Game1.nextMineLevel();
}
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();
}
}
static void blank_command(object sender, EventArgsCommand e) { }
static void RegisterNewItem(object sender, EventArgsCommand e)
{
#if DEBUG
SObject s = SGame.PullModItemFromDict(0, true);
s.Stack = 999;
Game1.player.addItemToInventory(s);
#endif
}
}
}

View File

@ -0,0 +1,93 @@
<?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.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\StardewModdingAPI\bin\x86\Debug\Mods\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x86</PlatformTarget>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<Choose>
<When Condition="'$(SteamInstallPath)' != ''">
<PropertyGroup>
<SteamPath>$(SteamInstallPath)</SteamPath>
</PropertyGroup>
</When>
<Otherwise>
<PropertyGroup>
<SteamPath>..\..\..\..\Games\SteamLibrary</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="Stardew Valley">
<HintPath>..\..\..\..\Games\SteamLibrary\steamapps\common\Stardew Valley\Stardew Valley.exe</HintPath>
</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>..\..\..\..\Games\SteamLibrary\steamapps\common\Stardew Valley\xTile.dll</HintPath>
</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>
<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>

BIN
UpgradeLog.htm Normal file

Binary file not shown.

BIN
UpgradeLog2.htm Normal file

Binary file not shown.

538
Vanilla Items List.txt Normal file
View File

@ -0,0 +1,538 @@
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
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
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 Shell
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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,78 +0,0 @@
<!--
This MSBuild file sets the common configuration and build scripts used by all the projects in this
repo. It imports the other MSBuild files as needed.
-->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<!--set general build properties -->
<Version>3.18.4</Version>
<Product>SMAPI</Product>
<LangVersion>latest</LangVersion>
<AssemblySearchPaths>$(AssemblySearchPaths);{GAC}</AssemblySearchPaths>
<DefineConstants Condition="$(OS) == 'Windows_NT' AND '$(BUILD_FOR_MOBILE)' == ''">$(DefineConstants);SMAPI_DEPRECATED;SMAPI_FOR_WINDOWS</DefineConstants>
<DebugSymbols>true</DebugSymbols>
<!--embed symbols for error stack trace line numbers on Linux/macOS: https://github.com/dotnet/runtime/issues/39987-->
<DebugType>embedded</DebugType>
<!--enable nullable annotations, except in .NET Standard 2.0 where they aren't supported-->
<Nullable Condition="'$(TargetFramework)' != 'netstandard2.0'">enable</Nullable>
<NoWarn Condition="'$(TargetFramework)' == 'netstandard2.0'">$(NoWarn);CS8632</NoWarn>
<!--set platform-->
<DefineConstants Condition="$(OS) == 'Windows_NT'">$(DefineConstants);SMAPI_FOR_WINDOWS</DefineConstants>
<CopyToGameFolder>false</CopyToGameFolder>
<!-- allow mods to be compiled as AnyCPU for compatibility with older platforms -->
<ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
<!--
suppress warnings that don't apply, so it's easier to spot actual issues.
warning | builds | summary | rationale
┄┄┄┄┄┄┄ | ┄┄┄┄┄┄┄┄┄┄ | ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ | ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
CS0436 | all | local type conflicts with imported type | SMAPI needs to use certain low-level code during very early compatibility checks, before it's safe to load any other DLLs.
CS0612 | deprecated | member is obsolete | internal references to deprecated code when deprecated code is enabled.
CS0618 | deprecated | member is obsolete (with message) | internal references to deprecated code when deprecated code is enabled.
CA1416 | all | platform code available on all platforms | Compiler doesn't recognize the #if constants used by SMAPI.
CS0809 | all | obsolete overload for non-obsolete member | This is deliberate to signal to mods that certain APIs are only implemented for the game and shouldn't be called by mods.
NU1701 | all | NuGet package targets older .NET version | All such packages are carefully tested to make sure they do work.
-->
<NoWarn Condition="$(DefineConstants.Contains(SMAPI_DEPRECATED))">$(NoWarn);CS0612;CS0618</NoWarn>
<NoWarn>$(NoWarn);CS0436;CA1416;CS0809;NU1701</NoWarn>
</PropertyGroup>
<ItemGroup Condition="$(COMPILE_WITH_PLUGIN) == 'True'">
<PackageReference Include="Microsoft.Net.Compilers" Version="3.3.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
<!--find game folder-->
<Import Project="find-game-folder.targets" />
<!-- <Target Name="ValidateInstallPath" AfterTargets="BeforeBuild">-->
<!-- &lt;!&ndash; if game path is invalid, show one user-friendly error instead of a slew of reference errors &ndash;&gt;-->
<!-- <Error Condition="!Exists('$(GamePath)')" Text="Failed to find the game install path automatically. You can specify where to find it; see https://smapi.io/package/custom-game-path." />-->
<!-- </Target>-->
<!-- common build settings -->
<PropertyGroup>
<DebugType>pdbonly</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<!--deploy local files-->
<Import Project="deploy-local-smapi.targets" Condition="'$(CopyToGameFolder)' == 'true'" />
<!-- launch SMAPI through Visual Studio -->
<PropertyGroup Condition="'$(MSBuildProjectName)' == 'SMAPI'">
<StartAction>Program</StartAction>
<StartProgram>$(GamePath)\StardewModdingAPI.exe</StartProgram>
<StartWorkingDirectory>$(GamePath)</StartWorkingDirectory>
</PropertyGroup>
<!-- Somehow this makes Visual Studio for macOS recognise the previous section. Nobody knows why. -->
<PropertyGroup Condition="'$(RunConfiguration)' == 'Default'" />
</Project>

View File

@ -1,13 +0,0 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!--set properties -->
<PropertyGroup>
<!-- <BUILD_FOR_MOBILE></BUILD_FOR_MOBILE>-->
<BUILD_FOR_MOBILE>GOOGLE</BUILD_FOR_MOBILE>
<!-- <BUILD_FOR_MOBILE>GOOGLE_145</BUILD_FOR_MOBILE>-->
<!-- <BUILD_FOR_MOBILE>AMAZON</BUILD_FOR_MOBILE>-->
<!-- <BUILD_FOR_MOBILE>SAMSUNG</BUILD_FOR_MOBILE>-->
<!-- <BUILD_FOR_MOBILE>GOOGLE_LEGACY</BUILD_FOR_MOBILE>-->
<COMPILE_WITH_PLUGIN>False</COMPILE_WITH_PLUGIN>
<DefineConstants Condition="'$(BUILD_FOR_MOBILE)' == 'GOOGLE'">SMAPI_DEPRECATED;SMAPI_FOR_MOBILE;ANDROID_TARGET_GOOGLE</DefineConstants>
</PropertyGroup>
</Project>

View File

@ -1,77 +0,0 @@
<!--
This MSBuild file copies SMAPI and the bundled mods into the local Stardew Valley folder on build
to simplify testing. This just avoids needing to run the SMAPI installer each time.
This assumes `find-game-folder.targets` has already been imported and validated.
-->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="CopySmapiFiles" AfterTargets="AfterBuild">
<CallTarget Targets="CopySMAPI;CopyDefaultMods" />
</Target>
<Target Name="CopySMAPI" Condition="'$(MSBuildProjectName)' == 'SMAPI'">
<!-- SMAPI -->
<ItemGroup>
<TranslationFiles Include="$(TargetDir)\i18n\*.json" />
</ItemGroup>
<Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)" />
<Copy SourceFiles="$(TargetDir)\$(TargetName).exe" DestinationFolder="$(GamePath)" Condition="$(OS) == 'Windows_NT'" />
<Copy SourceFiles="$(TargetDir)\$(TargetName)" DestinationFolder="$(GamePath)" Condition="$(OS) != 'Windows_NT'" />
<Copy SourceFiles="$(TargetDir)\$(TargetName).xml" DestinationFolder="$(GamePath)" />
<Copy SourceFiles="$(TargetDir)\SMAPI.config.json" DestinationFiles="$(GamePath)\smapi-internal\config.json" />
<Copy SourceFiles="$(TargetDir)\SMAPI.metadata.json" DestinationFiles="$(GamePath)\smapi-internal\metadata.json" />
<Copy SourceFiles="$(TargetDir)\Newtonsoft.Json.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\TMXTile.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\Pintail.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="@(TranslationFiles)" DestinationFolder="$(GamePath)\smapi-internal\i18n" />
<!-- Harmony + dependencies -->
<Copy SourceFiles="$(TargetDir)\0Harmony.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\0Harmony.xml" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\Mono.Cecil.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\Mono.Cecil.Mdb.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\Mono.Cecil.Pdb.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\MonoMod.Common.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<!-- FluentHttpClient + dependencies -->
<Copy SourceFiles="$(TargetDir)\Pathoschild.Http.Client.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\System.Net.Http.Formatting.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<!-- .NET dependencies -->
<Copy SourceFiles="$(TargetDir)\System.Management.dll" DestinationFolder="$(GamePath)\smapi-internal" Condition="$(OS) == 'Windows_NT'" />
<!-- Legacy .NET dependencies (remove in SMAPI 4.0.0) -->
<Copy SourceFiles="$(TargetDir)\System.Configuration.ConfigurationManager.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\System.Runtime.Caching.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\System.Security.Permissions.dll" DestinationFolder="$(GamePath)\smapi-internal" />
</Target>
<!-- .NET metadata files -->
<Target Name="CopyNetMetadata" Condition="'$(MSBuildProjectName)' == 'SMAPI.Installer'" AfterTargets="PostBuildEvent">
<Copy SourceFiles="$(TargetDir)\assets\runtimeconfig.json" DestinationFiles="$(GamePath)\StardewModdingAPI.runtimeconfig.json" />
<Copy SourceFiles="$(TargetDir)\assets\windows-exe-config.xml" DestinationFiles="$(GamePath)\StardewModdingAPI.exe.config" Condition="$(OS) == 'Windows_NT'" />
<Copy SourceFiles="$(GamePath)\Stardew Valley.deps.json" DestinationFiles="$(GamePath)\StardewModdingAPI.deps.json" Condition="!Exists('$(GamePath)\StardewModdingAPI.deps.json')" />
</Target>
<!-- bundled mods -->
<Target Name="CopyDefaultMods" Condition="'$(MSBuildProjectName)' == 'SMAPI.Mods.ConsoleCommands' OR '$(MSBuildProjectName)' == 'SMAPI.Mods.ErrorHandler' OR '$(MSBuildProjectName)' == 'SMAPI.Mods.SaveBackup'">
<ItemGroup>
<TranslationFiles Include="$(TargetDir)\i18n\*.json" />
</ItemGroup>
<Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)\Mods\$(AssemblyName)" />
<Copy SourceFiles="$(TargetDir)\manifest.json" DestinationFolder="$(GamePath)\Mods\$(AssemblyName)" />
<Copy SourceFiles="@(TranslationFiles)" DestinationFolder="$(GamePath)\Mods\$(AssemblyName)\i18n" />
</Target>
<!-- toolkit -->
<Target Name="CopyToolkit" Condition="'$(MSBuildProjectName)' == 'SMAPI.Toolkit'" AfterTargets="PostBuildEvent">
<Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\$(TargetName).xml" DestinationFolder="$(GamePath)\smapi-internal" />
</Target>
<Target Name="CopyToolkitCoreInterfaces" Condition="'$(MSBuildProjectName)' == 'SMAPI.Toolkit.CoreInterfaces'" AfterTargets="PostBuildEvent">
<Copy SourceFiles="$(TargetDir)\$(TargetName).dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\$(TargetName).xml" DestinationFolder="$(GamePath)\smapi-internal" />
</Target>
</Project>

View File

@ -1,66 +0,0 @@
<!--
This MSBuild file detects the Stardew Valley install path if possible, and sets the 'GamePath'
property.
-->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- import developer's custom path (if any) -->
<Import Condition="$(OS) != 'Windows_NT' AND Exists('$(HOME)\stardewvalley.targets')" Project="$(HOME)\stardewvalley.targets" />
<Import Condition="$(OS) == 'Windows_NT' AND Exists('$(USERPROFILE)\stardewvalley.targets')" Project="$(USERPROFILE)\stardewvalley.targets" />
<!-- find game path -->
<Choose>
<When Condition="$(OS) == 'Unix' OR $(OS) == 'OSX'">
<PropertyGroup>
<!-- Linux -->
<GamePath Condition="!Exists('$(GamePath)')">$(HOME)/GOG Games/Stardew Valley/game</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">$(HOME)/.steam/steam/steamapps/common/Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">$(HOME)/.local/share/Steam/steamapps/common/Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">$(HOME)/.var/app/com.valvesoftware.Steam/data/Steam/steamapps/common/Stardew Valley</GamePath>
<!-- macOS (may be 'Unix' or 'OSX') -->
<GamePath Condition="!Exists('$(GamePath)')">/Applications/Stardew Valley.app/Contents/MacOS</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">$(HOME)/Library/Application Support/Steam/steamapps/common/Stardew Valley/Contents/MacOS</GamePath>
</PropertyGroup>
</When>
<When Condition="$(OS) == 'Windows_NT'">
<PropertyGroup>
<!-- registry paths -->
<GamePath Condition="!Exists('$(GamePath)')">$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\GOG.com\Games\1453375253', 'PATH', null, RegistryView.Registry32))</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 413150', 'InstallLocation', null, RegistryView.Registry64, RegistryView.Registry32))</GamePath>
<!-- derive from Steam library path -->
<_SteamLibraryPath>$([MSBuild]::GetRegistryValueFromView('HKEY_CURRENT_USER\SOFTWARE\Valve\Steam', 'SteamPath', null, RegistryView.Registry32))</_SteamLibraryPath>
<GamePath Condition="!Exists('$(GamePath)') AND '$(_SteamLibraryPath)' != ''">$(_SteamLibraryPath)\steamapps\common\Stardew Valley</GamePath>
<!-- GOG paths -->
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files\GalaxyClient\Games\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files\GOG Galaxy\Games\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files\GOG Games\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files (x86)\GalaxyClient\Games\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files (x86)\GOG Galaxy\Games\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files (x86)\GOG Games\Stardew Valley</GamePath>
<!-- Xbox app paths -->
<!--
The Xbox app saves the install path to the registry, but we can't use it here since it
saves the internal readonly path (like C:\Program Files\WindowsApps\Mutable\<package ID>)
instead of the mods-enabled path (like C:\Program Files\ModifiableWindowsApps\Stardew Valley).
Fortunately we can cheat a bit: players can customize the install drive, but they can't
change the install path on the drive.
-->
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files\ModifiableWindowsApps\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">D:\Program Files\ModifiableWindowsApps\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">E:\Program Files\ModifiableWindowsApps\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">F:\Program Files\ModifiableWindowsApps\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">G:\Program Files\ModifiableWindowsApps\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">H:\Program Files\ModifiableWindowsApps\Stardew Valley</GamePath>
<!-- Steam paths -->
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files\Steam\steamapps\common\Stardew Valley</GamePath>
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley</GamePath>
</PropertyGroup>
</When>
</Choose>
</Project>

View File

@ -1,212 +0,0 @@
#!/usr/bin/env bash
#
#
# This is the Bash equivalent of ../windows/prepare-install-package.ps1.
# When making changes, both scripts should be updated.
#
#
##########
## Fetch values
##########
# paths
gamePath="/home/pathoschild/Stardew Valley"
bundleModNames=("ConsoleCommands" "ErrorHandler" "SaveBackup")
# build configuration
buildConfig="Release"
folders=("linux" "macOS" "windows")
declare -A runtimes=(["linux"]="linux-x64" ["macOS"]="osx-x64" ["windows"]="win-x64")
declare -A msBuildPlatformNames=(["linux"]="Unix" ["macOS"]="OSX" ["windows"]="Windows_NT")
# version number
version="$1"
if [ $# -eq 0 ]; then
echo "SMAPI release version (like '4.0.0'):"
read version
fi
##########
## Move to SMAPI root
##########
cd "`dirname "$0"`/../.."
##########
## Clear old build files
##########
echo "Clearing old builds..."
echo "-------------------------------------------------"
for path in bin */**/bin */**/obj; do
echo "$path"
rm -rf $path
done
echo ""
##########
## Compile files
##########
. ${0%/*}/set-smapi-version.sh "$version"
for folder in ${folders[@]}; do
runtime=${runtimes[$folder]}
msbuildPlatformName=${msBuildPlatformNames[$folder]}
echo "Compiling SMAPI for $folder..."
echo "-------------------------------------------------"
dotnet publish src/SMAPI --configuration $buildConfig -v minimal --runtime "$runtime" -p:OS="$msbuildPlatformName" -p:GamePath="$gamePath" -p:CopyToGameFolder="false" --self-contained true
echo ""
echo ""
echo "Compiling installer for $folder..."
echo "-------------------------------------------------"
dotnet publish src/SMAPI.Installer --configuration $buildConfig -v minimal --runtime "$runtime" -p:OS="$msbuildPlatformName" -p:GamePath="$gamePath" -p:CopyToGameFolder="false" -p:PublishTrimmed=True -p:TrimMode=Link --self-contained true
echo ""
echo ""
for modName in ${bundleModNames[@]}; do
echo "Compiling $modName for $folder..."
echo "-------------------------------------------------"
dotnet publish src/SMAPI.Mods.$modName --configuration $buildConfig -v minimal --runtime "$runtime" -p:OS="$msbuildPlatformName" -p:GamePath="$gamePath" -p:CopyToGameFolder="false"
echo ""
echo ""
done
done
##########
## Prepare install package
##########
echo "Preparing install package..."
echo "-------------------------------------------------"
# init paths
installAssets="src/SMAPI.Installer/assets"
packagePath="bin/SMAPI installer"
packageDevPath="bin/SMAPI installer for developers"
# init structure
for folder in ${folders[@]}; do
mkdir "$packagePath/internal/$folder/bundle/smapi-internal" --parents
done
# copy base installer files
for name in "install on Linux.sh" "install on macOS.command" "install on Windows.bat" "README.txt"; do
cp "$installAssets/$name" "$packagePath"
done
# copy per-platform files
for folder in ${folders[@]}; do
runtime=${runtimes[$folder]}
# get paths
smapiBin="src/SMAPI/bin/$buildConfig/$runtime/publish"
internalPath="$packagePath/internal/$folder"
bundlePath="$internalPath/bundle"
# installer files
cp -r "src/SMAPI.Installer/bin/$buildConfig/$runtime/publish"/* "$internalPath"
rm -rf "$internalPath/assets"
# runtime config for SMAPI
# This is identical to the one generated by the build, except that the min runtime version is
# set to 5.0.0 (instead of whatever version it was built with) and rollForward is set to latestMinor instead of
# minor.
cp "$installAssets/runtimeconfig.json" "$bundlePath/StardewModdingAPI.runtimeconfig.json"
# installer DLL config
if [ $folder == "windows" ]; then
cp "$installAssets/windows-exe-config.xml" "$packagePath/internal/windows/install.exe.config"
fi
# bundle root files
for name in "StardewModdingAPI" "StardewModdingAPI.dll" "StardewModdingAPI.xml" "steam_appid.txt"; do
if [ $name == "StardewModdingAPI" ] && [ $folder == "windows" ]; then
name="$name.exe"
fi
cp "$smapiBin/$name" "$bundlePath"
done
# bundle i18n
cp -r "$smapiBin/i18n" "$bundlePath/smapi-internal"
# bundle smapi-internal
for name in "0Harmony.dll" "0Harmony.xml" "Mono.Cecil.dll" "Mono.Cecil.Mdb.dll" "Mono.Cecil.Pdb.dll" "MonoMod.Common.dll" "Newtonsoft.Json.dll" "Pathoschild.Http.Client.dll" "Pintail.dll" "TMXTile.dll" "SMAPI.Toolkit.dll" "SMAPI.Toolkit.xml" "SMAPI.Toolkit.CoreInterfaces.dll" "SMAPI.Toolkit.CoreInterfaces.xml" "System.Net.Http.Formatting.dll"; do
cp "$smapiBin/$name" "$bundlePath/smapi-internal"
done
cp "$smapiBin/SMAPI.config.json" "$bundlePath/smapi-internal/config.json"
cp "$smapiBin/SMAPI.metadata.json" "$bundlePath/smapi-internal/metadata.json"
if [ $folder == "linux" ] || [ $folder == "macOS" ]; then
cp "$installAssets/unix-launcher.sh" "$bundlePath"
else
cp "$installAssets/windows-exe-config.xml" "$bundlePath/StardewModdingAPI.exe.config"
fi
# copy .NET dependencies
if [ $folder == "windows" ]; then
cp "$smapiBin/System.Management.dll" "$bundlePath/smapi-internal"
fi
# copy legacy .NET dependencies (remove in SMAPI 4.0.0)
cp "$smapiBin/System.Configuration.ConfigurationManager.dll" "$bundlePath/smapi-internal"
cp "$smapiBin/System.Runtime.Caching.dll" "$bundlePath/smapi-internal"
cp "$smapiBin/System.Security.Permissions.dll" "$bundlePath/smapi-internal"
# copy bundled mods
for modName in ${bundleModNames[@]}; do
fromPath="src/SMAPI.Mods.$modName/bin/$buildConfig/$runtime/publish"
targetPath="$bundlePath/Mods/$modName"
mkdir "$targetPath" --parents
cp "$fromPath/$modName.dll" "$targetPath"
cp "$fromPath/manifest.json" "$targetPath"
if [ -d "$fromPath/i18n" ]; then
cp -r "$fromPath/i18n" "$targetPath"
fi
done
done
# mark scripts executable
for path in "install on Linux.sh" "install on macOS.command" "bundle/unix-launcher.sh"; do
if [ -f "$packagePath/$path" ]; then
chmod 755 "$packagePath/$path"
fi
done
# split into main + for-dev folders
cp -r "$packagePath" "$packageDevPath"
for folder in ${folders[@]}; do
# disable developer mode in main package
sed --in-place --expression="s/\"DeveloperMode\": true/\"DeveloperMode\": false/" "$packagePath/internal/$folder/bundle/smapi-internal/config.json"
# convert bundle folder into final 'install.dat' files
for path in "$packagePath/internal/$folder" "$packageDevPath/internal/$folder"; do
pushd "$path/bundle" > /dev/null
zip "install.dat" * --recurse-paths --quiet
popd > /dev/null
mv "$path/bundle/install.dat" "$path/install.dat"
rm -rf "$path/bundle"
done
done
##########
## Create release zips
##########
# rename folders
mv "$packagePath" "bin/SMAPI $version installer"
mv "$packageDevPath" "bin/SMAPI $version installer for developers"
# package files
pushd bin > /dev/null
zip -9 "SMAPI $version installer.zip" "SMAPI $version installer" --recurse-paths --quiet
zip -9 "SMAPI $version installer for developers.zip" "SMAPI $version installer for developers" --recurse-paths --quiet
popd > /dev/null
echo ""
echo "Done! Package created in $(pwd)/bin"

View File

@ -1,26 +0,0 @@
#!/usr/bin/env bash
#
#
# This is the Bash equivalent of ../windows/set-smapi-version.ps1.
# When making changes, both scripts should be updated.
#
#
# get version number
version="$1"
if [ $# -eq 0 ]; then
echo "SMAPI release version (like '4.0.0'):"
read version
fi
# move to SMAPI root
cd "`dirname "$0"`/../.."
# apply changes
sed "s/<Version>.+<\/Version>/<Version>$version<\/Version>/" "build/common.targets" --in-place --regexp-extended
sed "s/RawApiVersion = \".+?\";/RawApiVersion = \"$version\";/" "src/SMAPI/Constants.cs" --in-place --regexp-extended
for modName in "ConsoleCommands" "ErrorHandler" "SaveBackup"; do
sed "s/\"(Version|MinimumApiVersion)\": \".+?\"/\"\1\": \"$version\"/g" "src/SMAPI.Mods.$modName/manifest.json" --in-place --regexp-extended
done

View File

@ -1,67 +0,0 @@
#!/usr/bin/env bash
##########
## Read config
##########
# get SMAPI version
version="$1"
if [ $# -eq 0 ]; then
echo "SMAPI release version (like '4.0.0'):"
read version
fi
# get Windows bin path
windowsBinPath="$2"
if [ $# -le 1 ]; then
echo "Windows compiled bin path:"
read windowsBinPath
fi
# installer internal folders
buildFolders=("linux" "macOS" "windows")
##########
## Finalize release package
##########
for folderName in "SMAPI $version installer" "SMAPI $version installer for developers"; do
# move files to Linux filesystem
echo "Preparing $folderName.zip..."
echo "-------------------------------------------------"
echo "copying '$windowsBinPath/$folderName' to Linux filesystem..."
cp -r "$windowsBinPath/$folderName" .
# fix permissions
echo "fixing permissions..."
find "$folderName" -type d -exec chmod 755 {} \;
find "$folderName" -type f -exec chmod 644 {} \;
find "$folderName" -name "*.sh" -exec chmod 755 {} \;
find "$folderName" -name "*.command" -exec chmod 755 {} \;
find "$folderName" -name "SMAPI.Installer" -exec chmod 755 {} \;
find "$folderName" -name "StardewModdingAPI" -exec chmod 755 {} \;
# convert bundle folder into final 'install.dat' files
for build in ${buildFolders[@]}; do
echo "packaging $folderName/internal/$build/install.dat..."
pushd "$folderName/internal/$build/bundle" > /dev/null
zip "install.dat" * --recurse-paths --quiet
mv install.dat ../
popd > /dev/null
rm -rf "$folderName/internal/$build/bundle"
done
# zip installer
echo "packaging installer..."
zip -9 "$folderName.zip" "$folderName" --recurse-paths --quiet
# move zip back to Windows bin path
echo "moving release zip to $windowsBinPath/$folderName.zip..."
mv "$folderName.zip" "$windowsBinPath"
rm -rf "$folderName"
echo ""
echo ""
done
echo "Done!"

View File

@ -1,11 +0,0 @@
function In-Place-Regex {
param (
[Parameter(Mandatory)][string]$Path,
[Parameter(Mandatory)][string]$Search,
[Parameter(Mandatory)][string]$Replace
)
$content = (Get-Content "$Path" -Encoding UTF8)
$content = ($content -replace "$Search", "$Replace")
[System.IO.File]::WriteAllLines((Get-Item "$Path").FullName, $content)
}

View File

@ -1,241 +0,0 @@
#
#
# This is the PowerShell equivalent of ../unix/prepare-install-package.sh, *except* that it doesn't
# set Linux permissions, create the install.dat files, or create the final zip (unless you specify
# --windows-only). Due to limitations in PowerShell, the final changes are handled by the
# windows/finalize-install-package.sh file in WSL.
#
# When making changes, make sure to update ../unix/prepare-install-package.ps1 too.
#
#
. "$PSScriptRoot/lib/in-place-regex.ps1"
##########
## Fetch values
##########
# paths
$gamePath = "C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley"
$bundleModNames = "ConsoleCommands", "ErrorHandler", "SaveBackup"
# build configuration
$buildConfig = "Release"
$folders = "linux", "macOS", "windows"
$runtimes = @{ linux = "linux-x64"; macOS = "osx-x64"; windows = "win-x64" }
$msBuildPlatformNames = @{ linux = "Unix"; macOS = "OSX"; windows = "Windows_NT" }
# version number
$version = $args[0]
if (!$version) {
$version = Read-Host "SMAPI release version (like '4.0.0')"
}
# Windows-only build
$windowsOnly = $false
foreach ($arg in $args) {
if ($arg -eq "--windows-only") {
$windowsOnly = $true
$folders = "windows"
$runtimes = @{ windows = "win-x64" }
$msBuildPlatformNames = @{ windows = "Windows_NT" }
}
}
##########
## Move to SMAPI root
##########
cd "$PSScriptRoot/../.."
##########
## Clear old build files
##########
echo "Clearing old builds..."
echo "-------------------------------------------------"
foreach ($path in (dir -Recurse -Include ('bin', 'obj'))) {
echo "$path"
rm -Recurse -Force "$path"
}
echo ""
##########
## Compile files
##########
. "$PSScriptRoot/set-smapi-version.ps1" "$version"
foreach ($folder in $folders) {
$runtime = $runtimes[$folder]
$msbuildPlatformName = $msBuildPlatformNames[$folder]
echo "Compiling SMAPI for $folder..."
echo "-------------------------------------------------"
dotnet publish src/SMAPI --configuration $buildConfig -v minimal --runtime "$runtime" -p:OS="$msbuildPlatformName" -p:GamePath="$gamePath" -p:CopyToGameFolder="false" --self-contained true
echo ""
echo ""
echo "Compiling installer for $folder..."
echo "-------------------------------------------------"
dotnet publish src/SMAPI.Installer --configuration $buildConfig -v minimal --runtime "$runtime" -p:OS="$msbuildPlatformName" -p:GamePath="$gamePath" -p:CopyToGameFolder="false" -p:PublishTrimmed=True -p:TrimMode=Link --self-contained true
echo ""
echo ""
foreach ($modName in $bundleModNames) {
echo "Compiling $modName for $folder..."
echo "-------------------------------------------------"
dotnet publish src/SMAPI.Mods.$modName --configuration $buildConfig -v minimal --runtime "$runtime" -p:OS="$msbuildPlatformName" -p:GamePath="$gamePath" -p:CopyToGameFolder="false"
echo ""
echo ""
}
}
##########
## Prepare install package
##########
echo "Preparing install package..."
echo "----------------------------"
# init paths
$installAssets = "src/SMAPI.Installer/assets"
$packagePath = "bin/SMAPI installer"
$packageDevPath = "bin/SMAPI installer for developers"
# init structure
foreach ($folder in $folders) {
mkdir "$packagePath/internal/$folder/bundle/smapi-internal" > $null
}
# copy base installer files
foreach ($name in @("install on Linux.sh", "install on macOS.command", "install on Windows.bat", "README.txt")) {
if ($windowsOnly -and ($name -eq "install on Linux.sh" -or $name -eq "install on macOS.command")) {
continue;
}
cp "$installAssets/$name" "$packagePath"
}
# copy per-platform files
foreach ($folder in $folders) {
$runtime = $runtimes[$folder]
# get paths
$smapiBin = "src/SMAPI/bin/$buildConfig/$runtime/publish"
$internalPath = "$packagePath/internal/$folder"
$bundlePath = "$internalPath/bundle"
# installer files
cp "src/SMAPI.Installer/bin/$buildConfig/$runtime/publish/*" "$internalPath" -Recurse
rm -Recurse -Force "$internalPath/assets"
# runtime config for SMAPI
# This is identical to the one generated by the build, except that the min runtime version is
# set to 5.0.0 (instead of whatever version it was built with) and rollForward is set to latestMinor instead of
# minor.
cp "$installAssets/runtimeconfig.json" "$bundlePath/StardewModdingAPI.runtimeconfig.json"
# installer DLL config
if ($folder -eq "windows") {
cp "$installAssets/windows-exe-config.xml" "$packagePath/internal/windows/install.exe.config"
}
# bundle root files
foreach ($name in @("StardewModdingAPI", "StardewModdingAPI.dll", "StardewModdingAPI.xml", "steam_appid.txt")) {
if ($name -eq "StardewModdingAPI" -and $folder -eq "windows") {
$name = "$name.exe"
}
cp "$smapiBin/$name" "$bundlePath"
}
# bundle i18n
cp -Recurse "$smapiBin/i18n" "$bundlePath/smapi-internal"
# bundle smapi-internal
foreach ($name in @("0Harmony.dll", "0Harmony.xml", "Mono.Cecil.dll", "Mono.Cecil.Mdb.dll", "Mono.Cecil.Pdb.dll", "MonoMod.Common.dll", "Newtonsoft.Json.dll", "Pathoschild.Http.Client.dll", "Pintail.dll", "TMXTile.dll", "SMAPI.Toolkit.dll", "SMAPI.Toolkit.xml", "SMAPI.Toolkit.CoreInterfaces.dll", "SMAPI.Toolkit.CoreInterfaces.xml", "System.Net.Http.Formatting.dll")) {
cp "$smapiBin/$name" "$bundlePath/smapi-internal"
}
if ($folder -eq "windows") {
cp "$smapiBin/VdfConverter.dll" "$bundlePath/smapi-internal"
}
cp "$smapiBin/SMAPI.config.json" "$bundlePath/smapi-internal/config.json"
cp "$smapiBin/SMAPI.metadata.json" "$bundlePath/smapi-internal/metadata.json"
if ($folder -eq "linux" -or $folder -eq "macOS") {
cp "$installAssets/unix-launcher.sh" "$bundlePath"
}
else {
cp "$installAssets/windows-exe-config.xml" "$bundlePath/StardewModdingAPI.exe.config"
}
# copy .NET dependencies
if ($folder -eq "windows") {
cp "$smapiBin/System.Management.dll" "$bundlePath/smapi-internal"
}
# copy legacy .NET dependencies (remove in SMAPI 4.0.0)
cp "$smapiBin/System.Configuration.ConfigurationManager.dll" "$bundlePath/smapi-internal"
cp "$smapiBin/System.Runtime.Caching.dll" "$bundlePath/smapi-internal"
cp "$smapiBin/System.Security.Permissions.dll" "$bundlePath/smapi-internal"
# copy bundled mods
foreach ($modName in $bundleModNames) {
$fromPath = "src/SMAPI.Mods.$modName/bin/$buildConfig/$runtime/publish"
$targetPath = "$bundlePath/Mods/$modName"
mkdir "$targetPath" > $null
cp "$fromPath/$modName.dll" "$targetPath"
cp "$fromPath/manifest.json" "$targetPath"
if (Test-Path "$fromPath/i18n" -PathType Container) {
cp -Recurse "$fromPath/i18n" "$targetPath"
}
}
}
# DISABLED: will be handled by Linux script
# mark scripts executable
#ForEach ($path in @("install on Linux.sh", "install on macOS.command", "bundle/unix-launcher.sh")) {
# if (Test-Path "$packagePath/$path" -PathType Leaf) {
# chmod 755 "$packagePath/$path"
# }
#}
# split into main + for-dev folders
cp -Recurse "$packagePath" "$packageDevPath"
foreach ($folder in $folders) {
# disable developer mode in main package
In-Place-Regex -Path "$packagePath/internal/$folder/bundle/smapi-internal/config.json" -Search "`"DeveloperMode`": true" -Replace "`"DeveloperMode`": false"
# convert bundle folder into final 'install.dat' files
if ($windowsOnly)
{
foreach ($path in @("$packagePath/internal/$folder", "$packageDevPath/internal/$folder"))
{
Compress-Archive -Path "$path/bundle/*" -CompressionLevel Optimal -DestinationPath "$path/install.zip"
mv "$path/install.zip" "$path/install.dat"
rm -Recurse -Force "$path/bundle"
}
}
}
###########
### Create release zips
###########
# rename folders
mv "$packagePath" "bin/SMAPI $version installer"
mv "$packageDevPath" "bin/SMAPI $version installer for developers"
# package files
if ($windowsOnly)
{
Compress-Archive -Path "bin/SMAPI $version installer" -DestinationPath "bin/SMAPI $version installer.zip" -CompressionLevel Optimal
Compress-Archive -Path "bin/SMAPI $version installer for developers" -DestinationPath "bin/SMAPI $version installer for developers.zip" -CompressionLevel Optimal
}
echo ""
echo "Done! See docs/technical/smapi.md to create the release zips."

View File

@ -1,25 +0,0 @@
#
#
# This is the PowerShell equivalent of ../unix/set-smapi-version.sh.
# When making changes, both scripts should be updated.
#
#
. "$PSScriptRoot\lib\in-place-regex.ps1"
# get version number
$version=$args[0]
if (!$version) {
$version = Read-Host "SMAPI release version (like '4.0.0')"
}
# move to SMAPI root
cd "$PSScriptRoot/../.."
# apply changes
In-Place-Regex -Path "build/common.targets" -Search "<Version>.+</Version>" -Replace "<Version>$version</Version>"
In-Place-Regex -Path "src/SMAPI/Constants.cs" -Search "RawApiVersion = `".+?`";" -Replace "RawApiVersion = `"$version`";"
ForEach ($modName in "ConsoleCommands","ErrorHandler","SaveBackup") {
In-Place-Regex -Path "src/SMAPI.Mods.$modName/manifest.json" -Search "`"(Version|MinimumApiVersion)`": `".+?`"" -Replace "`"`$1`": `"$version`""
}

View File

@ -1,79 +0,0 @@
**SMAPI** is an open-source modding framework and API for [Stardew Valley](https://stardewvalley.net/)
that lets you play the game with mods. It's safely installed alongside the game's executable, and
doesn't change any of your game files. It serves seven main purposes:
1. **Load mods into the game.**
_SMAPI loads mods when the game is starting up so they can interact with it. (Code mods aren't
possible without SMAPI to load them.)_
2. **Provide APIs and events for mods.**
_SMAPI provides APIs and events which let mods interact with the game in ways they otherwise
couldn't._
3. **Rewrite mods for compatibility.**
_SMAPI rewrites mods' compiled code before loading them so they work on Linux/macOS/Windows
without the mods needing to handle differences between the Linux/macOS and Windows versions of
the game. In some cases it also rewrites code broken by a game update so the mod doesn't break._
5. **Intercept errors and automatically fix saves.**
_SMAPI intercepts errors, shows the error info in the SMAPI console, and in most cases
automatically recovers the game. That prevents mods from crashing the game, and makes it
possible to troubleshoot errors in the game itself that would otherwise show a generic 'program
has stopped working' type of message._
_SMAPI also automatically fixes save data in some cases when a load would crash, e.g. due to a
custom location or NPC mod that was removed._
6. **Provide update checks.**
_SMAPI automatically checks for new versions of your installed mods, and notifies you when any
are available._
7. **Provide compatibility checks.**
_SMAPI automatically detects outdated or broken code in mods, and safely disables them before
they cause problems._
8. **Back up your save files.**
_SMAPI automatically creates a daily backup of your saves and keeps ten backups (via the bundled
Save Backup mod), in case something goes wrong._
## Documentation
Have questions? Come [ask the community](https://smapi.io/community) to get help from SMAPI
developers and other modders!
### For players
* [Player guide](https://stardewvalleywiki.com/Modding:Player_Guide)
### For modders
* [Modding documentation](https://smapi.io/docs)
* [Mod build configuration](technical/mod-package.md)
* [Release notes](release-notes.md)
### For SMAPI developers
* [Technical docs](technical/smapi.md)
## Translating SMAPI
SMAPI rarely shows text in-game, so it only has a few translations. Contributions are welcome! See
[Modding:Translations](https://stardewvalleywiki.com/Modding:Translations) on the wiki for help
contributing translations.
locale | status
----------- | :----------------
default | ✓ [fully translated](../src/SMAPI/i18n/default.json)
Chinese | ✓ [fully translated](../src/SMAPI/i18n/zh.json)
French | ✓ [fully translated](../src/SMAPI/i18n/fr.json)
German | ✓ [fully translated](../src/SMAPI/i18n/de.json)
Hungarian | ✓ [fully translated](../src/SMAPI/i18n/hu.json)
Italian | ✓ [fully translated](../src/SMAPI/i18n/it.json)
Japanese | ✓ [fully translated](../src/SMAPI/i18n/ja.json)
Korean | ✓ [fully translated](../src/SMAPI/i18n/ko.json)
[Polish] | ✓ [fully translated](../src/SMAPI/i18n/pl.json)
Portuguese | ✓ [fully translated](../src/SMAPI/i18n/pt.json)
Russian | ✓ [fully translated](../src/SMAPI/i18n/ru.json)
Spanish | ✓ [fully translated](../src/SMAPI/i18n/es.json)
[Thai] | ✓ [fully translated](../src/SMAPI/i18n/th.json)
Turkish | ✓ [fully translated](../src/SMAPI/i18n/tr.json)
[Ukrainian] | ✓ [fully translated](../src/SMAPI/i18n/uk.json)
[Polish]: https://www.nexusmods.com/stardewvalley/mods/3616
[Thai]: https://www.nexusmods.com/stardewvalley/mods/7052
[Ukrainian]: https://www.nexusmods.com/stardewvalley/mods/8427

View File

@ -1 +0,0 @@
[Documentation moved](technical/mod-package.md).

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,618 +0,0 @@
&larr; [SMAPI](../README.md)
The **mod build package** is an open-source NuGet package which automates the MSBuild configuration
for SMAPI mods and related tools. The package is fully compatible with Linux, macOS, and Windows.
## Contents
* [Use](#use)
* [Features](#features)
* [Configure](#configure)
* [Code warnings](#code-warnings)
* [FAQs](#faqs)
* [How do I set the game path?](#custom-game-path)
* [How do I change which files are included in the mod deploy/zip?](#how-do-i-change-which-files-are-included-in-the-mod-deployzip)
* [Can I use the package for non-mod projects?](#can-i-use-the-package-for-non-mod-projects)
* [For SMAPI developers](#for-smapi-developers)
* [Release notes](#release-notes)
## Use
1. Create an empty library project.
2. Reference the [`Pathoschild.Stardew.ModBuildConfig` NuGet package](https://www.nuget.org/packages/Pathoschild.Stardew.ModBuildConfig).
3. [Write your code](https://stardewvalleywiki.com/Modding:Creating_a_SMAPI_mod).
4. Compile on any platform.
5. Run the game to play with your mod.
## Features
The package includes several features to simplify mod development (see [_configure_](#configure) to
change how these work):
* **Detect game path:**
The package automatically finds your game folder by scanning the default install paths and
Windows registry. It adds two MSBuild properties for use in your `.csproj` file if needed:
`$(GamePath)` and `$(GameModsPath)`.
* **Add assembly references:**
The package adds assembly references to MonoGame, SMAPI, Stardew Valley, and xTile. It
automatically adjusts depending on which OS you're compiling it on. If you use
[Harmony](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Harmony), it can optionally add
a reference to that too.
* **Copy files into the `Mods` folder:**
The package automatically copies your mod's DLL and PDB files, `manifest.json`, [`i18n`
files](https://stardewvalleywiki.com/Modding:Translations) (if any), and the `assets` folder (if
any) into the `Mods` folder when you rebuild the code, with a subfolder matching the mod's project
name. That lets you try the mod in-game right after building it.
* **Create release zip:**
The package adds a zip file in your project's `bin` folder when you rebuild the code, in the
format recommended for uploading to mod sites like Nexus Mods. This includes the same files as
the previous feature.
* **Launch or debug mod:**
On Windows only, the package configures Visual Studio so you can launch the game and attach a
debugger using _Debug > Start Debugging_ or _Debug > Start Without Debugging_. This lets you [set
breakpoints](https://docs.microsoft.com/en-us/visualstudio/debugger/using-breakpoints?view=vs-2019)
in your code while the game is running, or [make simple changes to the mod code without needing to
restart the game](https://docs.microsoft.com/en-us/visualstudio/debugger/edit-and-continue?view=vs-2019).
This is disabled on Linux/macOS due to limitations with the Mono wrapper.
* **Preconfigure common settings:**
The package automatically enables `.pdb` files (so error logs show line numbers to simplify
debugging), and enables support for the simplified SDK-style `.csproj` format.
* **Add code warnings:**
The package runs code analysis on your mod and raises warnings for some common errors or
pitfalls. See [_code warnings_](#code-warnings) for more info.
## Configure
### How to set options
You can configure the package by setting build properties, which are essentially tags like this:
```xml
<PropertyGroup>
<ModFolderName>CustomModName</ModFolderName>
<EnableModDeploy>false</EnableModDeploy>
</PropertyGroup>
```
There are two places you can put them:
* **Global properties** apply to every mod project you open on your computer. That's recommended
for properties you want to set for all mods (e.g. a custom game path). Here's where to put them:
1. Open the home folder on your computer (see instructions for
[Linux](https://superuser.com/questions/409218/where-is-my-users-home-folder-in-ubuntu),
[macOS](https://www.cnet.com/how-to/how-to-find-your-macs-home-folder-and-add-it-to-finder/),
or [Windows](https://www.computerhope.com/issues/ch000109.htm)).
2. Create a `stardewvalley.targets` file with this content:
```xml
<Project>
<PropertyGroup>
</PropertyGroup>
</Project>
```
3. Add the properties between the `<PropertyGroup>` and `</PropertyGroup>`.
* **Project properties** apply to a specific project. This is mainly useful for mod-specific
options like the mod name. Here's where to put them:
1. Open the folder containing your mod's source code.
2. Open the `.csproj` file in a text editor (Notepad is fine).
3. Add the properties between the first `<PropertyGroup>` and `</PropertyGroup>` tags you find.
**Note:** you can't use a property before it's defined. That mainly means that when setting
`GameModsPath`, you'll need to either specify `GamePath` manually or put the full path in
`GameModsPath`.
### Available properties
These are the options you can set:
<ul>
<li>Game properties:
<table>
<tr>
<th>property</th>
<th>effect</th>
</tr>
<tr>
<td><code>GamePath</code></td>
<td>
The absolute path to the Stardew Valley folder. This is auto-detected, so you usually don't need to
change it.
</td>
</tr>
<tr>
<td><code>GameModsPath</code></td>
<td>
The absolute path to the folder containing the game's installed mods (defaults to
`$(GamePath)/Mods`), used when deploying the mod files.
</td>
</tr>
<tr>
</table>
</li>
<li>Mod build properties:
<table>
<tr>
<th>property</th>
<th>effect</th>
</tr>
<tr>
<td><code>EnableHarmony</code></td>
<td>
Whether to add a reference to [Harmony](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Harmony)
(default `false`). This is only needed if you use Harmony.
</td>
</tr>
<tr>
<td><code>EnableModDeploy</code></td>
<td>
Whether to copy the mod files into your game's `Mods` folder (default `true`).
</td>
</tr>
<tr>
<td><code>EnableModZip</code></td>
<td>
Whether to create a release-ready `.zip` file in the mod project's `bin` folder (default `true`).
</td>
</tr>
<tr>
<td><code>ModFolderName</code></td>
<td>
The mod name for its folder under `Mods` and its release zip (defaults to the project name).
</td>
</tr>
<tr>
<td><code>ModZipPath</code></td>
<td>
The folder path where the release zip is created (defaults to the project's `bin` folder).
</td>
</tr>
</table>
</li>
<li>Specialized properties:
<table>
<tr>
<th>property</th>
<th>effect</th>
</tr>
<tr>
<td><code>BundleExtraAssemblies</code></td>
<td>
**Most mods should not change this option.**
By default (when this is _not_ enabled), only the mod files [normally considered part of the
mod](#Features) will be added to the release `.zip` and copied into the `Mods` folder (i.e.
"deployed"). That includes the assembly files (`*.dll`, `*.pdb`, and `*.xml`) for your mod project,
but any other DLLs won't be deployed.
Enabling this option will add _all_ dependencies to the build output, then deploy _some_ of them
depending on the comma-separated value(s) you set:
<table>
<tr>
<th>option</th>
<th>result</th>
</tr>
<tr>
<td><code>ThirdParty</code></td>
<td>
Assembly files which don't match any other category.
</td>
</tr>
<tr>
<td><code>System</code></td>
<td>
Assembly files whose names start with `Microsoft.*` or `System.*`.
</td>
</tr>
<tr>
<td><code>Game</code></td>
<td>
Assembly files which are part of MonoGame, SMAPI, or Stardew Valley.
</td>
</tr>
<tr>
<td><code>All</code></td>
<td>
Equivalent to `System, Game, ThirdParty`.
</td>
</tr>
</table>
Most mods should omit the option. Some mods may need `ThirdParty` if they bundle third-party DLLs
with their mod. The other options are mainly useful for unit tests.
When enabling this option, you should **manually review which files get deployed** and use the
`IgnoreModFilePaths` or `IgnoreModFilePatterns` options to exclude files as needed.
</td>
</tr>
<tr>
<td><code>EnableGameDebugging</code></td>
<td>
Whether to configure the project so you can launch or debug the game through the _Debug_ menu in
Visual Studio (default `true`). There's usually no reason to change this, unless it's a unit test
project.
</td>
</tr>
<tr>
<td><code>IgnoreModFilePaths</code></td>
<td>
A comma-delimited list of literal file paths to ignore, relative to the mod's `bin` folder. Paths
are case-sensitive, but path delimiters are normalized automatically. For example, this ignores a
set of tilesheets:
```xml
<IgnoreModFilePaths>assets/paths.png, assets/springobjects.png</IgnoreModFilePaths>
```
</td>
</tr>
<tr>
<td><code>IgnoreModFilePatterns</code></td>
<td>
A comma-delimited list of regex patterns matching files to ignore when deploying or zipping the mod
files (default empty). For crossplatform compatibility, you should replace path delimiters with `[/\\]`.
For example, this excludes all `.txt` and `.pdf` files, as well as the `assets/paths.png` file:
```xml
<IgnoreModFilePatterns>\.txt$, \.pdf$, assets[/\\]paths.png</IgnoreModFilePatterns>
```
</td>
</tr>
</table>
</li>
</ul>
## Code warnings
### Overview
The NuGet package adds code warnings in Visual Studio specific to Stardew Valley. For example:
![](screenshots/code-analyzer-example.png)
You can [hide the warnings](https://visualstudiomagazine.com/articles/2017/09/01/hide-compiler-warnings.aspx)
if needed using the warning ID (shown under 'code' in the Error List).
See below for help with specific warnings.
### Avoid implicit net field cast
Warning text:
> This implicitly converts '{{expression}}' from {{net type}} to {{other type}}, but
> {{net type}} has unintuitive implicit conversion rules. Consider comparing against the actual
> value instead to avoid bugs.
Stardew Valley uses net types (like `NetBool` and `NetInt`) to handle multiplayer sync. These types
can implicitly convert to their equivalent normal values (like `bool x = new NetBool()`), but their
conversion rules are unintuitive and error-prone. For example,
`item?.category == null && item?.category != null` can both be true at once, and
`building.indoors != null` can be true for a null value.
Suggested fix:
* Some net fields have an equivalent non-net property like `monster.Health` (`int`) instead of
`monster.health` (`NetInt`). The package will add a separate [AvoidNetField](#avoid-net-field) warning for
these. Use the suggested property instead.
* For a reference type (i.e. one that can contain `null`), you can use the `.Value` property:
```c#
if (building.indoors.Value == null)
```
Or convert the value before comparison:
```c#
GameLocation indoors = building.indoors;
if(indoors == null)
// ...
```
* For a value type (i.e. one that can't contain `null`), check if the object is null (if applicable)
and compare with `.Value`:
```cs
if (item != null && item.category.Value == 0)
```
### Avoid net field
Warning text:
> '{{expression}}' is a {{net type}} field; consider using the {{property name}} property instead.
Your code accesses a net field, which has some unusual behavior (see [AvoidImplicitNetFieldCast](#avoid-implicit-net-field-cast)).
This field has an equivalent non-net property that avoids those issues.
Suggested fix: access the suggested property name instead.
### Avoid obsolete field
Warning text:
> The '{{old field}}' field is obsolete and should be replaced with '{{new field}}'.
Your code accesses a field which is obsolete or no longer works. Use the suggested field instead.
### Wrong processor architecture
Warning text:
> The target platform should be set to 'Any CPU' for compatibility with both 32-bit and 64-bit
> versions of Stardew Valley (currently set to '{{current platform}}').
Mods can be used in either 32-bit or 64-bit mode. Your project's target platform isn't set to the
default 'Any CPU', so it won't work in both. You can fix it by [setting the target platform to
'Any CPU'](https://docs.microsoft.com/en-ca/visualstudio/ide/how-to-configure-projects-to-target-platforms).
## FAQs
### How do I set the game path?<span id="custom-game-path"></span>
The package detects where your game is installed automatically, so you usually don't need to set it
manually. If it can't find your game or you have multiple installs, you can specify the path
yourself.
To do that:
1. Get the full folder path containing the Stardew Valley executable.
2. See [_configure_](#configure) to add this property:
```xml
<PropertyGroup>
<GamePath>PATH_HERE</GamePath>
</PropertyGroup>
```
3. Replace `PATH_HERE` with your game's folder path (don't add quotes).
The configuration will check your custom path first, then fall back to the default paths (so it'll
still compile on a different computer).
### How do I change which files are included in the mod deploy/zip?
* For normal files, you can [add/remove them in the build output](https://stackoverflow.com/a/10828462/262123).
* For assembly files (`*.dll`, `*.exe`, `*.pdb`, or `*.xml`), see the
[`BundleExtraAssemblies` option](#configure).
* To exclude a file which the package copies by default, see the [`IgnoreModFilePaths` or
`IgnoreModFilePatterns` options](#configure).
### Can I use the package for non-mod projects?
Yep, this works in unit tests and framework projects too. Just disable the mod-related package
features (see [_configure_](#configure)):
```xml
<EnableGameDebugging>false</EnableGameDebugging>
<EnableModDeploy>false</EnableModDeploy>
<EnableModZip>false</EnableModZip>
```
To copy referenced DLLs into your build output for unit tests, add this too:
```xml
<BundleExtraAssemblies>All</BundleExtraAssemblies>
```
## For SMAPI developers
The mod build package consists of three projects:
project | purpose
------------------------------------------------- | ----------------
`StardewModdingAPI.ModBuildConfig` | Configures the build (references, deploying the mod files, setting up debugging, etc).
`StardewModdingAPI.ModBuildConfig.Analyzer` | Adds C# analyzers which show code warnings in Visual Studio.
`StardewModdingAPI.ModBuildConfig.Analyzer.Tests` | Unit tests for the C# analyzers.
The NuGet package is generated automatically in `StardewModdingAPI.ModBuildConfig`'s `bin` folder
when you compile it.
## Release notes
## 4.1.1
Released 24 June 2023 for SMAPI 3.13.0 or later.
* Replaced `.pdb` files with embedded symbols by default. This fixes logged errors not having line numbers on Linux/macOS.
### 4.1.0
Released 08 January 2023 for SMAPI 3.13.0 or later.
* Added `manifest.json` format validation on build (thanks to tylergibbs2!).
* Fixed game DLLs not excluded from the release zip when they're referenced explicitly but `BundleExtraAssemblies` isn't set.
### 4.0.2
Released 09 October 2022 for SMAPI 3.13.0 or later.
* Switched to the newer crossplatform `portable` debug symbols (thanks to lanturnalis!).
* Fixed `BundleExtraAssemblies` option being partly case-sensitive.
* Fixed `BundleExtraAssemblies` not applying `All` value to game assemblies.
### 4.0.1
Released 14 April 2022 for SMAPI 3.13.0 or later.
* Added detection for Xbox app game folders.
* Fixed "_conflicts between different versions of Microsoft.Win32.Registry_" warnings in recent SMAPI versions.
* Internal refactoring.
### 4.0.0
Released 30 November 2021 for SMAPI 3.13.0 or later.
* Updated for Stardew Valley 1.5.5 and SMAPI 3.13.0. (Older versions are no longer supported.)
* Added `IgnoreModFilePaths` option to ignore literal paths.
* Added `BundleExtraAssemblies` option to copy bundled DLLs into the mod zip/folder.
* Removed the `GameExecutableName` and `GameFramework` options (since they now have the same value
on all platforms).
* Removed the `CopyModReferencesToBuildOutput` option (superseded by `BundleExtraAssemblies`).
* Improved analyzer performance by enabling parallel execution.
**Migration guide for mod authors:**
1. See [_migrate to 64-bit_](https://stardewvalleywiki.com/Modding:Migrate_to_64-bit_on_Windows) and
[_migrate to Stardew Valley 1.5.5_](https://stardewvalleywiki.com/Modding:Migrate_to_Stardew_Valley_1.5.5).
2. Possible changes in your `.csproj` or `.targets` files:
* Replace `$(GameExecutableName)` with `Stardew Valley`.
* Replace `$(GameFramework)` with `MonoGame` and remove any XNA Framework-specific logic.
* Replace `<CopyModReferencesToBuildOutput>true</CopyModReferencesToBuildOutput>` with
`<BundleExtraAssemblies>Game</BundleExtraAssemblies>`.
* If you need to bundle extra DLLs besides your mod DLL, see the [`BundleExtraAssemblies`
documentation](#configure).
### 3.3.0
Released 30 March 2021 for SMAPI 3.0.0 or later.
* Added a build warning when the mod isn't compiled for `Any CPU`.
* Added a `GameFramework` build property set to `MonoGame` or `Xna` based on the platform. This can
be overridden to change which framework it references.
* Added support for building mods against the 64-bit Linux version of the game on Windows.
* The package now suppresses the misleading 'processor architecture mismatch' warnings.
### 3.2.2
Released 23 September 2020 for SMAPI 3.0.0 or later.
* Reworked and streamlined how the package is compiled.
* Added [SMAPI-ModTranslationClassBuilder](https://github.com/Pathoschild/SMAPI-ModTranslationClassBuilder)
files to the ignore list.
### 3.2.1
Released 11 September 2020 for SMAPI 3.0.0 or later.
* Added more detailed logging.
* Fixed _path's format is not supported_ error when using default `Mods` path in 3.2.
### 3.2.0
Released 07 September 2020 for SMAPI 3.0.0 or later.
* Added option to change `Mods` folder path.
* Rewrote documentation to make it easier to read.
### 3.1.0
Released 01 February 2020 for SMAPI 3.0.0 or later.
* Added support for semantic versioning 2.0.
* `0Harmony.dll` is now ignored if the mod references Harmony directly (it's bundled with SMAPI).
### 3.0.0
Released 26 November 2019 for SMAPI 3.0.0 or later.
* Updated for SMAPI 3.0 and Stardew Valley 1.4.
* Added automatic support for `assets` folders.
* Added `$(GameExecutableName)` MSBuild variable.
* Added support for projects using the simplified `.csproj` format.
* Added option to disable game debugging config.
* Added `.pdb` files to builds by default (to enable line numbers in error stack traces).
* Added optional Harmony reference.
* Fixed `Newtonsoft.Json.pdb` included in release zips when Json.NET is referenced directly.
* Fixed `<IgnoreModFilePatterns>` not working for `i18n` files.
* Dropped support for older versions of SMAPI and Visual Studio.
* Migrated package icon to NuGet's new format.
### 2.2.0
Released 28 October 2018.
* Added support for SMAPI 2.8+ (still compatible with earlier versions).
* Added default game paths for 32-bit Windows.
* Fixed valid manifests marked invalid in some cases.
### 2.1.0
Released 27 July 2018.
* Added support for Stardew Valley 1.3.
* Added support for non-mod projects.
* Added C# analyzers to warn about implicit conversions of Netcode fields in Stardew Valley 1.3.
* Added option to ignore files by regex pattern.
* Added reference to new SMAPI DLL.
* Fixed some game paths not detected by NuGet package.
### 2.0.2
Released 01 November 2017.
* Fixed compatibility issue on Linux.
### 2.0.1
Released 11 October 2017.
* Fixed mod deploy failing to create subfolders if they don't already exist.
### 2.0.0
Released 11 October 2017.
* Added: mods are now copied into the `Mods` folder automatically (configurable).
* Added: release zips are now created automatically in your build output folder (configurable).
* Added: mod deploy and release zips now exclude Json.NET automatically, since it's provided by SMAPI.
* Added mod's version to release zip filename.
* Improved errors to simplify troubleshooting.
* Fixed release zip not having a mod folder.
* Fixed release zip failing if mod name contains characters that aren't valid in a filename.
### 1.7.1
Released 28 July 2017.
* Fixed issue where i18n folders were flattened.
* The manifest/i18n files in the project now take precedence over those in the build output if both
are present.
### 1.7.0
Released 28 July 2017.
* Added option to create release zips on build.
* Added reference to XNA's XACT library for audio-related mods.
### 1.6.2
Released 10 July 2017.
* Further improved crossplatform game path detection.
* Removed undocumented `GamePlatform` build property.
### 1.6.1
Released 09 July 2017.
* Improved crossplatform game path detection.
### 1.6.0
Released 05 June 2017.
* Added support for deploying mod files into `Mods` automatically.
* Added a build error if a game folder is found, but doesn't contain Stardew Valley or SMAPI.
### 1.5.0
Released 23 January 2017.
* Added support for setting a custom game path globally.
* Added default GOG path on macOS.
### 1.4.0
Released 11 January 2017.
* Fixed detection of non-default game paths on 32-bit Windows.
* Removed support for SilVerPLuM (discontinued).
* Removed support for overriding the target platform (no longer needed since SMAPI crossplatforms
mods automatically).
### 1.3.0
Released 31 December 2016.
* Added support for non-default game paths on Windows.
### 1.2.0
Released 24 October 2016.
* Exclude game binaries from mod build output.
### 1.1.0
Released 21 October 2016.
* Added support for overriding the target platform.
### 1.0.0
Released 21 October 2016.
* Initial release.
* Added support for detecting the game path automatically.
* Added support for injecting XNA/MonoGame references automatically based on the OS.
* Added support for mod builders like SilVerPLuM.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -1,176 +0,0 @@
&larr; [README](../README.md)
This file provides more technical documentation about SMAPI. If you only want to use or create
mods, this section isn't relevant to you; see the main README to use or create mods.
This document is about SMAPI itself; see also [mod build package](mod-package.md) and
[web services](web.md).
# Contents
* [Customisation](#customisation)
* [Configuration file](#configuration-file)
* [Command-line arguments](#command-line-arguments)
* [Compile flags](#compile-flags)
* [Compile from source code](#compile-from-source-code)
* [Main project](#main-project)
* [Custom Harmony build](#custom-harmony-build)
* [Prepare a release](#prepare-a-release)
* [On any platform](#on-any-platform)
* [On Windows](#on-windows)
* [Release notes](#release-notes)
## Customisation
### Configuration file
You can customise some SMAPI behaviour by editing the `smapi-internal/config.json` file in your
game folder. See documentation in the file for more info.
### Command-line arguments
The SMAPI installer recognises three command-line arguments:
argument | purpose
-------- | -------
`--install` | Preselects the install action, skipping the prompt asking what the user wants to do.
`--uninstall` | Preselects the uninstall action, skipping the prompt asking what the user wants to do.
`--game-path "path"` | Specifies the full path to the folder containing the Stardew Valley executable, skipping automatic detection and any prompt to choose a path. If the path is not valid, the installer displays an error.
`--no-prompt` | Don't let the installer wait for user input (e.g. for cases where it's being run by a script). If the installer is unable to continue without user input, it'll fail instead.
SMAPI itself recognises five arguments, but these are meant for internal use or testing, and might
change without warning. **On Linux/macOS**, command-line arguments won't work; see _environment
variables_ below instead.
argument | purpose
-------- | -------
`--developer-mode`<br />`--developer-mode-off` | Enable or disable features intended for mod developers. Currently this only makes `TRACE`-level messages appear in the console.
`--no-terminal` | SMAPI won't log anything to the console. On Linux/macOS only, this will also prevent the launch script from trying to open a terminal window. (Messages will still be written to the log file.)
`--use-current-shell` | On Linux/macOS only, the launch script won't try to open a terminal window. All console output will be sent to the shell running the launch script.
`--mods-path` | The path to search for mods, if not the standard `Mods` folder. This can be a path relative to the game folder (like `--mods-path "Mods (test)"`) or an absolute path.
### Environment variables
The above SMAPI arguments may not work on Linux/macOS due to the way the game launcher works. You
can set temporary environment variables instead. For example:
> SMAPI_MODS_PATH="Mods (multiplayer)" /path/to/StardewValley
environment variable | purpose
-------------------- | -------
`SMAPI_DEVELOPER_MODE` | Equivalent to `--developer-mode` and `--developer-mode-off` above. The value must be `true` or `false`.
`SMAPI_MODS_PATH` | Equivalent to `--mods-path` above.
`SMAPI_NO_TERMINAL` | Equivalent to `--no-terminal` above.
`SMAPI_USE_CURRENT_SHELL` | Equivalent to `--use-current-shell` above.
### Compile flags
SMAPI uses a small number of conditional compilation constants, which you can set by editing the
`<DefineConstants>` element in `SMAPI.csproj`. Supported constants:
flag | purpose
---- | -------
`SMAPI_FOR_WINDOWS` | Whether SMAPI is being compiled for Windows; if not set, the code assumes Linux/macOS. Set automatically in `common.targets`.
`SMAPI_DEPRECATED` | Whether to include deprecated code in the build.
## Compile from source code
### Main project
Using an official SMAPI release is recommended for most users, but you can compile from source
directly if needed. Just open the project in an IDE like [Visual
Studio](https://visualstudio.microsoft.com/vs/community/) or [Rider](https://www.jetbrains.com/rider/),
and build the `SMAPI` project. The project will automatically adjust the build settings for your
current OS and Stardew Valley install path.
Rebuilding the solution in debug mode will copy the SMAPI files into your game folder. Starting
the `SMAPI` project with debugging from Visual Studio or Rider should launch SMAPI with the
debugger attached, so you can intercept errors and step through the code being executed.
### Custom Harmony build
SMAPI uses [a custom build of Harmony 2.2.2](https://github.com/Pathoschild/Harmony#readme), which
is included in the `build` folder. To use a different build, just replace `0Harmony.dll` in that
folder before compiling.
## Prepare a release
### On any platform
**⚠ Ideally we'd have one set of instructions for all platforms. The instructions in this section
will produce a fully functional release for all supported platforms, _except_ that the application
icon for SMAPI on Windows will disappear due to [.NET runtime bug
3828](https://github.com/dotnet/runtime/issues/3828). Until that's fixed, see the _[on
Windows](#on-windows)_ section below to create a build that retains the icon.**
#### First-time setup
1. On Windows only:
1. [Install Windows Subsystem for Linux (WSL)](https://docs.microsoft.com/en-us/windows/wsl/install).
2. Run `sudo apt update` in WSL to update the package list.
3. The rest of the instructions below should be run in WSL.
2. Install the required software:
1. Install the [.NET 5 SDK](https://docs.microsoft.com/en-us/dotnet/core/install/linux-ubuntu).
_For Ubuntu-based systems, you can run `lsb_release -a` to get the Ubuntu version number._
2. [Install Steam](https://linuxconfig.org/how-to-install-steam-on-ubuntu-20-04-focal-fossa-linux).
3. Launch `steam` and install the game like usual.
4. Download and install your preferred IDE. For the [latest standalone Rider
version](https://www.jetbrains.com/help/rider/Installation_guide.html#prerequisites):
```sh
wget "<download url here>" -O rider-install.tar.gz
sudo tar -xzvf rider-install.tar.gz -C /opt
ln -s "/opt/JetBrains Rider-<version>/bin/rider.sh"
./rider.sh
```
3. Clone the SMAPI repo:
```sh
git clone https://github.com/Pathoschild/SMAPI.git
```
### Launch the game
1. Run these commands to start Steam:
```sh
export TERM=xterm
steam
```
2. Launch the game through the Steam UI.
### Prepare the release
1. Run `build/unix/prepare-install-package.sh VERSION_HERE` to create the release package in the
root `bin` folder.
Make sure you use a [semantic version](https://semver.org). Recommended format:
build type | format | example
:--------- | :----------------------- | :------
dev build | `<version>-alpha.<date>` | `4.0.0-alpha.20251230`
prerelease | `<version>-beta.<date>` | `4.0.0-beta.20251230`
release | `<version>` | `4.0.0`
### On Windows
#### First-time setup
1. Set up Windows Subsystem for Linux (WSL):
1. [Install WSL](https://docs.microsoft.com/en-us/windows/wsl/install).
2. Run `sudo apt update` in WSL to update the package list.
3. The rest of the instructions below should be run in WSL.
2. Install the required software:
1. Install the [.NET 5 SDK](https://dotnet.microsoft.com/download/dotnet/5.0).
2. Install [Stardew Valley](https://www.stardewvalley.net/).
3. Clone the SMAPI repo:
```sh
git clone https://github.com/Pathoschild/SMAPI.git
```
### Prepare the release
1. Run `build/windows/prepare-install-package.ps1 VERSION_HERE` in PowerShell to create the release
package folders in the root `bin` folder.
Make sure you use a [semantic version](https://semver.org). Recommended format:
build type | format | example
:--------- | :----------------------- | :------
dev build | `<version>-alpha.<date>` | `4.0.0-alpha.20251230`
prerelease | `<version>-beta.<date>` | `4.0.0-beta.20251230`
release | `<version>` | `4.0.0`
2. Launch WSL and run this script:
```bash
# edit to match the build created in steps 1
# In WSL, `/mnt/c/example` accesses `C:\example` on the Windows filesystem.
version="4.0.0"
binFolder="/mnt/e/source/_Stardew/SMAPI/bin"
build/windows/finalize-install-package.sh "$version" "$binFolder"
```
Note: to prepare a test Windows-only build, you can pass `--windows-only` in the first step and
skip the second one.
## Release notes
See [release notes](../release-notes.md).

View File

@ -1,388 +0,0 @@
&larr; [README](../README.md)
**SMAPI.Web** contains the code for the `smapi.io` website, including the mod compatibility list
and update check API.
## Contents
* [Log parser](#log-parser)
* [JSON validator](#json-validator)
* [Web API](#web-api)
* [Short URLs](#short-urls)
* [For SMAPI developers](#for-smapi-developers)
* [Local development](#local-development)
* [Production environment](#production-environment)
## Log parser
The log parser at https://smapi.io/log provides a web UI for uploading, parsing, and sharing SMAPI
logs.
The logs are saved in a compressed form to Amazon Blob storage for 30 days.
## JSON validator
### Overview
The JSON validator at https://smapi.io/json provides a web UI for uploading and sharing JSON files,
and validating them as plain JSON or against a predefined format like `manifest.json` or Content
Patcher's `content.json`.
The logs are saved in a compressed form to Amazon Blob storage for 30 days.
### Schema file format
Schema files are defined in `wwwroot/schemas` using the [JSON Schema](https://json-schema.org/)
format. The JSON validator UI recognises a superset of the standard fields to change output:
<dl>
<dt>Documentation URL</dt>
<dd>
The root schema may have a `@documentationURL` field, which is a web URL for the user
documentation:
```js
"@documentationUrl": "https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Manifest"
```
If present, this is shown in the JSON validator UI.
</dd>
<dt>Error messages</dt>
<dd>
Any part of the schema can define an `@errorMessages` field, which overrides matching schema
errors. You can override by error code (recommended), or by error type and a regex pattern matched
against the error message (more fragile):
```js
// by error type
"pattern": "^[a-zA-Z0-9_.-]+\\.dll$",
"@errorMessages": {
"pattern": "Invalid value; must be a filename ending with .dll."
}
```
```js
// by error type + message pattern
"@errorMessages": {
"oneOf:valid against no schemas": "Missing required field: EntryDll or ContentPackFor.",
"oneOf:valid against more than one schema": "Can't specify both EntryDll or ContentPackFor, they're mutually exclusive."
}
```
Error messages may contain special tokens:
* The `@value` token is replaced with the error's value field. This is usually (but not always) the
original field value.
* When an error has child errors, by default they're flattened into one message:
```
line | field | error
---- | ---------- | -------------------------------------------------------------------------
4 | Changes[0] | JSON does not match schema from 'then'.
| | ==> Changes[0].ToArea.Y: Invalid type. Expected Integer but got String.
| | ==> Changes[0].ToArea: Missing required fields: Height.
```
If you set the message for an error to `$transparent`, the parent error is omitted entirely and
the child errors are shown instead:
```
line | field | error
---- | ------------------- | ----------------------------------------------
8 | Changes[0].ToArea.Y | Invalid type. Expected Integer but got String.
8 | Changes[0].ToArea | Missing required fields: Height.
```
The child errors themselves may be marked `$transparent`, etc. If an error has no child errors,
this override is ignored.
Validation errors for `then` blocks are transparent by default, unless overridden.
</dd>
</dl>
### Using a schema file directly
You can reference the validator schemas in your JSON file directly using the `$schema` field, for
text editors that support schema validation. For example:
```js
{
"$schema": "https://smapi.io/schemas/manifest.json",
"Name": "Some mod",
...
}
```
Available schemas:
format | schema URL
------ | ----------
[SMAPI: `manifest.json`](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Manifest) | https://smapi.io/schemas/manifest.json
[SMAPI: translations (`i18n` folder)](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Translation) | https://smapi.io/schemas/i18n.json
[Content Patcher: `content.json`](https://github.com/Pathoschild/StardewMods/tree/develop/ContentPatcher#readme) | https://smapi.io/schemas/content-patcher.json
## Web API
### Overview
SMAPI provides a web API at `smapi.io/api` for use by SMAPI and external tools. The URL includes a
`{version}` token, which is the SMAPI version for backwards compatibility. This API is publicly
accessible but not officially released; it may change at any time.
### `/mods` endpoint
The API has one `/mods` endpoint. This crossreferences the mod against a variety of sources (e.g.
the wiki, Chucklefish, CurseForge, ModDrop, and Nexus) to provide metadata mainly intended for
update checks.
The API accepts a `POST` request with these fields:
<table>
<tr>
<th>field</th>
<th>summary</th>
</tr>
<tr>
<td><code>mods</code></td>
<td>
The mods for which to fetch metadata. Included fields:
field | summary
----- | -------
`id` | The unique ID in the mod's `manifest.json`. This is used to crossreference with the wiki, and to index mods in the response. If it's unknown (e.g. you just have an update key), you can use a unique fake ID like `FAKE.Nexus.2400`.
`updateKeys` | _(optional)_ [Update keys](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Manifest#Update_checks) which specify the mod pages to check, in addition to any mod pages linked to the `ID`.
`installedVersion` | _(optional)_ The installed version of the mod. If not specified, the API won't recommend an update.
`isBroken` | _(optional)_ Whether SMAPI failed to load the installed version of the mod, e.g. due to incompatibility. If true, the web API will be more permissive when recommending updates (e.g. allowing a stable → prerelease update).
</td>
</tr>
<tr>
<td><code>apiVersion</code></td>
<td>
_(optional)_ The installed version of SMAPI. If not specified, the API won't recommend an update.
</td>
</tr>
<tr>
<td><code>gameVersion</code></td>
<td>
_(optional)_ The installed version of Stardew Valley. This may be used to select updates.
</td>
</tr>
<tr>
<td><code>platform</code></td>
<td>
_(optional)_ The player's OS (`Android`, `Linux`, `Mac`, or `Windows`). This may be used to select updates.
</td>
</tr>
<tr>
<td><code>includeExtendedMetadata</code></td>
<td>
_(optional)_ Whether to include extra metadata that's not needed for SMAPI update checks, but which
may be useful to external tools.
</td>
</table>
Example request:
```js
POST https://smapi.io/api/v3.0/mods
{
"mods": [
{
"id": "Pathoschild.ContentPatcher",
"updateKeys": [ "nexus:1915" ],
"installedVersion": "1.9.2",
"isBroken": false
}
],
"apiVersion": "3.0.0",
"gameVersion": "1.4.0",
"platform": "Windows",
"includeExtendedMetadata": true
}
```
Response fields:
<table>
<tr>
<th>field</th>
<th>summary</th>
</tr>
<tr>
<td><code>id</code></td>
<td>
The mod ID you specified in the request.
</td>
</tr>
<tr>
<td><code>suggestedUpdate</code></td>
<td>
The update version recommended by the web API, if any. This is based on some internal rules (e.g.
it won't recommend a prerelease update if the player has a working stable version) and context
(e.g. whether the player is in the game beta channel). Choosing an update version yourself isn't
recommended, but you can set `includeExtendedMetadata: true` and check the `metadata` field if you
really want to do that.
</td>
</tr>
<tr>
<td><code>errors</code></td>
<td>
Human-readable errors that occurred fetching the version info (e.g. if a mod page has an invalid
version).
</td>
</tr>
<tr>
<td><code>metadata</code></td>
<td>
Extra metadata that's not needed for SMAPI update checks but which may be useful to external tools,
if you set `includeExtendedMetadata: true` in the request. Included fields:
field | summary
----- | -------
`id` | The known `manifest.json` unique IDs for this mod defined on the wiki, if any. That includes historical versions of the mod.
`name` | The normalised name for this mod based on the crossreferenced sites.
`nexusID` | The mod ID on [Nexus Mods](https://www.nexusmods.com/stardewvalley/), if any.
`chucklefishID` | The mod ID in the [Chucklefish mod repo](https://community.playstarbound.com/resources/categories/stardew-valley.22/), if any.
`curseForgeID` | The mod project ID on [CurseForge](https://www.curseforge.com/stardewvalley), if any.
`curseForgeKey` | The mod key on [CurseForge](https://www.curseforge.com/stardewvalley), if any. This is used in the mod page URL.
`modDropID` | The mod ID on [ModDrop](https://www.moddrop.com/stardew-valley), if any.
`gitHubRepo` | The GitHub repository containing the mod code, if any. Specified in the `Owner/Repo` form.
`customSourceUrl` | The custom URL to the mod code, if any. This is used for mods which aren't stored in a GitHub repo.
`customUrl` | The custom URL to the mod page, if any. This is used for mods which aren't stored on one of the standard mod sites covered by the ID fields.
`main` | The primary mod version, if any. This depends on the mod site, but it's typically either the version of the mod itself or of its latest non-optional download.
`optional` | The latest optional download version, if any.
`unofficial` | The version of the unofficial update defined on the wiki for this mod, if any.
`unofficialForBeta` | Equivalent to `unofficial`, but for beta versions of SMAPI or Stardew Valley.
`hasBetaInfo` | Whether there's an ongoing Stardew Valley or SMAPI beta which may affect update checks.
`compatibilityStatus` | The compatibility status for the mod for the stable version of the game, as defined on the wiki, if any. See [possible values](https://github.com/Pathoschild/SMAPI/blob/develop/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityStatus.cs).
`compatibilitySummary` | The human-readable summary of the mod's compatibility in HTML format, if any.
`brokeIn` | The SMAPI or Stardew Valley version that broke this mod, if any.
`betaCompatibilityStatus`<br />`betaCompatibilitySummary`<br />`betaBrokeIn` | Equivalent to the preceding fields, but for beta versions of SMAPI or Stardew Valley.
</td>
</tr>
</table>
Example response with `includeExtendedMetadata: false`:
```js
[
{
"id": "Pathoschild.ContentPatcher",
"suggestedUpdate": {
"version": "1.10.0",
"url": "https://www.nexusmods.com/stardewvalley/mods/1915"
},
"errors": []
}
]
```
Example response with `includeExtendedMetadata: true`:
```js
[
{
"id": "Pathoschild.ContentPatcher",
"suggestedUpdate": {
"version": "1.10.0",
"url": "https://www.nexusmods.com/stardewvalley/mods/1915"
},
"metadata": {
"id": [ "Pathoschild.ContentPatcher" ],
"name": "Content Patcher",
"nexusID": 1915,
"curseForgeID": 309243,
"curseForgeKey": "content-patcher",
"modDropID": 470174,
"gitHubRepo": "Pathoschild/StardewMods",
"main": {
"version": "1.10",
"url": "https://www.nexusmods.com/stardewvalley/mods/1915"
},
"hasBetaInfo": true,
"compatibilityStatus": "Ok",
"compatibilitySummary": "✓ use latest version."
},
"errors": []
}
]
```
## Short URLs
The SMAPI web services provides a few short URLs for convenience:
short url | → | target page
:-------- | - | :----------
[smapi.io/3.0](https://smapi.io/3.0) | → | [stardewvalleywiki.com/Modding:Migrate_to_SMAPI_3.0](https://stardewvalleywiki.com/Modding:Migrate_to_SMAPI_3.0)
[smapi.io/community](https://smapi.io/community) | → | [stardewvalleywiki.com/Modding:Community](https://stardewvalleywiki.com/Modding:Community)
[smapi.io/docs](https://smapi.io/docs) | → | [stardewvalleywiki.com/Modding:Index](https://stardewvalleywiki.com/Modding:Index)
[smapi.io/package](https://smapi.io/package) | → | [github.com/Pathoschild/SMAPI/blob/develop/docs/technical/mod-package.md](https://github.com/Pathoschild/SMAPI/blob/develop/docs/technical/mod-package.md)
[smapi.io/troubleshoot](https://smapi.io/troubleshoot) | → | [stardewvalleywiki.com/Modding:Player_Guide/Troubleshooting](https://stardewvalleywiki.com/Modding:Player_Guide/Troubleshooting)
[smapi.io/xnb](https://smapi.io/xnb) | → | [stardewvalleywiki.com/Modding:Using_XNB_mods](https://stardewvalleywiki.com/Modding:Using_XNB_mods)
## For SMAPI developers
### Local environment
A local environment lets you run a complete copy of the web project (including cache database) on
your machine, with no external dependencies aside from the actual mod sites.
1. Edit `appsettings.Development.json` and set these options:
property name | description
------------- | -----------
`NexusApiKey` | [Your Nexus API key](https://www.nexusmods.com/users/myaccount?tab=api#personal_key).
Optional settings:
property name | description
--------------------------- | -----------
`AzureBlobConnectionString` | The connection string for the Azure Blob storage account. Defaults to using the system's temporary file folder if not specified.
`GitHubUsername`<br />`GitHubPassword` | The GitHub credentials with which to query GitHub release info. Defaults to anonymous requests if not specified.
2. Launch `SMAPI.Web` from Visual Studio to run a local version of the site.
### Production environment
A production environment includes the web servers and cache database hosted online for public
access.
This section assumes you're creating a new environment on Azure, but the app isn't tied to any
Azure services. If you want to host it on a different site, you'll need to adjust the instructions
accordingly.
Initial setup:
1. Create an Azure Blob storage account for uploaded files.
2. Create an Azure App Services environment running the latest .NET on Linux or Windows.
3. Add these application settings in the new App Services environment:
property name | description
------------------------------- | -----------------
`ApiClients.AzureBlobConnectionString` | The connection string for the Azure Blob storage account created in step 2.
`ApiClients.GitHubUsername`<br />`ApiClients.GitHubPassword` | The login credentials for the GitHub account with which to fetch release info. If these are omitted, GitHub will impose much stricter rate limits.
`ApiClients:NexusApiKey` | The [Nexus API authentication key](https://github.com/Pathoschild/FluentNexus#init-a-client).
Optional settings:
property name | description
------------------------------- | -----------------
`BackgroundServices:Enabled` | Set to `true` to enable background processes like fetching data from the wiki, or false to disable them.
`Site:BetaEnabled` | Set to `true` to show a separate download button if there's a beta version of SMAPI in its GitHub releases.
`Site:BetaBlurb` | If `Site:BetaEnabled` is true and there's a beta version of SMAPI in its GitHub releases, this is shown on the beta download button as explanatory subtext.
`Site:SupporterList` | A list of Patreon supports to credit on the download page.
To deploy updates, just [redeploy the web project from Visual Studio](https://docs.microsoft.com/en-us/visualstudio/deployment/quickstart-deploy-to-azure).

View File

@ -1,19 +0,0 @@
Any raw assets you want to be deployed with your application can be placed in
this directory (and child directories) and given a Build Action of "AndroidAsset".
These files will be deployed with your package and will be accessible using Android's
AssetManager, like this:
public class ReadAsset : Activity
{
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
InputStream input = Assets.Open ("my_asset.txt");
}
}
Additionally, some Android functions will automatically load asset files:
Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");

View File

@ -1,110 +0,0 @@
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{45D7D2FB-6B70-45D1-A595-6E289D6A3468}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TemplateGuid>{122416d6-6b49-4ee2-a1e8-b825f31c79fe}</TemplateGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Loader</RootNamespace>
<AssemblyName>Loader</AssemblyName>
<FileAlignment>512</FileAlignment>
<AndroidApplication>True</AndroidApplication>
<AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
<AndroidResgenClass>Resource</AndroidResgenClass>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v12.0</TargetFrameworkVersion>
<TargetFramework>net5.0-android</TargetFramework>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
<AndroidEnableSGenConcurrent>true</AndroidEnableSGenConcurrent>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>True</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidUseSharedRuntime>true</AndroidUseSharedRuntime>
<AndroidLinkMode>None</AndroidLinkMode>
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
<AndroidManagedSymbols>false</AndroidManagedSymbols>
<AndroidUseAapt2>true</AndroidUseAapt2>
<AndroidSupportedAbis>armeabi-v7a;x86;arm64-v8a;x86_64</AndroidSupportedAbis>
<AndroidDexTool>d8</AndroidDexTool>
<!-- <AndroidLinkSkip>Mono.Android;MonoGame.Framework;mscorlib;System.Core;System;System.Drawing.Common;System.Numerics;System.Runtime.Serialization;System.Xml;System.Xml.Linq</AndroidLinkSkip>-->
<AndroidEnableMultiDex>true</AndroidEnableMultiDex>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugSymbols>False</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidManagedSymbols>false</AndroidManagedSymbols>
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
<AndroidLinkMode>None</AndroidLinkMode>
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
<AndroidUseAapt2>true</AndroidUseAapt2>
<AndroidSupportedAbis>armeabi-v7a;x86;arm64-v8a;x86_64</AndroidSupportedAbis>
<AndroidDexTool>d8</AndroidDexTool>
</PropertyGroup>
<ItemGroup>
<Compile Include="Resources\Resource.designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="Resources\AboutResources.txt" />
<None Include="Properties\AndroidManifest.xml" />
<None Include="Assets\AboutAssets.txt" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\icon.png" />
<AndroidResource Include="Resources\drawable\splash.png" />
<AndroidResource Include="Resources\drawable\splash_logos_crop.png" />
<AndroidResource Include="Resources\drawable\splash_screen.xml" />
<AndroidResource Include="Resources\values\colors.xml" />
<AndroidResource Include="Resources\values\ic_launcher_background.xml" />
<AndroidResource Include="Resources\values\strings.xml" />
<AndroidResource Include="Resources\values\styles.xml" />
</ItemGroup>
<ItemGroup>
<Reference Include="Mono.Android" />
<Reference Include="Mono.Security" />
<ProjectReference Include="..\SMAPI\SMAPI.csproj" />
<Reference Include="System" />
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\mipmap\ic_launcher.png" />
<AndroidResource Include="Resources\mipmap\ic_launcher_background.png" />
<AndroidResource Include="Resources\mipmap\ic_launcher_foreground.png" />
<AndroidResource Include="Resources\mipmap\ic_launcher_round.png" />
<AndroidResource Include="Resources\xml\provider_paths.xml">
<SubType>Designer</SubType>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidAsset Include="Assets\Content\**" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<!-- 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>

View File

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="156" android:versionName="1.5.6.31" android:installLocation="auto" package="com.zane.stardewvalley2" platformBuildVersionCode="31" platformBuildVersionName="9">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:label="SMAPI Stardew Valley"
android:icon="@mipmap/ic_launcher"
android:name="android.app.Application"
android:allowBackup="true"
android:resizeableActivity="false"
android:debuggable="true"
android:requestLegacyExternalStorage="true">
</application>
<uses-permission android:name="com.android.vending.CHECK_LICENSE" />
</manifest>

View File

@ -1,44 +0,0 @@
Images, layout descriptions, binary blobs and string dictionaries can be included
in your application as resource files. Various Android APIs are designed to
operate on the resource IDs instead of dealing with images, strings or binary blobs
directly.
For example, a sample Android app that contains a user interface layout (main.xml),
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
would keep its resources in the "Resources" directory of the application:
Resources/
drawable/
icon.png
layout/
main.xml
values/
strings.xml
In order to get the build system to recognize Android resources, set the build action to
"AndroidResource". The native Android APIs do not operate directly with filenames, but
instead operate on resource IDs. When you compile an Android application that uses resources,
the build system will package the resources for distribution and generate a class called "R"
(this is an Android convention) that contains the tokens for each one of the resources
included. For example, for the above Resources layout, this is what the R class would expose:
public class R {
public class drawable {
public const int icon = 0x123;
}
public class layout {
public const int main = 0x456;
}
public class strings {
public const int first_string = 0xabc;
public const int second_string = 0xbcd;
}
}
You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main
to reference the layout/main.xml file, or R.strings.first_string to reference the first
string in the dictionary file values/strings.xml.

View File

@ -1,175 +0,0 @@
#pragma warning disable 1591
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
[assembly: global::Android.Runtime.ResourceDesignerAttribute("Loader.Resource", IsApplication=true)]
namespace Loader
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "13.2.0.99")]
public partial class Resource
{
static Resource()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
public static void UpdateIdValues()
{
global::StardewModdingAPI.Resource.String.app_name = global::Loader.Resource.String.app_name;
global::StardewModdingAPI.Resource.String.hello = global::Loader.Resource.String.hello;
}
public partial class Attribute
{
static Attribute()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Attribute()
{
}
}
public partial class Color
{
// aapt resource value: 0x7F010000
public const int colorAccent = 2130771968;
// aapt resource value: 0x7F010001
public const int colorPrimary = 2130771969;
// aapt resource value: 0x7F010002
public const int colorPrimaryDark = 2130771970;
// aapt resource value: 0x7F010003
public const int ic_launcher_background = 2130771971;
static Color()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Color()
{
}
}
public partial class Drawable
{
// aapt resource value: 0x7F020000
public const int icon = 2130837504;
// aapt resource value: 0x7F020001
public const int splash = 2130837505;
// aapt resource value: 0x7F020002
public const int splash_logos_crop = 2130837506;
// aapt resource value: 0x7F020003
public const int splash_screen = 2130837507;
static Drawable()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Drawable()
{
}
}
public partial class Mipmap
{
// aapt resource value: 0x7F030000
public const int ic_launcher = 2130903040;
// aapt resource value: 0x7F030001
public const int ic_launcher_background = 2130903041;
// aapt resource value: 0x7F030002
public const int ic_launcher_foreground = 2130903042;
// aapt resource value: 0x7F030003
public const int ic_launcher_round = 2130903043;
static Mipmap()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Mipmap()
{
}
}
public partial class String
{
// aapt resource value: 0x7F040000
public const int action_settings = 2130968576;
// aapt resource value: 0x7F040001
public const int app_name = 2130968577;
// aapt resource value: 0x7F040002
public const int hello = 2130968578;
static String()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private String()
{
}
}
public partial class Style
{
// aapt resource value: 0x7F050000
public const int Theme_Splash = 2131034112;
static Style()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Style()
{
}
}
public partial class Xml
{
// aapt resource value: 0x7F060000
public const int provider_paths = 2131099648;
static Xml()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Xml()
{
}
}
}
}
#pragma warning restore 1591

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<color android:color="#ffffff" />
</item>
<item>
<bitmap android:tileMode="disabled" android:gravity="center" android:src="@drawable/splash_logos_crop"/>
</item>
</layer-list>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 314 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 965 B

Some files were not shown because too many files have changed in this diff Show More