Compare commits

...

250 Commits

Author SHA1 Message Date
erion 44f31419b0 Update to upstream 6e5f9365f0
Remove windows screen reader (probably not a good idea to have separate access forks, but this works for now.)
Send rate when initialising speech (TODO: expose this via a config parameter)
2023-01-20 18:33:52 +01:00
erion 4af19aeb7a Merge remote-tracking branch 'sda/master' into update 2023-01-20 14:26:52 +01:00
Mohammad Shoaib Khan 6e5f9365f0
Adding missing entries 2022-11-26 15:43:17 +05:30
Mohammad Shoaib Khan 4751af8fec
Fixed sapi 2022-11-26 15:31:31 +05:30
Bartholomue d66fd0afb0 IslandWestCave1 Definitions
Added definitions to IslandWestCave1 for the lion statue and note crystals so players can resolve that puzzle.Reverted the removal of the skull cavern door in 1.3.4, since that's an interaction tile used to enter the actual dungeon.
2022-11-26 15:29:52 +05:30
Mohammad Shoaib Khan 771ebb07e4
Added exception for warp points when checking if a tile is on map 2022-11-07 20:51:53 +05:30
Mohammad Shoaib Khan fe03dfb46e
Removed exit and entrance entries 2022-11-07 20:27:11 +05:30
Mohammad Shoaib Khan ec7148e613
Added warp point detection 2022-11-07 20:07:25 +05:30
Mohammad Shoaib Khan c4ce0a5280
Added config option to toggle detecting dropped items \n Fixed bug 2022-11-07 19:51:35 +05:30
Bartholomue dd0d052f64 Static Tiles Container Migration
Changed categorization of multiple objects to fit the new container category for ease of access to important items.
2022-10-29 17:57:04 +05:30
Mohammad Shoaib Khan 0f0824a422
Prioritised custom-tiles.json over static-tiles
- Bug fix in LibraryMuseum
2022-10-29 16:52:37 +05:30
Mohammad Shoaib Khan f5db6eb655
Renamed chest category to container 2022-10-29 14:48:13 +05:30
Mohammad Shoaib Khan 8aac4499fc
Added dropped item detection 2022-10-29 14:41:41 +05:30
Bartholomue c153462706 Desert Camel
Defined the location of the camel in the desert in the NPC category.
2022-10-29 13:25:47 +05:30
Bartholomue ac2a384d84 Dwarf Shop Etc
Defined the location of the Dwarf shop on Volcano Dungeon 5.
Defined two signs in town as decorations.
Removed the top and bottom tiles of the Joja Mart shop counter so that Accessible Tiles auto pathing won't navigate to the south / north end of the counter, as those tiles will only let you interact with them from the right.
2022-10-29 13:25:47 +05:30
Mohammad Shoaib Khan e1a19ae0c8
Bug fix 2022-10-29 13:20:11 +05:30
Mohammad Shoaib Khan b4560bc9e1
Added custom-tiles.json support
- users can add their own tiles in this file just asin static-tiles.json
- the file needs to be placed just where the static-tiles.json is
2022-10-27 20:05:12 +05:30
Mohammad Shoaib Khan 1187ba30e7
Mod Specific Tiles
We can add tiles that only get detected when the specified mod is loaded
Syntax: <location name>||<Mod's unique id>
2022-10-27 19:30:28 +05:30
Mohammad Shoaib Khan dccbf8b79f
Issue#45 Update buildlist on start of day
- also removed calling restrictions from buildlist command,
 it can now be called from any location
2022-10-27 15:14:37 +05:30
Mohammad Shoaib Khan 9d9b5f5c76
Added books and museum pieces in LibraryMuseum 2022-10-27 14:55:38 +05:30
Mohammad Shoaib Khan 1ea07533bb
Fixed minor code warnings 2022-10-27 13:12:41 +05:30
Mohammad Shoaib Khan f2ba3e8793 Implemented Tolk library for screen reader in windows 2022-10-24 12:12:53 +05:30
Mohammad Shoaib Khan 7a4ca89836 Issue#63 Prepend the player's name in tile viewer 2022-10-23 12:12:27 +05:30
Mohammad Shoaib Khan 1cf4637687 Merge branch 'master' into bugs 2022-10-23 12:03:40 +05:30
Mohammad Shoaib Khan 950ab21d61 Issue#56 Other currency narration 2022-10-23 12:00:30 +05:30
Mohammad Shoaib Khan 9d1c6f95dd Issue#52 Patched speech bubbles 2022-10-23 11:14:58 +05:30
Mohammad Shoaib Khan d131d26ac8 Issue#54 updated names in static-tiles.json according to the issue 2022-10-22 22:23:14 +05:30
Mohammad Shoaib Khan a9160b4ff6 Issue#54 the names in static tiles now ignore anything between square brackets 2022-10-22 22:19:38 +05:30
Mohammad Shoaib Khan 34cb47bdaa Issue#42 feeding benches in coops and barns are now dynamic 2022-10-22 21:53:10 +05:30
Mohammad Shoaib Khan 8c080e11f0 Change priority order in getObjectAtTile() 2022-10-10 16:14:14 +05:30
Mohammad Shoaib Khan 5205df8400 Replaced ^ with \n when narrating texts 2022-10-10 15:56:35 +05:30
Mohammad Shoaib Khan b600eda78e Added command to toggle tts 2022-10-09 13:50:35 +05:30
Mohammad Shoaib Khan 452bbdf86c Fixed duplicates in static-tiles.json 2022-10-09 13:38:26 +05:30
Mohammad Shoaib 66b4333d73
Merge pull request #49 from Bartholomue/master
Joja Mart Definitions ETC
2022-10-06 21:56:40 +05:30
Bartholomue a0336d630f Expanded Some Tile Ranges
Expanded some tile definitions to cover the full breadth of the objects, in cases where they only listed as one tile but actually spanned a few tiles (such as the exit in the secret woods).
2022-09-30 07:31:09 -05:00
Bartholomue ad22982b09 Recategorize Pierre's Bin
It was erroneously in the interactable category. It's now been moved to other in order to match the other special orders drop box definitions.
2022-09-26 13:11:52 -05:00
Bartholomue 542d5390a2 Pool And Saloon Definitions
Tile definitions for bathhouse Pool and Saloon, including pool entry steps, decoration, and furniture.
2022-09-18 13:17:34 -05:00
Bartholomue e1614917a2 Alphabetize Feeding Benches
Renamed the feeding benches to have an alphabet suffix, rather than number. The name sort is by string rather than integer value. Renaming to "Feeding Bench "J" (for example) makes sure that the tenth bench actually comes after the ninth bench, rather than after the first.
2022-09-09 09:24:00 -05:00
Bartholomue 01ec8209af Added Big Shed Redundancy
Duplicated "shed2" to "big shed" for stardew redundancy in name changes upon logging back in to the game.
2022-09-07 06:54:21 -05:00
Bartholomue 0edd6e54e0 Redact static-tiles.json
Re-added the redundancy entries for big barn/coop and deluxe barn/coop to cover Stardew inconsistent behavior.
Removed the static tile definitions for the lost books in preparation for them being made dynamic.
Recommend adding the lost books to the "Other" category so that they'll be easily navigable without cluttering up the other categories which contain important objects.
2022-09-07 03:23:41 -05:00
Bartholomue 2cebc7ce99 More Tile Definitions
Started adding [number] to tile definitions with the same name to coincide with the unique bracketed identifiers.

Adventure Guild:
Defined various decoration and furniture, though I did leave out all the barrels as I felt they weren't important enough to enumerate.

Archaeology House:
Defined the 21 lost books in Strings/Notes under the "Other" category, as their state requires previous interaction to determine.
Defined other various other decoration and furniture.

Town:
Defined the 5X4 fountain in the water category.
Expanded the mountain entrance definition to cover the entire 5X1 tile width.
Defined various benches as furniture.
Defined two bridges in the bridge category.
2022-08-26 07:59:41 -05:00
Mohammad Shoaib Khan 589ec8eff8 Bug fix 2022-08-23 08:58:50 +05:30
Mohammad Shoaib Khan 52c8b6e3e3 Reverted back to using escape key 2022-08-23 08:57:34 +05:30
Bartholomue a3df4e2591 Joja Mart Definitions
Some more static tile tweaks and additions:
Desert: Added a bench as furniture, helpful for finding a secret note dig spot.
Joja Mart: Added tile definitions for product shelves as decorations.
Railroad: Added a boulder as a decoration, helpful for finding a secret note dig spot.
Railroad: Added the summit boulder as a decoration.
Railroad: Fixed the size of the pond next to the spa to 3X3 rather than 4X3.
Railroad: Added a bench as furniture.
Railroad: Renamed the empty crate tile as "empty box" to match its examine description.
Railroad: Expanded the dumpster definition to span across two tiles, as it's sitting half way across both.
2022-08-22 16:32:42 -05:00
Bartholomue 1bac13bc14 Minor Static Tiles Additions
Added a boulder to the Railroad as a decoration, helpful for finding a secret note dig spot.
Added a 2X1 bench in the desert as furniture, helpful for finding a secret note dig spot.
Fixed the size of the pond next to the spa to 3X3 rather than 4X3.
2022-08-22 06:56:57 -05:00
Mohammad Shoaib Khan 9afeb7f778 Release 1.3.2 2022-08-20 23:28:36 +05:30
Mohammad Shoaib Khan bf1b904f2f Merge branch 'bugs' 2022-08-20 23:13:41 +05:30
Mohammad Shoaib Khan c44771bca8 Merge branch 'master' into bugs 2022-08-20 23:09:55 +05:30
Mohammad Shoaib Khan d4c5bb3e48 Merge branch 'master' into enhancements 2022-08-20 23:09:39 +05:30
Mohammad Shoaib Khan 9d2dd97607 Added 'not usable here' to inventories 2022-08-20 23:08:12 +05:30
Mohammad Shoaib Khan 3083ebca84 Added command to toggle warnings feature 2022-08-20 23:08:12 +05:30
Mohammad Shoaib Khan 895f329b8b Added watered/unwatered toggle for crops 2022-08-20 23:08:12 +05:30
Mohammad Shoaib Khan 741e6a5219 Added 'not usable here' to inventories 2022-08-20 22:58:42 +05:30
Mohammad Shoaib 928b481319
Merge pull request #44 from Bartholomue/master
Updated Static Tiles
2022-08-20 22:21:36 +05:30
Mohammad Shoaib 17c1c19e90
Removed trailing commas 2022-08-20 22:17:45 +05:30
Bartholomue 6f1ccdb951 Updated Static Tiles
Updated tile definitions for multiple locations, recategorizing some for easier object selection without clutter.
2022-08-19 10:05:23 -05:00
Mohammad Shoaib Khan d14af408df Added command to toggle warnings feature 2022-08-18 12:37:32 +05:30
Mohammad Shoaib Khan 6d18849f17 Added watered/unwatered toggle for crops 2022-08-18 12:18:40 +05:30
Mohammad Shoaib Khan 2df4cfca27 Issue#28 Prepend fish name to fish ponds 2022-08-17 13:28:49 +05:30
Mohammad Shoaib Khan 5bd3e4085d Merge branch 'enhancements'
Merging the new enhancements to master
2022-08-17 13:07:57 +05:30
Mohammad Shoaib Khan d12e9b2590 Fixed casing issue in static tiles location names and added shafts to read tile 2022-08-17 13:06:47 +05:30
Mohammad Shoaib Khan a908e09884 Issue#34 Patched upgrades for sprinklers 2022-08-17 12:34:02 +05:30
Mohammad Shoaib Khan d309844b28 Removed unnecessary code 2022-08-14 20:19:09 +05:30
Mohammad Shoaib Khan b4625ddfc8 Issue#29 Fixed crab pot not getting detected as a machine 2022-08-14 20:09:09 +05:30
Mohammad Shoaib Khan f2d607484e Issue#27 Added mill input and output tiles detection 2022-08-14 19:50:53 +05:30
Mohammad Shoaib Khan 18a7dc5d23 Issue#33 Fixed rings bug in crafting page 2022-08-13 14:21:22 +05:30
Mohammad Shoaib Khan 408a40121d Issue#25 Added command to toggle speaking in percentage for health n stamina 2022-08-13 13:06:24 +05:30
Mohammad Shoaib Khan 949e5c5bfc Issue#23 Added Signs to read tile 2022-08-13 12:15:38 +05:30
Mohammad Shoaib Khan 9199d764ba Issue#23 Added indoor pots i.e. garden pot to read tile 2022-08-13 12:00:45 +05:30
Mohammad Shoaib Khan df192ee90c Added docs and replaced escape key with enter 2022-08-12 16:37:39 +05:30
Mohammad Shoaib 4a1e4f9f4a Bug fix 2022-06-10 22:50:06 +05:30
Mohammad Shoaib 5b37859216 Fixed advanced game options menu 2022-06-06 22:43:25 +05:30
Mohammad Shoaib 1e096cf521 Release 1.3.0 2022-06-06 22:40:37 +05:30
Mohammad Shoaib 617251c3de Patched advanced options menu 2022-06-06 22:38:51 +05:30
Mohammad Shoaib 157bdd0150 Fixed moving animal 2022-06-06 22:27:04 +05:30
Mohammad Shoaib e534a6aed9 Fixed move animal to different building 2022-05-30 23:38:09 +05:30
Mohammad Shoaib 9c1a185047 Revamped character creation menu 2022-05-30 22:34:58 +05:30
Mohammad Shoaib d48e4cc1b6
Merge pull request #21 from bradjrenshaw/GingerIslandAdditions
Ginger island additions
2022-05-27 18:11:12 +05:30
bradjrenshaw 9f8d4273bc Fixed static tiles json formatting; fixed a few entrances with missing coordinates. 2022-05-18 19:16:44 -04:00
Mohammad Shoaib 93c75fca65
Merge pull request #20 from xyzz2001/patch-3
Update zh.json
2022-05-16 22:31:49 +05:30
Mohammad Shoaib 9c2931ec2f Added all keybinds to mod config 2022-05-16 22:01:02 +05:30
Mohammad Shoaib 923ad61580 Changed b key to c in buildingNAnimal menu 2022-05-16 21:35:01 +05:30
Mohammad Shoaib b1056a6cd1 Added chat menu keys to mod config 2022-05-16 21:34:19 +05:30
Mohammad Shoaib f85192178f Read tile pauses while auto walking 2022-05-16 21:07:08 +05:30
bradjrenshaw 661d6bb31f Add secondary entrances connecting various locations (Island North to south and Island North to Volcano Dungeon) 2022-05-15 18:58:23 -04:00
bradjrenshaw 7b81660dd2 Add shipwreck entrance and exit to static tiles. 2022-05-15 18:33:24 -04:00
xywlkj2001_星云 6a49fccb9c
Update zh.json 2022-05-16 04:22:07 +08:00
Mohammad Shoaib fcd6c8ecb3 Moved sounds to assets folder 2022-05-15 15:30:31 +05:30
Mohammad Shoaib 0942eec32e beta 1.2.3 2022-05-15 15:26:34 +05:30
Mohammad Shoaib 80aa0bf106 Added translations to other languages 2022-05-15 15:22:50 +05:30
Mohammad Shoaib af50fd114c Cleaned the code a bit 2022-05-15 15:03:04 +05:30
Mohammad Shoaib 7f81d72771 added more scenes to grandpa story 2022-05-15 15:00:08 +05:30
Mohammad Shoaib f9dd84efc8 Patched grandpa story and intro mini game 2022-05-15 13:28:20 +05:30
Mohammad Shoaib 4dcef714fa Fixed warning 2022-05-15 12:30:52 +05:30
Mohammad Shoaib 373a39fa33 Added pet's water bowl 2022-05-15 12:29:40 +05:30
Mohammad Shoaib 715601f694 Added footstep sounds to auto walk 2022-05-15 12:29:40 +05:30
Mohammad Shoaib cb49832b5a Player stops auto walking if WASD is pressed 2022-05-15 12:29:40 +05:30
Mohammad Shoaib c8b274347e Added auto walk feature to tile viewer 2022-05-15 12:29:40 +05:30
Mohammad Shoaib 3c76c424e8
Merge pull request #18 from bradjrenshaw/GemBirds
Ginger Island gem birds added to TileInfo npcs category.
2022-05-15 12:29:13 +05:30
bradjrenshaw 9e0c2dfab7 Fix incorrectly named Topaz Gem Bird. 2022-05-15 01:43:39 -04:00
bradjrenshaw e4187a636a Ginger Island gem birds added to TileInfo npcs category. 2022-05-15 00:22:36 -04:00
Mohammad Shoaib bd806c077a
Merge pull request #17 from bradjrenshaw/MiscFixes
Misc fixes
2022-05-14 08:15:53 +05:30
bradjrenshaw f4a47bf64a Added ore panning spot to interactables category. 2022-05-12 20:33:50 -04:00
bradjrenshaw 153b120730 Added old mariner to npcs category when present. 2022-05-12 20:05:01 -04:00
bradjrenshaw 569311c61e Fix incorrect AM and PM time announcement and :00 formatting. 2022-05-12 20:04:33 -04:00
Mohammad Shoaib d74ca682eb Added time warning 2022-05-12 23:22:59 +05:30
Mohammad Shoaib 6c04ba7bf4 Removed pause from hud message as it was pausing too much 2022-05-12 22:59:40 +05:30
Mohammad Shoaib 92b7230a4d Fix for stone types 2022-05-12 22:54:33 +05:30
Mohammad Shoaib f8a6cab24e Added missing entrances 2022-05-12 21:47:01 +05:30
Mohammad Shoaib ebc68f0ffa Added pause option to config
It now pauses in hud messages
2022-05-12 16:00:20 +05:30
Mohammad Shoaib 92d5ae22d9 Added other languages to warning message 2022-05-12 15:56:05 +05:30
Mohammad Shoaib 2d74653217 Read tile pauses when narrating a warning message 2022-05-12 15:28:09 +05:30
Mohammad Shoaib d8dbfe78d4 Added i18n folder for other language support
and logic for narrating warnings
2022-05-11 13:47:19 +05:30
Mohammad Shoaib 04ba44d045 Added warnings.cs 2022-05-11 13:29:12 +05:30
Mohammad Shoaib eb25243377 Code documentation 2022-05-11 13:14:46 +05:30
Mohammad Shoaib d27c7c749c
Merge pull request #16 from bradjrenshaw/TileViewer
Tile viewer
2022-05-11 13:00:04 +05:30
bradjrenshaw 55b9255dc8 Formatting and code cleanup; added documentation and some clarifying comments. 2022-05-10 17:30:40 -04:00
bradjrenshaw 8446049c55 Rename MouseHandler to TileViewer for accuracy 2022-05-10 16:36:23 -04:00
bradjrenshaw 7a1b768bdb Added option to allow tile cursor to view the entire map regardless if it is visible or not. 2022-05-10 16:31:54 -04:00
bradjrenshaw 955ffac65b Implementation of relative offset lock (keep mouse in position relative to you as yo umove). 2022-05-10 15:22:32 -04:00
bradjrenshaw c8bf29b6d0 Added precise tile cursor movement (defaults to 8 pixels per key press). 2022-05-10 15:09:54 -04:00
bradjrenshaw a8bcaf6cef Refactored keyboard tile cursor input to be triggered at the same time as the rest of keyboard input; fix bug in reading UI components with tile cursor. 2022-05-10 14:37:43 -04:00
bradjrenshaw 332dd858a8 Fixed bug with tile cursor not passing exact integer tile values causing terrain features like crops to not be read out. 2022-05-10 14:21:17 -04:00
Mohammad Shoaib c49dec4f29 Beta 1.2.2 2022-05-10 20:55:51 +05:30
Mohammad Shoaib 78a466a010 Added category to manual read tile key 2022-05-10 20:47:40 +05:30
Mohammad Shoaib 7688bc0dd5 Fixed giant crop narration 2022-05-10 20:47:21 +05:30
Mohammad Shoaib 6acfd313e8 Added quartz, etc. to mine item category 2022-05-10 20:05:02 +05:30
Mohammad Shoaib cedf2dcc03 Added area narration when switching to other area 2022-05-10 19:48:04 +05:30
Mohammad Shoaib 672f9a1f00 Added twig to debris category 2022-05-10 19:39:22 +05:30
bradjrenshaw 838273b3ce Read blocked or empty tiles if no object is on a tile. 2022-05-10 00:05:35 -04:00
bradjrenshaw bc6ec49619 Initial implementation of cursor movement and mouse snapping to cursor location. 2022-05-09 23:53:29 -04:00
bradjrenshaw 2717efa42e Merge branch 'master' into TileViewer 2022-05-09 20:05:12 -04:00
Mohammad Shoaib 730beeffaa removed birdie from static tiles
(as she was already detectable)
2022-05-10 00:21:37 +05:30
Mohammad Shoaib eebabe8fe6 Added statue of perfection & endless fortune 2022-05-10 00:02:15 +05:30
Mohammad Shoaib 415231c65d Added farm and farm cave tiles 2022-05-09 23:29:21 +05:30
Mohammad Shoaib 73fb957a46
Merge pull request #15 from bradjrenshaw/WalnutLocationFix
Walnut location fix
2022-05-09 20:59:54 +05:30
bradjrenshaw 72f1835929 Brief refactor for readability. 2022-05-08 19:31:04 -04:00
bradjrenshaw 43c3dbb0d8 Added diggable golden walnut locations to ginger Island tileInfo. 2022-05-08 19:22:28 -04:00
bradjrenshaw 0b318b29ad MouseHandler refactoring. 2022-05-08 18:46:37 -04:00
bradjrenshaw 797e0ab136 Add cursor keys to mod config. 2022-05-08 01:55:38 -04:00
bradjrenshaw 8dd96d84b3 Refactoring mouse handling into a separate class. 2022-05-08 01:47:32 -04:00
bradjrenshaw bd29fae9ca Initial refactor of mouse handling. 2022-05-06 19:47:11 -04:00
Mohammad Shoaib f9fbbc4181 Moved static-tiles.json to assets folder 2022-05-05 00:08:17 +05:30
Mohammad Shoaib 8355f05684 Added event tiles 2022-05-05 00:01:56 +05:30
Mohammad Shoaib 4cc4bba62e Added trailer tiles 2022-05-04 23:33:05 +05:30
Mohammad Shoaib 14506b1e11 Added Josh House tiles 2022-05-04 23:29:15 +05:30
Mohammad Shoaib 297eca3adc Added haley house tiles 2022-05-02 23:43:43 +05:30
Mohammad Shoaib 1aa569ab84 Added sam house tiles 2022-05-02 23:31:17 +05:30
Mohammad Shoaib b287021db7 Added science house tiles 2022-05-02 23:26:41 +05:30
Mohammad Shoaib 110989ebcc Added saloon tiles 2022-05-02 23:10:50 +05:30
Mohammad Shoaib 56c17bf96d Added animal shop tiles 2022-05-02 23:02:22 +05:30
Mohammad Shoaib 03c3ba4981 Added tiles for hospital & blacksmith 2022-05-02 22:46:08 +05:30
Mohammad Shoaib 4735bde498 Added secret woods tiles 2022-05-02 22:23:06 +05:30
Mohammad Shoaib a6f8e5fe35 Added wizard house tiles 2022-05-02 14:36:17 +05:30
Mohammad Shoaib c00ae77d03 Added sewer tiles 2022-05-02 14:28:13 +05:30
Mohammad Shoaib 03e8eead73 Added seed shop tiles 2022-05-02 14:21:25 +05:30
Mohammad Shoaib 83a26bb04a Added movie theater tiles 2022-05-02 14:12:17 +05:30
Mohammad Shoaib c2630ffb70 Added mountain, backwoods, railroad tiles 2022-05-02 13:41:56 +05:30
Mohammad Shoaib 825c8b6a56 Added mine tiles 2022-05-02 13:06:49 +05:30
Mohammad Shoaib 5cdf1da37f Added manor house tiles 2022-05-02 13:02:02 +05:30
Mohammad Shoaib 130204e39e Added mail box to read tile 2022-05-02 12:54:04 +05:30
Mohammad Shoaib ce69a5ad98 Added SearchLocation() to API 2022-05-01 14:26:08 +05:30
Mohammad Shoaib 6acfb4ff4e Implemented BFS to SearchLocation() 2022-05-01 14:17:31 +05:30
Mohammad Shoaib c3c0169ac6 Added method to scan entire location for tiles 2022-05-01 13:20:18 +05:30
Mohammad Shoaib 666dfd745b Added library museum tiles 2022-04-30 23:45:58 +05:30
Mohammad Shoaib 33b42b0901 Added joja mart tiles 2022-04-30 23:43:00 +05:30
Mohammad Shoaib d3055f7cbe Added ginger island tiles and stuff 2022-04-30 23:37:41 +05:30
Mohammad Shoaib b16fa50cd2 Added community center exit 2022-04-30 22:37:35 +05:30
Mohammad Shoaib 45ad9ec42f Added cellar exit 2022-04-30 22:34:31 +05:30
Mohammad Shoaib ababbcbe07 Added Beach night market static tiles 2022-04-30 22:29:20 +05:30
Mohammad Shoaib d933152982 Added forest static & dynamic tiles 2022-04-30 22:09:53 +05:30
Mohammad Shoaib cb450b95fa Added exits for farm houses 2022-04-30 20:23:17 +05:30
Mohammad Shoaib 97fabf7638 Added building human and animal doors to read tile 2022-04-30 20:16:51 +05:30
Mohammad Shoaib e1e75ab449 Added exit points to existing maps 2022-04-30 20:04:54 +05:30
Mohammad Shoaib 08af64d560 Added fish shop and boat tunnel static tiles 2022-04-29 20:22:56 +05:30
Mohammad Shoaib 9e28399a0e Added desert static tiles 2022-04-29 20:00:26 +05:30
Mohammad Shoaib c4290173fd Added missed rewards bundle to read tile 2022-04-29 19:46:26 +05:30
Mohammad Shoaib 2dbc52a994 Added club/casino static tiles 2022-04-29 19:41:30 +05:30
Mohammad Shoaib d4e31f8a1a Added caldera static tiles 2022-04-29 00:12:13 +05:30
Mohammad Shoaib 4dff690bbe Added beach bridge 2022-04-28 23:50:26 +05:30
Mohammad Shoaib fb7791e7ba Added adventure guild static tiles 2022-04-28 23:12:53 +05:30
Mohammad Shoaib 4db3c305f3 Added slime hutch static tiles 2022-04-28 23:05:52 +05:30
Mohammad Shoaib c0ad4b83ab Code organization 2022-04-28 22:54:19 +05:30
Mohammad Shoaib 253d01ac6f Added barn to static tiles 2022-04-28 20:12:07 +05:30
Mohammad Shoaib 5355a2015e Added more dynamic tiles 2022-04-28 20:00:27 +05:30
Mohammad Shoaib d9f99ac922 Added static tiles in coops 2022-04-24 13:14:52 +05:30
Mohammad Shoaib bafc966072 Added command to refresh static tiles json 2022-04-24 13:14:34 +05:30
Mohammad Shoaib 2a03fa6724 Added interactables to town location 2022-04-24 12:26:53 +05:30
Mohammad Shoaib 21c5080f73 Added more to the default exclusion list 2022-04-24 12:14:18 +05:30
Mohammad Shoaib 864ad56cfc Added bridge in CATEGORY 2022-04-24 12:10:13 +05:30
Mohammad Shoaib 4082cc033b multiple values for same tile 2022-04-24 11:57:14 +05:30
Mohammad Shoaib 98b4247cbb Bug fix 2022-04-23 18:29:35 +05:30
Mohammad Shoaib ca8ecd845b Added category to static tiles 2022-04-23 18:21:50 +05:30
Mohammad Shoaib 5148139825 Added static tiles to tile info 2022-04-23 17:54:19 +05:30
Mohammad Shoaib 4590f64736 Added quest progress for special order quests 2022-04-23 16:56:22 +05:30
Mohammad Shoaib bfdb7c9f5b Bug fix in quest log patch 2022-04-23 16:34:53 +05:30
Mohammad Shoaib 49ec3fc85b Fixed stuttering in animal menu 2022-04-23 15:35:36 +05:30
Mohammad Shoaib d051e4359c Fixed stuttering in pond query menu 2022-04-23 15:30:47 +05:30
Mohammad Shoaib d9bb4aac35 Bug fix in read tile 2022-04-15 16:02:24 +05:30
Mohammad Shoaib c3882e7ea9 Added lava and cooled lava to read tile 2022-04-15 15:28:19 +05:30
Mohammad Shoaib a0b6159df8 Fixed bug 2022-04-15 14:49:40 +05:30
Mohammad Shoaib 4351182984 Organized code and added parrot perch to read tile 2022-04-15 14:27:52 +05:30
Mohammad Shoaib f0a85e0be9 Patched field office menu 2022-04-14 12:59:23 +05:30
Mohammad Shoaib 559922c0a9 Fixed item list menu 2022-04-14 12:34:05 +05:30
Mohammad Shoaib ffe4fff5d2 Patched item list menu 2022-04-14 12:08:00 +05:30
Mohammad Shoaib bf9ca47dc4 Patched forge menu 2022-04-12 23:54:46 +05:30
Mohammad Shoaib 3b55110e11 Patched pond query menu 2022-04-12 23:37:34 +05:30
Mohammad Shoaib 38f32c2b48 Patched tailoring menu 2022-04-12 22:50:36 +05:30
Mohammad Shoaib a19186d30d Patching tailoring menu 2022-04-12 00:00:00 +05:30
Mohammad Shoaib a1a7ed9281 Bug fixes 2022-04-11 23:48:20 +05:30
Mohammad Shoaib 0e1ab968e7 Patched joja community development menu 2022-04-11 22:48:56 +05:30
Mohammad Shoaib 4b6879b1f3 Patched choose from list(jukebox) menu 2022-04-09 17:11:14 +05:30
Mohammad Shoaib 9e1c263cf8 Fixed warnings 2022-04-09 15:48:13 +05:30
Mohammad Shoaib 416b573f43 Fixed naming menu 2022-04-09 15:15:50 +05:30
Mohammad Shoaib 6c7b845f15 Added keybind to donate item to museum 2022-04-08 17:07:32 +05:30
Mohammad Shoaib 8bd44fe360 Fixed bug in crafting/cooking menu 2022-04-08 15:50:57 +05:30
Mohammad Shoaib 025956f66c Added recipe skipping when pressing c if it is not unlocked 2022-04-05 17:37:09 +05:30
Mohammad Shoaib afc54a0eef Patched Animal Query Menu 2022-04-05 17:21:35 +05:30
Mohammad Shoaib 041afa68a0 Added check for unknown recipe in crafting/cooking menu 2022-04-05 13:13:11 +05:30
Mohammad Shoaib 53f7e3ceb0 Patched collections page's letter viewer menu 2022-04-05 12:44:32 +05:30
Mohammad Shoaib 9fe91faeee Organized code 2022-04-05 12:17:28 +05:30
Mohammad Shoaib 7d18bbce44 Fixed back button not working in character customization 2022-04-02 16:50:40 +05:30
Mohammad Shoaib dc853a1d7c Added co-op related options to character customization menu 2022-04-02 16:33:53 +05:30
Mohammad Shoaib 7ecf7fb30c Added pet to character customization menu 2022-04-02 15:49:18 +05:30
Mohammad Shoaib bfad0676bd Code Cleanup 2022-03-31 14:31:09 +05:30
Mohammad Shoaib 7063305510 Fixed fullscreen indication 2022-03-29 19:59:47 +05:30
Mohammad Shoaib d24c224d1d Code cleanup 2022-03-29 19:57:38 +05:30
Mohammad Shoaib 5d8912ae7c Added fullscreen indication 2022-03-29 19:25:53 +05:30
Mohammad Shoaib 101d0f496c Removed duplicate code 2022-03-21 12:33:44 +05:30
Mohammad Shoaib fb087476d2 Added mod config with keybindings 2022-03-21 12:12:50 +05:30
Mohammad Shoaib 9607a008e0 Added mod config 2022-03-21 11:13:55 +05:30
Mohammad Shoaib 3b4bea69f2
Merge pull request #12 from bradjrenshaw/master
added config and option to toggle coordinate verbosity
2022-03-21 10:58:05 +05:30
bradjrenshaw f9b4bc88db added config and option to toggle coordinate verbosity; fixed coordinate formatting of comma. 2022-03-20 16:26:30 -04:00
Mohammad Shoaib b3a9e97016 more readability 2022-03-20 13:04:24 +05:30
Mohammad Shoaib b47e1b5cd6 Beta 1.1.2 2022-03-20 12:35:50 +05:30
Mohammad Shoaib 2a003fc6ba Fixed busy stone 2022-03-20 12:26:01 +05:30
Mohammad Shoaib b3bad018dc
Merge pull request #11 from TrueBlindGaming/master
fix for busy stones..mostly
2022-03-20 12:10:12 +05:30
TrueBlindGaming b7e01c1dc1 fix for missing bundle 2022-03-19 16:59:42 -06:00
TrueBlindGaming eb68e61bf7 fix for busy stones..mostly 2022-03-19 12:25:07 -06:00
Mohammad Shoaib 092960f571 Fixed museum menu 2022-03-19 15:44:57 +05:30
Mohammad Shoaib 32d354f23c Fixing museum menu 2022-03-19 15:23:18 +05:30
Mohammad Shoaib 5b0c6fe154 Fixed letter viewer menu patch 2022-03-19 14:30:37 +05:30
Mohammad Shoaib 49be1d3e6b Bug fix in bundle menu 2022-03-19 14:12:47 +05:30
Mohammad Shoaib 99961d8e62 Added alt + k to narrate current location name 2022-03-19 14:03:21 +05:30
Mohammad Shoaib 18a671d97a Added the Missing Bundle 2022-03-19 13:57:55 +05:30
Mohammad Shoaib 5e65ca8dbd Added the Missing Bundle 2022-03-19 13:12:45 +05:30
Mohammad Shoaib 2d3d79be04 Added alt + j keybind 2022-03-19 12:58:24 +05:30
Mohammad Shoaib 674cd80398 Added machine status 2022-03-19 12:58:24 +05:30
Mohammad Shoaib fcd2243cc5 Made reading floorings toggleable | Added names to tuples making it more readable 2022-03-19 12:58:24 +05:30
Mohammad Shoaib 0de1cd419b Removed [ ] from charachter customization menu 2022-03-19 12:58:24 +05:30
64 changed files with 7971 additions and 2631 deletions

5
.gitignore vendored
View File

@ -3,7 +3,12 @@
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
*.dll
stardew-access/assets/custom-tiles.json
.vscode/*
.git-old/
bin/
# User-specific files
*.rsuser

View File

@ -16,13 +16,13 @@ namespace stardew_access.ScreenReader
/// <param name="center">The starting point.</param>
/// <param name="limit">The limiting factor or simply radius of the search area.</param>
/// <returns>A dictionary with all the detected tiles along with the name of the object on it and it's category.</returns>
public Dictionary<Vector2, (string, string)> SearchNearbyTiles(Vector2 center, int limit)
public Dictionary<Vector2, (string name, string category)> SearchNearbyTiles(Vector2 center, int limit)
{
/*
* How to use the Dictionary to get the name and category of a tile:-
*
* string? objectName = detectedTiles.GetValueOrDefault(center).Item1;
* string? objectCategory = detectedTiles.GetValueOrDefault(center).Item2;
* string tileName = detectedTiles.GetValueOrDefault(tilePosition).name;
* string tileCategory = detectedTiles.GetValueOrDefault(tilePosition).category;
*
* Here detectedTiles is the Dictionary returned by this method
*/
@ -30,14 +30,32 @@ namespace stardew_access.ScreenReader
return new Radar().SearchNearbyTiles(center, limit, false);
}
/// <summary>
/// Search the entire location using Breadth First Search algorithm(BFS).
/// </summary>
/// <returns>A dictionary with all the detected tiles along with the name of the object on it and it's category.</returns>
public Dictionary<Vector2, (string name, string category)> SearchLocation()
{
/*
* How to use the Dictionary to get the name and category of a tile:-
*
* string tileName = detectedTiles.GetValueOrDefault(tilePosition).name;
* string tileCategory = detectedTiles.GetValueOrDefault(tilePosition).category;
*
* Here detectedTiles is the Dictionary returned by this method
*/
return new Radar().SearchLocation();
}
/// <summary>
/// Check the tile for any object
/// </summary>
/// <param name="tile">The tile where we want to check the name and category of object if any</param>
/// <returns>Name of the object as the first item (Item1) and category as the second item (Item2). Returns null if no object found.</returns>
public (string?, string?) GetNameWithCategoryNameAtTile(Vector2 tile)
/// <returns>Name of the object as the first item (name) and category as the second item (category). Returns null if no object found.</returns>
public (string? name, string? category) GetNameWithCategoryNameAtTile(Vector2 tile)
{
return ReadTile.getNameWithCategoryNameAtTile(tile);
return TileInfo.getNameWithCategoryNameAtTile(tile);
}
/// <summary>
@ -47,7 +65,7 @@ namespace stardew_access.ScreenReader
/// <returns>Name of the object. Returns null if no object found.</returns>
public string? GetNameAtTile(Vector2 tile)
{
return ReadTile.getNameAtTile(tile);
return TileInfo.getNameAtTile(tile);
}
/// <summary>Speaks the text via the loaded screen reader (if any).</summary>
@ -55,10 +73,10 @@ namespace stardew_access.ScreenReader
/// <param name="interrupt">Whether to skip the currently speaking text or not.</param>
public void Say(String text, Boolean interrupt)
{
if (MainClass.GetScreenReader() == null)
if (MainClass.ScreenReader == null)
return;
MainClass.GetScreenReader().Say(text, interrupt);
MainClass.ScreenReader.Say(text, interrupt);
}
/// <summary>Speaks the text via the loaded screen reader (if any).
@ -67,10 +85,10 @@ namespace stardew_access.ScreenReader
/// <param name="interrupt">Whether to skip the currently speaking text or not.</param>
public void SayWithChecker(String text, Boolean interrupt)
{
if (MainClass.GetScreenReader() == null)
if (MainClass.ScreenReader == null)
return;
MainClass.GetScreenReader().SayWithChecker(text, interrupt);
MainClass.ScreenReader.SayWithChecker(text, interrupt);
}
/// <summary>Speaks the text via the loaded screen reader (if any).
@ -80,10 +98,10 @@ namespace stardew_access.ScreenReader
/// <param name="interrupt">Whether to skip the currently speaking text or not.</param>
public void SayWithMenuChecker(String text, Boolean interrupt)
{
if (MainClass.GetScreenReader() == null)
if (MainClass.ScreenReader == null)
return;
MainClass.GetScreenReader().SayWithMenuChecker(text, interrupt);
MainClass.ScreenReader.SayWithMenuChecker(text, interrupt);
}
/// <summary>Speaks the text via the loaded screen reader (if any).
@ -93,10 +111,10 @@ namespace stardew_access.ScreenReader
/// <param name="interrupt">Whether to skip the currently speaking text or not.</param>
public void SayWithChatChecker(String text, Boolean interrupt)
{
if (MainClass.GetScreenReader() == null)
if (MainClass.ScreenReader == null)
return;
MainClass.GetScreenReader().SayWithChatChecker(text, interrupt);
MainClass.ScreenReader.SayWithChatChecker(text, interrupt);
}
/// <summary>Speaks the text via the loaded screen reader (if any).
@ -108,10 +126,10 @@ namespace stardew_access.ScreenReader
/// <param name="interrupt">Whether to skip the currently speaking text or not.</param>
public void SayWithTileQuery(String text, int x, int y, Boolean interrupt)
{
if (MainClass.GetScreenReader() == null)
if (MainClass.ScreenReader == null)
return;
MainClass.GetScreenReader().SayWithTileQuery(text, x, y, interrupt);
MainClass.ScreenReader.SayWithTileQuery(text, x, y, interrupt);
}
}

View File

@ -11,49 +11,66 @@ namespace stardew_access
{
internal static void Initialize()
{
IModHelper helper = MainClass.ModHelper;
//TODO organise this, create separate method for all commands
IModHelper? helper = MainClass.ModHelper;
if (helper == null)
return;
#region Read Tile
helper.ConsoleCommands.Add("readtile", "Toggle read tile feature.", (string commmand, string[] args) =>
{
MainClass.readTile = !MainClass.readTile;
MainClass.Config.ReadTile = !MainClass.Config.ReadTile;
helper.WriteConfig(MainClass.Config);
MainClass.GetMonitor().Log("Read Tile is " + (MainClass.readTile ? "on" : "off"), LogLevel.Info);
MainClass.InfoLog("Read Tile is " + (MainClass.Config.ReadTile ? "on" : "off"));
});
helper.ConsoleCommands.Add("snapmouse", "Toggle snap mouse feature.", (string commmand, string[] args) =>
helper.ConsoleCommands.Add("flooring", "Toggle flooring in read tile.", (string commmand, string[] args) =>
{
MainClass.snapMouse = !MainClass.snapMouse;
MainClass.Config.ReadFlooring = !MainClass.Config.ReadFlooring;
helper.WriteConfig(MainClass.Config);
MainClass.GetMonitor().Log("Snap Mouse is " + (MainClass.snapMouse ? "on" : "off"), LogLevel.Info);
MainClass.InfoLog("Flooring is " + (MainClass.Config.ReadFlooring ? "on" : "off"));
});
helper.ConsoleCommands.Add("radar", "Toggle radar feature.", (string commmand, string[] args) =>
helper.ConsoleCommands.Add("watered", "Toggle speaking watered or unwatered for crops.", (string commmand, string[] args) =>
{
MainClass.radar = !MainClass.radar;
MainClass.Config.WateredToggle = !MainClass.Config.WateredToggle;
helper.WriteConfig(MainClass.Config);
MainClass.GetMonitor().Log("Radar " + (MainClass.radar ? "on" : "off"), LogLevel.Info);
MainClass.InfoLog("Watered toggle is " + (MainClass.Config.WateredToggle ? "on" : "off"));
});
#endregion
#region Radar Feature
helper.ConsoleCommands.Add("radar", "Toggle radar feature.", (string commmand, string[] args) =>
{
MainClass.Config.Radar = !MainClass.Config.Radar;
helper.WriteConfig(MainClass.Config);
MainClass.InfoLog("Radar " + (MainClass.Config.Radar ? "on" : "off"));
});
helper.ConsoleCommands.Add("rdebug", "Toggle debugging in radar feature.", (string commmand, string[] args) =>
{
MainClass.radarDebug = !MainClass.radarDebug;
MainClass.GetMonitor().Log("Radar debugging " + (MainClass.radarDebug ? "on" : "off"), LogLevel.Info);
MainClass.InfoLog("Radar debugging " + (MainClass.radarDebug ? "on" : "off"));
});
helper.ConsoleCommands.Add("rstereo", "Toggle stereo sound in radar feature.", (string commmand, string[] args) =>
{
MainClass.radarStereoSound = !MainClass.radarStereoSound;
MainClass.Config.RadarStereoSound = !MainClass.Config.RadarStereoSound;
helper.WriteConfig(MainClass.Config);
MainClass.GetMonitor().Log("Stereo sound is " + (MainClass.radarStereoSound ? "on" : "off"), LogLevel.Info);
MainClass.InfoLog("Stereo sound is " + (MainClass.Config.RadarStereoSound ? "on" : "off"));
});
helper.ConsoleCommands.Add("rfocus", "Toggle focus mode in radar feature.", (string commmand, string[] args) =>
{
bool focus = MainClass.RadarFeature.ToggleFocus();
MainClass.GetMonitor().Log("Focus mode is " + (focus ? "on" : "off"), LogLevel.Info);
MainClass.InfoLog("Focus mode is " + (focus ? "on" : "off"));
});
helper.ConsoleCommands.Add("rdelay", "Set the delay of radar feature in milliseconds.", (string commmand, string[] args) =>
@ -72,19 +89,19 @@ namespace stardew_access
{
MainClass.RadarFeature.delay = delay;
if (delay >= 1000)
MainClass.GetMonitor().Log($"Delay set to {MainClass.RadarFeature.delay} milliseconds.", LogLevel.Info);
MainClass.InfoLog($"Delay set to {MainClass.RadarFeature.delay} milliseconds.");
else
MainClass.GetMonitor().Log($"Delay should be atleast 1 second or 1000 millisecond long.", LogLevel.Info);
MainClass.InfoLog($"Delay should be atleast 1 second or 1000 millisecond long.");
}
else
{
MainClass.GetMonitor().Log("Invalid delay amount, it can only be in numeric form.", LogLevel.Info);
MainClass.InfoLog("Invalid delay amount, it can only be in numeric form.");
}
}
else
{
MainClass.GetMonitor().Log("Enter the delay amount (in milliseconds)!", LogLevel.Info);
MainClass.InfoLog("Enter the delay amount (in milliseconds)!");
}
});
@ -105,19 +122,19 @@ namespace stardew_access
{
MainClass.RadarFeature.range = range;
if (range >= 2 && range <= 10)
MainClass.GetMonitor().Log($"Range set to {MainClass.RadarFeature.range}.", LogLevel.Info);
MainClass.InfoLog($"Range set to {MainClass.RadarFeature.range}.");
else
MainClass.GetMonitor().Log($"Range should be atleast 2 and maximum 10.", LogLevel.Info);
MainClass.InfoLog($"Range should be atleast 2 and maximum 10.");
}
else
{
MainClass.GetMonitor().Log("Invalid range amount, it can only be in numeric form.", LogLevel.Info);
MainClass.InfoLog("Invalid range amount, it can only be in numeric form.");
}
}
else
{
MainClass.GetMonitor().Log("Enter the range amount!", LogLevel.Info);
MainClass.InfoLog("Enter the range amount!");
}
});
@ -136,16 +153,16 @@ namespace stardew_access
if (!MainClass.RadarFeature.exclusions.Contains(keyToAdd))
{
MainClass.RadarFeature.exclusions.Add(keyToAdd);
MainClass.GetMonitor().Log($"Added {keyToAdd} key to exclusions list.", LogLevel.Info);
MainClass.InfoLog($"Added {keyToAdd} key to exclusions list.");
}
else
{
MainClass.GetMonitor().Log($"{keyToAdd} key already present in the list.", LogLevel.Info);
MainClass.InfoLog($"{keyToAdd} key already present in the list.");
}
}
else
{
MainClass.GetMonitor().Log("Unable to add the key to exclusions list.", LogLevel.Info);
MainClass.InfoLog("Unable to add the key to exclusions list.");
}
});
@ -161,16 +178,16 @@ namespace stardew_access
if (MainClass.RadarFeature.exclusions.Contains(keyToAdd))
{
MainClass.RadarFeature.exclusions.Remove(keyToAdd);
MainClass.GetMonitor().Log($"Removed {keyToAdd} key from exclusions list.", LogLevel.Info);
MainClass.InfoLog($"Removed {keyToAdd} key from exclusions list.");
}
else
{
MainClass.GetMonitor().Log($"Cannot find {keyToAdd} key in exclusions list.", LogLevel.Info);
MainClass.InfoLog($"Cannot find {keyToAdd} key in exclusions list.");
}
}
else
{
MainClass.GetMonitor().Log("Unable to remove the key from exclusions list.", LogLevel.Info);
MainClass.InfoLog("Unable to remove the key from exclusions list.");
}
});
@ -183,23 +200,23 @@ namespace stardew_access
{
toPrint = $"{toPrint}\t{i + 1}: {MainClass.RadarFeature.exclusions[i]}";
}
MainClass.GetMonitor().Log(toPrint, LogLevel.Info);
MainClass.InfoLog(toPrint);
}
else
{
MainClass.GetMonitor().Log("No exclusions found.", LogLevel.Info);
MainClass.InfoLog("No exclusions found.");
}
});
helper.ConsoleCommands.Add("reclear", "Clear the focus exclusions in the radar featrure.", (string commmand, string[] args) =>
{
MainClass.RadarFeature.exclusions.Clear();
MainClass.GetMonitor().Log($"Cleared the focus list in the exclusions feature.", LogLevel.Info);
MainClass.InfoLog($"Cleared the focus list in the exclusions feature.");
});
helper.ConsoleCommands.Add("recount", "Number of exclusions in the radar feature.", (string commmand, string[] args) =>
{
MainClass.GetMonitor().Log($"There are {MainClass.RadarFeature.exclusions.Count} exclusiond in the radar feature.", LogLevel.Info);
MainClass.InfoLog($"There are {MainClass.RadarFeature.exclusions.Count} exclusiond in the radar feature.");
});
#endregion
@ -216,16 +233,16 @@ namespace stardew_access
if (!MainClass.RadarFeature.focus.Contains(keyToAdd))
{
MainClass.RadarFeature.focus.Add(keyToAdd);
MainClass.GetMonitor().Log($"Added {keyToAdd} key to focus list.", LogLevel.Info);
MainClass.InfoLog($"Added {keyToAdd} key to focus list.");
}
else
{
MainClass.GetMonitor().Log($"{keyToAdd} key already present in the list.", LogLevel.Info);
MainClass.InfoLog($"{keyToAdd} key already present in the list.");
}
}
else
{
MainClass.GetMonitor().Log("Unable to add the key to focus list.", LogLevel.Info);
MainClass.InfoLog("Unable to add the key to focus list.");
}
});
@ -241,16 +258,16 @@ namespace stardew_access
if (MainClass.RadarFeature.focus.Contains(keyToAdd))
{
MainClass.RadarFeature.focus.Remove(keyToAdd);
MainClass.GetMonitor().Log($"Removed {keyToAdd} key from focus list.", LogLevel.Info);
MainClass.InfoLog($"Removed {keyToAdd} key from focus list.");
}
else
{
MainClass.GetMonitor().Log($"Cannot find {keyToAdd} key in focus list.", LogLevel.Info);
MainClass.InfoLog($"Cannot find {keyToAdd} key in focus list.");
}
}
else
{
MainClass.GetMonitor().Log("Unable to remove the key from focus list.", LogLevel.Info);
MainClass.InfoLog("Unable to remove the key from focus list.");
}
});
@ -263,23 +280,23 @@ namespace stardew_access
{
toPrint = $"{toPrint}\t{i + 1}): {MainClass.RadarFeature.focus[i]}";
}
MainClass.GetMonitor().Log(toPrint, LogLevel.Info);
MainClass.InfoLog(toPrint);
}
else
{
MainClass.GetMonitor().Log("No objects found in the focus list.", LogLevel.Info);
MainClass.InfoLog("No objects found in the focus list.");
}
});
helper.ConsoleCommands.Add("rfclear", "Clear the focus list in the radar featrure.", (string commmand, string[] args) =>
{
MainClass.RadarFeature.focus.Clear();
MainClass.GetMonitor().Log($"Cleared the focus list in the radar feature.", LogLevel.Info);
MainClass.InfoLog($"Cleared the focus list in the radar feature.");
});
helper.ConsoleCommands.Add("rfcount", "Number of list in the radar feature.", (string commmand, string[] args) =>
{
MainClass.GetMonitor().Log($"There are {MainClass.RadarFeature.focus.Count} objects in the focus list in the radar feature.", LogLevel.Info);
MainClass.InfoLog($"There are {MainClass.RadarFeature.focus.Count} objects in the focus list in the radar feature.");
});
#endregion
@ -290,14 +307,14 @@ namespace stardew_access
{
if (Game1.currentLocation is not Farm)
{
MainClass.GetMonitor().Log("Can only use this command in the farm", LogLevel.Info);
MainClass.InfoLog("Can only use this command in the farm");
return;
}
string? indexInString = args.ElementAtOrDefault(0);
if (indexInString == null)
{
MainClass.GetMonitor().Log("Enter the index too!", LogLevel.Info);
MainClass.InfoLog("Enter the index too!");
return;
}
@ -306,12 +323,12 @@ namespace stardew_access
if (!isParsable || !(index >= 0 && index <= 9))
{
MainClass.GetMonitor().Log("Index can only be a number and from 0 to 9 only", LogLevel.Info);
MainClass.InfoLog("Index can only be a number and from 0 to 9 only");
return;
}
BuildingNAnimalMenuPatches.marked[index] = new Vector2((int)Game1.player.getTileX(), (int)Game1.player.getTileY());
MainClass.GetMonitor().Log($"Location {(int)Game1.player.getTileX()}x {(int)Game1.player.getTileY()}y added at {index} index.", LogLevel.Info);
MainClass.InfoLog($"Location {(int)Game1.player.getTileX()}x {(int)Game1.player.getTileY()}y added at {index} index.");
});
helper.ConsoleCommands.Add("marklist", "List all marked positions.", (string commmand, string[] args) =>
@ -326,19 +343,170 @@ namespace stardew_access
}
if (toPrint == "")
MainClass.GetMonitor().Log("No positions marked!", LogLevel.Info);
MainClass.InfoLog("No positions marked!");
else
MainClass.GetMonitor().Log($"Marked positions:{toPrint}\nOpen command menu and use pageup and pagedown to check the list", LogLevel.Info);
MainClass.InfoLog($"Marked positions:{toPrint}\nOpen command menu and use pageup and pagedown to check the list");
});
helper.ConsoleCommands.Add("buildlist", "List all buildings for selection for upgrading/demolishing/painting", (string commmand, string[] args) =>
{
if ((Game1.activeClickableMenu is not CarpenterMenu && Game1.activeClickableMenu is not PurchaseAnimalsMenu) || !BuildingNAnimalMenuPatches.isOnFarm)
onBuildListCalled();
});
helper.ConsoleCommands.Add("buildsel", "Select the building index which you want to upgrade/demolish/paint", (string commmand, string[] args) =>
{
MainClass.GetMonitor().Log($"Cannot list buildings.", LogLevel.Info);
if ((Game1.activeClickableMenu is not CarpenterMenu && Game1.activeClickableMenu is not PurchaseAnimalsMenu && Game1.activeClickableMenu is not AnimalQueryMenu) || !BuildingNAnimalMenuPatches.isOnFarm)
{
MainClass.InfoLog($"Cannot select building.");
return;
}
string? indexInString = args.ElementAtOrDefault(0);
if (indexInString == null)
{
MainClass.InfoLog("Enter the index of the building too! Use buildlist");
return;
}
int index;
bool isParsable = int.TryParse(indexInString, out index);
if (!isParsable)
{
MainClass.InfoLog("Index can only be a number.");
return;
}
string? positionIndexInString = args.ElementAtOrDefault(1);
int positionIndex = 0;
if (BuildingNAnimalMenuPatches.isMoving)
{
if (BuildingNAnimalMenuPatches.isConstructing || BuildingNAnimalMenuPatches.isMoving)
{
if (BuildingNAnimalMenuPatches.availableBuildings[index] == null)
{
MainClass.InfoLog($"No building found with index {index}. Use buildlist.");
return;
}
if (positionIndexInString == null)
{
MainClass.InfoLog("Enter the index of marked place too! Use marklist.");
return;
}
isParsable = int.TryParse(positionIndexInString, out positionIndex);
if (!isParsable)
{
MainClass.InfoLog("Index can only be a number.");
return;
}
}
}
else if (BuildingNAnimalMenuPatches.isConstructing && !BuildingNAnimalMenuPatches.isUpgrading)
{
if (BuildingNAnimalMenuPatches.marked[index] == Vector2.Zero)
{
MainClass.InfoLog($"No marked position found at {index} index.");
return;
}
}
else
{
if (BuildingNAnimalMenuPatches.availableBuildings[index] == null)
{
MainClass.InfoLog($"No building found with index {index}. Use buildlist.");
return;
}
}
string? response = null;
if (Game1.activeClickableMenu is PurchaseAnimalsMenu)
{
BuildingNAnimalMenuPatches.PurchaseAnimal(BuildingNAnimalMenuPatches.availableBuildings[index]);
}
else if (Game1.activeClickableMenu is AnimalQueryMenu)
{
BuildingNAnimalMenuPatches.MoveAnimal(BuildingNAnimalMenuPatches.availableBuildings[index]);
}
else
{
if (BuildingNAnimalMenuPatches.isConstructing && !BuildingNAnimalMenuPatches.isUpgrading) { response = BuildingNAnimalMenuPatches.Contstruct(BuildingNAnimalMenuPatches.marked[index]); }
else if (BuildingNAnimalMenuPatches.isMoving) { response = BuildingNAnimalMenuPatches.Move(BuildingNAnimalMenuPatches.availableBuildings[index], BuildingNAnimalMenuPatches.marked[positionIndex]); }
else if (BuildingNAnimalMenuPatches.isDemolishing) { response = BuildingNAnimalMenuPatches.Demolish(BuildingNAnimalMenuPatches.availableBuildings[index]); }
else if (BuildingNAnimalMenuPatches.isUpgrading) { response = BuildingNAnimalMenuPatches.Upgrade(BuildingNAnimalMenuPatches.availableBuildings[index]); }
else if (BuildingNAnimalMenuPatches.isPainting) { response = BuildingNAnimalMenuPatches.Paint(BuildingNAnimalMenuPatches.availableBuildings[index]); }
}
if (response != null)
{
MainClass.InfoLog(response);
}
});
#endregion
#region Other
helper.ConsoleCommands.Add("refsr", "Refresh screen reader", (string commmand, string[] args) =>
{
MainClass.ScreenReader.InitializeScreenReader();
MainClass.InfoLog("Screen Reader refreshed!");
});
helper.ConsoleCommands.Add("refmc", "Refresh mod config", (string commmand, string[] args) =>
{
MainClass.Config = helper.ReadConfig<ModConfig>();
MainClass.InfoLog("Mod Config refreshed!");
});
helper.ConsoleCommands.Add("refst", "Refresh static tiles", (string commmand, string[] args) =>
{
MainClass.STiles = new Features.StaticTiles();
MainClass.InfoLog("Static tiles refreshed!");
});
helper.ConsoleCommands.Add("hnspercent", "Toggle between speaking in percentage or full health and stamina.", (string commmand, string[] args) =>
{
MainClass.Config.HealthNStaminaInPercentage = !MainClass.Config.HealthNStaminaInPercentage;
helper.WriteConfig(MainClass.Config);
MainClass.InfoLog("Speaking in percentage is " + (MainClass.Config.HealthNStaminaInPercentage ? "on" : "off"));
});
helper.ConsoleCommands.Add("snapmouse", "Toggle snap mouse feature.", (string commmand, string[] args) =>
{
MainClass.Config.SnapMouse = !MainClass.Config.SnapMouse;
helper.WriteConfig(MainClass.Config);
MainClass.InfoLog("Snap Mouse is " + (MainClass.Config.SnapMouse ? "on" : "off"));
});
helper.ConsoleCommands.Add("warning", "Toggle warnings feature.", (string commmand, string[] args) =>
{
MainClass.Config.Warning = !MainClass.Config.Warning;
helper.WriteConfig(MainClass.Config);
MainClass.InfoLog("Warnings is " + (MainClass.Config.Warning ? "on" : "off"));
});
helper.ConsoleCommands.Add("tts", "Toggles the screen reader/tts", (string commmand, string[] args) =>
{
MainClass.Config.TTS = !MainClass.Config.TTS;
helper.WriteConfig(MainClass.Config);
MainClass.InfoLog("TTS is " + (MainClass.Config.TTS ? "on" : "off"));
});
#endregion
}
internal static void onBuildListCalled()
{
string toPrint = "";
Farm farm = (Farm)Game1.getLocationFromName("Farm");
Netcode.NetCollection<Building> buildings = farm.buildings;
@ -356,109 +524,12 @@ namespace stardew_access
if (toPrint == "")
{
MainClass.GetMonitor().Log("No appropriate buildings to list", LogLevel.Info);
MainClass.InfoLog("No appropriate buildings to list");
}
else
{
MainClass.GetMonitor().Log($"Available buildings:{toPrint}\nOpen command menu and use pageup and pagedown to check the list", LogLevel.Info);
MainClass.InfoLog($"Available buildings:{toPrint}\nOpen command menu and use pageup and pagedown to check the list");
}
});
helper.ConsoleCommands.Add("buildsel", "Select the building index which you want to upgrade/demolish/paint", (string commmand, string[] args) =>
{
if ((Game1.activeClickableMenu is not CarpenterMenu && Game1.activeClickableMenu is not PurchaseAnimalsMenu) || !BuildingNAnimalMenuPatches.isOnFarm)
{
MainClass.GetMonitor().Log($"Cannot select building.", LogLevel.Info);
return;
}
string? indexInString = args.ElementAtOrDefault(0);
if (indexInString == null)
{
MainClass.GetMonitor().Log("Enter the index of the building too! Use buildlist", LogLevel.Info);
return;
}
int index;
bool isParsable = int.TryParse(indexInString, out index);
if (!isParsable)
{
MainClass.GetMonitor().Log("Index can only be a number.", LogLevel.Info);
return;
}
string? positionIndexInString = args.ElementAtOrDefault(1);
int positionIndex = 0;
if (BuildingNAnimalMenuPatches.isMoving)
{
if (BuildingNAnimalMenuPatches.isConstructing || BuildingNAnimalMenuPatches.isMoving)
{
if (BuildingNAnimalMenuPatches.availableBuildings[index] == null)
{
MainClass.GetMonitor().Log($"No building found with index {index}. Use buildlist.", LogLevel.Info);
return;
}
if (positionIndexInString == null)
{
MainClass.GetMonitor().Log("Enter the index of marked place too! Use marklist.", LogLevel.Info);
return;
}
isParsable = int.TryParse(positionIndexInString, out positionIndex);
if (!isParsable)
{
MainClass.GetMonitor().Log("Index can only be a number.", LogLevel.Info);
return;
}
}
}
else if (BuildingNAnimalMenuPatches.isConstructing && !BuildingNAnimalMenuPatches.isUpgrading)
{
if (BuildingNAnimalMenuPatches.marked[index] == Vector2.Zero)
{
MainClass.GetMonitor().Log($"No marked position found at {index} index.", LogLevel.Info);
return;
}
}
else
{
if (BuildingNAnimalMenuPatches.availableBuildings[index] == null)
{
MainClass.GetMonitor().Log($"No building found with index {index}. Use buildlist.", LogLevel.Info);
return;
}
}
string? response = null;
if (Game1.activeClickableMenu is PurchaseAnimalsMenu) { BuildingNAnimalMenuPatches.PurchaseAnimal(BuildingNAnimalMenuPatches.availableBuildings[index]); }
else
{
if (BuildingNAnimalMenuPatches.isConstructing && !BuildingNAnimalMenuPatches.isUpgrading) { response = BuildingNAnimalMenuPatches.Contstruct(BuildingNAnimalMenuPatches.marked[index]); }
else if (BuildingNAnimalMenuPatches.isMoving) { response = BuildingNAnimalMenuPatches.Move(BuildingNAnimalMenuPatches.availableBuildings[index], BuildingNAnimalMenuPatches.marked[positionIndex]); }
else if (BuildingNAnimalMenuPatches.isDemolishing) { response = BuildingNAnimalMenuPatches.Demolish(BuildingNAnimalMenuPatches.availableBuildings[index]); }
else if (BuildingNAnimalMenuPatches.isUpgrading) { response = BuildingNAnimalMenuPatches.Upgrade(BuildingNAnimalMenuPatches.availableBuildings[index]); }
else if (BuildingNAnimalMenuPatches.isPainting) { response = BuildingNAnimalMenuPatches.Paint(BuildingNAnimalMenuPatches.availableBuildings[index]); }
}
if (response != null)
{
MainClass.GetMonitor().Log(response, LogLevel.Info);
}
});
#endregion
helper.ConsoleCommands.Add("refsr", "Refresh screen reader", (string commmand, string[] args) =>
{
MainClass.GetScreenReader().InitializeScreenReader();
MainClass.GetMonitor().Log("Screen Reader refreshed!", LogLevel.Info);
});
}
}
}

View File

@ -1,5 +1,4 @@
using Microsoft.Xna.Framework.Audio;
using StardewModdingAPI;
using StardewValley;
namespace stardew_access
@ -16,6 +15,9 @@ namespace stardew_access
{
try
{
if (MainClass.ModHelper == null)
return;
Dictionary<String, TYPE> soundEffects = new Dictionary<String, TYPE>();
soundEffects.Add("drop_item", TYPE.Sound);
@ -55,7 +57,7 @@ namespace stardew_access
}
SoundEffect effect;
string filePath = Path.Combine(MainClass.ModHelper.DirectoryPath, "sounds", $"{soundEffect.Key}.wav");
string filePath = Path.Combine(MainClass.ModHelper.DirectoryPath, "assets", "sounds", $"{soundEffect.Key}.wav");
using (FileStream stream = new(filePath, FileMode.Open))
{
effect = SoundEffect.FromStream(stream);
@ -71,7 +73,7 @@ namespace stardew_access
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"Unable to initialize custom sounds:\n{e.Message}\n{e.StackTrace}", LogLevel.Error);
MainClass.ErrorLog($"Unable to initialize custom sounds:\n{e.Message}\n{e.StackTrace}");
}
}
}

View File

@ -6,90 +6,159 @@ namespace stardew_access.Features
internal class CurrentPlayer
{
public static int getHealth()
/// <summary>
/// Returns the percentage health remaining of player.
/// </summary>
public static int PercentHealth
{
get
{
if (Game1.player == null)
return 0;
int maxHealth = Game1.player.maxHealth;
int currentHealth = Game1.player.health;
int healthPercentage = (int)(currentHealth * 100) / maxHealth;
return healthPercentage;
return (CurrentHealth * 100) / Game1.player.maxHealth; ;
}
}
public static int getStamina()
/// <summary>
/// Returns the total health player has currently
/// </summary>
public static int CurrentHealth
{
get
{
if (Game1.player == null)
return 0;
int maxStamina = Game1.player.maxStamina;
int currentStamine = (int)Game1.player.stamina;
int staminaPercentage = (int)(currentStamine * 100) / maxStamina;
return staminaPercentage;
return Game1.player.health;
}
}
public static int getPositionX()
/// <summary>
/// Returns the percentage stamine/energy remaining of player.
/// </summary>
public static int PercentStamina
{
get
{
if (Game1.player == null)
return 0;
int x = (int)Game1.player.getTileLocation().X;
return x;
return (CurrentStamina * 100) / Game1.player.maxStamina.Value;
}
}
public static int getPositionY()
/// <summary>
/// Returns the total stamina player has currently
/// </summary>
public static int CurrentStamina
{
get
{
if (Game1.player == null)
return 0;
int y = (int)Game1.player.getTileLocation().Y;
return y;
return (int)Game1.player.stamina;
}
}
public static string getTimeOfDay()
/// <summary>
/// Returns the tile location of the player
/// </summary>
public static Vector2 Position
{
get
{
if (Game1.player == null)
return Vector2.Zero;
return Game1.player.getTileLocation();
}
}
/// <summary>
/// Returns the X coordinate of the player
/// </summary>
public static int PositionX
{
get
{
if (Game1.player == null)
return 0;
return (int)Position.X;
}
}
/// <summary>
/// Returns the Y coordinate of the player
/// </summary>
public static int PositionY
{
get
{
if (Game1.player == null)
return 0;
return (int)Position.Y;
}
}
/// <summary>
/// Returns the time in the 12 hours format
/// </summary>
public static string TimeOfDay
{
get
{
int timeOfDay = Game1.timeOfDay;
int minutes = timeOfDay % 100;
int hours = timeOfDay / 100;
string amOrpm = "A M";
if (hours >= 12)
string amOrpm = hours / 12 == 1 ? "PM" : "AM";
hours = hours % 12;
if (hours == 0) hours = 12;
return $"{hours}:{minutes:00} {amOrpm}";
}
}
/// <summary>
/// Returns the current season
/// </summary>
public static string Season => Game1.CurrentSeasonDisplayName;
/// <summary>
/// Returns the current date of month
/// </summary>
public static int Date => Game1.dayOfMonth;
/// <summary>
/// Returns the current day of week
/// </summary>
/// <returns></returns>
public static string Day => Game1.Date.DayOfWeek.ToString();
/// <summary>
/// Returns the amount of money the player has currently
/// </summary>
public static int Money
{
amOrpm = "P M";
if (hours > 12)
hours -= 12;
}
return $"{hours}:{minutes} {amOrpm}";
}
public static string getSeason()
{
return Game1.CurrentSeasonDisplayName;
}
public static int getDate()
{
return Game1.dayOfMonth;
}
public static string getDay()
{
return Game1.Date.DayOfWeek.ToString();
}
public static int getMoney()
get
{
if (Game1.player == null)
return -1;
return Game1.player.Money;
}
}
public static Vector2 getNextTile()
/// <summary>
/// Returns the tile position of the tile the player is facing
/// </summary>
/// <value></value>
public static Vector2 FacingTile
{
get
{
int x = Game1.player.GetBoundingBox().Center.X;
int y = Game1.player.GetBoundingBox().Center.Y;
@ -117,4 +186,5 @@ namespace stardew_access.Features
return new Vector2(x, y);
}
}
}
}

View File

@ -23,7 +23,7 @@ namespace stardew_access.Features
return;
previousSlotItem = currentSlotItem;
MainClass.GetScreenReader().Say($"{currentSlotItem.DisplayName} Selected", true);
MainClass.ScreenReader.Say($"{currentSlotItem.DisplayName} Selected", true);
}
// Narrates current location's name
@ -38,38 +38,11 @@ namespace stardew_access.Features
return;
previousLocation = currentLocation;
MainClass.GetScreenReader().Say($"{currentLocation.Name} Entered", true);
MainClass.ScreenReader.Say($"{currentLocation.Name} Entered", true);
}
public static void SnapMouseToPlayer()
public static void narrateHudMessages()
{
int x = Game1.player.GetBoundingBox().Center.X - Game1.viewport.X;
int y = Game1.player.GetBoundingBox().Center.Y - Game1.viewport.Y;
int offset = 64;
switch (Game1.player.FacingDirection)
{
case 0:
y -= offset;
break;
case 1:
x += offset;
break;
case 2:
y += offset;
break;
case 3:
x -= offset;
break;
}
Game1.setMousePosition(x, y);
}
public static async void narrateHudMessages()
{
MainClass.isNarratingHudMessage = true;
try
{
if (Game1.hudMessages.Count > 0)
@ -89,18 +62,16 @@ namespace stardew_access.Features
{
MainClass.hudMessageQueryKey = searchQuery;
MainClass.GetScreenReader().Say(toSpeak, true);
MainClass.ScreenReader.Say(toSpeak, true);
}
}
}
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"Unable to narrate hud messages:\n{e.Message}\n{e.StackTrace}", StardewModdingAPI.LogLevel.Error);
MainClass.ErrorLog($"Unable to narrate hud messages:\n{e.Message}\n{e.StackTrace}");
}
await Task.Delay(300);
MainClass.isNarratingHudMessage = false;
}
}
}

View File

@ -1,48 +1,9 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework;
using StardewValley;
using StardewValley.Objects;
using StardewValley.TerrainFeatures;
namespace stardew_access.Features
{
/// <summary>
/// This is a custom enum class and contains the name of groups the objects are divided into for the feature
/// </summary>
public class CATEGORY
{
private string _typeKeyWord;
private CATEGORY(string typeKeyWord)
{
_typeKeyWord = typeKeyWord;
}
public override string ToString()
{
return _typeKeyWord;
}
public static CATEGORY Farmers = new CATEGORY("farmer");
public static CATEGORY FarmAnimals = new CATEGORY("animal");
public static CATEGORY NPCs = new CATEGORY("npc");
public static CATEGORY Furnitures = new CATEGORY("furniture");
public static CATEGORY Flooring = new CATEGORY("flooring");
public static CATEGORY Debris = new CATEGORY("debris");
public static CATEGORY Crops = new CATEGORY("crop");
public static CATEGORY Trees = new CATEGORY("tree");
public static CATEGORY Bush = new CATEGORY("bush");
public static CATEGORY Buildings = new CATEGORY("building");
public static CATEGORY MineItems = new CATEGORY("mine item");
public static CATEGORY ResourceClumps = new CATEGORY("resource clump");
public static CATEGORY Chests = new CATEGORY("chest");
public static CATEGORY JunimoBundle = new CATEGORY("bundle");
public static CATEGORY Doors = new CATEGORY("door"); // Also includes ladders and elevators
public static CATEGORY Others = new CATEGORY("other");
public static CATEGORY WaterTiles = new CATEGORY("water");
}
public class Radar
{
private List<Vector2> closed;
@ -80,7 +41,11 @@ namespace stardew_access.Features
exclusions.Add("tree");
exclusions.Add("flooring");
exclusions.Add("water");
exclusions.Add("debris");
exclusions.Add("grass");
exclusions.Add("decoration");
exclusions.Add("bridge");
exclusions.Add("other");
/* Not excluded Categories
*
@ -89,19 +54,22 @@ namespace stardew_access.Features
* exclusions.Add("animal");
* exclusions.Add("npc");
* exclusions.Add("furniture")
* exclusions.Add("other");
* exclusions.Add("building");
* exclusions.Add("resource clump");
* exclusions.Add("mine item");
* exclusions.Add("chest");
* exclusions.Add("container");
* exclusions.Add("bundle");
* exclusions.Add("door");
* exclusions.Add("machine");
* exclusions.Add("interactable");
*/
}
public async void Run()
public void Run()
{
if (MainClass.radarDebug)
MainClass.GetMonitor().Log($"\n\nRead Tile started", StardewModdingAPI.LogLevel.Debug);
MainClass.DebugLog($"\n\nRead Tile started");
isRunning = true;
Vector2 currPosition = Game1.player.getTileLocation();
closed.Clear();
@ -111,10 +79,7 @@ namespace stardew_access.Features
SearchNearbyTiles(currPosition, range);
if (MainClass.radarDebug)
MainClass.GetMonitor().Log($"\nRead Tile stopped\n\n", StardewModdingAPI.LogLevel.Debug);
await Task.Delay(delay);
isRunning = false;
MainClass.DebugLog($"\nRead Tile stopped\n\n");
}
/// <summary>
@ -132,7 +97,6 @@ namespace stardew_access.Features
List<Vector2> searched = new List<Vector2>();
int[] dirX = { -1, 0, 1, 0 };
int[] dirY = { 0, 1, 0, -1 };
int count = 0;
toSearch.Enqueue(center);
searched.Add(center);
@ -151,7 +115,6 @@ namespace stardew_access.Features
detectedTiles.Add(item, (tileInfo.Item2, tileInfo.Item3));
}
}
count++;
for (int i = 0; i < 4; i++)
{
@ -168,6 +131,52 @@ namespace stardew_access.Features
return detectedTiles;
}
/// <summary>
/// Search the entire location using Breadth First Search algorithm(BFS).
/// </summary>
/// <returns>A dictionary with all the detected tiles along with the name of the object on it and it's category.</returns>
public Dictionary<Vector2, (string, string)> SearchLocation()
{
Dictionary<Vector2, (string, string)> detectedTiles = new Dictionary<Vector2, (string, string)>();
Vector2 position = Vector2.Zero;
(bool, string? name, string category) tileInfo;
Queue<Vector2> toSearch = new Queue<Vector2>();
List<Vector2> searched = new List<Vector2>();
int[] dirX = { -1, 0, 1, 0 };
int[] dirY = { 0, 1, 0, -1 };
int count = 0;
toSearch.Enqueue(Game1.player.getTileLocation());
searched.Add(Game1.player.getTileLocation());
while (toSearch.Count > 0)
{
Vector2 item = toSearch.Dequeue();
tileInfo = CheckTile(item, true);
if (tileInfo.Item1 && tileInfo.name != null)
{
// Add detected tile to the dictionary
detectedTiles.Add(item, (tileInfo.name, tileInfo.category));
}
count++;
for (int i = 0; i < 4; i++)
{
Vector2 dir = new Vector2(item.X + dirX[i], item.Y + dirY[i]);
if (!searched.Contains(dir) && (TileInfo.isWarpPointAtTile((int)dir.X, (int)dir.Y) || Game1.currentLocation.isTileOnMap(dir)))
{
toSearch.Enqueue(dir);
searched.Add(dir);
}
}
}
return detectedTiles;
}
/// <summary>
/// Checks if the provided tile position is within the range/radius and whether the tile has already been checked or not.
/// </summary>
@ -189,16 +198,16 @@ namespace stardew_access.Features
return true;
}
public (bool, string?, string) CheckTile(Vector2 position)
public (bool, string? name, string category) CheckTile(Vector2 position, bool lessInfo = false)
{
(string?, CATEGORY?) tileDetail = ReadTile.getNameWithCategoryAtTile(position);
if (tileDetail.Item1 == null)
(string? name, CATEGORY? category) tileDetail = TileInfo.getNameWithCategoryAtTile(position, lessInfo);
if (tileDetail.name == null)
return (false, null, CATEGORY.Others.ToString());
if (tileDetail.Item2 == null)
tileDetail.Item2 = CATEGORY.Others;
if (tileDetail.category == null)
tileDetail.category = CATEGORY.Others;
return (true, tileDetail.Item1, tileDetail.Item2.ToString());
return (true, tileDetail.name, tileDetail.category.ToString());
}
@ -208,9 +217,9 @@ namespace stardew_access.Features
{
if (Game1.currentLocation.isObjectAtTile((int)position.X, (int)position.Y))
{
(string?, CATEGORY) objDetails = ReadTile.getObjectAtTile((int)position.X, (int)position.Y);
string? objectName = objDetails.Item1;
CATEGORY category = objDetails.Item2;
(string? name, CATEGORY category) objDetails = TileInfo.getObjectAtTile((int)position.X, (int)position.Y);
string? objectName = objDetails.name;
CATEGORY category = objDetails.category;
StardewValley.Object obj = Game1.currentLocation.getObjectAtTile((int)position.X, (int)position.Y);
if (objectName != null)
@ -232,19 +241,19 @@ namespace stardew_access.Features
}
else
{
(string?, CATEGORY?) tileDetail = ReadTile.getNameWithCategoryAtTile(position);
if (tileDetail.Item1 != null)
(string? name, CATEGORY? category) tileDetail = TileInfo.getNameWithCategoryAtTile(position);
if (tileDetail.name != null)
{
if (tileDetail.Item2 == null)
tileDetail.Item2 = CATEGORY.Others;
if (tileDetail.category == null)
tileDetail.category = CATEGORY.Others;
PlaySoundAt(position, tileDetail.Item1, tileDetail.Item2);
PlaySoundAt(position, tileDetail.name, tileDetail.category);
}
}
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"{e.Message}\n{e.StackTrace}\n{e.Source}", StardewModdingAPI.LogLevel.Error);
MainClass.ErrorLog($"{e.Message}\n{e.StackTrace}\n{e.Source}");
}
}
@ -253,7 +262,7 @@ namespace stardew_access.Features
#region Check whether to skip the object or not
// Skip if player is directly looking at the tile
if (CurrentPlayer.getNextTile().Equals(position))
if (CurrentPlayer.FacingTile.Equals(position))
return;
if (!radarFocus)
@ -296,7 +305,7 @@ namespace stardew_access.Features
#endregion
if (MainClass.radarDebug)
MainClass.GetMonitor().Log($"{radarFocus}\tObject:{searchQuery.ToLower().Trim()}\tPosition: X={position.X} Y={position.Y}", StardewModdingAPI.LogLevel.Debug);
MainClass.ErrorLog($"{radarFocus}\tObject:{searchQuery.ToLower().Trim()}\tPosition: X={position.X} Y={position.Y}");
int px = (int)Game1.player.getTileX(); // Player's X postion
int py = (int)Game1.player.getTileY(); // Player's Y postion
@ -330,7 +339,7 @@ namespace stardew_access.Features
{
string soundName = $"_{post}";
if (!MainClass.radarStereoSound)
if (!MainClass.Config.RadarStereoSound)
soundName = $"_mono{soundName}";
if (category == CATEGORY.Farmers) // Villagers and farmers
@ -353,7 +362,7 @@ namespace stardew_access.Features
soundName = $"obj{soundName}";
else if (category == CATEGORY.MineItems) // Mine items
soundName = $"obj{soundName}";
else if (category == CATEGORY.Chests) // Chests
else if (category == CATEGORY.Containers) // Chests
soundName = $"obj{soundName}";
else if (category == CATEGORY.Debris) // Grass and debris
soundName = $"obj{soundName}";

View File

@ -1,54 +1,105 @@
using Microsoft.Xna.Framework;
using StardewModdingAPI;
using StardewValley;
using StardewValley.Buildings;
using StardewValley.Locations;
using StardewValley.Objects;
using StardewValley.TerrainFeatures;
namespace stardew_access.Features
{
/// <summary>
/// Reads the name and information about a tile.
/// </summary>
public class ReadTile
{
public static bool isReadingTile = false;
public static Vector2 prevTile;
private bool isBusy; // To pause execution of run method between fixed intervals
private int delay; // Length of each interval (in ms)
private bool shouldPause; // To pause the execution
private Vector2 prevTile;
public ReadTile()
{
isReadingTile = false;
isBusy = false;
delay = 100;
}
public static async void run(bool manuallyTriggered = false)
public void update()
{
isReadingTile = true;
if (this.isBusy)
return;
if (this.shouldPause)
return;
this.isBusy = true;
this.run();
Task.Delay(delay).ContinueWith(_ => { this.isBusy = false; });
}
/// <summary>
/// Pauses the feature for the provided time.
/// </summary>
/// <param name="time">The amount of time we want to pause the execution (in ms).<br/>Default is 2500 (2.5s).</param>
public void pauseUntil(int time = 2500)
{
this.shouldPause = true;
Task.Delay(time).ContinueWith(_ => { this.shouldPause = false; });
}
/// <summary>
/// Pauses the feature
/// </summary>
public void pause()
{
this.shouldPause = true;
}
/// <summary>
/// Resumes the feature
/// </summary>
public void resume()
{
this.shouldPause = false;
}
public void run(bool manuallyTriggered = false, bool playersPosition = false)
{
try
{
#region Get Next Grab Tile
Vector2 tile = CurrentPlayer.getNextTile();
int x = (int)tile.X;
int y = (int)tile.Y;
Vector2 tile;
#region Get Tile
int x, y;
if (!playersPosition)
{
// Grab tile
tile = CurrentPlayer.FacingTile;
}
else
{
// Player's standing tile
tile = CurrentPlayer.Position;
}
x = (int)tile.X;
y = (int)tile.Y;
#endregion
if (Context.IsPlayerFree)
{
if (!manuallyTriggered && prevTile != tile)
{
if (MainClass.GetScreenReader() != null)
MainClass.GetScreenReader().PrevTextTile = " ";
if (MainClass.ScreenReader != null)
MainClass.ScreenReader.PrevTextTile = " ";
}
bool isColliding = isCollidingAtTile(x, y);
bool isColliding = TileInfo.isCollidingAtTile(x, y);
string? toSpeak = getNameAtTile(tile);
(string? name, string? category) info = TileInfo.getNameWithCategoryNameAtTile(tile);
#region Narrate toSpeak
if (toSpeak != null)
if (MainClass.GetScreenReader() != null)
if (info.name != null)
if (MainClass.ScreenReader != null)
if (manuallyTriggered)
MainClass.GetScreenReader().Say(toSpeak, true);
MainClass.ScreenReader.Say($"{info.name}, Category: {info.category}", true);
else
MainClass.GetScreenReader().SayWithTileQuery(toSpeak, x, y, true);
MainClass.ScreenReader.SayWithTileQuery(info.name, x, y, true);
#endregion
#region Play colliding sound effect
@ -58,865 +109,15 @@ namespace stardew_access.Features
}
#endregion
if (!manuallyTriggered)
prevTile = tile;
}
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"Error in Read Tile:\n{e.Message}\n{e.StackTrace}", LogLevel.Debug);
MainClass.ErrorLog($"Error in Read Tile:\n{e.Message}\n{e.StackTrace}");
}
await Task.Delay(100);
isReadingTile = false;
}
///<summary>Returns the name of the object at tile alongwith it's category's name</summary>
public static (string?, string?) getNameWithCategoryNameAtTile(Vector2 tile)
{
(string?, CATEGORY?) tileDetail = getNameWithCategoryAtTile(tile);
if (tileDetail.Item2 == null)
tileDetail.Item2 = CATEGORY.Others;
return (tileDetail.Item1, tileDetail.Item2.ToString());
}
///<summary>Returns the name of the object at tile alongwith it's category</summary>
public static (string?, CATEGORY?) getNameWithCategoryAtTile(Vector2 tile)
{
int x = (int)tile.X;
int y = (int)tile.Y;
string? toReturn = "";
CATEGORY? category = CATEGORY.Others;
bool isColliding = isCollidingAtTile(x, y);
Dictionary<Vector2, Netcode.NetRef<TerrainFeature>> terrainFeature = Game1.currentLocation.terrainFeatures.FieldDict;
string? door = getDoorAtTile(x, y);
(CATEGORY?, string?) tileInfo = getTileInfo(x, y);
string? junimoBundle = getJunimoBundleAt(x, y);
string? resourceClump = getResourceClumpAtTile(x, y);
string? farmAnimal = getFarmAnimalAt(Game1.currentLocation, x, y);
if (Game1.currentLocation.isCharacterAtTile(tile) != null)
{
NPC npc = Game1.currentLocation.isCharacterAtTile(tile);
toReturn = npc.displayName;
if (npc.isVillager() || npc.CanSocialize)
category = CATEGORY.Farmers;
else
category = CATEGORY.NPCs;
}
else if (farmAnimal != null)
{
toReturn = farmAnimal;
category = CATEGORY.FarmAnimals;
}
else if (Game1.currentLocation.isWaterTile(x, y) && isColliding)
{
toReturn = "Water";
category = CATEGORY.WaterTiles;
}
else if (Game1.currentLocation.isObjectAtTile(x, y))
{
(string?, CATEGORY?) obj = getObjectAtTile(x, y);
toReturn = obj.Item1;
category = obj.Item2;
}
else if (terrainFeature.ContainsKey(tile))
{
(string?, CATEGORY) tf = getTerrainFeatureAtTile(terrainFeature[tile]);
string? terrain = tf.Item1;
if (terrain != null)
{
toReturn = terrain;
category = tf.Item2;
}
}
else if (Game1.currentLocation.getLargeTerrainFeatureAt(x, y) != null)
{
toReturn = getBushAtTile(x, y);
category = CATEGORY.Bush;
}
else if (resourceClump != null)
{
toReturn = resourceClump;
category = CATEGORY.ResourceClumps;
}
else if (door != null)
{
toReturn = door;
category = CATEGORY.Doors;
}
else if (isMineDownLadderAtTile(x, y))
{
toReturn = "Ladder";
category = CATEGORY.Doors;
}
else if (isMineUpLadderAtTile(x, y))
{
toReturn = "Up Ladder";
category = CATEGORY.Doors;
}
else if (isElevatorAtTile(x, y))
{
toReturn = "Elevator";
category = CATEGORY.Doors;
}
else if (tileInfo.Item2 != null)
{
toReturn = tileInfo.Item2;
category = tileInfo.Item1;
}
else if (junimoBundle != null)
{
toReturn = junimoBundle;
category = CATEGORY.JunimoBundle;
}
if (toReturn == "")
return (null, category);
return (toReturn, category);
}
///<summary>Returns the name of the object at tile</summary>
public static string? getNameAtTile(Vector2 tile)
{
int x = (int)tile.X;
int y = (int)tile.Y;
string? toReturn = "";
bool isColliding = isCollidingAtTile(x, y);
Dictionary<Vector2, Netcode.NetRef<TerrainFeature>> terrainFeature = Game1.currentLocation.terrainFeatures.FieldDict;
string? door = getDoorAtTile(x, y);
(CATEGORY?, string?) tileInfo = getTileInfo(x, y);
string? junimoBundle = getJunimoBundleAt(x, y);
string? resourceClump = getResourceClumpAtTile(x, y);
string? farmAnimal = getFarmAnimalAt(Game1.currentLocation, x, y);
if (Game1.currentLocation.isCharacterAtTile(tile) != null)
{
NPC npc = Game1.currentLocation.isCharacterAtTile(tile);
toReturn = npc.displayName;
}
else if (farmAnimal != null)
{
toReturn = farmAnimal;
}
else if (Game1.currentLocation.isWaterTile(x, y) && isColliding)
{
toReturn = "Water";
}
else if (Game1.currentLocation.isObjectAtTile(x, y))
{
toReturn = getObjectAtTile(x, y).Item1;
}
else if (terrainFeature.ContainsKey(tile))
{
string? terrain = getTerrainFeatureAtTile(terrainFeature[tile]).Item1;
if (terrain != null)
toReturn = terrain;
}
else if (Game1.currentLocation.getLargeTerrainFeatureAt(x, y) != null)
{
toReturn = getBushAtTile(x, y);
}
else if (resourceClump != null)
{
toReturn = resourceClump;
}
else if (door != null)
{
toReturn = door;
}
else if (isMineDownLadderAtTile(x, y))
{
toReturn = "Ladder";
}
else if (isMineUpLadderAtTile(x, y))
{
toReturn = "Up Ladder";
}
else if (isElevatorAtTile(x, y))
{
toReturn = "Elevator";
}
else if (tileInfo.Item2 != null)
{
toReturn = tileInfo.Item2;
}
else if (junimoBundle != null)
{
toReturn = junimoBundle;
}
if (toReturn == "")
return null;
return toReturn;
}
public static string? getBushAtTile(int x, int y)
{
string? toReturn = null;
Bush bush = (Bush)Game1.currentLocation.getLargeTerrainFeatureAt(x, y);
int size = bush.size;
#region Check if bush is harvestable or not
if (!bush.townBush && (int)bush.tileSheetOffset == 1 && bush.inBloom(Game1.GetSeasonForLocation(Game1.currentLocation), Game1.dayOfMonth))
{
// Taken from the game's code
string season = ((int)bush.overrideSeason == -1) ? Game1.GetSeasonForLocation(Game1.currentLocation) : Utility.getSeasonNameFromNumber(bush.overrideSeason);
int shakeOff = -1;
if (!(season == "spring"))
{
if (season == "fall")
{
shakeOff = 410;
}
}
else
{
shakeOff = 296;
}
if ((int)size == 3)
{
shakeOff = 815;
}
if ((int)size == 4)
{
shakeOff = 73;
}
if (shakeOff == -1)
{
return null;
}
toReturn = "Harvestable";
}
#endregion
if (bush.townBush)
toReturn = $"{toReturn} Town Bush";
else if (bush.greenhouseBush)
toReturn = $"{toReturn} Greenhouse Bush";
else
toReturn = $"{toReturn} Bush";
return toReturn;
}
public static string? getJunimoBundleAt(int x, int y)
{
if (Game1.currentLocation is not CommunityCenter)
return null;
CommunityCenter communityCenter = ((CommunityCenter)Game1.currentLocation);
string? name = (x, y) switch
{
(14, 5) => "Pantry",
(14, 23) => "Crafts Room",
(40, 10) => "Fish Tank",
(63, 14) => "Boiler Room",
(55, 6) => "Vault",
(46, 11) => "Bulletin Board",
_ => null,
};
if (name != null && communityCenter.shouldNoteAppearInArea(CommunityCenter.getAreaNumberFromName(name)))
return $"{name} bundle";
else
return null;
}
public static bool isCollidingAtTile(int x, int y)
{
Rectangle rect = new Rectangle(x * 64 + 1, y * 64 + 1, 62, 62);
if (Game1.currentLocation.isCollidingPosition(rect, Game1.viewport, true, 0, glider: false, Game1.player, pathfinding: true))
{
return true;
}
if (Game1.currentLocation is Woods && getStumpsInWoods(x, y) != null)
return true;
return false;
}
public static string? getFarmAnimalAt(GameLocation? location, int x, int y, bool onlyName = false)
{
if (location == null)
return null;
if (location is not Farm && location is not AnimalHouse)
return null;
List<FarmAnimal>? farmAnimals = null;
if (location is Farm)
farmAnimals = ((Farm)location).getAllFarmAnimals();
else if (location is AnimalHouse)
farmAnimals = ((AnimalHouse)location).animals.Values.ToList();
if (farmAnimals == null || farmAnimals.Count <= 0)
return null;
for (int i = 0; i < farmAnimals.Count; i++)
{
int fx = farmAnimals[i].getTileX();
int fy = farmAnimals[i].getTileY();
if (fx.Equals(x) && fy.Equals(y))
{
string name = farmAnimals[i].displayName;
int age = farmAnimals[i].age.Value;
string type = farmAnimals[i].displayType;
if (onlyName)
return name;
return $"{name}, {type}, age {age}";
}
}
return null;
}
/// <summary>
///
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns>Item1: This is the category of the tile. Default to Furnitures.
/// <br/>Item2: This is the name of the tile. Default to null if the tile tile has nothing on it.</returns>
public static (CATEGORY?, string?) getTileInfo(int x, int y)
{
int? index = null;
if (Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y] != null)
index = Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y].TileIndex;
/* Add More
MainClass.monitor.Log(index.ToString(), LogLevel.Debug);
*/
if (Game1.currentLocation is Farm)
{
Building building = ((Farm)Game1.currentLocation).getBuildingAt(new Vector2(x, y));
if (building != null)
{
return (CATEGORY.Buildings, building.buildingType.Value);
}
}
if (index != null)
{
switch (index)
{
case 1955:
case 41:
return (CATEGORY.Furnitures, "Mail Box");
case 1003:
return (CATEGORY.Furnitures, "Street lamp");
case 78:
return (CATEGORY.Furnitures, "Trash bin");
case 617:
return (CATEGORY.Furnitures, "Daily quest");
case 616:
return (CATEGORY.Furnitures, "Calender");
}
if (Game1.currentLocation is FarmHouse || Game1.currentLocation is IslandFarmHouse)
{
switch (index)
{
case 173:
return (CATEGORY.Furnitures, "Fridge");
case 169:
case 170:
case 171:
case 172:
return (CATEGORY.Furnitures, "Kitchen");
}
}
}
return (null, null);
}
public static (string?, CATEGORY) getTerrainFeatureAtTile(Netcode.NetRef<TerrainFeature> terrain)
{
string? toReturn = null;
CATEGORY category = CATEGORY.Others;
if (terrain.Get() is HoeDirt)
{
category = CATEGORY.Crops;
HoeDirt dirt = (HoeDirt)terrain.Get();
if (dirt.crop != null)
{
string cropName = Game1.objectInformation[dirt.crop.indexOfHarvest].Split('/')[0];
toReturn = $"{cropName}";
bool isWatered = dirt.state.Value == HoeDirt.watered;
bool isHarvestable = dirt.readyForHarvest();
bool isFertilized = dirt.fertilizer.Value != HoeDirt.noFertilizer;
if (isWatered)
toReturn = "Watered " + toReturn;
if (isFertilized)
toReturn = "Fertilized " + toReturn;
if (isHarvestable)
toReturn = "Harvestable " + toReturn;
}
else
{
toReturn = "Soil";
bool isWatered = dirt.state.Value == HoeDirt.watered;
bool isFertilized = dirt.fertilizer.Value != HoeDirt.noFertilizer;
if (isWatered)
toReturn = "Watered " + toReturn;
if (isFertilized)
toReturn = "Fertilized " + toReturn;
}
}
else if (terrain.Get() is GiantCrop)
{
category = CATEGORY.Crops;
int whichCrop = ((GiantCrop)terrain.Get()).which.Value;
switch (whichCrop)
{
case 0:
toReturn = "Cauliflower";
break;
case 1:
toReturn = "Melon";
break;
case 2:
toReturn = "Pumpkin";
break;
}
}
else if (terrain.Get() is CosmeticPlant)
{
category = CATEGORY.Furnitures;
CosmeticPlant cosmeticPlant = (CosmeticPlant)terrain.Get();
toReturn = cosmeticPlant.textureName().ToLower();
if (toReturn.Contains("terrain"))
toReturn.Replace("terrain", "");
if (toReturn.Contains("feature"))
toReturn.Replace("feature", "");
}
else if (terrain.Get() is Flooring)
{
category = CATEGORY.Flooring;
Flooring flooring = (Flooring)terrain.Get();
bool isPathway = flooring.isPathway.Get();
bool isSteppingStone = flooring.isSteppingStone.Get();
toReturn = "Flooring";
if (isPathway)
toReturn = "Pathway";
if (isSteppingStone)
toReturn = "Stepping Stone";
}
else if (terrain.Get() is FruitTree)
{
category = CATEGORY.Trees;
toReturn = getFruitTree((FruitTree)terrain.Get());
}
else if (terrain.Get() is Grass)
{
category = CATEGORY.Debris;
toReturn = "Grass";
}
else if (terrain.Get() is Tree)
{
category = CATEGORY.Trees;
toReturn = getTree((Tree)terrain.Get());
}
else if (terrain.Get() is Quartz)
{
category = CATEGORY.MineItems;
toReturn = "Quartz";
}
return (toReturn, category);
}
public static string getFruitTree(FruitTree fruitTree)
{
int stage = fruitTree.growthStage.Value;
int fruitIndex = fruitTree.indexOfFruit.Get();
string toReturn = Game1.objectInformation[fruitIndex].Split('/')[0];
if (stage == 0)
toReturn = $"{toReturn} seed";
else if (stage == 1)
toReturn = $"{toReturn} sprout";
else if (stage == 2)
toReturn = $"{toReturn} sapling";
else if (stage == 3)
toReturn = $"{toReturn} bush";
else if (stage >= 4)
toReturn = $"{toReturn} tree";
if (fruitTree.fruitsOnTree.Value > 0)
toReturn = $"Harvestable {toReturn}";
return toReturn;
}
public static string getTree(Tree tree)
{
int treeType = tree.treeType.Value;
int treeStage = tree.growthStage.Value;
string treeName = "tree";
string seedName = "";
// Return with the name if it's one of the 3 special trees
switch (treeType)
{
case 4:
case 5:
return "Winter Tree";
case 6:
return "Palm Tree";
case 7:
return "Mushroom Tree";
}
if (treeType <= 3)
seedName = Game1.objectInformation[308 + treeType].Split('/')[0];
else if (treeType == 8)
seedName = Game1.objectInformation[292].Split('/')[0];
if (treeStage >= 1)
{
switch (seedName.ToLower())
{
case "mahogany seed":
treeName = "Mahogany";
break;
case "acorn":
treeName = "Oak";
break;
case "maple seed":
treeName = "Maple";
break;
case "pine cone":
treeName = "Pine";
break;
}
if (treeStage == 1)
treeName = $"{treeName} sprout";
else if (treeStage == 2)
treeName = $"{treeName} sapling";
else if (treeStage == 3 || treeStage == 4)
treeName = $"{treeName} bush";
else if (treeStage >= 5)
treeName = $"{treeName} tree";
return treeName;
}
return seedName;
}
public static (string?, CATEGORY) getObjectAtTile(int x, int y)
{
string? toReturn = null;
StardewValley.Object obj = Game1.currentLocation.getObjectAtTile(x, y);
int index = obj.ParentSheetIndex;
toReturn = obj.DisplayName;
// Get object names based on index
switch (index)
{
case 313:
case 314:
case 315:
case 316:
case 317:
case 318:
case 452:
case 674:
case 675:
case 676:
case 677:
case 678:
case 679:
case 750:
case 784:
case 785:
case 786:
return ("Weed", CATEGORY.Debris);
case 792:
case 793:
case 794:
return ("Fertile weed", CATEGORY.Debris);
case 319:
case 320:
case 321:
return ("Ice crystal", CATEGORY.Debris);
case 75:
return ("Geode", CATEGORY.MineItems);
case 32:
case 34:
case 36:
case 38:
case 40:
case 42:
case 48:
case 50:
case 52:
case 54:
case 56:
case 58:
return ("Coloured stone", CATEGORY.Debris);
case 668:
case 670:
case 845:
case 846:
case 847:
return ("Mine stone", CATEGORY.MineItems);
case 818:
return ("Clay stone", CATEGORY.Debris);
case 816:
case 817:
return ("Fossil stone", CATEGORY.Debris);
case 118:
case 120:
case 122:
case 124:
return ("Barrel", CATEGORY.MineItems);
case 119:
case 121:
case 123:
case 125:
return ("Item box", CATEGORY.MineItems);
}
if (Game1.currentLocation is Mine or MineShaft)
{
switch (index)
{
case 76:
return ("Frozen geode", CATEGORY.MineItems);
case 77:
return ("Magma geode", CATEGORY.MineItems);
case 8:
case 66:
return ("Amethyst node", CATEGORY.MineItems);
case 14:
case 62:
return ("Aquamarine node", CATEGORY.MineItems);
case 843:
case 844:
return ("Cinder shard node", CATEGORY.MineItems);
case 2:
case 72:
return ("Diamond node", CATEGORY.MineItems);
case 12:
case 60:
return ("Emerald node", CATEGORY.MineItems);
case 44:
return ("Gem node", CATEGORY.MineItems);
case 6:
case 70:
return ("Jade node", CATEGORY.MineItems);
case 46:
return ("Mystic stone", CATEGORY.MineItems);
case 74:
return ("Prismatic node", CATEGORY.MineItems);
case 4:
case 64:
return ("Ruby node", CATEGORY.MineItems);
case 10:
case 68:
return ("Topaz node", CATEGORY.MineItems);
case 819:
return ("Omni geode node", CATEGORY.MineItems);
case 751:
case 849:
return ("Copper node", CATEGORY.MineItems);
case 764:
return ("Gold node", CATEGORY.MineItems);
case 765:
return ("Iridium node", CATEGORY.MineItems);
case 290:
case 850:
return ("Iron node", CATEGORY.MineItems);
}
}
if (obj is Chest)
{
Chest chest = (Chest)obj;
return (chest.DisplayName, CATEGORY.Chests);
}
if (obj is Furniture)
return (toReturn, CATEGORY.Furnitures);
if (toReturn != null)
return (toReturn, CATEGORY.Others);
return (null, CATEGORY.Others);
}
public static bool isMineDownLadderAtTile(int x, int y)
{
try
{
if (Game1.currentLocation is Mine or MineShaft)
{
int? index = null;
if (Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y] != null)
index = Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y].TileIndex;
if (index == 173 || index == 174)
return true;
}
}
catch (Exception) { }
return false;
}
public static bool isMineUpLadderAtTile(int x, int y)
{
try
{
if (Game1.currentLocation is Mine or MineShaft)
{
int? index = null;
if (Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y] != null)
index = Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y].TileIndex;
if (index == 115)
return true;
}
}
catch (Exception) { }
return false;
}
public static bool isElevatorAtTile(int x, int y)
{
try
{
if (Game1.currentLocation is Mine or MineShaft)
{
int? index = null;
if (Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y] != null)
index = Game1.currentLocation.Map.GetLayer("Buildings").Tiles[x, y].TileIndex;
if (index == 112)
return true;
}
}
catch (Exception) { }
return false;
}
public static string? getDoorAtTile(int x, int y)
{
Point tilePoint = new Point(x, y);
StardewValley.Network.NetPointDictionary<string, Netcode.NetString> doorList = Game1.currentLocation.doors;
for (int i = 0; i < doorList.Count(); i++)
{
if (doorList.ContainsKey(tilePoint))
{
string? doorName;
doorList.TryGetValue(tilePoint, out doorName);
if (doorName != null)
return $"{doorName} door";
else
return "door";
}
}
return null;
}
public static string? getResourceClumpAtTile(int x, int y)
{
if (Game1.currentLocation is Woods)
return getStumpsInWoods(x, y);
for (int i = 0; i < Game1.currentLocation.resourceClumps.Count; i++)
{
if (Game1.currentLocation.resourceClumps[i].occupiesTile(x, y))
{
int index = Game1.currentLocation.resourceClumps[i].parentSheetIndex;
switch (index)
{
case 600:
return "Large Stump";
case 602:
return "Hollow Log";
case 622:
return "Meteorite";
case 752:
case 754:
case 756:
case 758:
return "Mine Rock";
case 672:
return "Boulder";
default:
return "Unknown";
}
}
}
return null;
}
public static string? getStumpsInWoods(int x, int y)
{
if (Game1.currentLocation is not Woods)
return null;
if ((x == 8 || x == 9) && y == 7)
{
return "Old Master Cannoli";
}
Netcode.NetObjectList<ResourceClump> stumps = ((Woods)Game1.currentLocation).stumps;
for (int i = 0; i < stumps.Count; i++)
{
if (stumps[i].occupiesTile(x, y))
{
return "Large Stump";
}
}
return null;
}
}
}

View File

@ -0,0 +1,177 @@
using Newtonsoft.Json.Linq;
using StardewValley;
namespace stardew_access.Features
{
public class StaticTiles
{
private JObject? staticTilesData = null;
private JObject? customTilesData = null;
public StaticTiles()
{
if (MainClass.ModHelper == null)
return;
try
{
using (StreamReader file = new StreamReader(Path.Combine(MainClass.ModHelper.DirectoryPath, "assets", "static-tiles.json")))
{
string json = file.ReadToEnd();
staticTilesData = JObject.Parse(json);
}
MainClass.InfoLog($"Loaded static-tile.json");
}
catch (System.Exception)
{
MainClass.ErrorLog($"static-tiles.json file not found or an error occured while initializing static-tiles.json\nThe path of the file should be:\n\t{Path.Combine(MainClass.ModHelper.DirectoryPath, "assets", "static-tiles.json")}");
}
try
{
using (StreamReader file = new StreamReader(Path.Combine(MainClass.ModHelper.DirectoryPath, "assets", "custom-tiles.json")))
{
string json = file.ReadToEnd();
customTilesData = JObject.Parse(json);
}
MainClass.InfoLog($"Loaded custom-tile.json");
}
catch (System.Exception)
{
MainClass.ErrorLog($"custom-tiles.json file not found or an error occured while initializing custom-tiles.json\nThe path of the file should be:\n\t{Path.Combine(MainClass.ModHelper.DirectoryPath, "assets", "custom-tiles.json")}");
}
}
public bool isAvailable(string locationName)
{
List<JObject> allData = new List<JObject>();
if (customTilesData != null) allData.Add(customTilesData);
if (staticTilesData != null) allData.Add(staticTilesData);
foreach (JObject data in allData)
{
foreach (KeyValuePair<string, JToken?> location in data)
{
if (location.Key.Contains("||") && MainClass.ModHelper != null)
{
string uniqueModID = location.Key.Substring(location.Key.LastIndexOf("||") + 2);
string locationNameInJson = location.Key.Remove(location.Key.LastIndexOf("||"));
bool isLoaded = MainClass.ModHelper.ModRegistry.IsLoaded(uniqueModID);
if (!isLoaded) continue; // Skip if the specified mod is not loaded
if (locationName.ToLower().Equals(locationNameInJson.ToLower())) return true;
}
else if (locationName.ToLower().Equals(location.Key.ToLower()))
return true;
}
}
return false;
}
public string? getStaticTileInfoAt(int x, int y)
{
return getStaticTileInfoAtWithCategory(x, y).name;
}
public (string? name, CATEGORY category) getStaticTileInfoAtWithCategory(int x, int y)
{
List<JObject> allData = new List<JObject>();
if (customTilesData != null) allData.Add(customTilesData);
if (staticTilesData != null) allData.Add(staticTilesData);
foreach (JObject data in allData)
{
foreach (KeyValuePair<string, JToken?> location in data)
{
if (location.Key.Contains("||") && MainClass.ModHelper != null)
{
// Mod Specific Tiles
// We can add tiles that only get detected when the specified mod is loaded.
// Syntax: <location name>||<Mod's unique id, look into the mod's manifest.json for unique id>
// Example: THe following tile will only be detected if Stardew Valley Expanded mod is installed
// {
// .
// .
// .
// "Town||FlashShifter.StardewValleyExpandedCP":{
// "<Tile Name>":{
// "x": [<x location(s)>],
// "y": [<y location(s)>],
// "type": "<Category name>"
// }
// },
// .
// .
// .
// }
string uniqueModID = location.Key.Substring(location.Key.LastIndexOf("||") + 2);
string locationName = location.Key.Remove(location.Key.LastIndexOf("||"));
bool isLoaded = MainClass.ModHelper.ModRegistry.IsLoaded(uniqueModID);
if (!isLoaded) continue; // Skip if the specified mod is not loaded
if (!Game1.currentLocation.Name.ToLower().Equals(locationName.ToLower())) continue;
}
else if (!Game1.currentLocation.Name.ToLower().Equals(location.Key.ToLower())) continue;
if (location.Value != null)
foreach (var tile in ((JObject)location.Value))
{
if (tile.Value == null)
continue;
JToken? tileXArray = tile.Value["x"];
JToken? tileYArray = tile.Value["y"];
JToken? tileType = tile.Value["type"];
if (tileXArray == null || tileYArray == null || tileType == null)
continue;
bool isXPresent = false;
bool isYPresent = false;
foreach (var item in tileXArray)
{
if (short.Parse(item.ToString()) == x)
{
isXPresent = true;
break;
}
}
foreach (var item in tileYArray)
{
if (short.Parse(item.ToString()) == y)
{
isYPresent = true;
break;
}
}
if (isXPresent && isYPresent)
{
string key = tile.Key;
if (key.Contains('[') && key.Contains(']'))
{
int i1 = key.IndexOf('[');
int i2 = key.LastIndexOf(']');
if (i1 < i2)
{
key = key.Remove(i1, ++i2 - i1);
}
}
return (key.Trim(), CATEGORY.FromString(tileType.ToString().ToLower()));
}
}
}
}
return (null, CATEGORY.Others);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,291 @@
using Microsoft.Xna.Framework;
using xTile;
using StardewValley;
using StardewValley.Menus;
namespace stardew_access.Features
{
/// <summary>
/// Allows browsing of the map and snapping mouse to tiles with the arrow keys
/// </summary>
public class TileViewer
{
//None of these positions take viewport into account; other functions are responsible later
private Vector2 ViewingOffset = Vector2.Zero;
private Vector2 relativeOffsetLockPosition = Vector2.Zero;
private Boolean relativeOffsetLock = false;
private Vector2 prevPlayerPosition = Vector2.Zero, prevFacing = Vector2.Zero;
private Vector2 finalTile = Vector2.Zero;
private Vector2 prevTile = Vector2.Zero;
public Boolean isAutoWalking = false;
private Vector2 PlayerFacingVector
{
get
{
switch (Game1.player.FacingDirection)
{
case 0:
return new Vector2(0, -Game1.tileSize);
case 1:
return new Vector2(Game1.tileSize, 0);
case 2:
return new Vector2(0, Game1.tileSize);
case 3:
return new Vector2(-Game1.tileSize, 0);
default:
return Vector2.Zero;
}
}
}
private Vector2 PlayerPosition
{
get
{
int x = Game1.player.GetBoundingBox().Center.X;
int y = Game1.player.GetBoundingBox().Center.Y;
return new Vector2(x, y);
}
}
/// <summary>
/// Return the position of the tile cursor in pixels from the upper-left corner of the map.
/// </summary>
/// <returns>Vector2</returns>
public Vector2 GetTileCursorPosition()
{
Vector2 target = this.PlayerPosition;
if (this.relativeOffsetLock)
{
target += this.relativeOffsetLockPosition;
}
else
{
target += this.PlayerFacingVector + this.ViewingOffset;
}
return target;
}
/// <summary>
/// Return the tile at the position of the tile cursor.
/// </summary>
/// <returns>Vector2</returns>
public Vector2 GetViewingTile()
{
Vector2 position = this.GetTileCursorPosition();
return new Vector2((int)position.X / Game1.tileSize, (int)position.Y / Game1.tileSize);
}
/// <summary>
/// Handle keyboard input related to the tile viewer.
/// </summary>
public void HandleInput()
{
if (MainClass.Config.ToggleRelativeCursorLockKey.JustPressed())
{
this.relativeOffsetLock = !this.relativeOffsetLock;
if (this.relativeOffsetLock)
{
this.relativeOffsetLockPosition = this.PlayerFacingVector + this.ViewingOffset;
}
else
{
this.relativeOffsetLockPosition = Vector2.Zero;
}
MainClass.ScreenReader.Say("Relative cursor lock " + (this.relativeOffsetLock ? "enabled" : "disabled") + ".", true);
}
else if (MainClass.Config.TileCursorPreciseUpKey.JustPressed())
{
this.cursorMoveInput(new Vector2(0, -MainClass.Config.TileCursorPreciseMovementDistance), true);
}
else if (MainClass.Config.TileCursorPreciseRightKey.JustPressed())
{
this.cursorMoveInput(new Vector2(MainClass.Config.TileCursorPreciseMovementDistance, 0), true);
}
else if (MainClass.Config.TileCursorPreciseDownKey.JustPressed())
{
this.cursorMoveInput(new Vector2(0, MainClass.Config.TileCursorPreciseMovementDistance), true);
}
else if (MainClass.Config.TileCursorPreciseLeftKey.JustPressed())
{
this.cursorMoveInput(new Vector2(-MainClass.Config.TileCursorPreciseMovementDistance, 0), true);
}
else if (MainClass.Config.TileCursorUpKey.JustPressed())
{
this.cursorMoveInput(new Vector2(0, -Game1.tileSize));
}
else if (MainClass.Config.TileCursorRightKey.JustPressed())
{
this.cursorMoveInput(new Vector2(Game1.tileSize, 0));
}
else if (MainClass.Config.TileCursorDownKey.JustPressed())
{
this.cursorMoveInput(new Vector2(0, Game1.tileSize));
}
else if (MainClass.Config.TileCursorLeftKey.JustPressed())
{
this.cursorMoveInput(new Vector2(-Game1.tileSize, 0));
}
else if (MainClass.Config.AutoWalkToTileKey.JustPressed() && StardewModdingAPI.Context.IsPlayerFree)
{
this.startAutoWalking();
}
}
private void startAutoWalking()
{
PathFindController controller = new PathFindController(Game1.player, Game1.currentLocation, this.GetViewingTile().ToPoint(), Game1.player.FacingDirection);
controller.allowPlayerPathingInEvent = true;
if (controller.pathToEndPoint != null && controller.pathToEndPoint.Count > 0)
{
Game1.player.controller = controller;
this.isAutoWalking = true;
this.finalTile = this.GetViewingTile();
MainClass.ReadTileFeature.pause();
MainClass.ScreenReader.Say($"Moving to {this.finalTile.X}x {this.finalTile.Y}y", true);
}
else
{
MainClass.ScreenReader.Say($"Cannot move to {this.finalTile.X}x {this.finalTile.Y}y", true);
}
}
/// <summary>
/// Stop the auto walk controller and reset variables
/// </summary>
/// <param name="wasForced">Narrates a message if set to true.</param>
public void stopAutoWalking(bool wasForced = false)
{
this.finalTile = Vector2.Zero;
this.isAutoWalking = false;
Game1.player.controller = null;
MainClass.ReadTileFeature.resume();
if (wasForced)
MainClass.ScreenReader.Say("Stopped moving", true);
}
private void cursorMoveInput(Vector2 delta, Boolean precise = false)
{
if (!tryMoveTileView(delta)) return;
Vector2 position = this.GetTileCursorPosition();
Vector2 tile = this.GetViewingTile();
String? name = TileInfo.getNameAtTile(tile);
// Prepend the player's name if the viewing tile is occupied by the player itself
if (CurrentPlayer.PositionX == tile.X && CurrentPlayer.PositionY == tile.Y)
{
name = $"{Game1.player.displayName}, {name}";
}
if (name == null)
{
// Report if a tile is empty or blocked if there is nothing on it
if (TileInfo.isCollidingAtTile((int)tile.X, (int)tile.Y))
{
name = "blocked";
}
else
{
name = "empty";
}
}
if (precise)
{
MainClass.ScreenReader.Say($"{name}, {position.X}, {position.Y}", true);
}
else
{
MainClass.ScreenReader.Say($"{name}, {(int)(position.X / Game1.tileSize)}, {(int)(position.Y / Game1.tileSize)}", true);
}
}
private bool tryMoveTileView(Vector2 delta)
{
Vector2 dest = this.GetTileCursorPosition() + delta;
if (!isPositionOnMap(dest)) return false;
if ((MainClass.Config.LimitTileCursorToScreen && Utility.isOnScreen(dest, 0)) || !MainClass.Config.LimitTileCursorToScreen)
{
if (this.relativeOffsetLock)
{
this.relativeOffsetLockPosition += delta;
}
else
{
this.ViewingOffset += delta;
}
return true;
}
return false;
}
private void SnapMouseToPlayer()
{
Vector2 cursorPosition = this.GetTileCursorPosition();
if (allowMouseSnap(cursorPosition))
// Must account for viewport here
Game1.setMousePosition((int)cursorPosition.X - Game1.viewport.X, (int)cursorPosition.Y - Game1.viewport.Y);
}
/// <summary>
/// Handle tile viewer logic.
/// </summary>
public void update()
{
//Reset the viewing cursor to the player when they turn or move. This will not reset the locked offset relative cursor position.
if (this.prevFacing != this.PlayerFacingVector || this.prevPlayerPosition != this.PlayerPosition)
{
this.ViewingOffset = Vector2.Zero;
}
this.prevFacing = this.PlayerFacingVector;
this.prevPlayerPosition = this.PlayerPosition;
if (MainClass.Config.SnapMouse)
this.SnapMouseToPlayer();
if (this.isAutoWalking)
{
if (Vector2.Distance(this.prevTile, CurrentPlayer.Position) >= 2f)
{
prevTile = CurrentPlayer.Position;
Game1.player.checkForFootstep();
}
if (this.finalTile != Vector2.Zero && this.finalTile == CurrentPlayer.Position)
{
MainClass.ScreenReader.Say("Reached destination", true);
this.stopAutoWalking();
}
}
}
private static bool allowMouseSnap(Vector2 point)
{
// Utility.isOnScreen treats a vector as a pixel position, not a tile position
if (!Utility.isOnScreen(point, 0)) return false;
//prevent mousing over the toolbar or any other UI component with the tile cursor
foreach (IClickableMenu menu in Game1.onScreenMenus)
{
//must account for viewport here
if (menu.isWithinBounds((int)point.X - Game1.viewport.X, (int)point.Y - Game1.viewport.Y)) return false;
}
return true;
}
private static bool isPositionOnMap(Vector2 position)
{
// Check whether the position is a warp point, if so then return true, sometimes warp points are 1 tile off the map for example in coops and barns
if (TileInfo.isWarpPointAtTile((int)(position.X / Game1.tileSize), (int)(position.Y / Game1.tileSize))) return true;
//position does not take viewport into account since the entire map needs to be checked.
Map map = Game1.currentLocation.map;
if (position.X < 0 || position.X > map.Layers[0].DisplayWidth) return false;
if (position.Y < 0 || position.Y > map.Layers[0].DisplayHeight) return false;
return true;
}
}
}

View File

@ -0,0 +1,99 @@
namespace stardew_access.Features
{
/// <summary>
/// This is a custom enum class and contains the name of groups the objects are divided into for the feature
/// </summary>
public class CATEGORY
{
private string _typeKeyWord;
private CATEGORY(string typeKeyWord)
{
_typeKeyWord = typeKeyWord;
}
public override string ToString()
{
return _typeKeyWord;
}
public static CATEGORY FromString(string name)
{
if (name == "farmer")
return CATEGORY.Farmers;
else if (name == "animal")
return CATEGORY.FarmAnimals;
else if (name == "npc")
return CATEGORY.NPCs;
else if (name == "furniture")
return CATEGORY.Furnitures;
else if (name == "flooring")
return CATEGORY.Flooring;
else if (name == "debris")
return CATEGORY.Debris;
else if (name == "crop")
return CATEGORY.Crops;
else if (name == "tree")
return CATEGORY.Trees;
else if (name == "bush")
return CATEGORY.Bush;
else if (name == "building")
return CATEGORY.Buildings;
else if (name == "mine item")
return CATEGORY.MineItems;
else if (name == "resource clump")
return CATEGORY.ResourceClumps;
else if (name == "container")
return CATEGORY.Containers;
else if (name == "bundle")
return CATEGORY.JunimoBundle;
else if (name == "door")
return CATEGORY.Doors;
else if (name == "water")
return CATEGORY.WaterTiles;
else if (name == "interactable")
return CATEGORY.Interactables;
else if (name == "decoration")
return CATEGORY.Decor;
else if (name == "machine")
return CATEGORY.Machines;
else if (name == "bridge")
return CATEGORY.Bridges;
else if (name == "dropped item")
return CATEGORY.DroppedItems;
else if (name == "other")
return CATEGORY.Others;
return Others;
}
public static CATEGORY Farmers = new CATEGORY("farmer");
public static CATEGORY FarmAnimals = new CATEGORY("animal");
public static CATEGORY NPCs = new CATEGORY("npc");
public static CATEGORY Furnitures = new CATEGORY("furniture");
public static CATEGORY Flooring = new CATEGORY("flooring");
public static CATEGORY Debris = new CATEGORY("debris");
public static CATEGORY Crops = new CATEGORY("crop");
public static CATEGORY Trees = new CATEGORY("tree");
public static CATEGORY Bush = new CATEGORY("bush");
public static CATEGORY Buildings = new CATEGORY("building");
public static CATEGORY MineItems = new CATEGORY("mine item");
public static CATEGORY ResourceClumps = new CATEGORY("resource clump");
public static CATEGORY Containers = new CATEGORY("container");
public static CATEGORY JunimoBundle = new CATEGORY("bundle");
public static CATEGORY Doors = new CATEGORY("door"); // Also includes ladders and elevators
public static CATEGORY WaterTiles = new CATEGORY("water");
public static CATEGORY Interactables = new CATEGORY("interactable");
public static CATEGORY Decor = new CATEGORY("decoration");
public static CATEGORY Machines = new CATEGORY("machine");
public static CATEGORY Bridges = new CATEGORY("bridge");
public static CATEGORY DroppedItems = new CATEGORY("dropped item");
public static CATEGORY Others = new CATEGORY("other");
}
public enum MachineState
{
Ready, Busy, Waiting
}
}

View File

@ -0,0 +1,90 @@
namespace stardew_access.Features
{
/// <summary>
/// Warns the player when their health or stamina/energy is low. Also warns when its past midnight.
/// </summary>
public class Warnings
{
// Store the previously checked value
private int prevStamina;
private int prevHealth;
private int prevHour;
public Warnings()
{
prevStamina = 100;
prevHealth = 100;
prevHour = 6;
}
public void update()
{
this.checkForHealth();
this.checkForStamina();
this.checkForTimeOfDay();
}
/// <summary>
/// Warns when its past 12:00 am and 1:00 am
/// </summary>
private void checkForTimeOfDay()
{
if (MainClass.ModHelper == null)
return;
int hours = StardewValley.Game1.timeOfDay / 100;
string toSpeak = MainClass.ModHelper.Translation.Get("warnings.time", new { value = CurrentPlayer.TimeOfDay });
if (hours < 1 && prevHour > 2 || hours >= 1 && prevHour < 1)
{
MainClass.ScreenReader.Say(toSpeak, true);
// Pause the read tile feature to prevent interruption in warning message
MainClass.ReadTileFeature.pauseUntil();
}
prevHour = hours;
}
/// <summary>
/// Warns when stamina reaches below 50, 25 and 10.
/// </summary>
public void checkForStamina()
{
if (MainClass.ModHelper == null)
return;
int stamina = CurrentPlayer.PercentStamina;
string toSpeak = MainClass.ModHelper.Translation.Get("warnings.stamina", new { value = stamina });
if ((stamina <= 50 && prevStamina > 50) || (stamina <= 25 && prevStamina > 25) || (stamina <= 10 && prevStamina > 10))
{
MainClass.ScreenReader.Say(toSpeak, true);
// Pause the read tile feature to prevent interruption in warning message
MainClass.ReadTileFeature.pauseUntil();
}
prevStamina = stamina;
}
/// <summary>
/// Warns when health reaches below 50, 25 and 10.
/// </summary>
public void checkForHealth()
{
if (MainClass.ModHelper == null)
return;
int health = CurrentPlayer.PercentHealth;
string toSpeak = MainClass.ModHelper.Translation.Get("warnings.health", new { value = health });
if ((health <= 50 && prevHealth > 50) || (health <= 25 && prevHealth > 25) || (health <= 10 && prevHealth > 10))
{
MainClass.ScreenReader.Say(toSpeak, true);
// Pause the read tile feature to prevent interruption in warning message
MainClass.ReadTileFeature.pauseUntil();
}
prevHealth = health;
}
}
}

View File

@ -1,9 +1,11 @@
using HarmonyLib;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using stardew_access.Patches;
using StardewValley;
using StardewValley.Menus;
using StardewValley.Minigames;
namespace stardew_access
{
@ -26,6 +28,11 @@ namespace stardew_access
original: AccessTools.Method(typeof(IClickableMenu), nameof(IClickableMenu.drawHoverText), new Type[] { typeof(SpriteBatch), typeof(string), typeof(SpriteFont), typeof(int), typeof(int), typeof(int), typeof(string), typeof(int), typeof(string[]), typeof(Item), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(float), typeof(CraftingRecipe), typeof(IList<Item>) }),
postfix: new HarmonyMethod(typeof(DialoguePatches), nameof(DialoguePatches.HoverTextPatch))
);
harmony.Patch(
original: AccessTools.Method(typeof(NPC), nameof(NPC.drawAboveAlwaysFrontLayer)),
postfix: new HarmonyMethod(typeof(DialoguePatches), nameof(DialoguePatches.drawAboveAlwaysFrontLayerPatch))
);
#endregion
#region Title Menu Patches
@ -41,13 +48,18 @@ namespace stardew_access
harmony.Patch(
original: AccessTools.Method(typeof(CharacterCustomization), nameof(CharacterCustomization.draw), new Type[] { typeof(SpriteBatch) }),
postfix: new HarmonyMethod(typeof(TitleMenuPatches), nameof(TitleMenuPatches.NewGameMenuPatch))
postfix: new HarmonyMethod(typeof(TitleMenuPatches), nameof(TitleMenuPatches.CharacterCustomizationMenuPatch))
);
harmony.Patch(
original: AccessTools.Method(typeof(CoopMenu), nameof(CoopMenu.update), new Type[] { typeof(GameTime) }),
postfix: new HarmonyMethod(typeof(TitleMenuPatches), nameof(TitleMenuPatches.CoopMenuPatch))
);
harmony.Patch(
original: AccessTools.Method(typeof(AdvancedGameOptions), nameof(AdvancedGameOptions.draw), new Type[] { typeof(SpriteBatch) }),
postfix: new HarmonyMethod(typeof(TitleMenuPatches), nameof(TitleMenuPatches.AdvancedGameOptionsPatch))
);
#endregion
#region Game Menu Patches
@ -96,16 +108,28 @@ namespace stardew_access
postfix: new HarmonyMethod(typeof(GameMenuPatches), nameof(GameMenuPatches.SocialPagePatch))
);
harmony.Patch(
original: AccessTools.Method(typeof(CollectionsPage), nameof(CollectionsPage.draw), new Type[] { typeof(SpriteBatch) }),
postfix: new HarmonyMethod(typeof(GameMenuPatches), nameof(GameMenuPatches.CollectionsPagePatch))
);
#endregion
#region Bundle Menu Patches
harmony.Patch(
original: AccessTools.Method(typeof(JunimoNoteMenu), nameof(JunimoNoteMenu.draw), new Type[] { typeof(SpriteBatch) }),
postfix: new HarmonyMethod(typeof(GameMenuPatches), nameof(GameMenuPatches.JunimoNoteMenuPatch))
postfix: new HarmonyMethod(typeof(BundleMenuPatches), nameof(BundleMenuPatches.JunimoNoteMenuPatch))
);
harmony.Patch(
original: AccessTools.Method(typeof(JojaCDMenu), nameof(JojaCDMenu.draw), new Type[] { typeof(SpriteBatch) }),
postfix: new HarmonyMethod(typeof(BundleMenuPatches), nameof(BundleMenuPatches.JojaCDMenuPatch))
);
#endregion
#region Menu Patches
harmony.Patch(
original: AccessTools.Method(typeof(LetterViewerMenu), nameof(LetterViewerMenu.draw), new Type[] { typeof(SpriteBatch) }),
postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.LetterViewerMenuPatch))
postfix: new HarmonyMethod(typeof(DialoguePatches), nameof(DialoguePatches.LetterViewerMenuPatch))
);
harmony.Patch(
@ -124,7 +148,12 @@ namespace stardew_access
);
harmony.Patch(
original: AccessTools.Constructor(typeof(NamingMenu), new Type[] { typeof(NamingMenu.doneNamingBehavior), typeof(string), typeof(string) }),
original: AccessTools.Method(typeof(TitleTextInputMenu), nameof(TitleTextInputMenu.draw), new Type[] { typeof(SpriteBatch) }),
postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.TitleTextInputMenuPatch))
);
harmony.Patch(
original: AccessTools.Method(typeof(NamingMenu), nameof(NamingMenu.draw), new Type[] { typeof(SpriteBatch) }),
postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.NamingMenuPatch))
);
@ -137,6 +166,36 @@ namespace stardew_access
original: AccessTools.Method(typeof(LanguageSelectionMenu), nameof(LanguageSelectionMenu.draw), new Type[] { typeof(SpriteBatch) }),
postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.LanguageSelectionMenuPatch))
);
harmony.Patch(
original: AccessTools.Method(typeof(MuseumMenu), nameof(MuseumMenu.receiveKeyPress), new Type[] { typeof(Keys) }),
prefix: new HarmonyMethod(typeof(DonationMenuPatches), nameof(DonationMenuPatches.MuseumMenuKeyPressPatch))
);
harmony.Patch(
original: AccessTools.Method(typeof(ChooseFromListMenu), nameof(ChooseFromListMenu.draw), new Type[] { typeof(SpriteBatch) }),
postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.ChooseFromListMenuPatch))
);
harmony.Patch(
original: AccessTools.Method(typeof(TailoringMenu), nameof(TailoringMenu.draw), new Type[] { typeof(SpriteBatch) }),
postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.TailoringMenuPatch))
);
harmony.Patch(
original: AccessTools.Method(typeof(PondQueryMenu), nameof(PondQueryMenu.draw), new Type[] { typeof(SpriteBatch) }),
postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.PondQueryMenuPatch))
);
harmony.Patch(
original: AccessTools.Method(typeof(ForgeMenu), nameof(ForgeMenu.draw), new Type[] { typeof(SpriteBatch) }),
postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.ForgeMenuPatch))
);
harmony.Patch(
original: AccessTools.Method(typeof(ItemListMenu), nameof(ItemListMenu.draw), new Type[] { typeof(SpriteBatch) }),
postfix: new HarmonyMethod(typeof(MenuPatches), nameof(MenuPatches.ItemListMenuPatch))
);
#endregion
#region Quest Patches
@ -159,7 +218,7 @@ namespace stardew_access
#region Chat Menu Patches
harmony.Patch(
original: AccessTools.Method(typeof(ChatBox), nameof(ChatBox.update), new Type[] { typeof(GameTime) }),
postfix: new HarmonyMethod(typeof(ChatManuPatches), nameof(ChatManuPatches.ChatBoxPatch))
postfix: new HarmonyMethod(typeof(ChatMenuPatches), nameof(ChatMenuPatches.ChatBoxPatch))
);
#endregion
@ -186,6 +245,35 @@ namespace stardew_access
prefix: new HarmonyMethod(typeof(BuildingNAnimalMenuPatches), nameof(BuildingNAnimalMenuPatches.PurchaseAnimalsMenuPatch))
);
harmony.Patch(
original: AccessTools.Method(typeof(AnimalQueryMenu), nameof(AnimalQueryMenu.draw), new Type[] { typeof(SpriteBatch) }),
postfix: new HarmonyMethod(typeof(BuildingNAnimalMenuPatches), nameof(BuildingNAnimalMenuPatches.AnimalQueryMenuPatch))
);
#endregion
#region Donation Menus
harmony.Patch(
original: AccessTools.Method(typeof(MuseumMenu), nameof(MuseumMenu.draw), new Type[] { typeof(SpriteBatch) }),
postfix: new HarmonyMethod(typeof(DonationMenuPatches), nameof(DonationMenuPatches.MuseumMenuPatch))
);
harmony.Patch(
original: AccessTools.Method(typeof(FieldOfficeMenu), nameof(FieldOfficeMenu.draw), new Type[] { typeof(SpriteBatch) }),
postfix: new HarmonyMethod(typeof(DonationMenuPatches), nameof(DonationMenuPatches.FieldOfficeMenuPatch))
);
#endregion
#region Mini Games
harmony.Patch(
original: AccessTools.Method(typeof(Intro), nameof(Intro.draw), new Type[] { typeof(SpriteBatch) }),
postfix: new HarmonyMethod(typeof(MiniGamesPatches), nameof(MiniGamesPatches.IntroPatch))
);
harmony.Patch(
original: AccessTools.Method(typeof(GrandpaStory), nameof(GrandpaStory.draw), new Type[] { typeof(SpriteBatch) }),
postfix: new HarmonyMethod(typeof(MiniGamesPatches), nameof(MiniGamesPatches.GrandpaStoryPatch))
);
#endregion
harmony.Patch(

View File

@ -0,0 +1,88 @@
using StardewModdingAPI.Utilities;
namespace stardew_access
{
internal class ModConfig
{
// https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Input#SButton button key codes
#region Simulate mouse clicks
public KeybindList LeftClickMainKey { get; set; } = KeybindList.Parse("LeftControl + Enter"); // Primary key to simulate mouse left click
public KeybindList RightClickMainKey { get; set; } = KeybindList.Parse("LeftShift + Enter"); // Primary key to simulate mouse right click
public KeybindList LeftClickAlternateKey { get; set; } = KeybindList.Parse("OemOpenBrackets"); // Secondary key to simulate mouse left click
public KeybindList RightClickAlternateKey { get; set; } = KeybindList.Parse("OemCloseBrackets"); // Secondary key to simulate mouse right click
#endregion
#region Chat menu
public KeybindList ChatMenuNextKey { get; set; } = KeybindList.Parse("PageUp"); // Read previous chat message
public KeybindList ChatMenuPreviousKey { get; set; } = KeybindList.Parse("PageDown"); // Read next chat message
#endregion
#region Read tile
public Boolean ReadTile { get; set; } = true; // Toggle this feature.
public KeybindList ReadTileKey { get; set; } = KeybindList.Parse("J"); // Manually trigger read tile for the tile player is *looking at*.
public KeybindList ReadStandingTileKey { get; set; } = KeybindList.Parse("LeftAlt + J"); // Manually trigger read tile for the tile player is *standing on*.
public Boolean ReadFlooring { get; set; } = false; // Toggle reading floorings.
public Boolean WateredToggle { get; set; } = true; // Toggle speaking watered or unwatered for crops.
#endregion
#region Tile viewer
public KeybindList TileCursorUpKey { get; set; } = KeybindList.Parse("Up"); // Move the cursor one tile up
public KeybindList TileCursorRightKey { get; set; } = KeybindList.Parse("Right"); // Move the cursor one tile right
public KeybindList TileCursorDownKey { get; set; } = KeybindList.Parse("Down"); // Move the cursor one tile down
public KeybindList TileCursorLeftKey { get; set; } = KeybindList.Parse("Left"); // Move the cursor one tile left
public KeybindList TileCursorPreciseUpKey { get; set; } = KeybindList.Parse("LeftShift + Up"); // Move the cursor up by precision i.e. pixel by pixel
public KeybindList TileCursorPreciseRightKey { get; set; } = KeybindList.Parse("LeftShift + Right"); // Move the cursor right by precision i.e. pixel by pixel
public KeybindList TileCursorPreciseDownKey { get; set; } = KeybindList.Parse("LeftShift + Down"); // Move the cursor down by precision i.e. pixel by pixel
public KeybindList TileCursorPreciseLeftKey { get; set; } = KeybindList.Parse("LeftShift + Left"); // Move the cursor left by precision i.e. pixel by pixel
public KeybindList ToggleRelativeCursorLockKey { get; set; } = KeybindList.Parse("L"); // Toggles realative cursor lock i.e. if enabled, the cursor will reset when player moves.
public KeybindList AutoWalkToTileKey { get; set; } = KeybindList.Parse("LeftControl + Enter"); // Auto walk to the tile
public bool LimitTileCursorToScreen { get; set; } = false; // #TODO add command for this // Toggle whether to prevent cursor from going out of screen.
public int TileCursorPreciseMovementDistance { get; set; } = 8; // Specifies the number of pixels the cursor should move when using precision movement i.e. with *left shift*.
#endregion
#region Radar
public Boolean Radar { get; set; } = false;
public Boolean RadarStereoSound { get; set; } = true;
#endregion
#region Menu Keys
public KeybindList PrimaryInfoKey { get; set; } = KeybindList.Parse("C");
// Charachter Creatinon menu (new game menu) keys
public KeybindList CharacterCreationMenuNextKey { get; set; } = KeybindList.Parse("Right");
public KeybindList CharacterCreationMenuPreviousKey { get; set; } = KeybindList.Parse("Left");
// Bundle menu keys
public KeybindList BundleMenuIngredientsKey { get; set; } = KeybindList.Parse("I");
public KeybindList BundleMenuInventoryItemsKey { get; set; } = KeybindList.Parse("C");
public KeybindList BundleMenuPurchaseButtonKey { get; set; } = KeybindList.Parse("P");
public KeybindList BundleMenuIngredientsInputSlotKey { get; set; } = KeybindList.Parse("V");
public KeybindList BundleMenuBackButtonKey { get; set; } = KeybindList.Parse("Back");
// Menus with secondary inventory(shop inventory or chest inventory or crafting recipe list)
public KeybindList SnapToFirstInventorySlotKey { get; set; } = KeybindList.Parse("I");
public KeybindList SnapToFirstSecondaryInventorySlotKey { get; set; } = KeybindList.Parse("LeftShift + I");
// Crafting menu
public KeybindList CraftingMenuCycleThroughRecipiesKey { get; set; } = KeybindList.Parse("C");
#endregion
#region Others
public KeybindList HealthNStaminaKey { get; set; } = KeybindList.Parse("H"); // Narrate health and stamina.
public bool HealthNStaminaInPercentage { get; set; } = true;
public KeybindList PositionKey { get; set; } = KeybindList.Parse("K"); // Narrate player position.
public KeybindList LocationKey { get; set; } = KeybindList.Parse("LeftAlt + K"); // Narrate current location name.
public KeybindList MoneyKey { get; set; } = KeybindList.Parse("R"); // Narrate the money the player has currently.
public KeybindList TimeNSeasonKey { get; set; } = KeybindList.Parse("Q"); // Narrate the time of day, day and date and season
public Boolean VerboseCoordinates { get; set; } = true;
public Boolean SnapMouse { get; set; } = true; // Toggles the snap mouse feature
public Boolean Warning { get; set; } = true; // Toggles the warnings feature
public Boolean TTS { get; set; } = true; // Toggles the screen reader/tts.
public Boolean TrackDroppedItems {get; set;} = true; // Toggles detecting the dropped items.
#endregion
// TODO Add the exclusion and focus list too
// public String ExclusionList { get; set; } = "test";
}
}

View File

@ -6,48 +6,101 @@ using HarmonyLib;
using stardew_access.Patches;
using stardew_access.ScreenReader;
using Microsoft.Xna.Framework;
using StardewValley.Menus;
namespace stardew_access
{
public class MainClass : Mod
{
#region Global Vars & Properties
#pragma warning disable CS8603
private static int prevDate = -99;
private static ModConfig? config;
private Harmony? harmony;
public static bool readTile = true;
public static bool snapMouse = true;
public static bool isNarratingHudMessage = false;
public static bool radar = false;
public static bool radarDebug = false;
public static bool radarStereoSound = true;
private static IMonitor monitor;
public static string hudMessageQueryKey = "";
private static Radar radarFeature;
private static IMonitor? monitor;
private static Radar? radarFeature;
private static StaticTiles? sTiles;
private static IScreenReader? screenReader;
private static IModHelper modHelper;
private static IModHelper? modHelper;
private static TileViewer? tileViewer;
private static Warnings? warnings;
private static ReadTile? readTile;
public static IModHelper ModHelper { get => modHelper; }
public static Radar RadarFeature { get => radarFeature; set => radarFeature = value; }
internal static ModConfig Config { get => config; set => config = value; }
public static IModHelper? ModHelper { get => modHelper; }
public static IScreenReader GetScreenReader()
public static StaticTiles STiles
{
get
{
if (sTiles == null)
sTiles = new StaticTiles();
return sTiles;
}
set => sTiles = value;
}
public static Radar RadarFeature
{
get
{
if (radarFeature == null)
radarFeature = new Radar();
return radarFeature;
}
set => radarFeature = value;
}
public static string hudMessageQueryKey = "";
public static bool isNarratingHudMessage = false;
public static bool radarDebug = false;
public static IScreenReader ScreenReader
{
get
{
if (screenReader == null)
screenReader = new ScreenReaderController().Initialize();
return screenReader;
}
public static void SetScreenReader(IScreenReader value)
{
screenReader = value;
set => screenReader = value;
}
public static IMonitor GetMonitor()
public static TileViewer TileViewerFeature
{
return monitor;
get
{
if (tileViewer == null)
tileViewer = new TileViewer();
return tileViewer;
}
}
public static void SetMonitor(IMonitor value)
public static ReadTile ReadTileFeature
{
monitor = value;
get
{
if (readTile == null)
readTile = new ReadTile();
return readTile;
}
}
public static Warnings WarningsFeature
{
get
{
if (warnings == null)
warnings = new Warnings();
return warnings;
}
}
#pragma warning restore CS8603
#endregion
/*********
** Public methods
@ -57,20 +110,20 @@ namespace stardew_access
public override void Entry(IModHelper helper)
{
#region Initializations
Config = helper.ReadConfig<ModConfig>();
SetMonitor(base.Monitor); // Inititalize monitor
monitor = base.Monitor; // Inititalize monitor
modHelper = helper;
Game1.options.setGamepadMode("force_on");
SetScreenReader(new ScreenReaderController().Initialize());
ScreenReader = new ScreenReaderController().Initialize();
ScreenReader.Say("Initializing Stardew Access", true);
CustomSoundEffects.Initialize();
CustomCommands.Initialize();
RadarFeature = new Radar();
harmony = new Harmony(ModManifest.UniqueID);
HarmonyPatches.Initialize(harmony);
@ -94,9 +147,10 @@ namespace stardew_access
public void OnExit(object? sender, EventArgs? e)
{
// Don't if this ever gets called or not but, just in case if it does.
if (GetScreenReader() != null)
GetScreenReader().CloseScreenReader();
// This closes the connection with the screen reader, important for linux
// Don't know if this ever gets called or not but, just in case if it does.
if (ScreenReader != null)
ScreenReader.CloseScreenReader();
}
/// <summary>Returns the Screen Reader class for other mods to use.</summary>
@ -110,26 +164,43 @@ namespace stardew_access
if (!Context.IsPlayerFree)
return;
// Reset variables
MenuPatches.resetGlobalVars();
QuestPatches.resetGlobalVars();
// Narrates currently selected inventory slot
Other.narrateCurrentSlot();
// Narrate current location's name
Other.narrateCurrentLocation();
if (snapMouse)
Other.SnapMouseToPlayer();
//handle TileCursor update logic
TileViewerFeature.update();
if (!ReadTile.isReadingTile && readTile)
ReadTile.run();
if (Config.Warning)
WarningsFeature.update();
if (!RadarFeature.isRunning && radar)
if (Config.ReadTile)
ReadTileFeature.update();
if (!RadarFeature.isRunning && Config.Radar)
{
RadarFeature.isRunning = true;
RadarFeature.Run();
Task.Delay(RadarFeature.delay).ContinueWith(_ => { RadarFeature.isRunning = false; });
}
if (!isNarratingHudMessage)
{
isNarratingHudMessage = true;
Other.narrateHudMessages();
Task.Delay(300).ContinueWith(_ => { isNarratingHudMessage = false; });
}
if (Game1.player != null)
{
if (Game1.timeOfDay >= 600 && prevDate != CurrentPlayer.Date)
{
prevDate = CurrentPlayer.Date;
DebugLog("Refreshing buildlist...");
CustomCommands.onBuildListCalled();
}
}
}
@ -138,74 +209,172 @@ namespace stardew_access
if (e == null)
return;
#region Simulate left and right clicks
if (Game1.activeClickableMenu != null)
{
bool isLeftShiftPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftShift);
bool isLeftControlPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftControl);
bool isCustomizingChrachter = Game1.activeClickableMenu is CharacterCustomization || (TitleMenu.subMenu != null && TitleMenu.subMenu is CharacterCustomization);
// Perform Left Click
if (Equals(e.Button, SButton.OemOpenBrackets))
#region Mouse Click Simulation
// Main Keybinds
if (Config.LeftClickMainKey.JustPressed())
{
Game1.activeClickableMenu.receiveLeftClick(Game1.getMouseX(true), Game1.getMouseY(true));
}
if (isLeftControlPressed && Equals(e.Button, SButton.Enter))
if (Config.RightClickMainKey.JustPressed())
{
Game1.activeClickableMenu.receiveRightClick(Game1.getMouseX(true), Game1.getMouseY(true));
}
// Alternate Keybinds
if (!isCustomizingChrachter && Game1.activeClickableMenu is not AnimalQueryMenu && Config.LeftClickAlternateKey.JustPressed()) // Excluding the character creation menu
{
Game1.activeClickableMenu.receiveLeftClick(Game1.getMouseX(true), Game1.getMouseY(true));
}
if (!isCustomizingChrachter && Game1.activeClickableMenu is not AnimalQueryMenu && Config.RightClickAlternateKey.JustPressed()) // Excluding the character creation menu
{
Game1.activeClickableMenu.receiveRightClick(Game1.getMouseX(true), Game1.getMouseY(true));
}
#endregion
}
// Perform Right CLick
if (Equals(e.Button, SButton.OemCloseBrackets))
if (Game1.currentMinigame != null)
{
Game1.activeClickableMenu.receiveRightClick(Game1.getMouseX(true), Game1.getMouseY(true));
}
if (isLeftShiftPressed && Equals(e.Button, SButton.Enter))
bool isCustomizingChrachter = Game1.activeClickableMenu is CharacterCustomization || (TitleMenu.subMenu != null && TitleMenu.subMenu is CharacterCustomization);
#region Mouse Click Simulation
// Main Keybinds
if (Config.LeftClickMainKey.JustPressed())
{
Game1.activeClickableMenu.receiveRightClick(Game1.getMouseX(true), Game1.getMouseY(true));
Game1.currentMinigame.receiveLeftClick(Game1.getMouseX(true), Game1.getMouseY(true));
}
if (Config.RightClickMainKey.JustPressed())
{
Game1.currentMinigame.receiveRightClick(Game1.getMouseX(true), Game1.getMouseY(true));
}
// Alternate Keybinds
if (Config.LeftClickAlternateKey.JustPressed())
{
Game1.currentMinigame.receiveLeftClick(Game1.getMouseX(true), Game1.getMouseY(true));
}
if (Config.RightClickAlternateKey.JustPressed())
{
Game1.currentMinigame.receiveRightClick(Game1.getMouseX(true), Game1.getMouseY(true));
}
#endregion
}
#endregion
if (!Context.IsPlayerFree)
return;
// Narrate health and stamina
if (Equals(e.Button, SButton.H))
// Stops the auto walk controller if any movement key(WASD) is pressed
if (TileViewerFeature.isAutoWalking &&
(e.Button.Equals(SButtonExtensions.ToSButton(Game1.options.moveUpButton[0]))
|| e.Button.Equals(SButtonExtensions.ToSButton(Game1.options.moveDownButton[0]))
|| e.Button.Equals(SButtonExtensions.ToSButton(Game1.options.moveLeftButton[0]))
|| e.Button.Equals(SButtonExtensions.ToSButton(Game1.options.moveRightButton[0]))))
{
string toSpeak = $"Health is {CurrentPlayer.getHealth()} and Stamina is {CurrentPlayer.getStamina()}";
MainClass.GetScreenReader().Say(toSpeak, true);
TileViewerFeature.stopAutoWalking(wasForced: true);
}
// Narrate Current Location
if (Config.LocationKey.JustPressed())
{
string toSpeak = $"{Game1.currentLocation.Name}";
MainClass.ScreenReader.Say(toSpeak, true);
return;
}
// Narrate Position
if (Equals(e.Button, SButton.K))
if (Config.PositionKey.JustPressed())
{
string toSpeak = $"X: {CurrentPlayer.getPositionX()} , Y: {CurrentPlayer.getPositionY()}";
MainClass.GetScreenReader().Say(toSpeak, true);
string toSpeak;
if (Config.VerboseCoordinates)
{
toSpeak = $"X: {CurrentPlayer.PositionX}, Y: {CurrentPlayer.PositionY}";
}
else
{
toSpeak = $"{CurrentPlayer.PositionX}, {CurrentPlayer.PositionY}";
}
MainClass.ScreenReader.Say(toSpeak, true);
return;
}
// Narrate health and stamina
if (Config.HealthNStaminaKey.JustPressed())
{
if (ModHelper == null)
return;
string toSpeak;
if (Config.HealthNStaminaInPercentage)
toSpeak = ModHelper.Translation.Get("manuallytriggered.healthnstamina.percent", new { health = CurrentPlayer.PercentHealth, stamina = CurrentPlayer.PercentStamina });
else
toSpeak = ModHelper.Translation.Get("manuallytriggered.healthnstamina.normal", new { health = CurrentPlayer.CurrentHealth, stamina = CurrentPlayer.CurrentStamina });
MainClass.ScreenReader.Say(toSpeak, true);
return;
}
// Narrate money at hand
if (Equals(e.Button, SButton.R))
if (Config.MoneyKey.JustPressed())
{
string toSpeak = $"You have {CurrentPlayer.getMoney()}g";
MainClass.GetScreenReader().Say(toSpeak, true);
string toSpeak = $"You have {CurrentPlayer.Money}g";
MainClass.ScreenReader.Say(toSpeak, true);
return;
}
// Narrate time and season
if (Equals(e.Button, SButton.Q))
if (Config.TimeNSeasonKey.JustPressed())
{
string toSpeak = $"Time is {CurrentPlayer.getTimeOfDay()} and it is {CurrentPlayer.getDay()} {CurrentPlayer.getDate()} of {CurrentPlayer.getSeason()}";
MainClass.GetScreenReader().Say(toSpeak, true);
string toSpeak = $"Time is {CurrentPlayer.TimeOfDay} and it is {CurrentPlayer.Day} {CurrentPlayer.Date} of {CurrentPlayer.Season}";
MainClass.ScreenReader.Say(toSpeak, true);
return;
}
// Manual read tile
if (Equals(e.Button, SButton.J))
// Manual read tile at player's position
if (Config.ReadStandingTileKey.JustPressed())
{
ReadTile.run(manuallyTriggered: true);
ReadTileFeature.run(manuallyTriggered: true, playersPosition: true);
return;
}
/*if (Equals(e.Button, SButton.B))
// Manual read tile at looking tile
if (Config.ReadTileKey.JustPressed())
{
Game1.player.controller = new PathFindController(Game1.player, Game1.currentLocation, new Point(49,13), 2);
monitor.Log($"{Game1.player.controller.pathToEndPoint==null}", LogLevel.Debug); // true if path not found
}*/
ReadTileFeature.run(manuallyTriggered: true);
return;
}
// Tile viewing cursor keys
TileViewerFeature.HandleInput();
}
public static void ErrorLog(string message)
{
if (monitor == null)
return;
monitor.Log(message, LogLevel.Error);
}
public static void InfoLog(string message)
{
if (monitor == null)
return;
monitor.Log(message, LogLevel.Info);
}
public static void DebugLog(string message)
{
if (monitor == null)
return;
monitor.Log(message, LogLevel.Debug);
}
}
}

View File

@ -1,5 +1,4 @@
using Microsoft.Xna.Framework;
using StardewModdingAPI;
using StardewValley;
using StardewValley.Buildings;
using StardewValley.Locations;
@ -13,20 +12,100 @@ namespace stardew_access.Patches
internal static Vector2[] marked = new Vector2[10];
internal static Building?[] availableBuildings = new Building[100];
internal static CarpenterMenu? carpenterMenu = null;
internal static bool isNarratingAnimalInfo = false;
internal static string animalQueryMenuQuery = " ";
internal static string carpenterMenuQuery = "", purchaseAnimalMenuQuery = "";
internal static bool isSayingBlueprintInfo = false;
internal static string prevBlueprintInfo = "";
internal static bool isOnFarm = false, isUpgrading = false, isDemolishing = false, isPainting = false, isConstructing = false, isMoving = false, isMagicalConstruction = false;
internal static bool firstTimeInNamingMenu = true;
internal static PurchaseAnimalsMenu? purchaseAnimalsMenu;
internal static AnimalQueryMenu? animalQueryMenu;
private static FarmAnimal? animalBeingPurchasedOrMoved = null;
internal static void PurchaseAnimalsMenuPatch(PurchaseAnimalsMenu __instance, bool ___onFarm, bool ___namingAnimal, TextBox ___textBox)
internal static void AnimalQueryMenuPatch(AnimalQueryMenu __instance, bool ___confirmingSell, FarmAnimal ___animal, TextBox ___textBox, string ___parentName, bool ___movingAnimal)
{
try
{
int x = Game1.getMouseX(), y = Game1.getMouseY(); // Mouse x and y position
int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position
bool isPrimaryInfoKeyPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); // For narrating animal details
bool isEscPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape); // For escaping/unselecting from the animal name text box
string toSpeak = " ", details = " ";
isOnFarm = ___movingAnimal;
animalQueryMenu = __instance;
animalBeingPurchasedOrMoved = ___animal;
if (___textBox.Selected)
{
toSpeak = ___textBox.Text;
if (isEscPressed)
{
___textBox.Selected = false;
}
}
else
{
if (isPrimaryInfoKeyPressed & !isNarratingAnimalInfo)
{
string name = ___animal.displayName;
string type = ___animal.displayType;
int age = (___animal.GetDaysOwned() + 1) / 28 + 1;
string ageText = (age <= 1) ? Game1.content.LoadString("Strings\\UI:AnimalQuery_Age1") : Game1.content.LoadString("Strings\\UI:AnimalQuery_AgeN", age);
string parent = "";
if ((int)___animal.age.Value < (byte)___animal.ageWhenMature.Value)
{
ageText += Game1.content.LoadString("Strings\\UI:AnimalQuery_AgeBaby");
}
if (___parentName != null)
{
parent = Game1.content.LoadString("Strings\\UI:AnimalQuery_Parent", ___parentName);
}
details = $"Name: {name} Type: {type} \n\t Age: {ageText} {parent}";
animalQueryMenuQuery = " ";
isNarratingAnimalInfo = true;
Task.Delay(200).ContinueWith(_ => { isNarratingAnimalInfo = false; });
}
if (__instance.okButton != null && __instance.okButton.containsPoint(x, y))
toSpeak = "OK button";
else if (__instance.sellButton != null && __instance.sellButton.containsPoint(x, y))
toSpeak = $"Sell for {___animal.getSellPrice()}g button";
else if (___confirmingSell && __instance.yesButton != null && __instance.yesButton.containsPoint(x, y))
toSpeak = "Confirm selling animal";
else if (___confirmingSell && __instance.noButton != null && __instance.noButton.containsPoint(x, y))
toSpeak = "Cancel selling animal";
else if (__instance.moveHomeButton != null && __instance.moveHomeButton.containsPoint(x, y))
toSpeak = "Change home building button";
else if (__instance.allowReproductionButton != null && __instance.allowReproductionButton.containsPoint(x, y))
toSpeak = ((___animal.allowReproduction.Value) ? "Enabled" : "Disabled") + " allow reproduction button";
else if (__instance.textBoxCC != null && __instance.textBoxCC.containsPoint(x, y))
toSpeak = "Animal name text box";
}
if (animalQueryMenuQuery != toSpeak)
{
animalQueryMenuQuery = toSpeak;
MainClass.ScreenReader.Say($"{details} {toSpeak}", true);
}
}
catch (System.Exception e)
{
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
internal static void PurchaseAnimalsMenuPatch(PurchaseAnimalsMenu __instance, bool ___onFarm, bool ___namingAnimal, TextBox ___textBox, FarmAnimal ___animalBeingPurchased)
{
try
{
int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position
purchaseAnimalsMenu = __instance;
isOnFarm = ___onFarm;
animalBeingPurchasedOrMoved = ___animalBeingPurchased;
if (___onFarm && ___namingAnimal)
{
@ -61,7 +140,7 @@ namespace stardew_access.Patches
firstTimeInNamingMenu = false;
}
MainClass.GetScreenReader().Say(toSpeak, true);
MainClass.ScreenReader.Say(toSpeak, true);
}
}
else if (___onFarm && !___namingAnimal)
@ -90,7 +169,7 @@ namespace stardew_access.Patches
if (purchaseAnimalMenuQuery != toSpeak)
{
purchaseAnimalMenuQuery = toSpeak;
MainClass.GetScreenReader().Say(toSpeak, true);
MainClass.ScreenReader.Say(toSpeak, true);
}
return;
}
@ -98,7 +177,7 @@ namespace stardew_access.Patches
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error);
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
@ -125,8 +204,8 @@ namespace stardew_access.Patches
if (currentBluprint == null)
return;
int x = Game1.getMouseX(), y = Game1.getMouseY(); // Mouse x and y position
bool isBPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.B);
int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position
bool isPrimaryInfoKeyPressed = MainClass.Config.PrimaryInfoKey.JustPressed();
string ingredients = "";
string name = currentBluprint.displayName;
string upgradeName = currentBluprint.nameOfBuildingToUpgrade;
@ -143,7 +222,7 @@ namespace stardew_access.Patches
int itemStack = ___ingredients[i].Stack;
string itemQuality = "";
int qualityValue = ((StardewValley.Object)___ingredients[i]).quality;
int qualityValue = ((StardewValley.Object)___ingredients[i]).Quality;
if (qualityValue == 1)
{
itemQuality = "Silver quality";
@ -163,7 +242,7 @@ namespace stardew_access.Patches
blueprintInfo = $"{name}, Price: {price}, Ingredients: {ingredients}, Dimensions: {width} width and {height} height, Description: {description}";
if (isBPressed && !isSayingBlueprintInfo)
if (isPrimaryInfoKeyPressed && !isSayingBlueprintInfo)
{
SayBlueprintInfo(blueprintInfo);
}
@ -180,7 +259,7 @@ namespace stardew_access.Patches
if (carpenterMenuQuery != toSpeak)
{
carpenterMenuQuery = toSpeak;
MainClass.GetScreenReader().Say(toSpeak, true);
MainClass.ScreenReader.Say(toSpeak, true);
}
return;
}
@ -191,7 +270,7 @@ namespace stardew_access.Patches
if (carpenterMenuQuery != toSpeak)
{
carpenterMenuQuery = toSpeak;
MainClass.GetScreenReader().Say(toSpeak, true);
MainClass.ScreenReader.Say(toSpeak, true);
}
return;
}
@ -202,7 +281,7 @@ namespace stardew_access.Patches
if (carpenterMenuQuery != toSpeak)
{
carpenterMenuQuery = toSpeak;
MainClass.GetScreenReader().Say(toSpeak, true);
MainClass.ScreenReader.Say(toSpeak, true);
}
return;
}
@ -213,7 +292,7 @@ namespace stardew_access.Patches
if (carpenterMenuQuery != toSpeak)
{
carpenterMenuQuery = toSpeak;
MainClass.GetScreenReader().Say(toSpeak, true);
MainClass.ScreenReader.Say(toSpeak, true);
}
return;
}
@ -224,7 +303,7 @@ namespace stardew_access.Patches
if (carpenterMenuQuery != toSpeak)
{
carpenterMenuQuery = toSpeak;
MainClass.GetScreenReader().Say(toSpeak, true);
MainClass.ScreenReader.Say(toSpeak, true);
}
return;
}
@ -235,7 +314,7 @@ namespace stardew_access.Patches
if (carpenterMenuQuery != toSpeak)
{
carpenterMenuQuery = toSpeak;
MainClass.GetScreenReader().Say(toSpeak, true);
MainClass.ScreenReader.Say(toSpeak, true);
}
return;
}
@ -246,7 +325,7 @@ namespace stardew_access.Patches
if (carpenterMenuQuery != toSpeak)
{
carpenterMenuQuery = toSpeak;
MainClass.GetScreenReader().Say(toSpeak, true);
MainClass.ScreenReader.Say(toSpeak, true);
}
return;
}
@ -269,14 +348,14 @@ namespace stardew_access.Patches
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error);
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
private static async void SayBlueprintInfo(string info)
{
isSayingBlueprintInfo = true;
MainClass.GetScreenReader().Say(info, true);
MainClass.ScreenReader.Say(info, true);
await Task.Delay(300);
isSayingBlueprintInfo = false;
}
@ -300,7 +379,7 @@ namespace stardew_access.Patches
{
if (isDemolishing && toDemolish != null && farm.buildings.Contains(toDemolish))
{
if ((int)toDemolish.daysOfConstructionLeft > 0 || (int)toDemolish.daysUntilUpgrade > 0)
if ((int)toDemolish.daysOfConstructionLeft.Value > 0 || (int)toDemolish.daysUntilUpgrade.Value > 0)
{
response = Game1.content.LoadString("Strings\\UI:Carpenter_CantDemolish_DuringConstruction");
}
@ -345,8 +424,8 @@ namespace stardew_access.Patches
}
if (farm.destroyStructure(toDemolish))
{
_ = (int)toDemolish.tileY;
_ = (int)toDemolish.tilesHigh;
_ = (int)toDemolish.tileY.Value;
_ = (int)toDemolish.tilesHigh.Value;
Game1.flashAlpha = 1f;
toDemolish.showDestroyedAnimation(Game1.getFarm());
Game1.playSound("explosion");
@ -356,7 +435,7 @@ namespace stardew_access.Patches
// freeze = true;
if (chest != null)
{
farm.objects[new Vector2((int)toDemolish.tileX + (int)toDemolish.tilesWide / 2, (int)toDemolish.tileY + (int)toDemolish.tilesHigh / 2)] = chest;
farm.objects[new Vector2((int)toDemolish.tileX.Value + (int)toDemolish.tilesWide.Value / 2, (int)toDemolish.tileY.Value + (int)toDemolish.tilesHigh.Value / 2)] = chest;
}
}
}
@ -385,7 +464,7 @@ namespace stardew_access.Patches
if (toDemolish != null && toDemolish.indoors.Value is Cabin)
{
Cabin cabin = (Cabin)toDemolish.indoors.Value;
if (cabin.farmhand.Value != null && (bool)cabin.farmhand.Value.isCustomized)
if (cabin.farmhand.Value != null && (bool)cabin.farmhand.Value.isCustomized.Value)
{
Game1.currentLocation.createQuestionDialogue(Game1.content.LoadString("Strings\\UI:Carpenter_DemolishCabinConfirm", cabin.farmhand.Value.Name), Game1.currentLocation.createYesNoResponses(), delegate (Farmer f, string answer)
{
@ -458,7 +537,7 @@ namespace stardew_access.Patches
Game1.playSound("axe");
DelayedAction.functionAfterDelay(carpenterMenu.returnToCarpentryMenuAfterSuccessfulBuild, 1500);
// freeze = true;
// Game1.multiplayer.globalChatInfoMessage("BuildingBuild", Game1.player.Name, Utility.AOrAn(carpenterMenu.CurrentBlueprint.displayName), carpenterMenu.CurrentBlueprint.displayName, Game1.player.farmName);
// Game1.multiplayer.globalChatInfoMessage("BuildingBuild", Game1.player.Name, Utility.AOrAn(carpenterMenu.CurrentBlueprint.displayName), carpenterMenu.CurrentBlueprint.displayName, Game1.player.farmName.Value);
}
else if (toUpgrade != null)
{
@ -517,7 +596,7 @@ namespace stardew_access.Patches
string? name = buildingToMove.nameOfIndoorsWithoutUnique;
name = (name == "null") ? buildingToMove.buildingType.Value : name;
if ((int)buildingToMove.daysOfConstructionLeft > 0)
if ((int)buildingToMove.daysOfConstructionLeft.Value > 0)
{
buildingToMove = null;
return "Building under construction, cannot move";
@ -566,9 +645,70 @@ namespace stardew_access.Patches
if (purchaseAnimalsMenu == null)
return;
int x = (selection.tileX * Game1.tileSize) - Game1.viewport.X;
int y = (selection.tileY * Game1.tileSize) - Game1.viewport.Y;
int x = (selection.tileX.Value * Game1.tileSize) - Game1.viewport.X;
int y = (selection.tileY.Value * Game1.tileSize) - Game1.viewport.Y;
if (animalBeingPurchasedOrMoved != null && !selection.buildingType.Value.Contains(animalBeingPurchasedOrMoved.buildingTypeILiveIn.Value))
{
string warn = Game1.content.LoadString("Strings\\StringsFromCSFiles:PurchaseAnimalsMenu.cs.11326", animalBeingPurchasedOrMoved.displayType);
MainClass.ScreenReader.Say(warn, true);
return;
}
if (((AnimalHouse)selection.indoors.Value).isFull())
{
string warn = Game1.content.LoadString("Strings\\StringsFromCSFiles:PurchaseAnimalsMenu.cs.11321");
MainClass.ScreenReader.Say(warn, true);
return;
}
purchaseAnimalsMenu.receiveLeftClick(x, y);
}
public static void MoveAnimal(Building? selection)
{
if (selection == null)
return;
if (animalQueryMenu == null)
return;
if (animalBeingPurchasedOrMoved == null)
return;
// The following code is taken from the game's source code [AnimalQueryMenu.cs::receiveLeftClick]
if (selection.buildingType.Value.Contains(animalBeingPurchasedOrMoved.buildingTypeILiveIn.Value))
{
if (((AnimalHouse)selection.indoors.Value).isFull())
{
string warn = Game1.content.LoadString("Strings\\UI:AnimalQuery_Moving_BuildingFull");
MainClass.ScreenReader.Say(warn, true);
return;
}
if (selection.Equals(animalBeingPurchasedOrMoved.home))
{
string warn = Game1.content.LoadString("Strings\\UI:AnimalQuery_Moving_AlreadyHome");
MainClass.ScreenReader.Say(warn, true);
return;
}
((AnimalHouse)animalBeingPurchasedOrMoved.home.indoors.Value).animalsThatLiveHere.Remove(animalBeingPurchasedOrMoved.myID.Value);
if (((AnimalHouse)animalBeingPurchasedOrMoved.home.indoors.Value).animals.ContainsKey(animalBeingPurchasedOrMoved.myID.Value))
{
((AnimalHouse)selection.indoors.Value).animals.Add(animalBeingPurchasedOrMoved.myID.Value, animalBeingPurchasedOrMoved);
((AnimalHouse)animalBeingPurchasedOrMoved.home.indoors.Value).animals.Remove(animalBeingPurchasedOrMoved.myID.Value);
}
animalBeingPurchasedOrMoved.home = selection;
animalBeingPurchasedOrMoved.homeLocation.Value = new Vector2((int)selection.tileX.Value, (int)selection.tileY.Value);
((AnimalHouse)selection.indoors.Value).animalsThatLiveHere.Add(animalBeingPurchasedOrMoved.myID.Value);
animalBeingPurchasedOrMoved.makeSound();
Game1.globalFadeToBlack(animalQueryMenu.finishedPlacingAnimal);
}
else
{
string warn = Game1.content.LoadString("Strings\\UI:AnimalQuery_Moving_CantLiveThere");
MainClass.ScreenReader.Say(warn, true);
}
return;
}
}
}

View File

@ -0,0 +1,360 @@
using StardewValley;
using StardewValley.Locations;
using StardewValley.Menus;
namespace stardew_access.Patches
{
internal class BundleMenuPatches
{
internal static string junimoNoteMenuQuery = "";
internal static string currentJunimoArea = "";
internal static string jojaCDMenuQuery = "";
internal static bool isUsingCustomButtons = false;
internal static int currentIngredientListItem = -1, currentIngredientInputSlot = -1, currentInventorySlot = -1;
#region Joja Mart Bundle/Quests
internal static void JojaCDMenuPatch(JojaCDMenu __instance)
{
try
{
int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position
string toSpeak = "";
for (int i = 0; i < __instance.checkboxes.Count; i++)
{
ClickableComponent c = __instance.checkboxes[i];
if (!c.containsPoint(x, y))
continue;
if (c.name.Equals("complete"))
{
toSpeak = $"Completed {getNameFromIndex(i)}";
}
else
{
toSpeak = $"{getNameFromIndex(i)} Cost: {__instance.getPriceFromButtonNumber(i)}g Description: {__instance.getDescriptionFromButtonNumber(i)}";
}
break;
}
if (jojaCDMenuQuery != toSpeak)
{
jojaCDMenuQuery = toSpeak;
MainClass.ScreenReader.Say(toSpeak, true);
}
}
catch (System.Exception e)
{
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
private static string getNameFromIndex(int i)
{
string name = i switch
{
0 => "Bus",
1 => "Minecarts",
2 => "Bridge",
3 => "Greenhouse",
4 => "Panning",
_ => "",
};
if (name != "")
return $"{name} Project";
else
return "unkown";
}
#endregion
#region Community Center Bundles
internal static void JunimoNoteMenuPatch(JunimoNoteMenu __instance, bool ___specificBundlePage, int ___whichArea, Bundle ___currentPageBundle)
{
try
{
int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position
if (!___specificBundlePage)
{
currentIngredientListItem = -1;
isUsingCustomButtons = false;
string areaName = __instance.scrambledText ? CommunityCenter.getAreaEnglishDisplayNameFromNumber(___whichArea) : CommunityCenter.getAreaDisplayNameFromNumber(___whichArea);
string reward = __instance.getRewardNameForArea(___whichArea);
if (__instance.scrambledText)
{
string toSpeak = "Scrambled Text";
if (junimoNoteMenuQuery != toSpeak)
{
junimoNoteMenuQuery = toSpeak;
MainClass.ScreenReader.Say(toSpeak, true);
}
return;
}
if (currentJunimoArea != areaName)
{
currentJunimoArea = areaName;
MainClass.ScreenReader.Say($"Area {areaName}, {reward}", true);
return;
}
for (int i = 0; i < __instance.bundles.Count; i++)
{
if (__instance.bundles[i].containsPoint(x, y))
{
string toSpeak = $"{__instance.bundles[i].name} bundle";
if (junimoNoteMenuQuery != toSpeak)
{
junimoNoteMenuQuery = toSpeak;
MainClass.ScreenReader.Say(toSpeak, true);
}
return;
}
}
if (__instance.presentButton != null && __instance.presentButton.containsPoint(x, y))
{
string toSpeak = "Present Button";
if (junimoNoteMenuQuery != toSpeak)
{
junimoNoteMenuQuery = toSpeak;
MainClass.ScreenReader.Say(toSpeak, true);
}
return;
}
if (__instance.fromGameMenu)
{
if (__instance.areaNextButton.visible && __instance.areaNextButton.containsPoint(x, y))
{
string toSpeak = "Next Area Button";
if (junimoNoteMenuQuery != toSpeak)
{
junimoNoteMenuQuery = toSpeak;
MainClass.ScreenReader.Say(toSpeak, true);
}
return;
}
if (__instance.areaBackButton.visible && __instance.areaBackButton.containsPoint(x, y))
{
string toSpeak = "Previous Area Button";
if (junimoNoteMenuQuery != toSpeak)
{
junimoNoteMenuQuery = toSpeak;
MainClass.ScreenReader.Say(toSpeak, true);
}
return;
}
}
}
else
{
bool isIPressed = MainClass.Config.BundleMenuIngredientsKey.JustPressed(); // For the ingredients
bool isCPressed = MainClass.Config.BundleMenuInventoryItemsKey.JustPressed(); // For the items in inventory
bool isPPressed = MainClass.Config.BundleMenuPurchaseButtonKey.JustPressed(); // For the Purchase Button
bool isVPressed = MainClass.Config.BundleMenuIngredientsInputSlotKey.JustPressed(); // For the ingredient input slots
bool isBackPressed = MainClass.Config.BundleMenuBackButtonKey.JustPressed(); // For the back button
bool isLeftShiftPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftShift);
if (isIPressed && !isUsingCustomButtons)
{
isUsingCustomButtons = true;
JunimoNoteCustomButtons(__instance, ___currentPageBundle, 0, isLeftShiftPressed);
Task.Delay(200).ContinueWith(_ => { isUsingCustomButtons = false; });
}
else if (isVPressed && !isUsingCustomButtons)
{
isUsingCustomButtons = true;
JunimoNoteCustomButtons(__instance, ___currentPageBundle, 1, isLeftShiftPressed);
Task.Delay(200).ContinueWith(_ => { isUsingCustomButtons = false; });
}
else if (isCPressed && !isUsingCustomButtons)
{
isUsingCustomButtons = true;
JunimoNoteCustomButtons(__instance, ___currentPageBundle, 2, isLeftShiftPressed);
Task.Delay(200).ContinueWith(_ => { isUsingCustomButtons = false; });
}
else if (isBackPressed && __instance.backButton != null && !__instance.backButton.containsPoint(x, y))
{
__instance.backButton.snapMouseCursorToCenter();
MainClass.ScreenReader.Say("Back Button", true);
}
else if (isPPressed && __instance.purchaseButton != null && !__instance.purchaseButton.containsPoint(x, y))
{
__instance.purchaseButton.snapMouseCursorToCenter();
MainClass.ScreenReader.Say("Purchase Button", true);
}
}
}
catch (Exception e)
{
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
private static void JunimoNoteCustomButtons(JunimoNoteMenu __instance, Bundle ___currentPageBundle, int signal, bool isLeftShiftPressed = false)
{
try
{
switch (signal)
{
case 0: // For ingredient list
{
if (___currentPageBundle.ingredients.Count >= 0)
{
currentIngredientListItem = currentIngredientListItem + (isLeftShiftPressed ? -1 : 1);
if (currentIngredientListItem >= ___currentPageBundle.ingredients.Count)
if (isLeftShiftPressed)
currentIngredientListItem = ___currentPageBundle.ingredients.Count - 1;
else
currentIngredientListItem = 0;
if (currentIngredientListItem < 0)
if (isLeftShiftPressed)
currentIngredientListItem = ___currentPageBundle.ingredients.Count - 1;
else
currentIngredientListItem = 0;
ClickableTextureComponent c = __instance.ingredientList[currentIngredientListItem];
BundleIngredientDescription ingredient = ___currentPageBundle.ingredients[currentIngredientListItem];
Item item = new StardewValley.Object(ingredient.index, ingredient.stack, isRecipe: false, -1, ingredient.quality);
bool completed = false;
if (___currentPageBundle != null && ___currentPageBundle.ingredients != null && currentIngredientListItem < ___currentPageBundle.ingredients.Count && ___currentPageBundle.ingredients[currentIngredientListItem].completed)
{
completed = true;
}
string toSpeak = item.DisplayName;
if (!completed)
{
int quality = ingredient.quality;
if (quality == 1)
{
toSpeak = $"Silver quality {toSpeak}";
}
else if (quality == 2 || quality == 3)
{
toSpeak = $"Gold quality {toSpeak}";
}
else if (quality >= 4)
{
toSpeak = $"Iridium quality {toSpeak}";
}
toSpeak = $"{ingredient.stack} {toSpeak}";
}
if (completed)
toSpeak = $"Completed {toSpeak}";
c.snapMouseCursorToCenter();
MainClass.ScreenReader.Say(toSpeak, true);
}
}
break;
case 1: // For input slot list
{
if (__instance.ingredientSlots.Count >= 0)
{
currentIngredientInputSlot = currentIngredientInputSlot + (isLeftShiftPressed ? -1 : 1);
if (currentIngredientInputSlot >= __instance.ingredientSlots.Count)
if (isLeftShiftPressed)
currentIngredientInputSlot = __instance.ingredientSlots.Count - 1;
else
currentIngredientInputSlot = 0;
if (currentIngredientInputSlot < 0)
if (isLeftShiftPressed)
currentIngredientInputSlot = __instance.ingredientSlots.Count - 1;
else
currentIngredientInputSlot = 0;
ClickableTextureComponent c = __instance.ingredientSlots[currentIngredientInputSlot];
Item item = c.item;
string toSpeak;
if (item == null)
{
toSpeak = $"Input Slot {currentIngredientInputSlot + 1}";
}
else
{
toSpeak = item.DisplayName;
}
c.snapMouseCursorToCenter();
MainClass.ScreenReader.Say(toSpeak, true);
}
}
break;
case 2: // For inventory slots
{
if (__instance.inventory != null && __instance.inventory.actualInventory.Count >= 0)
{
int prevSlotIndex = currentInventorySlot;
currentInventorySlot = currentInventorySlot + (isLeftShiftPressed ? -1 : 1);
if (currentInventorySlot >= __instance.inventory.actualInventory.Count)
if (isLeftShiftPressed)
currentInventorySlot = __instance.inventory.actualInventory.Count - 1;
else
currentInventorySlot = 0;
if (currentInventorySlot < 0)
if (isLeftShiftPressed)
currentInventorySlot = __instance.inventory.actualInventory.Count - 1;
else
currentInventorySlot = 0;
Item item = __instance.inventory.actualInventory[currentInventorySlot];
ClickableComponent c = __instance.inventory.inventory[currentInventorySlot];
string toSpeak;
if (item != null)
{
toSpeak = item.DisplayName;
if ((item as StardewValley.Object) != null)
{
int quality = ((StardewValley.Object)item).Quality;
if (quality == 1)
{
toSpeak = $"Silver quality {toSpeak}";
}
else if (quality == 2 || quality == 3)
{
toSpeak = $"Gold quality {toSpeak}";
}
else if (quality >= 4)
{
toSpeak = $"Iridium quality {toSpeak}";
}
}
toSpeak = $"{item.Stack} {toSpeak}";
if (!__instance.inventory.highlightMethod(__instance.inventory.actualInventory[currentInventorySlot]))
{
toSpeak = $"{toSpeak} not usable here";
}
}
else
{
toSpeak = "Empty Slot";
}
c.snapMouseCursorToCenter();
MainClass.ScreenReader.Say(toSpeak, true);
}
}
break;
}
}
catch (Exception e)
{
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
#endregion
}
}

View File

@ -1,10 +1,9 @@
using StardewModdingAPI;
using StardewValley;
using StardewValley;
using StardewValley.Menus;
namespace stardew_access.Patches
{
internal class ChatManuPatches
internal class ChatMenuPatches
{
private static int currentChatMessageIndex = 0;
private static bool isChatRunning = false;
@ -17,19 +16,23 @@ namespace stardew_access.Patches
if (__instance.chatBox.Selected)
{
bool isPrevArrowPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.PageUp);
bool isNextArrowPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.PageDown);
bool isPrevButtonPressed = MainClass.Config.ChatMenuNextKey.JustPressed();
bool isNextButtonPressed = MainClass.Config.ChatMenuPreviousKey.JustPressed();
if (___messages.Count > 0)
{
#region To narrate previous and next chat messages
if (isNextArrowPressed && !isChatRunning)
if (isNextButtonPressed && !isChatRunning)
{
isChatRunning = true;
CycleThroughChatMessages(true, ___messages);
Task.Delay(200).ContinueWith(_ => { isChatRunning = false; });
}
else if (isPrevArrowPressed && !isChatRunning)
else if (isPrevButtonPressed && !isChatRunning)
{
isChatRunning = true;
CycleThroughChatMessages(false, ___messages);
Task.Delay(200).ContinueWith(_ => { isChatRunning = false; });
}
#endregion
}
@ -42,44 +45,28 @@ namespace stardew_access.Patches
toSpeak += $"{message.message}, ";
});
if (toSpeak != " ")
MainClass.GetScreenReader().SayWithChatChecker(toSpeak, false);
MainClass.ScreenReader.SayWithChatChecker(toSpeak, false);
#endregion
}
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error);
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
private static async void CycleThroughChatMessages(bool increase, List<ChatMessage> ___messages)
private static void CycleThroughChatMessages(bool increase, List<ChatMessage> ___messages)
{
isChatRunning = true;
string toSpeak = " ";
if (increase)
{
++currentChatMessageIndex;
if (currentChatMessageIndex > ___messages.Count - 1)
{
currentChatMessageIndex = ___messages.Count - 1;
}
}
else
{
--currentChatMessageIndex;
if (currentChatMessageIndex < 0)
{
currentChatMessageIndex = 0;
}
}
currentChatMessageIndex = (increase) ? (Math.Min(currentChatMessageIndex + 1, ___messages.Count - 1)) : (currentChatMessageIndex = Math.Max(currentChatMessageIndex - 1, 0));
___messages[currentChatMessageIndex].message.ForEach(message =>
{
toSpeak += $"{message.message}, ";
});
MainClass.GetScreenReader().Say(toSpeak, true);
await Task.Delay(200);
isChatRunning = false;
MainClass.ScreenReader.Say(toSpeak, true);
}
}
}

View File

@ -2,12 +2,12 @@
using StardewModdingAPI;
using StardewValley;
using StardewValley.Menus;
using System.Text;
namespace stardew_access.Patches
{
internal class DialoguePatches
{
internal static string currentLetterText = " ";
internal static string currentDialogue = " ";
internal static bool isDialogueAppearingFirstTime = true;
@ -56,7 +56,7 @@ namespace stardew_access.Patches
else
toSpeak = response;
MainClass.GetScreenReader().Say(toSpeak, true);
MainClass.ScreenReader.Say(toSpeak, true);
}
}
else
@ -64,7 +64,7 @@ namespace stardew_access.Patches
if (currentDialogue != dialogueText)
{
currentDialogue = dialogueText;
MainClass.GetScreenReader().Say(dialogueText, true);
MainClass.ScreenReader.Say(dialogueText, true);
}
}
}
@ -105,8 +105,7 @@ namespace stardew_access.Patches
else
toSpeak = response;
MainClass.GetMonitor().Log(toSpeak, LogLevel.Debug);
MainClass.GetScreenReader().Say(toSpeak, true);
MainClass.ScreenReader.Say(toSpeak, true);
}
}
else
@ -114,7 +113,7 @@ namespace stardew_access.Patches
if (currentDialogue != dialogueText)
{
currentDialogue = dialogueText;
MainClass.GetScreenReader().Say(dialogueText, true);
MainClass.ScreenReader.Say(dialogueText, true);
}
}
}
@ -124,13 +123,13 @@ namespace stardew_access.Patches
if (currentDialogue != __instance.getCurrentString())
{
currentDialogue = __instance.getCurrentString();
MainClass.GetScreenReader().Say(__instance.getCurrentString(), true);
MainClass.ScreenReader.Say(__instance.getCurrentString(), true);
}
}
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"Unable to narrate dialog:\n{e.StackTrace}\n{e.Message}", LogLevel.Error);
MainClass.ErrorLog($"Unable to narrate dialog:\n{e.StackTrace}\n{e.Message}");
}
}
@ -149,81 +148,89 @@ namespace stardew_access.Patches
#region Skip narrating hover text for certain menus
if (Game1.activeClickableMenu is TitleMenu && !(((TitleMenu)Game1.activeClickableMenu).GetChildMenu() is CharacterCustomization))
return;
if (Game1.activeClickableMenu is LetterViewerMenu || Game1.activeClickableMenu is QuestLog)
else if (Game1.activeClickableMenu is LetterViewerMenu || Game1.activeClickableMenu is QuestLog)
return;
if (Game1.activeClickableMenu is Billboard)
else if (Game1.activeClickableMenu is Billboard)
return;
if (Game1.activeClickableMenu is GeodeMenu)
else if (Game1.activeClickableMenu is GeodeMenu)
return;
if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is InventoryPage)
else if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is InventoryPage)
return;
if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is CraftingPage)
else if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is CraftingPage)
return;
if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is OptionsPage)
else if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is OptionsPage)
return;
if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is ExitPage)
else if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is ExitPage)
return;
if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is SocialPage)
else if (Game1.activeClickableMenu is GameMenu && ((GameMenu)Game1.activeClickableMenu).GetCurrentPage() is SocialPage)
return;
if (Game1.activeClickableMenu is ItemGrabMenu)
else if (Game1.activeClickableMenu is ItemGrabMenu)
return;
if (Game1.activeClickableMenu is ShopMenu)
else if (Game1.activeClickableMenu is ShopMenu)
return;
if (Game1.activeClickableMenu is ConfirmationDialog)
else if (Game1.activeClickableMenu is ConfirmationDialog)
return;
if (Game1.activeClickableMenu is JunimoNoteMenu)
else if (Game1.activeClickableMenu is JunimoNoteMenu)
return;
if (Game1.activeClickableMenu is CarpenterMenu)
else if (Game1.activeClickableMenu is CarpenterMenu)
return;
if (Game1.activeClickableMenu is PurchaseAnimalsMenu)
else if (Game1.activeClickableMenu is PurchaseAnimalsMenu)
return;
else if (Game1.activeClickableMenu is CraftingPage)
return;
else if (Game1.activeClickableMenu is AnimalQueryMenu)
return;
else if (Game1.activeClickableMenu is ConfirmationDialog)
return;
else if (Game1.activeClickableMenu is ReadyCheckDialog)
return;
else if (Game1.activeClickableMenu is JojaCDMenu)
return;
else if (Game1.activeClickableMenu is TailoringMenu)
return;
else if (Game1.activeClickableMenu is PondQueryMenu)
return;
else if (Game1.activeClickableMenu is ForgeMenu)
return;
else if (Game1.activeClickableMenu is ItemListMenu)
return;
else if (Game1.activeClickableMenu is FieldOfficeMenu)
return;
else if (Game1.activeClickableMenu is MuseumMenu)
return;
#endregion
StringBuilder toSpeak = new StringBuilder(" ");
string toSpeak = " ";
#region Add item count before title
if (hoveredItem != null && hoveredItem.HasBeenInInventory)
{
int count = hoveredItem.Stack;
if (count > 1)
toSpeak.Append($"{count} ");
toSpeak = $"{toSpeak} {count} ";
}
#endregion
#region Add title if any
if (boldTitleText != null)
toSpeak.Append($"{boldTitleText}\n");
toSpeak = $"{toSpeak} {boldTitleText}\n";
#endregion
#region Add quality of item
if (hoveredItem is StardewValley.Object && ((StardewValley.Object)hoveredItem).quality > 0)
if (hoveredItem is StardewValley.Object && ((StardewValley.Object)hoveredItem).Quality > 0)
{
int quality = ((StardewValley.Object)hoveredItem).quality;
int quality = ((StardewValley.Object)hoveredItem).Quality;
if (quality == 1)
{
toSpeak.Append("Silver quality");
toSpeak = $"{toSpeak} Silver quality";
}
else if (quality == 2 || quality == 3)
{
toSpeak.Append("Gold quality");
toSpeak = $"{toSpeak} Gold quality";
}
else if (quality >= 4)
{
toSpeak.Append("Iridium quality");
toSpeak = $"{toSpeak} Iridium quality";
}
}
#endregion
@ -234,26 +241,29 @@ namespace stardew_access.Patches
string itemName = Game1.objectInformation[extraItemToShowIndex].Split('/')[0];
if (extraItemToShowAmount != -1)
toSpeak.Append($"Required: {extraItemToShowAmount} {itemName}");
toSpeak = $"{toSpeak} Required: {extraItemToShowAmount} {itemName}";
else
toSpeak.Append($"Required: {itemName}");
toSpeak = $"{toSpeak} Required: {itemName}";
}
#endregion
#region Add money
if (moneyAmountToDisplayAtBottom != -1)
toSpeak.Append($"\nCost: {moneyAmountToDisplayAtBottom}g\n");
toSpeak = $"{toSpeak} \nCost: {moneyAmountToDisplayAtBottom}g\n";
#endregion
#region Add the base text
toSpeak.Append(text);
if (text == "???")
toSpeak = "unknown";
else
toSpeak = $"{toSpeak} {text}";
#endregion
#region Add crafting ingredients
if (craftingIngredients != null)
{
toSpeak.Append($"\n{craftingIngredients.description}");
toSpeak.Append("\nIngredients\n");
toSpeak = $"{toSpeak} \n{craftingIngredients.description}";
toSpeak = $"{toSpeak} \nIngredients\n";
craftingIngredients.recipeList.ToList().ForEach(recipe =>
{
@ -261,7 +271,7 @@ namespace stardew_access.Patches
int item = recipe.Key;
string name = craftingIngredients.getNameFromIndex(item);
toSpeak.Append($" ,{count} {name}");
toSpeak = $"{toSpeak} ,{count} {name}";
});
}
#endregion
@ -270,11 +280,11 @@ namespace stardew_access.Patches
if (hoveredItem is StardewValley.Object && ((StardewValley.Object)hoveredItem).Edibility != -300)
{
int stamina_recovery = ((StardewValley.Object)hoveredItem).staminaRecoveredOnConsumption();
toSpeak.Append($"{stamina_recovery} Energy\n");
toSpeak = $"{toSpeak} {stamina_recovery} Energy\n";
if (stamina_recovery >= 0)
{
int health_recovery = ((StardewValley.Object)hoveredItem).healthRecoveredOnConsumption();
toSpeak.Append($"{health_recovery} Health");
toSpeak = $"{toSpeak} {health_recovery} Health";
}
}
#endregion
@ -293,7 +303,7 @@ namespace stardew_access.Patches
{
int count = int.Parse(buffName.Substring(0, buffName.IndexOf(' ')));
if (count != 0)
toSpeak.Append($"{buffName}\n");
toSpeak = $"{toSpeak} {buffName}\n";
}
catch (Exception) { }
}
@ -307,15 +317,110 @@ namespace stardew_access.Patches
if (toSpeak.ToString() != " ")
{
if (Context.IsPlayerFree)
MainClass.GetScreenReader().SayWithChecker(toSpeak.ToString(), true); // Normal Checker
MainClass.ScreenReader.SayWithChecker(toSpeak.ToString(), true); // Normal Checker
else
MainClass.GetScreenReader().SayWithMenuChecker(toSpeak.ToString(), true); // Menu Checker
MainClass.ScreenReader.SayWithMenuChecker(toSpeak.ToString(), true); // Menu Checker
}
#endregion
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"Unable to narrate dialog:\n{e.StackTrace}\n{e.Message}", LogLevel.Error);
MainClass.ErrorLog($"Unable to narrate dialog:\n{e.StackTrace}\n{e.Message}");
}
}
internal static void LetterViewerMenuPatch(LetterViewerMenu __instance)
{
try
{
if (!__instance.IsActive())
return;
NarrateLetterContent(__instance);
}
catch (Exception e)
{
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
internal static void NarrateLetterContent(LetterViewerMenu __instance)
{
int x = Game1.getMousePosition().X, y = Game1.getMousePosition().Y;
#region Texts in the letter
string message = __instance.mailMessage[__instance.page];
string toSpeak = $"{message}";
if (__instance.ShouldShowInteractable())
{
if (__instance.moneyIncluded > 0)
{
string moneyText = Game1.content.LoadString("Strings\\UI:LetterViewer_MoneyIncluded", __instance.moneyIncluded);
toSpeak += $"\t\n\t ,Included money: {moneyText}";
}
else if (__instance.learnedRecipe != null && __instance.learnedRecipe.Length > 0)
{
string recipeText = Game1.content.LoadString("Strings\\UI:LetterViewer_LearnedRecipe", __instance.cookingOrCrafting);
toSpeak += $"\t\n\t ,Learned Recipe: {recipeText}";
}
}
if (currentLetterText != toSpeak)
{
currentLetterText = toSpeak;
// snap mouse to accept quest button
if (__instance.acceptQuestButton != null && __instance.questID != -1)
{
toSpeak += "\t\n Left click to accept quest.";
__instance.acceptQuestButton.snapMouseCursorToCenter();
}
if (__instance.mailMessage.Count > 1)
toSpeak = $"Page {__instance.page + 1} of {__instance.mailMessage.Count}:\n\t{toSpeak}";
MainClass.ScreenReader.Say(toSpeak, true);
}
#endregion
#region Narrate items given in the mail
if (__instance.ShouldShowInteractable())
{
foreach (ClickableComponent c in __instance.itemsToGrab)
{
if (c.item == null)
continue;
string name = c.item.DisplayName;
if (c.containsPoint(x, y))
MainClass.ScreenReader.SayWithChecker($"Left click to collect {name}", false);
}
}
#endregion
#region Narrate buttons
if (__instance.backButton != null && __instance.backButton.visible && __instance.backButton.containsPoint(x, y))
MainClass.ScreenReader.SayWithChecker($"Previous page button", false);
if (__instance.forwardButton != null && __instance.forwardButton.visible && __instance.forwardButton.containsPoint(x, y))
MainClass.ScreenReader.SayWithChecker($"Next page button", false);
#endregion
}
internal static void drawAboveAlwaysFrontLayerPatch(NPC __instance, string ___textAboveHead, int ___textAboveHeadTimer)
{
try
{
if (___textAboveHeadTimer > 2900 && ___textAboveHead != null)
{
MainClass.ScreenReader.SayWithChecker($"{__instance.displayName} says {___textAboveHead}", true);
}
}
catch (Exception e)
{
MainClass.ErrorLog($"Error in patch:NPCShowTextAboveHeadPatch \n{e.Message}\n{e.StackTrace}");
}
}
}

View File

@ -0,0 +1,417 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using StardewValley;
using StardewValley.Locations;
using StardewValley.Menus;
namespace stardew_access.Patches
{
internal class DonationMenuPatches
{
internal static string museumQueryKey = " ";
internal static string fieldOfficeMenuQuery = " ";
private static bool isMoving = false;
private static (int x, int y)[] donationTiles =
{
(26,5),(26,6),(26,7),(26,8),(26,9),(26,10),(26,11),
(29,5),(30,5),(31,5),(32,5),(33,5),(34,5),(35,5),(36,5),
(28,6),(29,6),(30,6),(31,6),(32,6),(33,6),(34,6),(35,6),(36,6),(37,6),
(28,9),(29,9),(30,9),(31,9),(32,9),(33,9),(34,9),(35,9),(36,9),
(28,10),(29,10),(30,10),(31,10),(32,10),(33,10),(34,10),(35,10),(36,10),
(30,13),(31,13),(32,13),(33,13),(34,13),
(30,14),(31,14),(32,14),(33,14),(34,14),
(28,15),(29,15),(30,15),(31,15),(32,15),(33,15),(34,15),(35,15),(36,15),
(28,16),(29,16),(30,16),(31,16),(32,16),(33,16),(34,16),(35,16),(36,16),
(39,6),(40,6),(41,6),(42,6),(43,6),(44,6),(45,6),(46,6),
(39,7),(40,7),(41,7),(42,7),(43,7),(44,7),(45,7),(46,7),
(48,5),(48,6),(48,7),
(42,15),(43,15),(44,15),(45,15),(46,15),(47,15),
(42,16),(43,16),(44,16),(45,16),(46,16),(47,16),
};
#region Museum
internal static bool MuseumMenuKeyPressPatch()
{
try
{
if (isMoving)
return false;
if (!isMoving)
{
isMoving = true;
Task.Delay(200).ContinueWith(_ => { isMoving = false; });
}
}
catch (Exception e)
{
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
return true;
}
internal static void MuseumMenuPatch(MuseumMenu __instance, bool ___holdingMuseumPiece)
{
try
{
int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position
if (__instance.heldItem != null)
{
// Museum Inventory
string toSpeak = " ";
int tileX = (int)(Utility.ModifyCoordinateFromUIScale(x) + (float)Game1.viewport.X) / 64;
int tileY = (int)(Utility.ModifyCoordinateFromUIScale(y) + (float)Game1.viewport.Y) / 64;
LibraryMuseum libraryMuseum = (LibraryMuseum)Game1.currentLocation;
if (libraryMuseum.isTileSuitableForMuseumPiece(tileX, tileY))
toSpeak = $"slot {tileX}x {tileY}y";
if (museumQueryKey != toSpeak)
{
museumQueryKey = toSpeak;
MainClass.ScreenReader.Say(toSpeak, true);
}
}
else
{
// Player Inventory
int i = narrateHoveredItemInInventory(__instance.inventory, __instance.inventory.inventory, __instance.inventory.actualInventory, x, y);
if (i != -9999)
{
bool isPrimaryInfoKeyPressed = MainClass.Config.PrimaryInfoKey.JustPressed(); // For donating hovered item
if (isPrimaryInfoKeyPressed && __instance.inventory.actualInventory[i] != null)
{
foreach (var tile in donationTiles)
{
#region Manually donates the hovered item (https://github.com/veywrn/StardewValley/blob/3ff171b6e9e6839555d7881a391b624ccd820a83/StardewValley/Menus/MuseumMenu.cs#L206-L247)
int tileX = tile.x;
int tileY = tile.y;
if (((LibraryMuseum)Game1.currentLocation).isTileSuitableForMuseumPiece(tileX, tileY) && ((LibraryMuseum)Game1.currentLocation).isItemSuitableForDonation(__instance.inventory.actualInventory[i]))
{
int objectID = __instance.inventory.actualInventory[i].ParentSheetIndex;
int rewardsCount = ((LibraryMuseum)Game1.currentLocation).getRewardsForPlayer(Game1.player).Count;
((LibraryMuseum)Game1.currentLocation).museumPieces.Add(new Vector2(tileX, tileY), ((StardewValley.Object)__instance.inventory.actualInventory[i]).ParentSheetIndex);
Game1.playSound("stoneStep");
if (((LibraryMuseum)Game1.currentLocation).getRewardsForPlayer(Game1.player).Count > rewardsCount)
{
Game1.playSound("reward");
}
else
{
Game1.playSound("newArtifact");
}
Game1.player.completeQuest(24);
__instance.inventory.actualInventory[i].Stack--;
if (__instance.inventory.actualInventory[i].Stack <= 0)
{
__instance.inventory.actualInventory[i] = null;
}
int pieces = ((LibraryMuseum)Game1.currentLocation).museumPieces.Count();
Game1.stats.checkForArchaeologyAchievements();
switch (pieces)
{
case 95:
globalChatInfoMessage("MuseumComplete", Game1.player.farmName.Value);
break;
case 40:
globalChatInfoMessage("Museum40", Game1.player.farmName.Value);
break;
default:
globalChatInfoMessage("donation", Game1.player.Name, "object:" + objectID);
break;
}
break;
}
#endregion
}
}
}
else if (__instance.okButton != null && __instance.okButton.containsPoint(x, y))
{
if (museumQueryKey != $"ok button")
{
museumQueryKey = $"ok button";
MainClass.ScreenReader.Say("ok button", true);
}
}
}
}
catch (Exception e)
{
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
// Returns the index of the hovered item or -9999
internal static int narrateHoveredItemInInventory(InventoryMenu inventoryMenu, List<ClickableComponent> inventory, IList<Item> actualInventory, int x, int y)
{
#region Narrate hovered item
for (int i = 0; i < inventory.Count; i++)
{
if (inventory[i].containsPoint(x, y))
{
string toSpeak = "";
if ((i + 1) <= actualInventory.Count)
{
if (actualInventory[i] != null)
{
string name = actualInventory[i].DisplayName;
int stack = actualInventory[i].Stack;
string quality = "";
#region Add quality of item
if (actualInventory[i] is StardewValley.Object && ((StardewValley.Object)actualInventory[i]).Quality > 0)
{
int qualityIndex = ((StardewValley.Object)actualInventory[i]).Quality;
if (qualityIndex == 1)
{
quality = "Silver quality";
}
else if (qualityIndex == 2 || qualityIndex == 3)
{
quality = "Gold quality";
}
else if (qualityIndex >= 4)
{
quality = "Iridium quality";
}
}
#endregion
if (inventoryMenu.highlightMethod(inventoryMenu.actualInventory[i]))
name = $"Donatable {name}";
if (stack > 1)
toSpeak = $"{stack} {name} {quality}";
else
toSpeak = $"{name} {quality}";
}
else
{
// For empty slot
toSpeak = "Empty Slot";
}
}
else
{
// For empty slot
toSpeak = "Empty Slot";
}
if (museumQueryKey != $"{toSpeak}:{i}")
{
museumQueryKey = $"{toSpeak}:{i}";
MainClass.ScreenReader.Say(toSpeak, true);
}
return i;
}
}
#endregion
return -9999;
}
#region These methods are taken from the game's source code, https://github.com/veywrn/StardewValley/blob/3ff171b6e9e6839555d7881a391b624ccd820a83/StardewValley/Multiplayer.cs#L1331-L1395
internal static void globalChatInfoMessage(string messageKey, params string[] args)
{
if (Game1.IsMultiplayer || Game1.multiplayerMode != 0)
{
receiveChatInfoMessage(Game1.player, messageKey, args);
sendChatInfoMessage(messageKey, args);
}
}
internal static void sendChatInfoMessage(string messageKey, params string[] args)
{
if (Game1.IsClient)
{
Game1.client.sendMessage(15, messageKey, args);
}
else if (Game1.IsServer)
{
foreach (long id in Game1.otherFarmers.Keys)
{
Game1.server.sendMessage(id, 15, Game1.player, messageKey, args);
}
}
}
internal static void receiveChatInfoMessage(Farmer sourceFarmer, string messageKey, string[] args)
{
if (Game1.chatBox != null)
{
try
{
string[] processedArgs = args.Select(delegate (string arg)
{
if (arg.StartsWith("achievement:"))
{
int key = Convert.ToInt32(arg.Substring("achievement:".Length));
return Game1.content.Load<Dictionary<int, string>>("Data\\Achievements")[key].Split('^')[0];
}
return arg.StartsWith("object:") ? new StardewValley.Object(Convert.ToInt32(arg.Substring("object:".Length)), 1).DisplayName : arg;
}).ToArray();
ChatBox chatBox = Game1.chatBox;
LocalizedContentManager content = Game1.content;
string path = "Strings\\UI:Chat_" + messageKey;
object[] substitutions = processedArgs;
chatBox.addInfoMessage(content.LoadString(path, substitutions));
}
catch (ContentLoadException)
{
}
catch (FormatException)
{
}
catch (OverflowException)
{
}
catch (KeyNotFoundException)
{
}
}
}
#endregion
#endregion
#region Field Office
internal static void FieldOfficeMenuPatch(FieldOfficeMenu __instance)
{
try
{
int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position
string toSpeak = " ";
if (__instance.trashCan != null && __instance.trashCan.containsPoint(x, y))
{
toSpeak = "Trashcan";
}
else if (__instance.okButton != null && __instance.okButton.containsPoint(x, y))
{
toSpeak = "ok button";
}
else if (__instance.dropItemInvisibleButton != null && __instance.dropItemInvisibleButton.containsPoint(x, y))
{
toSpeak = "drop item";
}
else
{
for (int i = 0; i < __instance.inventory.inventory.Count; i++)
{
if (!__instance.inventory.inventory[i].containsPoint(x, y))
continue;
if (__instance.inventory.actualInventory[i] == null)
toSpeak = "Empty slot";
else
{
toSpeak = $"{__instance.inventory.actualInventory[i].Stack} {__instance.inventory.actualInventory[i].DisplayName}";
if (!__instance.inventory.highlightMethod(__instance.inventory.actualInventory[i]))
{
toSpeak = $"{toSpeak} not usable here";
}
}
if (fieldOfficeMenuQuery != $"{toSpeak}:{i}")
{
fieldOfficeMenuQuery = $"{toSpeak}:{i}";
MainClass.ScreenReader.Say(toSpeak, true);
}
return;
}
for (int i = 0; i < __instance.pieceHolders.Count; i++)
{
if (!__instance.pieceHolders[i].containsPoint(x, y))
continue;
if (__instance.pieceHolders[i].item == null)
toSpeak = i switch
{
0 => "Center skeleton slot",
1 => "Center skeleton slot",
2 => "Center skeleton slot",
3 => "Center skeleton slot",
4 => "Center skeleton slot",
5 => "Center skeleton slot",
6 => "Snake slot",
7 => "Snake slot",
8 => "Snake slot",
9 => "Bat slot",
10 => "Frog slot",
_ => "Donation slot"
};
else
toSpeak = $"Slot {i + 1} finished: {__instance.pieceHolders[i].item.DisplayName}";
if (__instance.heldItem != null && __instance.pieceHolders[i].item == null)
{
int highlight = getPieceIndexForDonationItem(__instance.heldItem.ParentSheetIndex);
if (highlight != -1 && highlight == i)
toSpeak += "Donatable ";
}
if (fieldOfficeMenuQuery != $"{toSpeak}:{i}")
{
fieldOfficeMenuQuery = $"{toSpeak}:{i}";
MainClass.ScreenReader.Say(toSpeak, true);
}
return;
}
}
if (fieldOfficeMenuQuery != toSpeak)
{
fieldOfficeMenuQuery = toSpeak;
MainClass.ScreenReader.Say(toSpeak, true);
if (__instance.dropItemInvisibleButton != null && __instance.dropItemInvisibleButton.containsPoint(x, y))
Game1.playSound("drop_item");
}
}
catch (System.Exception e)
{
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
internal static int getPieceIndexForDonationItem(int itemIndex)
{
switch (itemIndex)
{
case 820:
return 5;
case 821:
return 4;
case 822:
return 3;
case 823:
return 0;
case 824:
return 1;
case 825:
return 8;
case 826:
return 7;
case 827:
return 9;
case 828:
return 10;
default:
return -1;
}
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@ -2,16 +2,366 @@
using stardew_access.Features;
using StardewModdingAPI;
using StardewValley;
using StardewValley.Buildings;
using StardewValley.Menus;
namespace stardew_access.Patches
{
internal class MenuPatches
{
private static string currentLetterText = " ";
private static string currentLevelUpTitle = " ";
internal static string currentLevelUpTitle = " ";
internal static bool firstTimeInNamingMenu = true;
internal static bool isNarratingPondInfo = false;
internal static string tailoringMenuQuery = " ";
internal static string pondQueryMenuQuery = " ";
internal static string forgeMenuQuery = " ";
internal static string itemListMenuQuery = " ";
public static Vector2? prevTile = null;
internal static void ItemListMenuPatch(ItemListMenu __instance, string ___title, int ___currentTab, int ___totalValueOfItems, List<Item> ___itemsToList)
{
try
{
int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position
string toSpeak = " ", currentList = " ";
for (int i = ___currentTab * __instance.itemsPerCategoryPage; i < ___currentTab * __instance.itemsPerCategoryPage + __instance.itemsPerCategoryPage; i++)
{
if (i == 0)
currentList = ___title;
if (___itemsToList.Count > i)
{
if (___itemsToList[i] == null)
{
currentList = $"{currentList}, \n" + Game1.content.LoadString("Strings\\UI:ItemList_ItemsLostValue", ___totalValueOfItems);
continue;
}
currentList = $"{currentList}, \n {___itemsToList[i].Stack} {___itemsToList[i].DisplayName}";
}
}
if (__instance.okButton != null && __instance.okButton.containsPoint(x, y))
toSpeak = $"Page {___currentTab + 1} of {((int)___itemsToList.Count / __instance.itemsPerCategoryPage) + 1} \n {currentList} \n ok button";
else if (__instance.forwardButton != null && __instance.forwardButton.containsPoint(x, y))
toSpeak = "Next page button";
else if (__instance.backButton != null && __instance.backButton.containsPoint(x, y))
toSpeak = "Previous page button";
if (itemListMenuQuery != toSpeak)
{
itemListMenuQuery = toSpeak;
MainClass.ScreenReader.Say(toSpeak, true);
}
}
catch (System.Exception e)
{
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
internal static void ForgeMenuPatch(ForgeMenu __instance)
{
try
{
int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position
string toSpeak = " ";
if (__instance.leftIngredientSpot != null && __instance.leftIngredientSpot.containsPoint(x, y))
{
if (__instance.leftIngredientSpot.item == null)
{
toSpeak = "Input weapon or tool here";
}
else
{
Item item = __instance.leftIngredientSpot.item;
toSpeak = $"Weapon slot: {item.Stack} {item.DisplayName}";
}
}
else if (__instance.rightIngredientSpot != null && __instance.rightIngredientSpot.containsPoint(x, y))
{
if (__instance.rightIngredientSpot.item == null)
{
toSpeak = "Input gemstone here";
}
else
{
Item item = __instance.rightIngredientSpot.item;
toSpeak = $"Gemstone slot: {item.Stack} {item.DisplayName}";
}
}
else if (__instance.startTailoringButton != null && __instance.startTailoringButton.containsPoint(x, y))
{
toSpeak = "Star forging button";
}
else if (__instance.unforgeButton != null && __instance.unforgeButton.containsPoint(x, y))
{
toSpeak = "Unforge button";
}
else if (__instance.trashCan != null && __instance.trashCan.containsPoint(x, y))
{
toSpeak = "Trashcan";
}
else if (__instance.okButton != null && __instance.okButton.containsPoint(x, y))
{
toSpeak = "ok button";
}
else if (__instance.dropItemInvisibleButton != null && __instance.dropItemInvisibleButton.containsPoint(x, y))
{
toSpeak = "drop item";
}
else if (__instance.equipmentIcons.Count > 0 && __instance.equipmentIcons[0].containsPoint(x, y))
{
toSpeak = "Left ring Slot";
if (Game1.player.leftRing.Value != null)
toSpeak = $"{toSpeak}: {Game1.player.leftRing.Value.DisplayName}";
}
else if (__instance.equipmentIcons.Count > 0 && __instance.equipmentIcons[1].containsPoint(x, y))
{
toSpeak = "Right ring Slot";
if (Game1.player.rightRing.Value != null)
toSpeak = $"{toSpeak}: {Game1.player.rightRing.Value.DisplayName}";
}
else
{
for (int i = 0; i < __instance.inventory.inventory.Count; i++)
{
if (!__instance.inventory.inventory[i].containsPoint(x, y))
continue;
if (__instance.inventory.actualInventory[i] == null)
toSpeak = "Empty slot";
else
{
toSpeak = $"{__instance.inventory.actualInventory[i].Stack} {__instance.inventory.actualInventory[i].DisplayName}";
if (!__instance.inventory.highlightMethod(__instance.inventory.actualInventory[i]))
{
toSpeak = $"{toSpeak} not usable here";
}
}
if (forgeMenuQuery != $"{toSpeak}:{i}")
{
forgeMenuQuery = $"{toSpeak}:{i}";
MainClass.ScreenReader.Say(toSpeak, true);
}
return;
}
}
if (forgeMenuQuery != toSpeak)
{
forgeMenuQuery = toSpeak;
MainClass.ScreenReader.Say(toSpeak, true);
if (__instance.dropItemInvisibleButton != null && __instance.dropItemInvisibleButton.containsPoint(x, y))
Game1.playSound("drop_item");
}
}
catch (System.Exception e)
{
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
internal static void PondQueryMenuPatch(PondQueryMenu __instance, StardewValley.Object ____fishItem, FishPond ____pond, string ____statusText, bool ___confirmingEmpty)
{
try
{
int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position
bool isPrimaryInfoKeyPressed = MainClass.Config.PrimaryInfoKey.JustPressed();
string toSpeak = " ", extra = "";
if (___confirmingEmpty)
{
if (__instance.yesButton != null && __instance.yesButton.containsPoint(x, y))
toSpeak = "Confirm button";
else if (__instance.noButton != null && __instance.noButton.containsPoint(x, y))
toSpeak = "Cancel button";
}
else
{
if (isPrimaryInfoKeyPressed && !isNarratingPondInfo)
{
string pond_name_text = Game1.content.LoadString("Strings\\UI:PondQuery_Name", ____fishItem.DisplayName);
string population_text = Game1.content.LoadString("Strings\\UI:PondQuery_Population", string.Concat(____pond.FishCount), ____pond.maxOccupants.Value);
bool has_unresolved_needs = ____pond.neededItem.Value != null && ____pond.HasUnresolvedNeeds() && !____pond.hasCompletedRequest.Value;
string bring_text = "";
if (has_unresolved_needs && ____pond.neededItem.Value != null)
bring_text = Game1.content.LoadString("Strings\\UI:PondQuery_StatusRequest_Bring") + $": {____pond.neededItemCount} {____pond.neededItem.Value.DisplayName}";
extra = $"{pond_name_text} {population_text} {bring_text} Status: {____statusText}";
pondQueryMenuQuery = " ";
isNarratingPondInfo = true;
Task.Delay(200).ContinueWith(_ => { isNarratingPondInfo = false; });
}
if (__instance.okButton != null && __instance.okButton.containsPoint(x, y))
toSpeak = "Ok button";
else if (__instance.changeNettingButton != null && __instance.changeNettingButton.containsPoint(x, y))
toSpeak = "Change netting button";
else if (__instance.emptyButton != null && __instance.emptyButton.containsPoint(x, y))
toSpeak = "Empty pond button";
}
if (pondQueryMenuQuery != toSpeak)
{
pondQueryMenuQuery = toSpeak;
MainClass.ScreenReader.Say(extra + " \n\t" + toSpeak, true);
}
}
catch (System.Exception e)
{
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
internal static void TailoringMenuPatch(TailoringMenu __instance)
{
try
{
int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position
string toSpeak = " ";
if (__instance.leftIngredientSpot != null && __instance.leftIngredientSpot.containsPoint(x, y))
{
if (__instance.leftIngredientSpot.item == null)
{
toSpeak = "Input cloth here";
}
else
{
Item item = __instance.leftIngredientSpot.item;
toSpeak = $"Cloth slot: {item.Stack} {item.DisplayName}";
}
}
else if (__instance.rightIngredientSpot != null && __instance.rightIngredientSpot.containsPoint(x, y))
{
if (__instance.rightIngredientSpot.item == null)
{
toSpeak = "Input ingredient here";
}
else
{
Item item = __instance.rightIngredientSpot.item;
toSpeak = $"Ingredient slot: {item.Stack} {item.DisplayName}";
}
}
else if (__instance.startTailoringButton != null && __instance.startTailoringButton.containsPoint(x, y))
{
toSpeak = "Star tailoring button";
}
else if (__instance.trashCan != null && __instance.trashCan.containsPoint(x, y))
{
toSpeak = "Trashcan";
}
else if (__instance.okButton != null && __instance.okButton.containsPoint(x, y))
{
toSpeak = "ok button";
}
else if (__instance.dropItemInvisibleButton != null && __instance.dropItemInvisibleButton.containsPoint(x, y))
{
toSpeak = "drop item";
}
else if (__instance.equipmentIcons.Count > 0 && __instance.equipmentIcons[0].containsPoint(x, y))
{
toSpeak = "Hat Slot";
if (Game1.player.hat.Value != null)
toSpeak = $"{toSpeak}: {Game1.player.hat.Value.DisplayName}";
}
else if (__instance.equipmentIcons.Count > 0 && __instance.equipmentIcons[1].containsPoint(x, y))
{
toSpeak = "Shirt Slot";
if (Game1.player.shirtItem.Value != null)
toSpeak = $"{toSpeak}: {Game1.player.shirtItem.Value.DisplayName}";
}
else if (__instance.equipmentIcons.Count > 0 && __instance.equipmentIcons[2].containsPoint(x, y))
{
toSpeak = "Pants Slot";
if (Game1.player.pantsItem.Value != null)
toSpeak = $"{toSpeak}: {Game1.player.pantsItem.Value.DisplayName}";
}
else
{
for (int i = 0; i < __instance.inventory.inventory.Count; i++)
{
if (!__instance.inventory.inventory[i].containsPoint(x, y))
continue;
if (__instance.inventory.actualInventory[i] == null)
toSpeak = "Empty slot";
else
{
toSpeak = $"{__instance.inventory.actualInventory[i].Stack} {__instance.inventory.actualInventory[i].DisplayName}";
if (!__instance.inventory.highlightMethod(__instance.inventory.actualInventory[i]))
{
toSpeak = $"{toSpeak} not usable here";
}
}
if (tailoringMenuQuery != $"{toSpeak}:{i}")
{
tailoringMenuQuery = $"{toSpeak}:{i}";
MainClass.ScreenReader.Say(toSpeak, true);
}
return;
}
}
if (tailoringMenuQuery != toSpeak)
{
tailoringMenuQuery = toSpeak;
MainClass.ScreenReader.Say(toSpeak, true);
if (__instance.dropItemInvisibleButton != null && __instance.dropItemInvisibleButton.containsPoint(x, y))
Game1.playSound("drop_item");
}
}
catch (System.Exception e)
{
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
internal static void ChooseFromListMenuPatch(ChooseFromListMenu __instance, List<string> ___options, int ___index, bool ___isJukebox)
{
try
{
int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position
string toSpeak = "";
if (__instance.okButton != null && __instance.okButton.containsPoint(x, y))
toSpeak = "Select " + (___isJukebox ? Utility.getSongTitleFromCueName(___options[___index]) : ___options[___index]) + " button";
else if (__instance.cancelButton != null && __instance.cancelButton.containsPoint(x, y))
toSpeak = "Cancel button";
else if (__instance.backButton != null && __instance.backButton.containsPoint(x, y))
toSpeak = "Previous option: " + (___isJukebox ? Utility.getSongTitleFromCueName(___options[Math.Max(0, ___index - 1)]) : ___options[Math.Max(0, ___index - 1)]) + " button";
else if (__instance.forwardButton != null && __instance.forwardButton.containsPoint(x, y))
toSpeak = "Next option: " + (___isJukebox ? Utility.getSongTitleFromCueName(___options[Math.Min(___options.Count, ___index + 1)]) : ___options[Math.Min(___options.Count, ___index + 1)]) + " button";
MainClass.ScreenReader.SayWithMenuChecker(toSpeak, true);
}
catch (System.Exception e)
{
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
internal static bool PlaySoundPatch(string cueName)
{
try
@ -24,8 +374,8 @@ namespace stardew_access.Patches
if (cueName == "grassyStep" || cueName == "sandyStep" || cueName == "snowyStep" || cueName == "stoneStep" || cueName == "thudStep" || cueName == "woodyStep")
{
Vector2 nextTile = CurrentPlayer.getNextTile();
if (ReadTile.isCollidingAtTile((int)nextTile.X, (int)nextTile.Y))
Vector2 nextTile = CurrentPlayer.FacingTile;
if (TileInfo.isCollidingAtTile((int)nextTile.X, (int)nextTile.Y))
{
if (prevTile != nextTile)
{
@ -38,7 +388,7 @@ namespace stardew_access.Patches
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error);
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
return true;
@ -48,17 +398,17 @@ namespace stardew_access.Patches
{
try
{
int x = Game1.getMouseX(), y = Game1.getMouseY(); // Mouse x and y position
int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position
if (__instance.nextPageButton != null && __instance.nextPageButton.containsPoint(x, y))
{
MainClass.GetScreenReader().SayWithMenuChecker($"Next Page Button", true);
MainClass.ScreenReader.SayWithMenuChecker($"Next Page Button", true);
return;
}
if (__instance.previousPageButton != null && __instance.previousPageButton.containsPoint(x, y))
{
MainClass.GetScreenReader().SayWithMenuChecker($"Previous Page Button", true);
MainClass.ScreenReader.SayWithMenuChecker($"Previous Page Button", true);
return;
}
@ -66,14 +416,14 @@ namespace stardew_access.Patches
{
if (__instance.languages[i].containsPoint(x, y))
{
MainClass.GetScreenReader().SayWithMenuChecker($"{__instance.languageList[i]} Button", true);
MainClass.ScreenReader.SayWithMenuChecker($"{__instance.languageList[i]} Button", true);
break;
}
}
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error);
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
@ -81,35 +431,81 @@ namespace stardew_access.Patches
{
try
{
int x = Game1.getMouseX(), y = Game1.getMouseY(); // Mouse x and y position
int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position
for (int i = 0; i < ___elevators.Count; i++)
{
if (___elevators[i].containsPoint(x, y))
{
MainClass.GetScreenReader().SayWithMenuChecker($"{___elevators[i].name} level", true);
MainClass.ScreenReader.SayWithMenuChecker($"{___elevators[i].name} level", true);
break;
}
}
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error);
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
internal static void NamingMenuPatch(NamingMenu __instance, string title, TextBox ___textBox)
internal static void TitleTextInputMenuPatch(TitleTextInputMenu __instance)
{
try
{
__instance.textBoxCC.snapMouseCursor();
___textBox.SelectMe();
string toSpeak = $"{title}";
string toSpeak = "";
int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position
MainClass.GetScreenReader().SayWithChecker(toSpeak, true);
if (__instance.pasteButton != null && __instance.pasteButton.containsPoint(x, y))
toSpeak = $"Paste button";
if (toSpeak != "")
MainClass.ScreenReader.SayWithChecker(toSpeak, true);
}
catch (System.Exception e)
{
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
internal static void NamingMenuPatch(NamingMenu __instance, TextBox ___textBox, string ___title)
{
try
{
string toSpeak = "";
int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position
bool isEscPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape); // For escaping/unselecting from the animal name text box
if (firstTimeInNamingMenu)
{
firstTimeInNamingMenu = false;
___textBox.Selected = false;
}
if (___textBox.Selected)
{
___textBox.Update();
toSpeak = ___textBox.Text;
if (isEscPressed)
{
___textBox.Selected = false;
}
}
else
{
if (__instance.textBoxCC != null && __instance.textBoxCC.containsPoint(x, y))
toSpeak = $"{___title} text box";
else if (__instance.doneNamingButton != null && __instance.doneNamingButton.containsPoint(x, y))
toSpeak = $"Done naming button";
else if (__instance.randomButton != null && __instance.randomButton.containsPoint(x, y))
toSpeak = $"Random button";
}
if (toSpeak != "")
MainClass.ScreenReader.SayWithChecker(toSpeak, true);
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error);
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
@ -117,21 +513,23 @@ namespace stardew_access.Patches
{
try
{
int x = Game1.getMouseX(), y = Game1.getMouseY();
int x = Game1.getMouseX(true), y = Game1.getMouseY(true);
string toSpeak = ___message;
MainClass.GetScreenReader().SayWithMenuChecker(___message, true);
if (__instance.okButton.containsPoint(x, y))
{
MainClass.GetScreenReader().SayWithMenuChecker("Ok Button", false);
toSpeak += "\n\tOk Button";
}
else if (__instance.cancelButton.containsPoint(x, y))
{
MainClass.GetScreenReader().SayWithMenuChecker("Cancel Button", false);
toSpeak += "\n\tCancel Button";
}
MainClass.ScreenReader.SayWithMenuChecker(toSpeak, true);
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error);
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
@ -139,13 +537,9 @@ namespace stardew_access.Patches
{
try
{
int x = Game1.getMouseX(), y = Game1.getMouseY();
int x = Game1.getMouseX(true), y = Game1.getMouseY(true);
string leftProfession = " ", rightProfession = " ", extraInfo = " ", newCraftingRecipe = " ", toSpeak = " ";
bool isOpenBracketPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.OemOpenBrackets); // for left click
bool isLeftCtrlPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftControl);
bool isEnterPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Enter);
if (!__instance.informationUp)
{
return;
@ -167,7 +561,7 @@ namespace stardew_access.Patches
if (__instance.leftProfession.containsPoint(x, y))
{
if (isOpenBracketPressed || (isLeftCtrlPressed && isEnterPressed && __instance.readyToClose()))
if ((MainClass.Config.LeftClickMainKey.JustPressed() || MainClass.Config.LeftClickAlternateKey.JustPressed()) && __instance.readyToClose())
{
Game1.player.professions.Add(___professionsToChoose[0]);
__instance.getImmediateProfessionPerk(___professionsToChoose[0]);
@ -184,7 +578,7 @@ namespace stardew_access.Patches
if (__instance.rightProfession.containsPoint(x, y))
{
if (isOpenBracketPressed || (isLeftCtrlPressed && isEnterPressed && __instance.readyToClose()))
if ((MainClass.Config.LeftClickMainKey.JustPressed() || MainClass.Config.LeftClickAlternateKey.JustPressed()) && __instance.readyToClose())
{
Game1.player.professions.Add(___professionsToChoose[1]);
__instance.getImmediateProfessionPerk(___professionsToChoose[1]);
@ -216,23 +610,23 @@ namespace stardew_access.Patches
if (__instance.okButton.containsPoint(x, y))
{
if (isOpenBracketPressed || (isLeftCtrlPressed && isEnterPressed))
if (MainClass.Config.LeftClickMainKey.JustPressed() || MainClass.Config.LeftClickAlternateKey.JustPressed())
__instance.okButtonClicked();
toSpeak = $"{___title} {extraInfo} {newCraftingRecipe}. Left click to close.";
}
if (toSpeak != " ")
MainClass.GetScreenReader().SayWithMenuChecker(toSpeak, true);
MainClass.ScreenReader.SayWithMenuChecker(toSpeak, true);
else if (__instance.isProfessionChooser && currentLevelUpTitle != $"{___title}. Select a new profession.")
{
MainClass.GetScreenReader().SayWithMenuChecker($"{___title}. Select a new profession.", true);
MainClass.ScreenReader.SayWithMenuChecker($"{___title}. Select a new profession.", true);
currentLevelUpTitle = $"{___title}. Select a new profession.";
}
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error);
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
@ -240,106 +634,77 @@ namespace stardew_access.Patches
{
try
{
bool isLeftControlPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftControl);
bool isOpenBracketPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.OemOpenBrackets); // for left click
bool isEnterPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Enter);
if (__instance.currentPage == -1)
{
int total = ___categoryTotals[5];
string toSpeak;
if (__instance.okButton.containsPoint(Game1.getMouseX(), Game1.getMouseY()))
if (__instance.okButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true)))
{
// Perform Left Click
if (isOpenBracketPressed || (isLeftControlPressed && isEnterPressed))
if (MainClass.Config.LeftClickMainKey.JustPressed() || MainClass.Config.LeftClickAlternateKey.JustPressed())
{
Game1.activeClickableMenu.receiveLeftClick(Game1.getMouseX(true), Game1.getMouseY(true));
}
toSpeak = $"{total}g in total. Press left mouse button to save.";
MainClass.GetScreenReader().SayWithChecker(toSpeak, true);
MainClass.ScreenReader.SayWithChecker(toSpeak, true);
}
for (int i = 0; i < __instance.categories.Count; i++)
{
if (__instance.categories[i].containsPoint(Game1.getMouseX(), Game1.getMouseY()))
if (__instance.categories[i].containsPoint(Game1.getMouseX(true), Game1.getMouseY(true)))
{
toSpeak = $"Money recieved from {__instance.getCategoryName(i)}: {___categoryTotals[i]}g.";
MainClass.GetScreenReader().SayWithChecker(toSpeak, true);
MainClass.ScreenReader.SayWithChecker(toSpeak, true);
}
}
}
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error);
}
}
internal static void LetterViewerMenuPatch(LetterViewerMenu __instance)
{
try
{
if (!__instance.IsActive())
return;
#region Texts in the letter
string message = __instance.mailMessage[__instance.page];
string toSpeak = $"{message}";
if (__instance.ShouldShowInteractable())
{
if (__instance.moneyIncluded > 0)
{
string moneyText = Game1.content.LoadString("Strings\\UI:LetterViewer_MoneyIncluded", __instance.moneyIncluded);
toSpeak += $"\t\n\t ,Included money: {moneyText}";
}
else if (__instance.learnedRecipe != null && __instance.learnedRecipe.Length > 0)
{
string recipeText = Game1.content.LoadString("Strings\\UI:LetterViewer_LearnedRecipe", __instance.cookingOrCrafting);
toSpeak += $"\t\n\t ,Learned Recipe: {recipeText}";
}
}
if (currentLetterText != toSpeak)
{
currentLetterText = toSpeak;
// snap mouse to accept quest button
if (__instance.acceptQuestButton != null && __instance.acceptQuestButton.visible)
{
toSpeak += "\t\n Left click to accept quest.";
__instance.acceptQuestButton.snapMouseCursorToCenter();
}
MainClass.GetScreenReader().Say(toSpeak, false);
}
#endregion
#region Narrate items given in the mail
if (__instance.ShouldShowInteractable())
{
foreach (ClickableComponent c in __instance.itemsToGrab)
{
string name = c.name;
string label = c.label;
if (c.containsPoint(Game1.getMousePosition().X, Game1.getMousePosition().Y))
MainClass.GetScreenReader().SayWithChecker($"Grab: {name} \t\n {label}", false);
}
}
#endregion
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error);
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
#region Cleanup on exitting a menu
internal static void Game1ExitActiveMenuPatch()
{
try
{
if (Game1.activeClickableMenu is GameMenu)
Cleanup(Game1.activeClickableMenu);
}
catch (Exception e)
{
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
internal static void IClickableMenuOnExitPatch(IClickableMenu __instance)
{
try
{
Cleanup(__instance);
}
catch (Exception e)
{
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
private static void Cleanup(IClickableMenu menu)
{
if (menu is LetterViewerMenu)
{
DialoguePatches.currentLetterText = " ";
}
else if (menu is LevelUpMenu)
{
currentLevelUpTitle = " ";
}
else if (menu is Billboard)
{
QuestPatches.currentDailyQuestText = " ";
}
else if (menu is GameMenu)
{
GameMenuPatches.gameMenuQueryKey = "";
GameMenuPatches.craftingPageQueryKey = "";
@ -350,53 +715,26 @@ namespace stardew_access.Patches
GameMenuPatches.currentSelectedCraftingRecipe = -1;
GameMenuPatches.isSelectingRecipe = false;
}
if (Game1.activeClickableMenu is JunimoNoteMenu)
else if (menu is JunimoNoteMenu)
{
GameMenuPatches.currentIngredientListItem = -1;
GameMenuPatches.currentIngredientInputSlot = -1;
GameMenuPatches.currentInventorySlot = -1;
GameMenuPatches.junimoNoteMenuQuery = "";
BundleMenuPatches.currentIngredientListItem = -1;
BundleMenuPatches.currentIngredientInputSlot = -1;
BundleMenuPatches.currentInventorySlot = -1;
BundleMenuPatches.junimoNoteMenuQuery = "";
}
if (Game1.activeClickableMenu is ShopMenu)
else if (menu is ShopMenu)
{
GameMenuPatches.shopMenuQueryKey = "";
}
if (Game1.activeClickableMenu is ItemGrabMenu)
else if (menu is ItemGrabMenu)
{
GameMenuPatches.itemGrabMenuQueryKey = "";
}
GameMenuPatches.hoveredItemQueryKey = "";
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error);
}
}
internal static void IClickableMenuOnExitPatch(IClickableMenu __instance)
{
try
{
if (__instance is GeodeMenu)
else if (menu is GeodeMenu)
{
GameMenuPatches.geodeMenuQueryKey = "";
}
if (__instance is ItemGrabMenu)
{
GameMenuPatches.itemGrabMenuQueryKey = "";
}
if (__instance is ShopMenu)
{
GameMenuPatches.shopMenuQueryKey = "";
}
if (__instance is CarpenterMenu)
else if (menu is CarpenterMenu)
{
BuildingNAnimalMenuPatches.carpenterMenuQuery = "";
BuildingNAnimalMenuPatches.isUpgrading = false;
@ -406,37 +744,58 @@ namespace stardew_access.Patches
BuildingNAnimalMenuPatches.isConstructing = false;
BuildingNAnimalMenuPatches.carpenterMenu = null;
}
if (__instance is PurchaseAnimalsMenu)
else if (menu is PurchaseAnimalsMenu)
{
BuildingNAnimalMenuPatches.purchaseAnimalMenuQuery = "";
BuildingNAnimalMenuPatches.firstTimeInNamingMenu = true;
BuildingNAnimalMenuPatches.purchaseAnimalsMenu = null;
}
if (__instance is DialogueBox)
else if (menu is DialogueBox)
{
DialoguePatches.isDialogueAppearingFirstTime = true;
DialoguePatches.currentDialogue = " ";
}
else if (menu is JojaCDMenu)
{
BundleMenuPatches.jojaCDMenuQuery = "";
}
else if (menu is QuestLog)
{
QuestPatches.questLogQuery = " ";
}
else if (menu is TailoringMenu)
{
tailoringMenuQuery = " ";
}
else if (menu is ForgeMenu)
{
forgeMenuQuery = " ";
}
else if (menu is ItemListMenu)
{
itemListMenuQuery = " ";
}
else if (menu is FieldOfficeMenu)
{
DonationMenuPatches.fieldOfficeMenuQuery = " ";
}
else if (menu is MuseumMenu)
{
DonationMenuPatches.museumQueryKey = " ";
}
else if (menu is PondQueryMenu)
{
pondQueryMenuQuery = " ";
}
GameMenuPatches.hoveredItemQueryKey = "";
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error);
}
}
#endregion
internal static void ExitEventPatch()
{
if (MainClass.GetScreenReader() != null)
MainClass.GetScreenReader().CloseScreenReader();
}
internal static void resetGlobalVars()
{
currentLetterText = " ";
currentLevelUpTitle = " ";
if (MainClass.ScreenReader != null)
MainClass.ScreenReader.CloseScreenReader();
}
}
}

View File

@ -0,0 +1,120 @@
using Microsoft.Xna.Framework;
using StardewValley;
using StardewValley.Minigames;
namespace stardew_access.Patches
{
public class MiniGamesPatches
{
public static string grandpaStoryQuery = " ";
public static string introQuery = " ";
internal static void IntroPatch(Intro __instance, int ___currentState)
{
try
{
if (MainClass.ModHelper == null)
return;
string toSpeak = " ";
if (___currentState == 3)
{
toSpeak = MainClass.ModHelper.Translation.Get("intro.scene3");
}
else if (___currentState == 4)
{
toSpeak = MainClass.ModHelper.Translation.Get("intro.scene4");
}
if (toSpeak != " " && introQuery != toSpeak)
{
introQuery = toSpeak;
MainClass.ScreenReader.Say(toSpeak, false);
return;
}
}
catch (System.Exception e)
{
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
internal static void GrandpaStoryPatch(GrandpaStory __instance, StardewValley.Menus.LetterViewerMenu ___letterView, bool ___drawGrandpa, bool ___letterReceived, bool ___mouseActive, Queue<string> ___grandpaSpeech, int ___grandpaSpeechTimer, int ___totalMilliseconds, int ___scene, int ___parallaxPan)
{
try
{
int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position
string toSpeak = " ";
if (___letterView != null)
{
DialoguePatches.NarrateLetterContent(___letterView);
}
if (MainClass.ModHelper == null)
return;
if (___scene == 0)
{
toSpeak = MainClass.ModHelper.Translation.Get("grandpastory.scene0");
}
else if (___drawGrandpa)
{
if (___grandpaSpeech.Count > 0 && ___grandpaSpeechTimer > 3000)
{
toSpeak = ___grandpaSpeech.Peek();
}
}
else if (___scene == 3)
{
toSpeak = Game1.content.LoadString("Strings\\StringsFromCSFiles:GrandpaStory.cs.12059");
}
else if (___scene == 4)
{
toSpeak = MainClass.ModHelper.Translation.Get("grandpastory.scene4");
}
else if (___scene == 5)
{
toSpeak = MainClass.ModHelper.Translation.Get("grandpastory.scene5");
}
else if (___scene == 6)
{
if (___grandpaSpeechTimer > 3000)
{
if (clickableGrandpaLetterRect(___parallaxPan, ___grandpaSpeechTimer).Contains(x, y))
{
toSpeak = MainClass.ModHelper.Translation.Get("grandpastory.letteropen");
}
else if (___letterView == null)
{
Point pos = clickableGrandpaLetterRect(___parallaxPan, ___grandpaSpeechTimer).Center;
Game1.setMousePositionRaw((int)((float)pos.X * Game1.options.zoomLevel), (int)((float)pos.Y * Game1.options.zoomLevel));
return;
}
}
else
{
toSpeak = MainClass.ModHelper.Translation.Get("grandpastory.scene6");
}
}
if (toSpeak != " " && grandpaStoryQuery != toSpeak)
{
grandpaStoryQuery = toSpeak;
MainClass.ScreenReader.Say(toSpeak, false);
}
}
catch (System.Exception e)
{
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
// This method is taken from the game's source code
private static Rectangle clickableGrandpaLetterRect(int ___parallaxPan, int ___grandpaSpeechTimer)
{
return new Rectangle((int)Utility.getTopLeftPositionForCenteringOnScreen(Game1.viewport, 1294, 730).X + (286 - ___parallaxPan) * 4, (int)Utility.getTopLeftPositionForCenteringOnScreen(Game1.viewport, 1294, 730).Y + 218 + Math.Max(0, Math.Min(60, (___grandpaSpeechTimer - 5000) / 8)), 524, 344);
}
}
}

View File

@ -1,5 +1,4 @@
using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI;
using StardewValley;
using StardewValley.Menus;
using StardewValley.Quests;
@ -8,14 +7,16 @@ namespace stardew_access.Patches
{
internal class QuestPatches
{
private static string currentDailyQuestText = " ";
internal static string currentDailyQuestText = " ";
internal static string questLogQuery = " ";
internal static bool isNarratingQuestInfo = false, firstTimeInIndividualQuest = true;
#region For Special Orders Board
internal static void SpecialOrdersBoardPatch(SpecialOrdersBoard __instance)
{
try
{
int x = Game1.getMouseX(), y = Game1.getMouseY(); // Mouse x and y position
int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position
if (__instance.acceptLeftQuestButton.visible && __instance.acceptLeftQuestButton.containsPoint(x, y))
{
@ -23,7 +24,7 @@ namespace stardew_access.Patches
toSpeak = $"Left Quest:\n\t{toSpeak}\n\tPress left click to accept this quest.";
MainClass.GetScreenReader().SayWithMenuChecker(toSpeak, true);
MainClass.ScreenReader.SayWithMenuChecker(toSpeak, true);
return;
}
@ -33,13 +34,13 @@ namespace stardew_access.Patches
toSpeak = $"Right Quest:\n\t{toSpeak}\n\tPress left click to accept this quest.";
MainClass.GetScreenReader().SayWithMenuChecker(toSpeak, true);
MainClass.ScreenReader.SayWithMenuChecker(toSpeak, true);
return;
}
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error);
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
@ -83,7 +84,7 @@ namespace stardew_access.Patches
#region Callender
for (int i = 0; i < __instance.calendarDays.Count; i++)
{
if (__instance.calendarDays[i].containsPoint(Game1.getMouseX(), Game1.getMouseY()))
if (__instance.calendarDays[i].containsPoint(Game1.getMouseX(true), Game1.getMouseY(true)))
{
string toSpeak = $"Day {i + 1}";
@ -99,7 +100,7 @@ namespace stardew_access.Patches
if (Game1.dayOfMonth == i + 1)
toSpeak += $", Current";
MainClass.GetScreenReader().SayWithChecker(toSpeak, true);
MainClass.ScreenReader.SayWithChecker(toSpeak, true);
}
}
#endregion
@ -114,7 +115,7 @@ namespace stardew_access.Patches
if (currentDailyQuestText != toSpeak)
{
currentDailyQuestText = toSpeak;
MainClass.GetScreenReader().Say(toSpeak, true);
MainClass.ScreenReader.Say(toSpeak, true);
}
}
else
@ -134,7 +135,7 @@ namespace stardew_access.Patches
__instance.acceptQuestButton.snapMouseCursorToCenter();
}
MainClass.GetScreenReader().Say(toSpeak, true);
MainClass.ScreenReader.Say(toSpeak, true);
}
}
#endregion
@ -142,7 +143,7 @@ namespace stardew_access.Patches
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error);
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
#endregion
@ -152,70 +153,88 @@ namespace stardew_access.Patches
{
try
{
bool snapMouseToRewardBox = false;
bool isPrimaryInfoKeyPressed = MainClass.Config.PrimaryInfoKey.JustPressed();
int x = Game1.getMouseX(true), y = Game1.getMouseY(true); // Mouse x and y position
string toSpeak = " ", extra = "";
if (___questPage == -1)
{
#region Quest Lists
if (!firstTimeInIndividualQuest)
firstTimeInIndividualQuest = true;
for (int i = 0; i < __instance.questLogButtons.Count; i++)
{
if (___pages.Count() > 0 && ___pages[___currentPage].Count() > i)
{
if (!__instance.questLogButtons[i].containsPoint(x, y))
continue;
string name = ___pages[___currentPage][i].GetName();
int daysLeft = ___pages[___currentPage][i].GetDaysLeft();
string toSpeak = $"{name} quest";
toSpeak = $"{name}";
if (daysLeft > 0 && ___pages[___currentPage][i].ShouldDisplayAsComplete())
toSpeak += $"\t\n {daysLeft} days left";
toSpeak += ___pages[___currentPage][i].ShouldDisplayAsComplete() ? " completed!" : "";
if (__instance.questLogButtons[i].containsPoint(Game1.getOldMouseX(), Game1.getOldMouseY()))
break;
}
}
if (__instance.backButton != null && __instance.backButton.visible && __instance.backButton.containsPoint(x, y))
toSpeak = "Previous page button";
else if (__instance.forwardButton != null && __instance.forwardButton.visible && __instance.forwardButton.containsPoint(x, y))
toSpeak = "Next page button";
else if (__instance.upperRightCloseButton != null && __instance.upperRightCloseButton.visible && __instance.upperRightCloseButton.containsPoint(x, y))
toSpeak = "Close menu button";
if (questLogQuery != toSpeak)
{
MainClass.GetScreenReader().SayWithChecker(toSpeak, true);
}
}
questLogQuery = toSpeak;
MainClass.ScreenReader.Say(toSpeak, true);
}
#endregion
}
else
{
#region Individual quest
bool containsReward = __instance.HasReward() || __instance.HasMoneyReward();
string description = Game1.parseText(____shownQuest.GetDescription(), Game1.dialogueFont, __instance.width - 128);
string title = ____shownQuest.GetName();
string toSpeak = " ";
if (firstTimeInIndividualQuest || (isPrimaryInfoKeyPressed && !isNarratingQuestInfo))
{
if (firstTimeInIndividualQuest)
toSpeak = "Back button";
if (____shownQuest.ShouldDisplayAsComplete())
{
#region Quest completed menu
toSpeak = $"Quest: {title} Completed!";
extra = $"Quest: {title} Completed!";
if (__instance.HasReward())
{
snapMouseToRewardBox = true;
if (__instance.HasMoneyReward())
{
toSpeak += $"you recieved {____shownQuest.GetMoneyReward()}g";
}
toSpeak += "... left click to collect reward";
}
extra += $"you recieved {____shownQuest.GetMoneyReward()}g";
#endregion
}
else
{
#region Quest in-complete menu
toSpeak = $"Title: {title}. \t\n Description: {description}";
extra = $"Title: {title}. \t\n Description: {description}";
for (int j = 0; j < ____objectiveText.Count; j++)
{
if (____shownQuest != null)
{
_ = ____shownQuest is SpecialOrder;
}
string parsed_text = Game1.parseText(____objectiveText[j], width: __instance.width - 192, whichFont: Game1.dialogueFont);
if (____shownQuest != null && ____shownQuest is SpecialOrder)
{
OrderObjective order_objective = ((SpecialOrder)____shownQuest).objectives[j];
if (order_objective.GetMaxCount() > 1 && order_objective.ShouldShowProgress())
parsed_text += "\n\t" + order_objective.GetCount() + " of " + order_objective.GetMaxCount() + " completed";
}
toSpeak += $"\t\nOrder {j + 1}: {parsed_text} \t\n";
extra += $"\t\nOrder {j + 1}: {parsed_text} \t\n";
}
if (____shownQuest != null)
@ -223,30 +242,46 @@ namespace stardew_access.Patches
int daysLeft = ____shownQuest.GetDaysLeft();
if (daysLeft > 0)
toSpeak += $"\t\n{daysLeft} days left.";
extra += $"\t\n{daysLeft} days left.";
}
#endregion
}
// Move mouse to reward button
if (snapMouseToRewardBox)
__instance.rewardBox.snapMouseCursorToCenter();
isNarratingQuestInfo = true;
Task.Delay(200).ContinueWith(_ => { isNarratingQuestInfo = false; });
questLogQuery = " ";
}
if (!firstTimeInIndividualQuest)
if (__instance.backButton != null && __instance.backButton.visible && __instance.backButton.containsPoint(x, y))
toSpeak = (___currentPage > 0) ? "Previous page button" : "Back button";
else if (__instance.forwardButton != null && __instance.forwardButton.visible && __instance.forwardButton.containsPoint(x, y))
toSpeak = "Next page button";
else if (__instance.cancelQuestButton != null && __instance.cancelQuestButton.visible && __instance.cancelQuestButton.containsPoint(x, y))
toSpeak = "Cancel quest button";
else if (__instance.upperRightCloseButton != null && __instance.upperRightCloseButton.visible && __instance.upperRightCloseButton.containsPoint(x, y))
toSpeak = "Close menu button";
else if (containsReward && __instance.rewardBox.containsPoint(x, y))
toSpeak = "Left click to collect reward";
if (firstTimeInIndividualQuest || (questLogQuery != toSpeak))
{
questLogQuery = toSpeak;
MainClass.ScreenReader.Say(extra + " \n\t" + toSpeak, true);
if (firstTimeInIndividualQuest)
firstTimeInIndividualQuest = false;
}
MainClass.GetScreenReader().SayWithChecker(toSpeak, true);
#endregion
}
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error);
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
#endregion
internal static void resetGlobalVars()
{
currentDailyQuestText = " ";
}
}
}

View File

@ -1,7 +1,7 @@
using Microsoft.Xna.Framework;
using StardewModdingAPI;
using StardewValley;
using StardewValley;
using StardewValley.Characters;
using StardewValley.Menus;
using static StardewValley.Menus.CharacterCustomization;
using static StardewValley.Menus.LoadGameMenu;
namespace stardew_access.Patches
@ -10,13 +10,83 @@ namespace stardew_access.Patches
{
private static int saveGameIndex = -1;
private static bool isRunning = false;
private const int MAX_COMPONENTS = 20;
public static string characterCreationMenuQueryKey = " ";
public static string advancedGameOptionsQueryKey = " ";
public static string prevPetName = " ";
internal static void AdvancedGameOptionsPatch(AdvancedGameOptions __instance)
{
try
{
int currentItemIndex = Math.Max(0, Math.Min(__instance.options.Count - 7, __instance.currentItemIndex));
int x = Game1.getMouseX(true), y = Game1.getMouseY(true);
if (__instance.okButton != null && __instance.okButton.containsPoint(x, y))
{
string toSpeak = "OK Button";
if (advancedGameOptionsQueryKey != toSpeak)
{
advancedGameOptionsQueryKey = toSpeak;
MainClass.ScreenReader.Say(toSpeak, true);
}
return;
}
for (int i = 0; i < __instance.optionSlots.Count; i++)
{
if (__instance.optionSlots[i].bounds.Contains(x, y) && currentItemIndex + i < __instance.options.Count && __instance.options[currentItemIndex + i].bounds.Contains(x - __instance.optionSlots[i].bounds.X, y - __instance.optionSlots[i].bounds.Y))
{
OptionsElement optionsElement = __instance.options[currentItemIndex + i];
string toSpeak = optionsElement.label;
if (optionsElement is OptionsButton)
toSpeak = $" {toSpeak} Button";
else if (optionsElement is OptionsCheckbox)
toSpeak = (((OptionsCheckbox)optionsElement).isChecked ? "Enabled" : "Disabled") + $" {toSpeak} Checkbox";
else if (optionsElement is OptionsDropDown)
toSpeak = $"{toSpeak} Dropdown, option {((OptionsDropDown)optionsElement).dropDownDisplayOptions[((OptionsDropDown)optionsElement).selectedOption]} selected";
else if (optionsElement is OptionsSlider)
toSpeak = $"{((OptionsSlider)optionsElement).value}% {toSpeak} Slider";
else if (optionsElement is OptionsPlusMinus)
toSpeak = $"{((OptionsPlusMinus)optionsElement).displayOptions[((OptionsPlusMinus)optionsElement).selected]} selected of {toSpeak}";
else if (optionsElement is OptionsInputListener)
{
string buttons = "";
((OptionsInputListener)optionsElement).buttonNames.ForEach(name => { buttons += $", {name}"; });
toSpeak = $"{toSpeak} is bound to {buttons}. Left click to change.";
}
else if (optionsElement is OptionsTextEntry)
{
toSpeak = $"Seed text box";
}
else
{
if (toSpeak.Contains(":"))
toSpeak = toSpeak.Replace(":", "");
toSpeak = $"{toSpeak} Options:";
}
if (advancedGameOptionsQueryKey != toSpeak)
{
advancedGameOptionsQueryKey = toSpeak;
MainClass.ScreenReader.Say(toSpeak, true);
}
return;
}
}
}
catch (Exception e)
{
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
internal static void CoopMenuPatch(CoopMenu __instance, CoopMenu.Tab ___currentTab)
{
try
{
int x = Game1.getMouseX(), y = Game1.getMouseY();
int x = Game1.getMouseX(true), y = Game1.getMouseY(true);
string toSpeak = " ";
#region Join/Host Button (Important! This should be checked before checking other buttons)
@ -45,11 +115,11 @@ namespace stardew_access.Patches
#endregion
if (toSpeak != " ")
MainClass.GetScreenReader().SayWithChecker(toSpeak, true);
MainClass.ScreenReader.SayWithChecker(toSpeak, true);
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error);
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
@ -64,7 +134,7 @@ namespace stardew_access.Patches
__instance.buttons.ForEach(component =>
{
if (component.containsPoint(Game1.getMouseX(), Game1.getMouseY()))
if (component.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true)))
{
string name = component.name;
string label = component.label;
@ -72,38 +142,48 @@ namespace stardew_access.Patches
}
});
if (__instance.muteMusicButton.containsPoint(Game1.getMouseX(), Game1.getMouseY()))
if (__instance.muteMusicButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true)))
{
toSpeak = "Mute Music Button";
}
if (__instance.aboutButton.containsPoint(Game1.getMouseX(), Game1.getMouseY()))
if (__instance.aboutButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true)))
{
toSpeak = "About Button";
}
if (__instance.languageButton.containsPoint(Game1.getMouseX(), Game1.getMouseY()))
if (__instance.languageButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true)))
{
toSpeak = "Language Button";
}
if (__instance.windowedButton.containsPoint(Game1.getMouseX(), Game1.getMouseY()))
if (__instance.windowedButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true)))
{
toSpeak = "Fullscreen toggle Button";
toSpeak = "Fullscreen: " + ((Game1.isFullscreen) ? "enabled" : "disabled");
}
if (TitleMenu.subMenu != null && __instance.backButton.containsPoint(Game1.getMouseX(), Game1.getMouseY()))
if (TitleMenu.subMenu != null && __instance.backButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true)))
{
string text = "Back Button";
MainClass.GetScreenReader().SayWithChecker(text, true);
MainClass.ScreenReader.SayWithChecker(text, true);
}
// Fix for back button not working using keyboard
if (TitleMenu.subMenu is CharacterCustomization && ((CharacterCustomization)TitleMenu.subMenu).backButton.containsPoint(Game1.getMouseX(true), Game1.getMouseY(true)))
{
// Perform Left Click
if (MainClass.Config.LeftClickMainKey.JustPressed())
{
__instance.backButtonPressed();
}
}
if (TitleMenu.subMenu == null && toSpeak != "")
MainClass.GetScreenReader().SayWithChecker(toSpeak, true);
MainClass.ScreenReader.SayWithChecker(toSpeak, true);
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error);
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
@ -111,7 +191,7 @@ namespace stardew_access.Patches
{
try
{
int x = Game1.getMouseX(), y = Game1.getMouseY();
int x = Game1.getMouseX(true), y = Game1.getMouseY(true);
if (___menu.slotButtons[i].containsPoint(x, y))
{
if (__instance.Farmer != null)
@ -119,7 +199,7 @@ namespace stardew_access.Patches
#region Farms
if (___menu.deleteButtons.Count > 0 && ___menu.deleteButtons[i].containsPoint(x, y))
{
MainClass.GetScreenReader().SayWithChecker($"Delete {__instance.Farmer.farmName} Farm", true);
MainClass.ScreenReader.SayWithChecker($"Delete {__instance.Farmer.farmName.Value} Farm", true);
return;
}
@ -128,523 +208,230 @@ namespace stardew_access.Patches
// Used diff. functions to narrate to prevent it from speaking the message again on selecting another button.
string message = "Really delete farm?";
MainClass.GetScreenReader().SayWithChecker(message, true);
MainClass.ScreenReader.SayWithChecker(message, true);
if (___menu.okDeleteButton.containsPoint(x, y))
{
MainClass.GetScreenReader().SayWithMenuChecker("Ok Button", false);
MainClass.ScreenReader.SayWithMenuChecker("Ok Button", false);
}
else if (___menu.cancelDeleteButton.containsPoint(x, y))
{
MainClass.GetScreenReader().SayWithMenuChecker("Cancel Button", false);
MainClass.ScreenReader.SayWithMenuChecker("Cancel Button", false);
}
return;
}
String farmerName = __instance.Farmer.displayName;
String farmName = __instance.Farmer.farmName;
String farmName = __instance.Farmer.farmName.Value;
String money = __instance.Farmer.Money.ToString();
String hoursPlayed = Utility.getHoursMinutesStringFromMilliseconds(__instance.Farmer.millisecondsPlayed);
string dateStringForSaveGame = ((!__instance.Farmer.dayOfMonthForSaveGame.HasValue ||
!__instance.Farmer.seasonForSaveGame.HasValue ||
!__instance.Farmer.yearForSaveGame.HasValue) ? __instance.Farmer.dateStringForSaveGame : Utility.getDateStringFor(__instance.Farmer.dayOfMonthForSaveGame.Value, __instance.Farmer.seasonForSaveGame.Value, __instance.Farmer.yearForSaveGame.Value));
string toSpeak = $"{farmName} Farm Selected, \t\n Farmer:{farmerName}, \t\nMoney:{money}, \t\nHours Played:{hoursPlayed}, \t\nDate:{dateStringForSaveGame}";
string toSpeak = $"{farmName} Farm Selected, \t\n Farmer: {farmerName}, \t\nMoney: {money}, \t\nHours Played: {hoursPlayed}, \t\nDate: {dateStringForSaveGame}";
MainClass.GetScreenReader().SayWithChecker(toSpeak, true);
MainClass.ScreenReader.SayWithChecker(toSpeak, true);
#endregion
}
}
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error);
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
internal static void NewGameMenuPatch(CharacterCustomization __instance, bool ___skipIntro)
internal static void CharacterCustomizationMenuPatch(CharacterCustomization __instance, bool ___skipIntro,
ClickableComponent ___startingCabinsLabel, ClickableComponent ___difficultyModifierLabel, TextBox ___nameBox,
TextBox ___farmnameBox, TextBox ___favThingBox)
{
try
{
bool isNextArrowPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Right);
bool isPrevArrowPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Left);
bool isEscPressed = Game1.input.GetKeyboardState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape); // For escaping/unselecting from the animal name text box
string toSpeak = " ";
string currentPetName = getCurrentPetName();
if (isNextArrowPressed && !isRunning)
if (___nameBox.Selected)
{
_ = CycleThroughItems(true, __instance, ___skipIntro);
toSpeak = ___nameBox.Text;
if (isEscPressed)
{
___nameBox.Selected = false;
}
else if (isPrevArrowPressed && !isRunning)
}
else if (___farmnameBox.Selected)
{
_ = CycleThroughItems(false, __instance, ___skipIntro);
toSpeak = ___farmnameBox.Text;
if (isEscPressed)
{
___farmnameBox.Selected = false;
}
}
else if (___favThingBox.Selected)
{
toSpeak = ___favThingBox.Text;
if (isEscPressed)
{
___favThingBox.Selected = false;
}
}
else if (MainClass.Config.CharacterCreationMenuNextKey.JustPressed() && !isRunning)
{
isRunning = true;
CycleThroughItems(true, __instance, ___skipIntro, ___startingCabinsLabel, ___difficultyModifierLabel);
Task.Delay(200).ContinueWith(_ => { isRunning = false; });
}
else if (MainClass.Config.CharacterCreationMenuPreviousKey.JustPressed() && !isRunning)
{
isRunning = true;
CycleThroughItems(false, __instance, ___skipIntro, ___startingCabinsLabel, ___difficultyModifierLabel);
Task.Delay(200).ContinueWith(_ => { isRunning = false; });
}
if (prevPetName != currentPetName)
{
prevPetName = currentPetName;
toSpeak = $"Current Pet: {currentPetName} \n {toSpeak}";
}
if (characterCreationMenuQueryKey != toSpeak && toSpeak != " ")
{
characterCreationMenuQueryKey = toSpeak;
MainClass.ScreenReader.Say(toSpeak, true);
}
}
catch (Exception e)
{
MainClass.GetMonitor().Log($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}", LogLevel.Error);
MainClass.ErrorLog($"Unable to narrate Text:\n{e.Message}\n{e.StackTrace}");
}
}
private static async Task CycleThroughItems(bool increase, CharacterCustomization __instance, bool ___skipIntro)
private static void CycleThroughItems(bool increase, CharacterCustomization __instance, bool ___skipIntro,
ClickableComponent ___startingCabinsLabel, ClickableComponent ___difficultyModifierLabel)
{
isRunning = true;
string toSpeak = " ";
Dictionary<ClickableComponent, string> buttons = new();
#region Add buttons with their names IF they are available
#region Character related
if (__instance.nameBoxCC != null && __instance.nameBoxCC.visible)
buttons.Add(__instance.nameBoxCC, "Farmer's Name Text box");
if (__instance.farmnameBoxCC != null && __instance.farmnameBoxCC.visible)
buttons.Add(__instance.farmnameBoxCC, "Farm's Name Text box");
if (__instance.favThingBoxCC != null && __instance.favThingBoxCC.visible)
buttons.Add(__instance.favThingBoxCC, "Favourite Thing Text box");
if (__instance.petPortraitBox.HasValue) // Cannot get petButtons like with others
{
ClickableComponent petPrev = __instance.getComponentWithID(511);
buttons.Add(petPrev, "Previous pet button");
ClickableComponent petNext = __instance.getComponentWithID(510);
buttons.Add(petNext, "Next pet button");
}
if (__instance.randomButton != null && __instance.randomButton.visible)
buttons.Add(__instance.randomButton, "Random Skin Button");
if (__instance.genderButtons.Count > 0)
{
buttons.Add(__instance.genderButtons[0], ((Game1.player.IsMale) ? "Selected " : "") + "Gender: Male Button");
buttons.Add(__instance.genderButtons[1], ((!Game1.player.IsMale) ? "Selected " : "") + "Gender: Female Button");
}
#endregion
#region Farm layout related
if (__instance.farmTypeButtons.Count > 0)
{
for (int i = 0; i < __instance.farmTypeButtons.Count; i++)
{
buttons.Add(__instance.farmTypeButtons[i], ((i == Game1.whichFarm) ? "Selected " : "") + getFarmHoverText(__instance.farmTypeButtons[i]));
}
}
if (__instance.farmTypeNextPageButton != null && __instance.farmTypeNextPageButton.visible)
buttons.Add(__instance.farmTypeNextPageButton, "Next Farm Type Page Button");
if (__instance.farmTypePreviousPageButton != null && __instance.farmTypePreviousPageButton.visible)
buttons.Add(__instance.farmTypePreviousPageButton, "Previous Farm Type Page Button");
#endregion
#region Co-op related
if (__instance.source == Source.HostNewFarm)
{
ClickableComponent cabinLeft = __instance.getComponentWithID(621);
if (Game1.startingCabins > 0)
buttons.Add(cabinLeft, "Decrease starting cabins button");
buttons.Add(___startingCabinsLabel, $"Starting cabins: {Game1.startingCabins}");
ClickableComponent cabinRight = __instance.getComponentWithID(622);
if (Game1.startingCabins < 3)
buttons.Add(cabinRight, "Increase starting cabins button");
if (Game1.startingCabins > 0)
{
buttons.Add(__instance.cabinLayoutButtons[0], "Cabin layout to nearby Button");
buttons.Add(__instance.cabinLayoutButtons[1], "Cabin layout to separate Button");
}
ClickableComponent difficultyLeft = __instance.getComponentWithID(627);
buttons.Add(difficultyLeft, "Increase profit margin button");
buttons.Add(___difficultyModifierLabel, "Profit Margin: " + (((Game1.player.difficultyModifier * 100) == 100f) ? "normal" : Game1.player.difficultyModifier.ToString()));
ClickableComponent difficultyRight = __instance.getComponentWithID(628);
buttons.Add(difficultyRight, "Decrease profit margin button");
ClickableComponent walletLeft = __instance.getComponentWithID(631);
buttons.Add(walletLeft, "Money style to " + ((!Game1.player.team.useSeparateWallets.Value) ? "separate wallets" : "shared wallets") + " button");
}
#endregion
if (__instance.skipIntroButton != null && __instance.skipIntroButton.visible)
buttons.Add(__instance.skipIntroButton, (___skipIntro ? "Enabled" : "Disabled") + " Skip Intro Button");
if (__instance.advancedOptionsButton != null && __instance.advancedOptionsButton.visible)
buttons.Add(__instance.advancedOptionsButton, "Advanced Options Button");
if (__instance.okButton != null && __instance.okButton.visible)
buttons.Add(__instance.okButton, "OK Button");
if (__instance.backButton != null && __instance.backButton.visible)
buttons.Add(__instance.backButton, "Back Button");
#endregion
int size = buttons.Count - 1;
if (increase)
{
saveGameIndex++;
if (saveGameIndex > MAX_COMPONENTS)
saveGameIndex = 1;
if (saveGameIndex > size)
saveGameIndex = 0;
}
else
{
saveGameIndex--;
if (saveGameIndex < 1)
saveGameIndex = MAX_COMPONENTS;
if (saveGameIndex < 0)
saveGameIndex = size;
}
switch (saveGameIndex)
{
case 1:
{
#region Skip if button is not available
if (!__instance.nameBoxCC.visible)
{
if (increase)
{
++saveGameIndex;
goto case 2;
}
else
{
--saveGameIndex;
goto case MAX_COMPONENTS;
}
}
#endregion
__instance.nameBoxCC.snapMouseCursorToCenter();
toSpeak = "Enter Farmer's Name";
}
break;
case 2:
{
#region Skip if button is not available
if (!__instance.farmnameBoxCC.visible)
{
if (increase)
{
++saveGameIndex;
goto case 3;
}
else
{
--saveGameIndex;
goto case 1;
}
}
#endregion
__instance.farmnameBoxCC.snapMouseCursorToCenter();
toSpeak = "Enter Farm's Name";
}
break;
case 3:
{
#region Skip if button is not available
if (!__instance.favThingBoxCC.visible)
{
if (increase)
{
++saveGameIndex;
goto case 4;
}
else
{
--saveGameIndex;
goto case 2;
}
}
#endregion
__instance.favThingBoxCC.snapMouseCursorToCenter();
toSpeak = "Enter Favourite Thing";
}
break;
case 4:
{
#region Skip if button is not available
if (!__instance.skipIntroButton.visible)
{
if (increase)
{
++saveGameIndex;
goto case 5;
}
else
{
--saveGameIndex;
goto case 3;
}
}
#endregion
__instance.skipIntroButton.snapMouseCursor();
toSpeak = (___skipIntro ? "Enabled" : "Disabled") + " Skip Intro Button";
}
break;
case 5:
{
#region Skip if button is not available
if (!__instance.randomButton.visible)
{
if (increase)
{
++saveGameIndex;
goto case 6;
}
else
{
--saveGameIndex;
goto case 5;
}
}
#endregion
__instance.randomButton.snapMouseCursor();
toSpeak = "Random Skin Button";
break;
}
case 6:
{
#region Skip if button is not available
if (__instance.genderButtons.Count <= 0)
{
if (increase)
{
++saveGameIndex;
goto case 8;
}
else
{
--saveGameIndex;
goto case 6;
}
}
#endregion
__instance.genderButtons[0].snapMouseCursor();
toSpeak = "Gender Male Button";
break;
}
case 7:
{
#region Skip if button is not available
if (__instance.genderButtons.Count <= 0)
{
if (increase)
{
++saveGameIndex;
goto case 8;
}
else
{
--saveGameIndex;
goto case 6;
}
}
#endregion
__instance.genderButtons[1].snapMouseCursor();
toSpeak = "Gender Female Button";
break;
}
case 8:
{
#region Skip if button is not available
if (__instance.farmTypeButtons.Count <= 0)
{
if (increase)
{
++saveGameIndex;
goto case 9;
}
else
{
--saveGameIndex;
goto case 7;
}
}
#endregion
__instance.farmTypeButtons[0].snapMouseCursor();
toSpeak = getFarmHoverText(__instance.farmTypeButtons[0]);
break;
}
case 9:
{
#region Skip if button is not available
if (__instance.farmTypeButtons.Count <= 0)
{
if (increase)
{
++saveGameIndex;
goto case 10;
}
else
{
--saveGameIndex;
goto case 8;
}
}
#endregion
__instance.farmTypeButtons[1].snapMouseCursor();
toSpeak = getFarmHoverText(__instance.farmTypeButtons[1]);
break;
}
case 10:
{
#region Skip if button is not available
if (__instance.farmTypeButtons.Count <= 0)
{
if (increase)
{
++saveGameIndex;
goto case 11;
}
else
{
--saveGameIndex;
goto case 9;
}
}
#endregion
__instance.farmTypeButtons[2].snapMouseCursor();
toSpeak = getFarmHoverText(__instance.farmTypeButtons[2]);
break;
}
case 11:
{
#region Skip if button is not available
if (__instance.farmTypeButtons.Count <= 0)
{
if (increase)
{
++saveGameIndex;
goto case 12;
}
else
{
--saveGameIndex;
goto case 10;
}
}
#endregion
__instance.farmTypeButtons[3].snapMouseCursor();
toSpeak = getFarmHoverText(__instance.farmTypeButtons[3]);
break;
}
case 12:
{
#region Skip if button is not available
if (__instance.farmTypeButtons.Count <= 0)
{
if (increase)
{
++saveGameIndex;
goto case 13;
}
else
{
--saveGameIndex;
goto case 11;
}
}
#endregion
__instance.farmTypeButtons[4].snapMouseCursor();
toSpeak = getFarmHoverText(__instance.farmTypeButtons[4]);
break;
}
case 13:
{
#region Skip if button is not available
if (__instance.farmTypeButtons.Count <= 0)
{
if (increase)
{
++saveGameIndex;
goto case 14;
}
else
{
--saveGameIndex;
goto case 12;
}
}
#endregion
__instance.farmTypeButtons[5].snapMouseCursor();
toSpeak = getFarmHoverText(__instance.farmTypeButtons[5]);
break;
}
case 14:
{
#region Skip if button is not available
if (__instance.farmTypeButtons.Count <= 0)
{
if (increase)
{
++saveGameIndex;
goto case 15;
}
else
{
--saveGameIndex;
goto case 13;
}
}
#endregion
__instance.farmTypeButtons[6].snapMouseCursor();
toSpeak = getFarmHoverText(__instance.farmTypeButtons[6]);
break;
}
case 15:
{
#region Skip if button is not available
if (__instance.farmTypeNextPageButton == null)
{
if (increase)
{
++saveGameIndex;
goto case 16;
}
else
{
--saveGameIndex;
goto case 14;
}
}
#endregion
__instance.farmTypeNextPageButton.snapMouseCursor();
toSpeak = "Next Farm Type Page Button";
break;
}
case 16:
{
#region Skip if button is not available
if (__instance.farmTypePreviousPageButton == null)
{
if (increase)
{
++saveGameIndex;
goto case 17;
}
else
{
--saveGameIndex;
goto case 15;
}
}
#endregion
__instance.farmTypePreviousPageButton.snapMouseCursor();
toSpeak = "Previous Farm Type Page Button";
break;
}
case 17:
{
#region Skip if button is not available
if (__instance.cabinLayoutButtons.Count <= 0)
{
if (increase)
{
++saveGameIndex;
goto case 18;
}
else
{
--saveGameIndex;
goto case 16;
}
}
#endregion
__instance.cabinLayoutButtons[0].snapMouseCursor();
toSpeak = "Cabin layout nearby";
break;
}
case 18:
{
#region Skip if button is not available
if (__instance.cabinLayoutButtons.Count <= 0)
{
if (increase)
{
++saveGameIndex;
goto case 19;
}
else
{
--saveGameIndex;
goto case 17;
}
}
#endregion
__instance.cabinLayoutButtons[1].snapMouseCursor();
toSpeak = "Cabin layout separate";
break;
}
case 19:
{
#region Skip if button is not available
if (!__instance.okButton.visible)
{
if (increase)
{
++saveGameIndex;
goto case 18;
}
else
{
--saveGameIndex;
goto case 20;
}
}
#endregion
__instance.okButton.snapMouseCursor();
toSpeak = "Ok Button";
}
break;
case 20:
{
#region Exit if button is not available
if (!__instance.backButton.visible)
{
break;
}
#endregion
__instance.backButton.snapMouseCursor();
toSpeak = "Back Button";
}
break;
}
buttons.ElementAt(saveGameIndex).Key.snapMouseCursor();
toSpeak = buttons.ElementAt(saveGameIndex).Value;
if (toSpeak != " ")
{
MainClass.GetScreenReader().Say(toSpeak, true);
MainClass.ScreenReader.Say(toSpeak, true);
}
}
await Task.Delay(200);
isRunning = false;
private static string getCurrentPetName()
{
return ((Game1.player.catPerson) ? "Cat" : "Dog") + " Breed: " + Game1.player.whichPetBreed;
}
private static string getFarmHoverText(ClickableTextureComponent farm)

View File

@ -6,16 +6,9 @@ namespace stardew_access.ScreenReader
{
public IScreenReader Initialize()
{
IScreenReader ScreenReader = new ScreenReaderWindows(); // Default is windows
IScreenReader ScreenReader = new ScreenReaderMac(); // Mac by default
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
ScreenReaderWindows screenReaderWindows = new ScreenReaderWindows();
screenReaderWindows.InitializeScreenReader();
ScreenReader = screenReaderWindows;
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
ScreenReaderLinux screenReaderLinux = new ScreenReaderLinux();
screenReaderLinux.InitializeScreenReader();

View File

@ -1,3 +1,8 @@
/*
Linux speech dispatcher library used:
https://github.com/shoaib11120/libspeechdwrapper
*/
using System.Runtime.InteropServices;
namespace stardew_access.ScreenReader
@ -36,10 +41,16 @@ namespace stardew_access.ScreenReader
public void InitializeScreenReader()
{
MainClass.InfoLog("Initializing speech dispatcher...");
int res = Initialize();
if (res == 1)
{
initialized = true;
MainClass.InfoLog("Successfully initialized.");
}
else
{
MainClass.ErrorLog("Unable to initialize.");
}
}
@ -57,10 +68,24 @@ namespace stardew_access.ScreenReader
if (text == null)
return;
if (initialized)
{
if (!initialized)
return;
if (!MainClass.Config.TTS)
return;
if (text.Contains('^')) text = text.Replace('^', '\n');
GoString str = new GoString(text, text.Length);
Speak(str, interrupt);
int re = Speak(str, interrupt);
if (re == 1)
{
MainClass.DebugLog($"Speaking(interrupt: {interrupt}) = {text}");
}
else
{
MainClass.ErrorLog($"Failed to output text: {text}");
}
}

View File

@ -11,40 +11,42 @@ namespace stardew_access.ScreenReader
public string PrevTextTile
{
get;
set;
get { return prevTextTile; }
set { prevTextTile = value; }
}
public string prevText = "", prevTextTile = " ", prevChatText = "", prevMenuText = "";
public void InitializeScreenReader()
{
MainClass.GetMonitor().Log("Screen reader initialized");
MainClass.InfoLog("Screen reader initialized");
_speakProcess = new Process();
_speakProcess.StartInfo.FileName = "mac";
_speakProcess.StartInfo.Verb = "mac";
_speakProcess.StartInfo.RedirectStandardInput = true;
_speakProcess.Start();
// set rate (probably should be done through a config parameter)
_speakProcess.StandardInput.WriteLine("r600");
Speak("Mac screen reader ready", true);
}
public void CloseScreenReader()
{
MainClass.GetMonitor().Log("Screen reader closed");
MainClass.InfoLog("Screen reader closed");
_speakProcess.Kill();
}
public void Say(string text, bool interrupt)
{
if (text == null) return;
MainClass.GetMonitor().Log($"{text}");
MainClass.InfoLog($"{text}");
Speak(text, interrupt);
}
public void SayWithChecker(string text, bool interrupt)
{
if (text == null) return;
MainClass.GetMonitor().Log($"{text}");
MainClass.InfoLog($"{text}");
if (text != prevText)
{
Speak(text, interrupt);
@ -55,7 +57,7 @@ namespace stardew_access.ScreenReader
public void SayWithMenuChecker(string text, bool interrupt)
{
if (text == null) return;
MainClass.GetMonitor().Log($"{text}");
MainClass.InfoLog($"{text}");
if (text != prevMenuText)
{
Speak(text, interrupt);
@ -66,7 +68,7 @@ namespace stardew_access.ScreenReader
public void SayWithChatChecker(string text, bool interrupt)
{
if (text == null) return;
MainClass.GetMonitor().Log($"{text}");
MainClass.InfoLog($"{text}");
if (text != prevChatText)
{
Speak(text, interrupt);
@ -77,7 +79,7 @@ namespace stardew_access.ScreenReader
public void SayWithTileQuery(string text, int x, int y, bool interrupt)
{
if (text == null) return;
MainClass.GetMonitor().Log($"{text}");
MainClass.InfoLog($"{text}");
if (text != prevTextTile)
{
Speak(text, interrupt);

View File

@ -19,26 +19,26 @@ namespace stardew_access.ScreenReader
public void InitializeScreenReader()
{
MainClass.GetMonitor().Log("Screen reader initialized");
MainClass.InfoLog("Screen reader initialized");
Speak("Mac screen reader ready", true);
}
public void CloseScreenReader()
{
MainClass.GetMonitor().Log("Screen reader closed");
MainClass.InfoLog("Screen reader closed");
}
public void Say(string text, bool interrupt)
{
if (text == null) return;
MainClass.GetMonitor().Log($"{text}");
MainClass.InfoLog($"{text}");
Speak(text, interrupt);
}
public void SayWithChecker(string text, bool interrupt)
{
if (text == null) return;
MainClass.GetMonitor().Log($"{text}");
MainClass.InfoLog($"{text}");
if (text != prevText) {
Speak(text, interrupt);
prevText = text;
@ -48,7 +48,7 @@ namespace stardew_access.ScreenReader
public void SayWithMenuChecker(string text, bool interrupt)
{
if (text == null) return;
MainClass.GetMonitor().Log($"{text}");
MainClass.InfoLog($"{text}");
if (text != prevMenuText) {
Speak(text, interrupt);
prevMenuText = text;
@ -58,7 +58,7 @@ namespace stardew_access.ScreenReader
public void SayWithChatChecker(string text, bool interrupt)
{
if (text == null) return;
MainClass.GetMonitor().Log($"{text}");
MainClass.InfoLog($"{text}");
if (text != prevChatText) {
Speak(text, interrupt);
prevChatText = text;
@ -68,7 +68,7 @@ namespace stardew_access.ScreenReader
public void SayWithTileQuery(string text, int x, int y, bool interrupt)
{
if (text == null) return;
MainClass.GetMonitor().Log($"{text}");
MainClass.InfoLog($"{text}");
if (text != prevTextTile) {
Speak(text, interrupt);
prevTextTile = text;

View File

@ -1,106 +0,0 @@
using AccessibleOutput;
namespace stardew_access.ScreenReader
{
public class ScreenReaderWindows : IScreenReader
{
public IAccessibleOutput? screenReader = null;
public string prevText = "", prevTextTile = " ", prevChatText = "", prevMenuText = "";
public string PrevTextTile
{
get { return prevTextTile; }
set { prevTextTile = value; }
}
public void InitializeScreenReader()
{
NvdaOutput? nvdaOutput = null;
JawsOutput? jawsOutput = null;
SapiOutput? sapiOutput = null;
// Initialize NVDA
try
{
nvdaOutput = new NvdaOutput();
}
catch (Exception) { }
// Initialize JAWS
try
{
jawsOutput = new JawsOutput();
}
catch (Exception) { }
// Initialize SAPI
try
{
sapiOutput = new SapiOutput();
}
catch (Exception) { }
if (nvdaOutput != null && nvdaOutput.IsAvailable())
screenReader = nvdaOutput;
else if (jawsOutput != null && jawsOutput.IsAvailable())
screenReader = jawsOutput;
else if (sapiOutput != null && sapiOutput.IsAvailable())
screenReader = sapiOutput;
}
public void CloseScreenReader()
{
}
public void Say(string text, bool interrupt)
{
if (text == null)
return;
if (screenReader == null)
return;
screenReader.Speak(text, interrupt);
}
public void SayWithChecker(string text, bool interrupt)
{
if (prevText != text)
{
prevText = text;
Say(text, interrupt);
}
}
public void SayWithMenuChecker(string text, bool interrupt)
{
if (prevMenuText != text)
{
prevMenuText = text;
Say(text, interrupt);
}
}
public void SayWithChatChecker(string text, bool interrupt)
{
if (prevChatText != text)
{
prevChatText = text;
Say(text, interrupt);
}
}
public void SayWithTileQuery(string text, int x, int y, bool interrupt)
{
string query = $"{text} x:{x} y:{y}";
if (prevTextTile != query)
{
prevTextTile = query;
Say(text, interrupt);
}
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
{
"warnings.health": "Warnung! Die Gesundheit liegt bei {{value}} Prozent!",
"warnings.stamina": "Warnung! Ausdauer beträgt ar {{value}} Prozent!",
"warnings.time": "Warnung! Zeit ist {{value}}",
"grandpastory.scene0":"Opa, auf seinem Sterbebett.",
"grandpastory.scene4":"Mitarbeiter der JoJa corp.",
"grandpastory.scene5":"Mitarbeiter in ihren Kabinen, einige von ihnen sehen erschöpft aus, Sie selbst eingeschlossen.",
"grandpastory.scene6":"Du erreichst deinen Schreibtisch und findest Opas Brief.",
"grandpastory.letteropen":"Linksklick, um Opas Brief zu öffnen",
"intro.scene3":"Fahrt zur Bushaltestelle Stardew Valley",
"intro.scene4":"Stardew Valley 0.5 Meilen entfernt",
"manuallytriggered.healthnstamina.percent":"Gesundheit ist {{health}} % und Ausdauer ist {{stamina}} %",
"manuallytriggered.healthnstamina.normal":"Gesundheit ist {{health}} und Ausdauer ist {{stamina}}",
"readtile.sprinkler.pressurenozzle":"Druck-{{value}}",
"readtile.sprinkler.enricher":"Bereichernd {{value}}"
}

View File

@ -0,0 +1,16 @@
{
"warnings.health": "Warning! Health is at {{value}} percent!",
"warnings.stamina": "Warning! Stamina is at {{value}} percent!",
"warnings.time": "Warning! Time is {{value}}",
"grandpastory.scene0":"Grandpa, on his deathbed.",
"grandpastory.scene4":"Employees working in JoJa corp.",
"grandpastory.scene5":"Employees in their cubicles, some of them look exhausted including yourself.",
"grandpastory.scene6":"You reach your desk finding grandpa's letter.",
"grandpastory.letteropen":"Left click to open grandpa's letter",
"intro.scene3":"Travelling to Stardew Valley bus stop",
"intro.scene4":"Stardew valley 0.5 miles away",
"manuallytriggered.healthnstamina.percent":"Health is {{health}} % and Stamina is {{stamina}} %",
"manuallytriggered.healthnstamina.normal":"Health is {{health}} and Stamina is {{stamina}}",
"readtile.sprinkler.pressurenozzle":"Pressurized {{value}}",
"readtile.sprinkler.enricher":"Enriching {{value}}"
}

View File

@ -0,0 +1,16 @@
{
"warnings.health": "¡Advertencia! ¡La salud está al {{value}} por ciento!",
"warnings.stamina": "¡Advertencia! ¡La resistencia es un {{value}} por ciento!",
"warnings.time": "¡Advertencia! El tiempo es {{valor}}",
"grandpastory.scene0":"Abuelo, en su lecho de muerte.",
"grandpastory.scene4":"Empleados que trabajan en JoJa corp.",
"grandpastory.scene5":"Empleados en sus cubículos, algunos de ellos parecen exhaustos, incluido usted.",
"grandpastory.scene6":"Llegas a tu escritorio y encuentras la carta del abuelo.",
"grandpastory.letteropen":"Haz clic izquierdo para abrir la carta del abuelo.",
"intro.scene3":"Viajando a la parada de autobús de Stardew Valley",
"intro.scene4":"Valle de Stardew a 0.5 millas de distancia",
"manuallytriggered.healthnstamina.percent":"La salud es {{health}} % y la resistencia es {{stamina}} %",
"manuallytriggered.healthnstamina.normal":"La salud es {{health}} y la resistencia es {{stamina}}",
"readtile.sprinkler.pressurenozzle":"presurizada {{value}}",
"readtile.sprinkler.enricher":"Enriquecedora {{value}}"
}

View File

@ -0,0 +1,16 @@
{
"warnings.health": "Avertissement! La santé est à {{value}} pour cent!",
"warnings.stamina": "Avertissement! L'endurance est à {{value}} pour cent!",
"warnings.time": "Avertissement! Le temps est de {{value}}",
"grandpastory.scene0":"Grand-père, sur son lit de mort.",
"grandpastory.scene4":"Les employés travaillant chez JoJa corp.",
"grandpastory.scene5":"Employés dans leurs cabines, certains ont l'air épuisés, y compris vous-même.",
"grandpastory.scene6":"Vous atteignez votre bureau en trouvant la lettre de grand-père.",
"grandpastory.letteropen":"Clic gauche pour ouvrir la lettre de grand-père",
"intro.scene3":"Se rendre à l'arrêt de bus Stardew Valley",
"intro.scene4":"Vallée de Stardew à 0.5 miles",
"manuallytriggered.healthnstamina.percent":"La santé est de {{health}} % et l'endurance est de {{stamina}} %",
"manuallytriggered.healthnstamina.normal":"La santé est {{health}} et l'endurance est {{stamina}}",
"readtile.sprinkler.pressurenozzle":"Sous pression {{value}}",
"readtile.sprinkler.enricher":"Enrichissant {{value}}"
}

View File

@ -0,0 +1,16 @@
{
"warnings.health": "Figyelem! Az egészségi állapot {{érték}} százalék!",
"warnings.stamina": "Figyelem! Az állóképesség {{value}} százalék!",
"warnings.time": "Figyelem! Az idő {{érték}}",
"grandpastory.scene0":"Nagypapa, a halálos ágyán.",
"grandpastory.scene4":"A JoJa corp.-nál dolgozó alkalmazottak",
"grandpastory.scene5":"Alkalmazottak a fülkéiben, néhányuk kimerültnek tűnik, beleértve Önt is.",
"grandpastory.scene6":"Az asztalodhoz érve megtalálod a nagypapa levelét.",
"grandpastory.letteropen":"Kattintson a bal gombbal a nagypapa levelének megnyitásához",
"intro.scene3":"Utazás a Stardew Valley buszmegállóhoz",
"intro.scene4":"Stardew-völgy 0.5 mérföldre van",
"manuallytriggered.healthnstamina.percent":"Az egészségi állapot {{health}} %, az állóképesség pedig {{stamina}} %",
"manuallytriggered.healthnstamina.normal":"Az egészség {{health}}, az állóképesség pedig {{stamina}}",
"readtile.sprinkler.pressurenozzle":"Nyomás alatt {{value}}",
"readtile.sprinkler.enricher":"Gazdagítás {{value}}"
}

View File

@ -0,0 +1,16 @@
{
"warnings.health": "Avvertimento! La salute è al {{value}} percento!",
"warnings.stamina": "Avvertimento! La resistenza è al {{value}} percento!",
"warnings.time": "Avvertimento! L'ora è {{value}}",
"grandpastory.scene0":"Il nonno, sul letto di morte.",
"grandpastory.scene4":"Dipendenti che lavorano in JoJa corp.",
"grandpastory.scene5":"Impiegati nei loro cubicoli, alcuni di loro sembrano esausti, compreso te.",
"grandpastory.scene6":"Raggiungi la tua scrivania e trovi la lettera del nonno.",
"grandpastory.letteropen":"Fare clic con il tasto sinistro per aprire la lettera del nonno",
"intro.scene3":"In viaggio verso la fermata dell'autobus di Stardew Valley",
"intro.scene4":"Stardew Valley 0.5 miglia di distanza",
"manuallytriggered.healthnstamina.percent":"La salute è {{health}} % e la resistenza è {{stamina}} %",
"manuallytriggered.healthnstamina.normal":"La salute è {{health}} e la resistenza è {{stamina}}",
"readtile.sprinkler.pressurenozzle":"Pressurizzato {{value}}",
"readtile.sprinkler.enricher":"Arricchimento {{value}}"
}

View File

@ -0,0 +1,16 @@
{
"warnings.health": "警告!健康状態は{{value}}パーセントです!",
"warnings.stamina": "警告!スタミナは{{value}}パーセントです!",
"warnings.time": "警告!時間は{{value}}です",
"grandpastory.scene0":"おじいちゃん、彼の死の床に。",
"grandpastory.scene4":"JoJacorpで働く従業員。",
"grandpastory.scene5":"彼らのキュービクルの従業員、彼らの何人かはあなた自身を含めて疲れ果てているように見えます。",
"grandpastory.scene6":"おじいちゃんの手紙を見つけて机に着きます。",
"grandpastory.letteropen":"左クリックしておじいちゃんの手紙を開く",
"intro.scene3":"スターデューバレーバス停への移動",
"intro.scene4":"0.5マイル離れたスターデューバレー",
"manuallytriggered.healthnstamina.percent":"体力は {{health}} %、スタミナは {{stamina}} %",
"manuallytriggered.healthnstamina.normal":"体力は{{health}}、スタミナは{{stamina}}です",
"readtile.sprinkler.pressurenozzle":"加圧 {{value}}",
"readtile.sprinkler.enricher":"豊かにする {{value}}"
}

View File

@ -0,0 +1,16 @@
{
"warnings.health": "경고! 건강은 {{value}}퍼센트입니다!",
"warnings.stamina": "경고! 체력은 {{value}}퍼센트입니다!",
"warnings.time": "경고! 시간은 {{value}}입니다",
"grandpastory.scene0":"임종을 앞둔 할아버지.",
"grandpastory.scene4":"(주)조자에서 근무하는 직원들",
"grandpastory.scene5":"칸막이에 있는 직원들, 당신을 포함하여 몇몇은 지쳐 보입니다.",
"grandpastory.scene6":"책상에 다가가 할아버지의 편지를 찾습니다.",
"grandpastory.letteropen":"할아버지의 편지를 열려면 왼쪽 클릭",
"intro.scene3":"스타듀밸리 버스정류장으로 이동",
"intro.scene4":"스타듀 밸리에서 0.8km 떨어짐",
"manuallytriggered.healthnstamina.percent":"체력은 {{health}} %이고 체력은 {{stamina}} %입니다.",
"manuallytriggered.healthnstamina.normal":"체력은 {{health}}이고 체력은 {{stamina}}입니다.",
"readtile.sprinkler.pressurenozzle":"가압 {{value}}",
"readtile.sprinkler.enricher":"풍부하게 하기 {{value}}"
}

View File

@ -0,0 +1,16 @@
{
"warnings.health": "Aviso! A saúde está em {{value}} por cento!",
"warnings.stamina": "Aviso! A resistência está em {{value}} por cento!",
"warnings.time": "Aviso! O tempo é {{value}}",
"grandpastory.scene0":"Vovô, em seu leito de morte.",
"grandpastory.scene4":"Funcionários que trabalham na JoJa corp.",
"grandpastory.scene5":"Funcionários em seus cubículos, alguns deles parecem exaustos, incluindo você.",
"grandpastory.scene6":"Você chega à sua mesa encontrando a carta do vovô.",
"grandpastory.letteropen":"Clique com o botão esquerdo para abrir a carta do vovô",
"intro.scene3":"Viajar para o ponto de ônibus Stardew Valley",
"intro.scene4":"Vale Stardew a 0.5 km de distância",
"manuallytriggered.healthnstamina.percent":"Saúde é {{health}} % e Stamina é {{stamina}} %",
"manuallytriggered.healthnstamina.normal":"Saúde é {{health}} e Stamina é {{stamina}}",
"readtile.sprinkler.pressurenozzle":"Pressurizada {{value}}",
"readtile.sprinkler.enricher":"Enriquecimento {{value}}"
}

View File

@ -0,0 +1,16 @@
{
"warnings.health": "Предупреждение! Здоровье составляет {{value}} процентов!",
"warnings.stamina": "Предупреждение! Выносливость составляет {{value}} процентов!",
"warnings.time": "Предупреждение! Время {{value}}",
"grandpastory.scene0":"Дедушка на смертном одре.",
"grandpastory.scene4":"Сотрудники, работающие в JoJa corp.",
"grandpastory.scene5":"Сотрудники в своих кабинетах, некоторые из них выглядят измученными, в том числе и вы.",
"grandpastory.scene6":"Вы подходите к своему столу и находите дедушкино письмо.",
"grandpastory.letteropen":"Щелкните левой кнопкой мыши, чтобы открыть письмо дедушки",
"intro.scene3":"Поездка на автобусную остановку Stardew Valley",
"intro.scene4":"Долина Стардью: 0.8 км",
"manuallytriggered.healthnstamina.percent":"Здоровье составляет {{health}}%, а выносливость - {{stamina}}%",
"manuallytriggered.healthnstamina.normal":"Здоровье – {{health}}, а выносливость – {{stamina}}.",
"readtile.sprinkler.pressurenozzle":"под давлением {{value}}",
"readtile.sprinkler.enricher":"Обогащение {{value}}"
}

View File

@ -0,0 +1,16 @@
{
"warnings.health": "Uyarı! Sağlık yüzde {{değer}}!",
"warnings.stamina": "uyarı! Dayanıklılık yüzde {{değer}}!",
"warnings.time": "Uyarı! Zaman {{değer}}",
"grandpastory.scene0":"Büyükbaba, ölüm döşeğinde.",
"grandpastory.scene4":"JoJa şirketinde çalışan çalışanlar",
"grandpastory.scene5":"Kabinlerinde çalışanlar, siz de dahil olmak üzere bazıları bitkin görünüyor.",
"grandpastory.scene6":"Dedenizin mektubunu bulmak için masanıza ulaşıyorsunuz.",
"grandpastory.letteropen":"Büyükbabanın mektubunu açmak için sol tıklayın",
"intro.scene3":"Stardew Valley otobüs durağına seyahat",
"intro.scene4":"Stardew vadisi 0.5 mil uzakta",
"manuallytriggered.healthnstamina.percent":"Sağlık %{{health}} ve Dayanıklılık %{{stamina}}",
"manuallytriggered.healthnstamina.normal":"Sağlık {{health}} ve Dayanıklılık {{stamina}}",
"readtile.sprinkler.pressurenozzle":"basınçlı {{value}}",
"readtile.sprinkler.enricher":"zenginleştirici {{value}}"
}

View File

@ -0,0 +1,16 @@
{
"warnings.health": "警告!健康状况为 {{value}} 百分!",
"warnings.stamina": "警告!耐力为 {{value}} 百分!",
"warnings.time": "警告!时间是 {{value}}",
"grandpastory.scene0":"爷爷,去世前。",
"grandpastory.scene4":"在 JoJa corp. 工作的员工",
"grandpastory.scene5":"员工在他们的办公室里,他们看起来很累,这其中也包括你自己。",
"grandpastory.scene6":"你走到办公桌前,找到了爷爷的信。",
"grandpastory.letteropen":"左方括号打开爷爷的信",
"intro.scene3":"前往星露谷物语巴士站",
"intro.scene4":"星露谷物语 0.5 英里外",
"manuallytriggered.healthnstamina.percent":"健康为 {{health}} %,耐力为 {{stamina}} %",
"manuallytriggered.healthnstamina.normal":"健康为 {{health}},耐力为 {{stamina}}",
"readtile.sprinkler.pressurenozzle":"加压 {{value}}",
"readtile.sprinkler.enricher":"丰富 {{value}}"
}

View File

@ -1,7 +1,7 @@
{
"Name": "Stardew Access",
"Author": "Mohammad Shoaib",
"Version": "1.1.0",
"Version": "1.3.4",
"Description": "An accessibility mod with screen reader support!",
"UniqueID": "shoaib.stardewaccess",
"EntryDll": "stardew-access.dll",

View File

@ -7,13 +7,14 @@
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
<PlatformTarget>AnyCPU</PlatformTarget>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<!-- To generate dlls of nugget packages -->
<!-- <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> -->
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AccessibleOutput" Version="1.0.0" />
<PackageReference Include="Lib.Harmony" Version="2.2.0" />
<PackageReference Include="Pathoschild.Stardew.ModBuildConfig" Version="4.0.0" />
<Reference Include="./TolkDotNet.dll" />
</ItemGroup>
</Project>