From 1c00a53657f3c7bafc68be4febd7003eb32b3a81 Mon Sep 17 00:00:00 2001 From: MouseyPounds Date: Tue, 26 Nov 2019 13:38:42 -0500 Subject: [PATCH] update for game version 1.4 --- .gitignore | 3 +- README.md | 1 + all_icons.png | Bin 9524 -> 10819 bytes shirt.css | 400 ++++++++++++ shirt_css_gen.pl | 37 ++ shirt_sprites.png | Bin 0 -> 18209 bytes stardew-predictor.css | 40 +- stardew-predictor.js | 1430 ++++++++++++++++++++++++++++++++++++++--- 8 files changed, 1796 insertions(+), 115 deletions(-) create mode 100644 shirt.css create mode 100644 shirt_css_gen.pl create mode 100644 shirt_sprites.png diff --git a/.gitignore b/.gitignore index a1b8d2c..ebf4e20 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ desktop.ini -*.psp \ No newline at end of file +*.psp +temp/ \ No newline at end of file diff --git a/README.md b/README.md index fe54b4f..a7a4dda 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ The app is written in Javascript and uses [jQuery](https://jquery.com/) and [Big ## Changelog +* 26 Nov 2019 - v3.0 - Support for Stardew Valley 1.4 * 5 July 2019 - v2.2.1 - Fixed a wiki link error. * 30 Jan 2019 - v2.2 - Improved support for iOS save files * 24 Oct 2018 - v2.1 - Added Wallpaper predictions and moved images to spritesheets diff --git a/all_icons.png b/all_icons.png index c56fb7a043f314e3b3717ab2a353bd34353ab4ef..05fa85501396c2a256da5fcfba58b9ee95e10bbd 100644 GIT binary patch delta 4607 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 delta 3302 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 w>FIJ^?w>|CVY0WA$Wr~m)} diff --git a/shirt.css b/shirt.css new file mode 100644 index 0000000..adddb99 --- /dev/null +++ b/shirt.css @@ -0,0 +1,400 @@ +/* shirt.css + * https://mouseypounds.github.io/stardew-predictor/ + */ + +.shirt-item { + width: 36px; +} +span.shirt { + white-space: nowrap; +} +img.shirt { + width: 32px; + height: 32px; + vertical-align: middle; + background-image:url("./shirt_sprites.png") +} +img#shirt_1 { + background-position: 0px 0px; +} +img#shirt_2 { + background-position: -32px 0px; +} +img#shirt_3 { + background-position: -64px 0px; +} +img#shirt_4 { + background-position: -96px 0px; +} +img#shirt_5 { + background-position: -128px 0px; +} +img#shirt_6 { + background-position: -160px 0px; +} +img#shirt_7 { + background-position: -192px 0px; +} +img#shirt_8 { + background-position: -224px 0px; +} +img#shirt_9 { + background-position: -256px 0px; +} +img#shirt_10 { + background-position: -288px 0px; +} +img#shirt_11 { + background-position: -320px 0px; +} +img#shirt_12 { + background-position: -352px 0px; +} +img#shirt_13 { + background-position: -384px 0px; +} +img#shirt_14 { + background-position: -416px 0px; +} +img#shirt_15 { + background-position: -448px 0px; +} +img#shirt_16 { + background-position: -480px 0px; +} +img#shirt_17 { + background-position: 0px -32px; +} +img#shirt_18 { + background-position: -32px -32px; +} +img#shirt_19 { + background-position: -64px -32px; +} +img#shirt_20 { + background-position: -96px -32px; +} +img#shirt_21 { + background-position: -128px -32px; +} +img#shirt_22 { + background-position: -160px -32px; +} +img#shirt_23 { + background-position: -192px -32px; +} +img#shirt_24 { + background-position: -224px -32px; +} +img#shirt_25 { + background-position: -256px -32px; +} +img#shirt_26 { + background-position: -288px -32px; +} +img#shirt_27 { + background-position: -320px -32px; +} +img#shirt_28 { + background-position: -352px -32px; +} +img#shirt_29 { + background-position: -384px -32px; +} +img#shirt_30 { + background-position: -416px -32px; +} +img#shirt_31 { + background-position: -448px -32px; +} +img#shirt_32 { + background-position: -480px -32px; +} +img#shirt_33 { + background-position: 0px -64px; +} +img#shirt_34 { + background-position: -32px -64px; +} +img#shirt_35 { + background-position: -64px -64px; +} +img#shirt_36 { + background-position: -96px -64px; +} +img#shirt_37 { + background-position: -128px -64px; +} +img#shirt_38 { + background-position: -160px -64px; +} +img#shirt_39 { + background-position: -192px -64px; +} +img#shirt_40 { + background-position: -224px -64px; +} +img#shirt_41 { + background-position: -256px -64px; +} +img#shirt_42 { + background-position: -288px -64px; +} +img#shirt_43 { + background-position: -320px -64px; +} +img#shirt_44 { + background-position: -352px -64px; +} +img#shirt_45 { + background-position: -384px -64px; +} +img#shirt_46 { + background-position: -416px -64px; +} +img#shirt_47 { + background-position: -448px -64px; +} +img#shirt_48 { + background-position: -480px -64px; +} +img#shirt_49 { + background-position: 0px -96px; +} +img#shirt_50 { + background-position: -32px -96px; +} +img#shirt_51 { + background-position: -64px -96px; +} +img#shirt_52 { + background-position: -96px -96px; +} +img#shirt_53 { + background-position: -128px -96px; +} +img#shirt_54 { + background-position: -160px -96px; +} +img#shirt_55 { + background-position: -192px -96px; +} +img#shirt_56 { + background-position: -224px -96px; +} +img#shirt_57 { + background-position: -256px -96px; +} +img#shirt_58 { + background-position: -288px -96px; +} +img#shirt_59 { + background-position: -320px -96px; +} +img#shirt_60 { + background-position: -352px -96px; +} +img#shirt_61 { + background-position: -384px -96px; +} +img#shirt_62 { + background-position: -416px -96px; +} +img#shirt_63 { + background-position: -448px -96px; +} +img#shirt_64 { + background-position: -480px -96px; +} +img#shirt_65 { + background-position: 0px -128px; +} +img#shirt_66 { + background-position: -32px -128px; +} +img#shirt_67 { + background-position: -64px -128px; +} +img#shirt_68 { + background-position: -96px -128px; +} +img#shirt_69 { + background-position: -128px -128px; +} +img#shirt_70 { + background-position: -160px -128px; +} +img#shirt_71 { + background-position: -192px -128px; +} +img#shirt_72 { + background-position: -224px -128px; +} +img#shirt_73 { + background-position: -256px -128px; +} +img#shirt_74 { + background-position: -288px -128px; +} +img#shirt_75 { + background-position: -320px -128px; +} +img#shirt_76 { + background-position: -352px -128px; +} +img#shirt_77 { + background-position: -384px -128px; +} +img#shirt_78 { + background-position: -416px -128px; +} +img#shirt_79 { + background-position: -448px -128px; +} +img#shirt_80 { + background-position: -480px -128px; +} +img#shirt_81 { + background-position: 0px -160px; +} +img#shirt_82 { + background-position: -32px -160px; +} +img#shirt_83 { + background-position: -64px -160px; +} +img#shirt_84 { + background-position: -96px -160px; +} +img#shirt_85 { + background-position: -128px -160px; +} +img#shirt_86 { + background-position: -160px -160px; +} +img#shirt_87 { + background-position: -192px -160px; +} +img#shirt_88 { + background-position: -224px -160px; +} +img#shirt_89 { + background-position: -256px -160px; +} +img#shirt_90 { + background-position: -288px -160px; +} +img#shirt_91 { + background-position: -320px -160px; +} +img#shirt_92 { + background-position: -352px -160px; +} +img#shirt_93 { + background-position: -384px -160px; +} +img#shirt_94 { + background-position: -416px -160px; +} +img#shirt_95 { + background-position: -448px -160px; +} +img#shirt_96 { + background-position: -480px -160px; +} +img#shirt_97 { + background-position: 0px -192px; +} +img#shirt_98 { + background-position: -32px -192px; +} +img#shirt_99 { + background-position: -64px -192px; +} +img#shirt_100 { + background-position: -96px -192px; +} +img#shirt_101 { + background-position: -128px -192px; +} +img#shirt_102 { + background-position: -160px -192px; +} +img#shirt_103 { + background-position: -192px -192px; +} +img#shirt_104 { + background-position: -224px -192px; +} +img#shirt_105 { + background-position: -256px -192px; +} +img#shirt_106 { + background-position: -288px -192px; +} +img#shirt_107 { + background-position: -320px -192px; +} +img#shirt_108 { + background-position: -352px -192px; +} +img#shirt_109 { + background-position: -384px -192px; +} +img#shirt_110 { + background-position: -416px -192px; +} +img#shirt_111 { + background-position: -448px -192px; +} +img#shirt_112 { + background-position: -480px -192px; +} +img#shirt_113 { + background-position: 0px -224px; +} +img#shirt_114 { + background-position: -32px -224px; +} +img#shirt_115 { + background-position: -64px -224px; +} +img#shirt_116 { + background-position: -96px -224px; +} +img#shirt_117 { + background-position: -128px -224px; +} +img#shirt_118 { + background-position: -160px -224px; +} +img#shirt_119 { + background-position: -192px -224px; +} +img#shirt_120 { + background-position: -224px -224px; +} +img#shirt_121 { + background-position: -256px -224px; +} +img#shirt_122 { + background-position: -288px -224px; +} +img#shirt_123 { + background-position: -320px -224px; +} +img#shirt_124 { + background-position: -352px -224px; +} +img#shirt_125 { + background-position: -384px -224px; +} +img#shirt_126 { + background-position: -416px -224px; +} +img#shirt_127 { + background-position: -448px -224px; +} +img#shirt_128 { + background-position: -480px -224px; +} diff --git a/shirt_css_gen.pl b/shirt_css_gen.pl new file mode 100644 index 0000000..b0671a6 --- /dev/null +++ b/shirt_css_gen.pl @@ -0,0 +1,37 @@ +#!/usr/bin/perl +# +# shirt_css_gen.pl +# +# small perl script to generate the css for shirt images used in Stardew Predictor +# prints to STDOUT and should be redirected to shirt.css + +my $tile_width = 32; +my $tile_height = 32; +my $image_width = 512; + + print <<"END_PRINT"; +/* shirt.css + * https://mouseypounds.github.io/stardew-predictor/ + */ + +img.shirt { + width: ${tile_width}px; + height: ${tile_height}px; + vertical-align: middle; + background-image:url("./shirt_sprites.png") +} +END_PRINT + +for (my $i = 0; $i < 128; $i++) { + use integer; + + my $x = 0 - ($tile_width * ($i % ($image_width/$tile_width))); + my $y = 0 - ($tile_height * ($i / ($image_width/$tile_width))); + my $id = "shirt_" . ($i+1); + + print <<"END_PRINT"; +img#$id { + background-position: ${x}px ${y}px; +} +END_PRINT +} \ No newline at end of file diff --git a/shirt_sprites.png b/shirt_sprites.png new file mode 100644 index 0000000000000000000000000000000000000000..9ef935f54a858498a029ef5d3c721e9f1eda39bd GIT binary patch literal 18209 zcmaL9dpy(s|2{tFPNVn*8mlo|)2Lqd|T4W!*x0XJ0dd=R`JEbq3+k9!)o8*TZCCSKe zI=#i+rL00=_@uKvgaj0^0(z^Ket%^cc@)j)Al^KXg0B=7yRy-4Hh zp#;arl?RdAdyP-gqX$0?_-dsqva|FtV&uhBIUfVQy@<@P=W{ECSq%26k2!YYc^A3D zc?UbYe7^I?J9l5vlUF+FS-!XeVQk_wn|xBD)SgdCTub~849E0f#D|QZm47Ga9#&#C zf03#yW>ZjJtqMenf$hQ}*c2t)=H9gqoOepLJq33z=a@aet)j@5E2n@*zak&RMpqf# zx^$Sv>?^V2CaUQv&-)DGvzn1ZXIyyi0tsTi$M3;eZr{5Qn*yPnwn9E%f9bCKE2IJU zxt%yZ=Sm)bR46Q*_wgC}dJu=j0rAsazYYRV3F~@;nocvgJqk8I?| zvuijze?!2-t{shR_H~K3Dh5|iLDd-2MgFhqw)p&ZGwxy9@ZQ%RaiZvi9{qL9@IKI? z6Oma=p!@04j>Gtw)8s!%L3QGMZL38b=J(9>%5Xy<^mUebS_L|21}YgC5i#q7M`<%i z4%EfQ4{6uNVMgN{0z~nf?50ByJxO!EJv4q0_#p$2RNtyn z%qVwBp*4Gm#~m;=i-Jd);jBp(G;8$jIEqnXZ*}*g>UPQV{bI=;qOK>rdqRxHL@{l&eleMfHQlR8-bS(|} z!Sm#ZV`%m-x17A@rt9*?;X+Gd*y6MdFT*gb?#d{+75Xv;MQ^3F z4PQu%8pMP;EUrAgz~4QDsxJ;$SSo4E8rC_XArG|Oz$Qf99c)ZJTDZ$8Evql<5^yl5 z_aK2f>1JLxS>JJbR9%_6y%(j^Sl*O1TP63YCqrSeb}0GfIQv z5kNH8T;&JwO7++f?0kG(@o^#rCpM-n88V+{FIFA-HW-K4rO|=%I}@9LPCJ3qb9l>1 z+Jmn2jxX;=UK6O$3hav{!-UJ6>Cis?5=jBCGnn^?y8Pibd}jA`xbO3$#>U`Dimecp zk%oJV3A^5R_QqG&UGbPdNE^?w>^iwPGjIcc=-&Anv4z32k`ymGP}9e@?Sq91rHFm( z5{ZGV;Q@?rvz|jF`9GWMzjy4O)W54}Bk=60_Hx4rr9?_U*U)w2##nsb*kto%qK`Cyi)CS8JvdDc)g6(+7AZ&!>CRZ;WNDoB!1)0||440Sv^oK>uew#as za((<#Zj$mB?i0<+1^cdFA4X4%-HJ9mzFUEH!q@(^JV}^vGgWG1wH)2T$RF}cW@J=} zgW1dxpsm)CTosF(p!@oDd`vUH-GM`+fr$;<5D<|(R^aWld;Y_RVW!XYXo^2X!@QAQ z;Ncx%RqM)V+y0CW?+f&#eNp9>5gAdd+)qL|Xbm zhsZI8qvq2_ln)Gc z^y8&Z^|kd#Tv6dyJ%2{U&pvr~nfA>*hVdfwAK*k514XAUsX>%9Q-;<4S;>(4qg#?E z?wCL-24X1^_l^k5r*U-k?xOjDPKneIz23;>jd}3u+fw+|_7N3w=@zQg3s$C8R)Xr3 zA-<79U!~CJmDF_KsMD387aAVLz`3Tk;?D-Y{UI#X`Xx6|E|JxIWCD_U8%Ucs9XpJ> zTYaH(8s?q49^EBG;lz6ignss9@V9Ome6##1w_|g9WS}f2=B7qtKBu?XOj2KGyL|X3k2748!5jCv&jBD38(ZfVmaCT znYT#9#{b1kvWGZ-LH>4*?AYh+`p0(XDtqcNfyCpBOHBRsbAmAc1I+`LPdTSfw3I_4 zJiZ<;y?8wz)excHRt~Aq@%j0dZ+2~er{rXJVSswZ;<<|misViZ6Act~AmHJ#-0=TE zV?xO#OTF2HkJf$)Qg3`YExb|et7@4~Lz1Qdf}(#4H! zx;l=$)vtklZ$@Fm?xY}ZJRIy-tR_3XXRyO&Z3~3;1&a7L5%pD1ET_) z-K6|{>kc31?i0S(YV5>KrX&ZhMOtXGo%mTt8g+5Gq+UFN5PEt3pVw2f6q83-= zHrB5}eLJ8TTUrv)F;H#?Sh$9%u_nkU=|I7|VBR^tvZzq*B{1kd95OH}z$)-6eQcvd zH}k5U7BU|YDdO}rB3lQ@KD)R=T^EF1`B}8kf&UQN`6Bi_RumQbIc`Um2)B#o2L>tl$@uzwKB}tzcz0q*J)+VJGO}Q#iT;%j;?Av|p zjobWq3Ot_~w@vitAnpnO02kq|j!235Y=1)u-g5sz`Rp@J<&-L`IVfXQ5N51Lu z`v1_W&l3Oc!!`f24>>7Vu{D=Hpd-f`tZulr?;pNovX^W%`NwjL+38Iz)pOx5fh$QR zr9YO<@uEi!f;=WF4hnfvcp#O$PO#!0-vX7&IHI+v;{-m&tu5WK5Bt7MqJ<9ee7}m7S#N9tMi0a*V$((NA0Jk`>xsNH< zMtRv#Z+Rhv7P6fZ0&jN{^$g@5k2U#?hk42kogifm9}Gjc=|MC)$QZ8Tc6bh)wseBX z0Mb(r-9!6o(^AY-C3tUr_t*`bKl96#h;*ruU^T>pH{KjiUaRMT#wlQE@2=MukEA{4 zE9~&1K_NEb{S!W2a|BTzhUorG-7KxubMvAK`kIs&|!|XC*?ynw!G+J9OoNmiy9#{U0Yt#mDt^ zY-C79Wcpu1wLF=0s4f{V{@OE8pK;9lEk1D-J~8S9@jlkGaf@=Mtd?M5)P<44xkWmi z!U4MVXcCB4-W5?VBz6ALlLqMQOagvI6n5Wy;NdeJxQd5Dm&=7pg49l~ow{FNtf%w2 zo!lv#t?eLI5-(tuK~8jXR1Gravsz;EwzZu#}@gm+$nVx+F;Go&%62F_v z_l3A0p=FmtvdGREQ$4?W7X5hOVk@A7zSF$Ul(C$`m{kUQOc z+_y~H<33bo^jhR~w(Fr{-9L9)gEdaZIf)vsWytRwef=B@EE>wyJxuSRXPY|{-OM9z zZtvbT`uft)Q~#^7icc!4hPF`7V+rzTRSM5DG7Jur3$#DdYt|aP`G2~pjsNd%N>UW_ zOTMARUE$HJY_*LXRk)isT*zymHSY=5!0E}oWpAS1X^zdjU7bmUvPKP?bNOkrF6()b zS7>*u4yq#lTzm%>O?mSW=W5c_%I!bHyFm)^qFkL0Pm)i!=#-#!%OQG|y;K7gqA6;T z(?)2R*H|&5@qC>LNR7w5+oL_M{IicQ>hf0Knim0!Sx81~eSEjLF6ra_R-LMzHLXDx zyJx2ty_4J}JdM5-o#DRD&Ch=*I2IQEI21r(tpFrbD=5RZZ=(47qi`#AsY0=+P49h+ zqKDTKZrVPI%-#!K?ZRbs;|otbSMM}IdJUc&DxNK~xSA7)`hX|n zA-^S&al(XOJ=a?CGEZ>mLOZc{1Eu^sp`2BM5fi^9I*qeGaa9V01w7^wEmR!vUC0v( zSjFA$uh}k1c?|ZkW0IcThZheeqhXBUq!kuF{Y@-YcwIDrY3tm|EaiN`ojFFsW`wJ= z7=5E56aMLJ!)xJocmz(B z7b~iNbsCY8k(uVQyFRR^c@0dLw8X(lxy58Y6!TA;gGR16^I}spc9bydAX*{^Mpco9 z^@YOPZEA%^fQR#+-*KDVnFR8SWSm2v12zULP~^=NF;LD=OO}@70^-BwdP3zb&(VQ+ z_51QG1@XFZ017V5H3Hh$P#GTI*QIA!hkjgz^;E6h;S*nuXz|Y!;&}YPh{*dG%Z%v! zXn6F?OyMx5W-HS-Iij<6`T*4;fgiP22mlp~CSC#FTlpH**M+1BeAS{)R zz$kUKC;qqnpoX%6w|(_!1L@;vADw4BcF$%Qd#C$m+9vg1(Qmc|ltSVDI29VxbW!;8 z%^bb^u_d+>g8Sbf{9iIG){uJ{L;rC}o*b)W6+1B{^7_{Q1Yd5_T^aBG%b$N|L7`Xr zQatL$zr;X%A{Ux#!ma$PI4lCSPKILHL5Y2KnqBuu;h%KL5ubJek@2!}^)IW-sf$0= zTk@F|8&n?SXGar-PK5GZf4Kz;!!zOu;|fR8=1n?Kn1uv?4afKg`UMoSQ}91f3&!!` z%wc3!-)WGWJfVWz1-VBZA#9yHi4lusJIJ@TF<#=y)X6tDsB_U*gRHo*y(8B)slbbt zchZVDlH67SGvS-=ixG@Aq zS@Ee)P=8Wh&vcGX=AVRVNrt8VnA?{fppnt{EGp zUef%NSN7mMX4(6YS)k6>rBQM70krYs4d8_^EB+xghRdBNkXZvMP?CjtWL*}&?Th4U zKm)&jr49zIl0D5BsNSu_N)>#r~!i(KJsa=8*h&l!A-E7?q&^Q4c3A5R(Jn!QkDs<6B3D80>}E-7e`?S1P;a+%WY z+ch?MiP+K7{x=m(!XU2T;6I)Q!j6q#i2;^MGygf_rnL}{^c z*gb6AB{{}sB$^(%+T-6o_YPI)mAVO zSNG>n59vUki}5SLS`n~Copu@Gg-#X6gnf=zrZ&~UvW+5~$I_z^E%p!8Eg!REA6pt` zn2hQq9h~Q^(gXP`*S&N}j52(ygc;EpqRFx%IKdf9GY&UV+=R35OeWTMh=Oi=D0wok zyuPQ#QrW-Dx%@(7oJnK&{6`O^3n_m*r%$9HRGErs6fdANB%fTw@cHh3md8oMNM?bc zejUpZ2kt1PxYT5xeMv77z}kP%Ey^_Nc%e#V7C`0g$u#5op^;WSiP@14x?0RRp-O)a zTX`VZdX$`*8q^T$+ z_;lR8fFoXLxJC>*GVqLlc}~>2N(r}+uS*qbpc9qx*m`s0AAvtI7;kZ5a-;l(ck4af zfwbowHBIiv2%zo9EA#px?RVQ*6~`^RQ`D|L?rRIyFmpftS4!ZRrJjfw(1Z95D)+ab z=;F=Y{Sfcb*TG?bde9DuKEnqAxkv9gA%IOj(0hw|c-#{QYKhI|!aE}ee-Yr@dZ}bV$(>%Q!fg=Pz*wOFk-&ct_ zMAO8b7CibPgQM|Aag0QxIf?g0$j>!3tv_l9VPy-i?9(|`YPA?bi|0(NA%V2jIRcnE0TR(R9aH& zHlT)Z9e;O}=pQ(8Z5*B8(bE2<|23+4t)2I>7xgMuiuS}<`Xweuj9Yh{~|zErHjaS$VIT zf4(~>7f}28v!hFjnLNyW9I#{f+1cnJZ@lQ?I;Gf2@*WnSL6@|{WvW?JWd!am-Llo^ zsZQQY-9z0)Evv;KU7~j^n`W>7%rB?-!t-#}6q{ zh3aRCx6d^X;A0T?-uPGM@&k1>U9dna6uEXa)P1S(rs&iy_nUMNr`JP$J5*HG1_lQ^ zf}JOKJg!PsC_I-KDY9eCck7E4Gh-T~uuDpb>Ah@Ri1(&W%xsxZg=o6_SjMQXJ+wO{ zLbZNJpg@GCAFquI#eRihJ8|}0I#f@VwH(Cg9p)bgg5BO#k-UO-(`xsy%R|M-;h!(L zPz0gnRT0Zac~+I4cXK$Ecf0LfP~0H_8uz?Hn)T>h^%;_UBB==Dw}){p(I3$XUCVU1 zU=BXa;c9A2IQaK_cY%Emf6P=j$6ppce-RK29DH*cEp|UU9$nF>~Z6)#ybXG zY;C|CJ?xttoqh{g-h;Cz;CK^lg(CrKfUNM ztnDg3!aB$9HO~kb;mSsaXX7C+8SFF*W6ZJ+b=V<=yq5fVf*Oz+1AmT?)~LEQTo{q} z6LgD=c04I``g@+M^*L@RvPnZ_(c0&&;TrM3ZVl|_y1BEZ!UA-8MTtuO66-Xs!^r15 zH#w@ufDJcwyra%Y8-@_?w36D1h)~jY&36P9A&m~;&Ni1%3j3EYx}fI9C(y9sIwK&a zS2%txszdM5S1H5_+#NXuI$%OK67=T;{Rf_$eu%g%|1oEfx($AmDt*Ze4~*ypMjPr% zB21G8RfawuplQf2Sau@|nAUsD%^GvMbyoa{z&)*B346TsH!GD=jB4%n(Od+dB*K0N zbOpW|GO?%&HyJ=m=%1Xm_I>24wDZ_qv5D$2l%G70pLQY~gr?mS>&953O{_!kYUA1n zlv1s1o$a2TC5dg~L$vGtlSm{}o`{-_$CJ)ugueaX@{y~ptEQ%02|PnGb!FLHtW7@$ z1g~MiHHQzgS1vNv7oRurNJnj<-E2{m2#f!*yswMy^a#JyH+2{E)TBFIQ_6+Rw4993 zvMiCgAlfTmf)>VrQ5Pi$?PZ|`uU_SvoV*V%$4b$0gG4N?*0FpSBD}ISyhlcN+er3- zVBYy^l0@;m`fuzORwLz}eem$gL1HEJgm9Nmf31=8(v6M_!z;Uq+eqLaD=L5w;d42s z@Wrc{_0HdWA zWKt6UslrHlT#bQC1Gw^f*G!I$U+-|R+QkSvbWD}i_o$g(rPCX!_U+UxMpU$P6!k3% zJ}060d1E{AUFMHKv$wcT<-%k8`=j{ErlW23=Bx}nCf>$_1GJU;oj*a}PJ6A() zpkGuW-rS!9I3FLSR5c@d?_q!Jr0#cLBVejBa{Ji(fBCecW4#RYww<1L{rzZq{ey`|3osn0Dy9~dA3kG4`0Mvr} zIbG@W^tL=usa*uSihys=_X{=WSOeaYg75EE7B!51ZsQ`q{2cFGGL|gn(m9$hN__4lT>X$XHg?Pg|qs^4-D9SN<8{z)5FodMoStr zBZQ<&RW5(LP(0i)*dYUQwN@ul5=YBrJcVO6#;y#j0j5078}M(j@KKg%>kWlSEl}z@ zPwM;%WX^3zxTapOz+G8J2Mm6@BKdL^&5f*Kj?bUa*ExYew1^@o!fgTisu@6t)e5NM z6*_&|$|~ueI~>{ksfuxsBD=Apgfk@2P!boj$@%%;>5C$rS{DL+kqmrtu$PHZiDdqP=fEatOp5(;?ZIqOWk&qevASS{fN4SuJER^FV(eH7+@&V@( z@`k|9A=@4#T!&4cUPKCiE#5o#!lpFlHc|IbT_Bd_Q83I8T=PWag2Xj_F_{Zy=q|H4 zcaF8ulLHhoM02^(z|j0E4aK%ocVb@2mf;4@+8r;8cj(`H#szME@ZJY2nWjK1m<2^C z5Zi0=?ZEWt6Lqzli^7`^f(n>VT5dkdKSFbBD(pPcs zPw^I-bilUsDb1&i^xsIMpFdwUSO~Qz^EmeW0fns`or;EFU%7f$k~IZ+SCXOA z#B@=eXYQB;js`7)A`i>Ayf6c(tX2ShrZa)V>kzo$oF`zI<oG?W=i#r8UzJ#}TkwaB z^+PK7Zst+XG`VjIzp@G6zuTd~=5hzCtIKDBO@8aYi+o_mD)lnrm=0M1eIqK;(fnwTcUT9c#{~yVGg~y}c zKQzM;B6$lYVhxs}lH2T`z9->gxZwJ7?Dd=_!Kc>Xwc}|;#q54nM0uS1bV*;8j0n6V zN$2JcKQs~t?+AhA6xh|UlxEtmtkaMuzXSQrqPe92`C!IA`X7#G5;VeXC^H||=Iu`n za5jTRV3p{FdNgMpu=Cy+OOX3!H4Gnh+dkj7;goD(U%jJ^l7Y5kE$J3^Ep7bUI9jJa zCee5|V&zL`PVw#Ww&04^@~uX~9+K? zq_oGt(4!44&{IzqQaF28lRhTAEKO*vSXbm0db{$o`_9fu%6W14JyOTEc z-UsmfS?OcC(tCo~(3Bqe!YkM{=$wbxuQ-y$CEr_qB##H;8sT+D6=F>y>dPG?T8hho zeU8;6VWLmt$=R&M*m{L4^^^h=uFhRBr=#bvvt%!X^&cjb8D^Q8P$lF3K{4b^4U9Jc zb_*T{83HvAX$6NH1W;ChALCDyG*K!+M8MZN%cFL4)t}X~jcwxNoNvXOS+6C%WxMj8 zhhp2K4op|&UEG;1T|zN$TmqcC!=eqjh;Re911qC14=;DWM$_l^h{M`f!7B1ydvf8X z&$EwmY;gnS(g)Mf1f0h%Jrj9UvEcpEU&YDvil?-5=Pd5eqssb-m11VkvIelv<0#6T zIJu#@?al8EtV~d#Y8)f)R^ej5RIFoxRV+>a+#McvW!fL29XJtP3$)atk^elSpt^n_ zATp-b^{!VRM(@AqcAbvP8CogyQ+nj|Xg6I4?UNcOemxZwtxnz1=Oy^zvvr@;rU8On zb&wWsEiy0)I)s1;BiVA~k!W=6NzYJCa!`8LvoZ2d=XMYQ9aGhXbU*6V90`Fi( zi|a3Gk5dVKBg}nCSdn*LVPkk)o2y*db1tTq@v=3%^O`~28Qx6V$~0Jkzz9`9 zQW)}tx{%3UiJK;Hhi`;KRvaZLoLiE;X7{H;vK{+Mboh!s2?&Ta7bUvmL2T*RByl*= zcqu-X+5auq>6;N2bj!3e)`EnlSDLna^!0u%oE(dIpwh_&?jh}$OxjQ}<(-gGyOEV( zI18!111~l;6Nd~WJTN3~b@jn_%qr*AU*4Q=HA*%jDzuz>j*#?8SsSadvgj8`&Jz9M zO;=5YDrJ8G6`cMX(*-)$K_l!~##XeW#b$p^vn^Z39;hoFr%s1D?Roy9Ooo-LC5Y@; zZT$$NSpxRSuf-a5TPUE}#0Kpub?uDNEpK{DjwIItJ7`Sl_BBUir)9}n zVvo?gmGoDG(lEMw);wZvp}a0(R?`UCb#f9WL*6RT21m=zV1+bTC^qIw^jU z|EUKZR9ow>o{?kRyJid+J*}QWRu}*?MIhPhfq!uyKN%qUvNpPKq<=Rk)n$(*la~5C zNHH<1@E`&-@@BQ=r-5?QzM6euL$4>MCB8X{5^g#u?L^?{!Kauf*_M3*-6GH+n#eS2chf2nfaTr1}u65iJI7iB>!9z`cAeinh{EJ&ldzQ=Fi`}`?fPKk(?$Wi@gWCO zvzsIK2wdYKDQkx&oj+VZcZRpvx1<(1KII|&qnOm0cj}sYi*I@XocXRfj5S(2UDd90 z;Dl?3j<(Sb)&zBu1cnNRe9AN~{K|$(n7EA}45$VBgw^yW=AhQu9(38fw&-JkQugxr zyXi8;531$V7NsqH0|)^cIhb+JjI zQq>&5YT89T2=EatJ&SwJb~|T0f6E|jNyOh^N@Eoo9*{uGB0(_6*=iRKX2odJ;jJiY zZ5|X^JQERs9c_*;Hp2P(`c5;Co!EKKnRk8myY?cdJ!9jY#T8u2?-tg=dTrB1!KO`F zNR-L9Z}$s?hwTdE7_s?h=mpBK`wL{O9w|0^x-EM}3c*uJ{O`c#BFkq9LFbC0eGOC zl2QjFp=u>rZYb<}{MY;}A3OOQ;VivBNWRb?Eh_X49um2<49QjBGQ{}Fm@f2FG-M4z zN~ccJi!uOV4 zeCw=3efaC+-Sy&lRc2USnRdwAFGHG#*KVLH+LDc6g7@S6spGN=72n|^hsV&!<=Y_0 zX-{AlIRQ1Jc0>nCsBH(1p`E_R^2CHJLTuP^uef0p7pBA_-(MFSZn5=t0b6bgUi}P4 zVqYZ28qZk6=nOug4Vgy)ejn4-yh$sJqb{G(v5*a$m$_LlZWs;beQo-gn8&YF{kCJ)IHL>{On);_`@CFu^L}BEUS#^bjTdtg}~+g)K|bPo2PxU zBH-G3bMJl&)+P|{NX1jyWc1J6Sq4=dFgzNpe?dNS?cb>JrTB;@)siUez;03Dli%Ye z(^Uo(kw?Sm6!bCF8~E06A87O>nrrG@ik}}A+%ufFB1{yJfZ~}hbym`nOD04AdDRwH zr_0y6&UJO%9$3f)_5g7IK_}hJLe?1g|`NpXU1dWIQ>mje2I@ zB4-0L)uLS`*WdyF!)>23uOvQ3WTo`GzY4k?q=a@7tDek_Iqj%t^>fIUijc zasf_^HE$$M?P6x;)_j@XT$mzCz}tgxdoyrtJ6N(&JN5>Jqeib|Y= zv}k47Xpkn*!=>Bvc3I_XC1J%z92q*iL2t{vXl1ei3^-5trng;hd*N1aj4&xk!LIln ziR4B`1jaUQDG3|BOswX4Nbx>1v%Fpn#V$yLQwS>Fo0r(u815H{8=Fl+ zThtzs?u+(y={hD$wkUHQ%48hUdxHO*_aCFs`T-vbuLq*PK_e@_4_Wu^>q1!*r71@I zm7SOczhtuWt&X}5RkE~xLH^a+A$zV)&xWk5k^VhR{AKK3MA%)O*#QaGl}zUcUU(_u zEjYu&Iwl&udz2&i{7ui|feUvg8Uv=+s-Jv9uaf8+fde}Q3t*~7D_c*h%m)xVrtcG( zy`Y3p?OTI`ESmHndIOxA+yAlm$}CX-(l}A&E`bo6h8g#?v8{{t~xpRy5v05n?7YiOcds zCqN3mA@xLX$Y1}CKXN#dj+d#=AV>(nWaBO3(q5<|0rlt#3ToNWDph_Lr&*#9^Bg;F?@g##o ze_pDuuol|8;-@FbSOIU2zhTV}ITba-r|xm-+0BqO3EdtLC>~@WbIZj~o_hRfIS!X+ zFHI&z??Rc~mtMAb^9-R`ao8yaaqvLiYjn^zibDzES7vmTcRTzxV2c0PR1PUt9irJ8 zDH*K)J2SHDKazs$e+M9zWfs?b>I%QSH0yxS8VGigS|DBhmY}Xr6-vo&Abuyj`ef`v zKzL}-VBvF7cp$%^_<@i+0nhzAg3~e;5UxfG!xzhj`R&hWBJST~(t)hCI6Q``fKhHd z=g)Wkpo}K}y5=e-k8LGuZ?b-036xxmeU)TVyA0ud%Y{+juPYA*)IUnXsOY z*}_N}!9vxS*Mp0Cn_s+l?I@ftad9bPz>XKcwu@M8uZXL>ALpxh4qTPs4UH*d>UKmI z)a^Kt^=cY-W9n9bD2EVX-DkeQ>bG%0eVjZ*EHC#B4DmkF`GCaj)b9y~la_abPEWAW z{b1eB%Y+x)jkFR%l>#eGG-UTz{#lRGHLH!7dXRE$oS@BB2oNpm!y(Q?z)Erb^CjFc zebx>}Z}!r!>--(%zc&M)h}eXC{Rpwq!8POq6j@|naweGZnjEdJ(x0h7A(&XS2!a3+ z&R*nT{AZ?p=m}QfeDaHa+l@(;<=_UvXnJIO;sRxY=OU=iGg2teHAT_pAwi?)oE&lc>aV$z5fhY#w{R?l!gSxmhl#x@MqgAiBVWxT- zaapf=j-NBmL?~IglxFWKB#4D{+=Og^VR>nFx%6(#qMmYhXO*psMVD#gvqoN5ADG)N zEZ*iejHKl0FW{3VC3JJ%-}I@?db$c-z`cy7&dJH=HE@7TkN+q4?B(?F&L$d2yZ8fL zA57QKUN_}Ny*{Cp_c~xIlB4E(4SQurXTAb6ro05q|7d9w#$GwN%7(2hOm$r-a!M+p zq#nCj7cnd&T720k4+XVtiRXA+V`PkB+sKI}DR5d!18nIKp?6JjPZo5>{nbR=g9u@i zXnIL>r>!pT$jM+@$JV1ir{7;9(*5tpqgEWkGiDExsdEi0U)H(D3rWX8OOOQfN`jKW zENK>hU^hZ|#~2y+HS7O3W({_4O4-4FwI6;gfC1+>`o6ujQ=v}h?k&&lBDxWliz%@PoeTN_UQ=MrhD{Y98^g>=i!V)`(KbE?f&n zUd7d$#WEE&uzB(2jse@~U4Ox-e*%nSJjpAeKWwi&Rj`I77I9c|CKoTp*cna1k#Oe+ zzQrwN4wcb^yLx8}rS){)>P``%Y+Gd>-?W45W-K(IAMKp2Sx7Nz;L<_QZ}dF{#hVc< za8y0sE|hN@yB0O1aseFnd1m_&R4hTDVhQLOQ6;#<=;dHDaM%b$$FU_sQ4G1#@30@9Y;yyFh z9W_Tt{9Ptob+3f-Zu8cqk|Lb7^=8_IPpRb5FU#TZn!u-aM&Gt|TDLFB4<$m$Q1Bt* z+&1;HY8F(Q$)h7{N)zzp^BMarg%2kWwPfl&D;Julc5c2yGA7K-M*mnaztn-_Un%6@ z4V~LdOFVgx0EDTeQtTU27VaGA)XwV>xsh%sLrG&BYPINcPRA|x-WDXX2Y($LO1dYE z@EM*&NY5XZSf#2JQY4>)<5LpOo)Zw-{CM!77Qd8gA_q9Qz z{`p4>z2s+k{MCuAcpUql$RI$;DIs?k?E^(aal;1b0T4KMi`)#Hz*SW)9=xGy(R;WR zx{)7x*-Sh<97?GAH`k`6ouO3$B|9s)q0Jj(o~fm5n!y-kU4+k=h>V?F0D8gzRI(Dx zALaUhr*mmFWg$-@mrcT{{MDa)x)#j+A-4>a{OpA?7FS(nGr=ATm~ACt^M=8YR#{n7 zVZbJea`!dJOLC#aQD6G2Jw1yjOIHr_8cg0dU*Fj5T&lN~#qy-4n)}1d{CAqQhF{r_ zq59e5L==f1BG6;2v>%-S>0HgHt3JYHj#*Y=DYLx9#|#S883p=V>;;t&Fr_`y4*@9| zIO1jY@5xlSOQkX(fAd_05nymCTMvA7wKjIm+k6McSx(ZUJHGqNJW!3I{<2ufXzRGl4zDhT>)y}2#0v;z#^k$IdY>LPZZ zuE20Hvf9YEGBpF2=&gROiJs;Fs)zh)Y%RDl8=hEFgGHyN{3UP2gKx5g%ioApL`0Ci z=oMMt4(P{!Cg}~_Q;q~{)k71m&##V|bg7_Qj4xpk-cP1^Gi*FEN7m!sS^OWTwOHBV ziJR?^T87(9LNnUkvtcR7TV6~ctWv$n64w)k_V1@9Wx{klRpBGF^QuAa*Fl>Kcy>hz z%nT(LS9~Y48rLF#TR3ozyDikk?G@bwXW_FUzNYsepy!kZhM@lp{Mv#5{>6Bl>MwH_ zm~+%bOUZ#wJ8mL4*~aht2(|ooL`unW@R|$qD)ADTE-P7O=D=?Gk$GTx(IbK7`wHjD zbj674cGyiX##zt1j1zWr+jXE))r(!p!*j9@eB+NQ?-&-vrA(U=P>X_ru?(%EA>dpL z%{&oBj)=$O##&45`8qSjAHUdF=LQJMr|x z0F3xHClkE9ZQEtnUMfqOCdAGiuwPY7###9=1AAxAhFwg5(~PVlkakSqlB+T@;;`}S zQK)w-NZnCS;+}I0q;Ig^9X5SRn=Lc1-cFrCe%zN4zYzw@ToK5Vq*xJ~XxnNI*#ok|$`1>w&`50czWIjZiB!X@ zf0zLg6PF9q-$c>_;S|Cni=J@6ABW zaejGkWM@41gb`0vJ(B?1n(sectepzBKQq^~jEk(eaip%XiMex#V1#_jz|Fkx{DZ_o zGalW$gT&0D4u?Zn&+nvH|G;rBK3)o(Qpbpy(XNt!Rrd(mP-p9e_QI?_jFq()>wVG6 z>fMT%NwY#0>^iADVPF6Vx%q&x1x2E|Z3Tf5Z))JIUwSIH!)|oxb?teVlFnwM`HG0J z9^orPj-bZrv(d!xlV3sL?7K7{M>WTY?|RJ%ZU+m$f$3L9*#O+8?@;e@`&~9SJksaUb%h7ZMkmH~uym8v%8`y9IBFGh5wq;<9dV`S^nYf|aYuJu=`ZerEeCuMT zDJEhHp2Y8J>U;Ee==`*;Ee;Xk;E^x)81#aQ6K~<6_~97H6<_yBu@v^)m**k09@V_ zd5#r;MwKtS5OqY45mKbL+2i)zK1K`IiHTkz*A`Rw=?yoOYk$W42 z*`EWv%_fO{-^D@nZ*h=|4<$iI=*Grg%hxNs!I9$*r^8`yPRXHAC7M z_#R!Dx`@TF_Dav6rf8JEJZ9y^fo{-8_={xnXEF~{pDR0pBTXl8-&|1Pakw>GCrbPL z2?dIGVUb^gs()Ded|7YSoeohl!UF~HNYNiFgG7;uF=&~93{P>kK0JzmmT&G(8DZ}7 zfOsb10V%>C0r{xlE|KE9P%iQJCzVsF;0!dJmJN;kJXwtPG3%%dHR$h8Wydw5i6uv` zfs_y&OB{(ifB0+uCbpc7eB{6cs(d1hxXt0F`9CaBlNVPo<5K97fj!nj5*Py;Nc-a? zFXb^C_-V-Nzz{MA;-MgHTq1mGhClBUe84KKmi94nmoVUKbjrehs=Z&EMle3r@_<{Vkvk{vjD5#rOi5 z{X46Dej(3W-#(Ig_UubffywfOAU&t(#ixBXH2W0QyPl2~`#s^T_u!7#DvNnH|Irm_ z%OBgb96J_pgloD0_}>CP8h-)?Os*y(v`hC;BnH6wJ&t&=bcPG&4`-< z5=}-c$r*@$9s7y@4rTn;vCol3x8I>o8ZVve$a`x^LyatUA30rJn<@fP{L)yVcqK#R z^Offb0cW`b-M%|fbBF)VDaO-vG7>-{(OV8^nNXDR0sb!COPsy|{F_XfxVM+~>^y^H z4{2%Bp{Nvaq=M19B)2z zWn0B*j5s?O@TOsSi`d$d+bA*<@Sa;G;i9v11ENyJS3b?M7jj-LjYeE6Z<`F2l{#_M zZbm;^y=xsMPT_8x`=)2FBg@P7c{pf9y$M@G+1*-HhmVZZKDK{YhcWQOdwPYo&Y~|}2va$tCH@AJDroA5Bx>3XpAHEk9Ti)6ayfCjW zy`61RQf>?EGGD}J7PTyvUmA0Qa6l#It{1Cwcc~ladZEeXXDSMTaR<7gCvZJoO=yR> ra!7h^0rUjfVEv>2@51#QklVBQKo9#dyc_)J8;Hq;E9Z;Pq96Pp8^&Qq literal 0 HcmV?d00001 diff --git a/stardew-predictor.css b/stardew-predictor.css index 05f68df..b36067b 100644 --- a/stardew-predictor.css +++ b/stardew-predictor.css @@ -58,6 +58,10 @@ h3 { .pts, .strong { font-weight: bold; } +.none { + font-style: italic; + opacity: 0.5; +} .ach_yes,.ms_yes,.pt_yes { color: #004000; } @@ -159,7 +163,9 @@ h3 { .tabset > input:nth-child(9):checked ~ .tab-panels > .tab-panel:nth-child(5), .tabset > input:nth-child(11):checked ~ .tab-panels > .tab-panel:nth-child(6), .tabset > input:nth-child(13):checked ~ .tab-panels > .tab-panel:nth-child(7), -.tabset > input:nth-child(15):checked ~ .tab-panels > .tab-panel:nth-child(8) { +.tabset > input:nth-child(15):checked ~ .tab-panels > .tab-panel:nth-child(8), +.tabset > input:nth-child(17):checked ~ .tab-panels > .tab-panel:nth-child(9), +.tabset > input:nth-child(19):checked ~ .tab-panels > .tab-panel:nth-child(10) { display: block; } .tabset > label { @@ -216,7 +222,8 @@ table.output th, table.output td { .calendar td { text-align: left; padding: 0; - width: 8.5em; + min-width: 8.5em; + white-space: nowrap; } tr.past > td, td.past, td.count { background-color: #ccc; @@ -238,7 +245,7 @@ tr.current > td, td.current { display: inline-block; padding: 3px; } -td .train, td .night { +td .train, td .night, td .crane { text-align: center; width: 100%; } @@ -252,14 +259,23 @@ th.day { th.item { width: 10em; } +th.shirt-item { + min-width: 36px; +} +.shirt-name { + white-space: nowrap; +} th.multi, th.item, td.item { border-left-width: 2px; } th.qty { - width: 2.5em; + width: 1.5em; +} +th.price { + width: 3em; } -th.price, th.index { - width: 4em; +th.index { + width: 2em; } th.geode-result, th.wp-result { width: 12.25em; @@ -278,7 +294,7 @@ fieldset { display: flex; justify-content: space-between; } -#mines-buttons, #train-buttons, #night-buttons, #winterstar-buttons { +#mines-buttons, #train-buttons, #night-buttons, #crane-buttons, #winterstar-buttons { max-width: 57em; } #cart-buttons, #cart-search, #krobus-buttons, #krobus-search, #wallpaper-buttons, #wallpaper-search { @@ -330,6 +346,10 @@ img#event_w { background-position: -64px 0px; width: 36px; } +img#movie_gs { + background-position: 0px -64px; + height: 32px; +} img#geode_r { background-position: 0px -96px; } @@ -342,6 +362,9 @@ img#geode_m { img#geode_o { background-position: -48px -96px; } +img#geode_t { + background-position: -32px -112px; +} img#gunther { background-position: -64px -96px; } @@ -360,6 +383,9 @@ img#icon_s { img#icon_i { background-position: -112px -96px; } +img#icon_q { + background-position: -48px -112px; +} /* tooltip implementation based on code from Andrea Ligios https://stackoverflow.com/questions/7117073/how-to-add-a-tooltip-to-a-div http://jsfiddle.net/AndreaLigios/jtLbpy62/ diff --git a/stardew-predictor.js b/stardew-predictor.js index 22e85ca..9f5ab2e 100644 --- a/stardew-predictor.js +++ b/stardew-predictor.js @@ -1158,6 +1158,679 @@ window.onload = function () { 'Yam': 160, 'Yam Seeds': 30 }; + save.cartItems_1_4 = { + 16: 'Wild Horseradish', + 18: 'Daffodil', + 20: 'Leek', + 22: 'Dandelion', + 24: 'Parsnip', + 78: 'Cave Carrot', + 88: 'Coconut', + 90: 'Cactus Fruit', + 92: 'Sap', + 128: 'Pufferfish', + 129: 'Anchovy', + 130: 'Tuna', + 131: 'Sardine', + 132: 'Bream', + 136: 'Largemouth Bass', + 137: 'Smallmouth Bass', + 138: 'Rainbow Trout', + 139: 'Salmon', + 140: 'Walleye', + 141: 'Perch', + 142: 'Carp', + 143: 'Catfish', + 144: 'Pike', + 145: 'Sunfish', + 146: 'Red Mullet', + 147: 'Herring', + 148: 'Eel', + 149: 'Octopus', + 150: 'Red Snapper', + 151: 'Squid', + 154: 'Sea Cucumber', + 155: 'Super Cucumber', + 156: 'Ghostfish', + 164: 'Sandfish', + 165: 'Scorpion Carp', + 167: 'Joja Cola', + 174: 'Large Egg (White)', + 176: 'Egg (Brown)', + 180: 'Egg (Brown)', + 182: 'Large Egg (Brown)', + 184: 'Milk', + 186: 'Large Milk', + 188: 'Green Bean', + 190: 'Cauliflower', + 192: 'Potato', + 194: 'Fried Egg', + 195: 'Omelet', + 196: 'Salad', + 197: 'Cheese Cauliflower', + 198: 'Baked Fish', + 199: 'Parsnip Soup', + 200: 'Vegetable Medley', + 201: 'Complete Breakfast', + 202: 'Fried Calamari', + 203: 'Strange Bun', + 204: 'Lucky Lunch', + 205: 'Fried Mushroom', + 206: 'Pizza', + 207: 'Bean Hotpot', + 208: 'Glazed Yams', + 209: 'Carp Surprise', + 210: 'Hashbrowns', + 211: 'Pancakes', + 212: 'Salmon Dinner', + 213: 'Fish Taco', + 214: 'Crispy Bass', + 215: 'Pepper Poppers', + 216: 'Bread', + 218: 'Tom Kha Soup', + 219: 'Trout Soup', + 220: 'Chocolate Cake', + 221: 'Pink Cake', + 222: 'Rhubarb Pie', + 223: 'Cookie', + 224: 'Spaghetti', + 225: 'Fried Eel', + 226: 'Spicy Eel', + 227: 'Sashimi', + 228: 'Maki Roll', + 229: 'Tortilla', + 230: 'Red Plate', + 231: 'Eggplant Parmesan', + 232: 'Rice Pudding', + 233: 'Ice Cream', + 234: 'Blueberry Tart', + 235: 'Autumn\'s Bounty', + 236: 'Pumpkin Soup', + 237: 'Super Meal', + 238: 'Cranberry Sauce', + 239: 'Stuffing', + 240: 'Farmer\'s Lunch', + 241: 'Survival Burger', + 242: 'Dish O\' The Sea', + 243: 'Miner\'s Treat', + 244: 'Roots Platter', + 248: 'Garlic', + 250: 'Kale', + 251: 'Tea Sapling', + 252: 'Rhubarb', + 253: 'Triple Shot Espresso', + 254: 'Melon', + 256: 'Tomato', + 257: 'Morel', + 258: 'Blueberry', + 259: 'Fiddlehead Fern', + 260: 'Hot Pepper', + 262: 'Wheat', + 264: 'Radish', + 265: 'Seafoam Pudding', + 266: 'Red Cabbage', + 267: 'Flounder', + 268: 'Starfruit', + 269: 'Midnight Carp', + 270: 'Corn', + 271: 'Unmilled Rice', + 272: 'Eggplant', + 273: 'Rice Shoot', + 274: 'Artichoke', + 276: 'Pumpkin', + 278: 'Bok Choy', + 280: 'Yam', + 281: 'Chanterelle', + 282: 'Cranberries', + 283: 'Holly', + 284: 'Beet', + 286: 'Cherry Bomb', + 287: 'Bomb', + 288: 'Mega Bomb', + 293: 'Brick Floor', + 296: 'Salmonberry', + 298: 'Hardwood Fence', + 299: 'Amaranth Seeds', + 300: 'Amaranth', + 301: 'Grape Starter', + 302: 'Hops Starter', + 303: 'Pale Ale', + 304: 'Hops', + 306: 'Mayonnaise', + 307: 'Duck Mayonnaise', + 309: 'Acorn', + 310: 'Maple Seed', + 311: 'Pine Cone', + 322: 'Wood Fence', + 323: 'Stone Fence', + 324: 'Iron Fence', + 325: 'Gate', + 328: 'Wood Floor', + 329: 'Stone Floor', + 330: 'Clay', + 331: 'Weathered Floor', + 333: 'Crystal Floor', + 334: 'Copper Bar', + 335: 'Iron Bar', + 336: 'Gold Bar', + 337: 'Iridium Bar', + 338: 'Refined Quartz', + 340: 'Honey', + 342: 'Pickles', + 344: 'Jelly', + 346: 'Beer', + 347: 'Rare Seed', + 348: 'Wine', + 350: 'Juice', + 368: 'Basic Fertilizer', + 369: 'Quality Fertilizer', + 370: 'Basic Retaining Soil', + 371: 'Quality Retaining Soil', + 372: 'Clam', + 376: 'Poppy', + 378: 'Copper Ore', + 380: 'Iron Ore', + 382: 'Coal', + 384: 'Gold Ore', + 386: 'Iridium Ore', + 388: 'Wood', + 390: 'Stone', + 392: 'Nautilus Shell', + 393: 'Coral', + 394: 'Rainbow Shell', + 396: 'Spice Berry', + 397: 'Sea Urchin', + 398: 'Grape', + 399: 'Spring Onion', + 400: 'Strawberry', + 401: 'Straw Floor', + 402: 'Sweet Pea', + 404: 'Common Mushroom', + 405: 'Wood Path', + 406: 'Wild Plum', + 407: 'Gravel Path', + 408: 'Hazelnut', + 409: 'Crystal Path', + 410: 'Blackberry', + 411: 'Cobblestone Path', + 412: 'Winter Root', + 414: 'Crystal Fruit', + 415: 'Stepping Stone Path', + 416: 'Snow Yam', + 418: 'Crocus', + 420: 'Red Mushroom', + 421: 'Sunflower', + 422: 'Purple Mushroom', + 424: 'Cheese', + 425: 'Fairy Seeds', + 426: 'Goat Cheese', + 427: 'Tulip Bulb', + 428: 'Cloth', + 429: 'Jazz Seeds', + 430: 'Truffle', + 431: 'Sunflower Seeds', + 432: 'Truffle Oil', + 433: 'Coffee Bean', + 436: 'Goat Milk', + 438: 'L. Goat Milk', + 440: 'Wool', + 442: 'Duck Egg', + 444: 'Duck Feather', + 445: 'Caviar', + 446: 'Rabbit\'s Foot', + 453: 'Poppy Seeds', + 455: 'Spangle Seeds', + 456: 'Algae Soup', + 457: 'Pale Broth', + 459: 'Mead', + 465: 'Speed-Gro', + 466: 'Deluxe Speed-Gro', + 472: 'Parsnip Seeds', + 473: 'Bean Starter', + 474: 'Cauliflower Seeds', + 475: 'Potato Seeds', + 476: 'Garlic Seeds', + 477: 'Kale Seeds', + 478: 'Rhubarb Seeds', + 479: 'Melon Seeds', + 480: 'Tomato Seeds', + 481: 'Blueberry Seeds', + 482: 'Pepper Seeds', + 483: 'Wheat Seeds', + 484: 'Radish Seeds', + 485: 'Red Cabbage Seeds', + 486: 'Starfruit Seeds', + 487: 'Corn Seeds', + 488: 'Eggplant Seeds', + 489: 'Artichoke Seeds', + 490: 'Pumpkin Seeds', + 491: 'Bok Choy Seeds', + 492: 'Yam Seeds', + 493: 'Cranberry Seeds', + 494: 'Beet Seeds', + 495: 'Spring Seeds', + 496: 'Summer Seeds', + 497: 'Fall Seeds', + 498: 'Winter Seeds', + 499: 'Ancient Seeds', + 591: 'Tulip', + 593: 'Summer Spangle', + 595: 'Fairy Rose', + 597: 'Blue Jazz', + 599: 'Sprinkler', + 604: 'Plum Pudding', + 605: 'Artichoke Dip', + 606: 'Stir Fry', + 607: 'Roasted Hazelnuts', + 608: 'Pumpkin Pie', + 609: 'Radish Salad', + 610: 'Fruit Salad', + 611: 'Blackberry Cobbler', + 612: 'Cranberry Candy', + 613: 'Apple', + 614: 'Green Tea', + 618: 'Bruschetta', + 621: 'Quality Sprinkler', + 628: 'Cherry Sapling', + 629: 'Apricot Sapling', + 630: 'Orange Sapling', + 631: 'Peach Sapling', + 632: 'Pomegranate Sapling', + 633: 'Apple Sapling', + 634: 'Apricot', + 635: 'Orange', + 636: 'Peach', + 637: 'Pomegranate', + 638: 'Cherry', + 648: 'Coleslaw', + 649: 'Fiddlehead Risotto', + 651: 'Poppyseed Muffin', + 684: 'Bug Meat', + 685: 'Bait', + 686: 'Spinner', + 687: 'Dressed Spinner', + 691: 'Barbed Hook', + 692: 'Lead Bobber', + 693: 'Treasure Hunter', + 694: 'Trap Bobber', + 695: 'Cork Bobber', + 698: 'Sturgeon', + 699: 'Tiger Trout', + 700: 'Bullhead', + 701: 'Tilapia', + 702: 'Chub', + 703: 'Magnet', + 704: 'Dorado', + 705: 'Albacore', + 706: 'Shad', + 707: 'Lingcod', + 708: 'Halibut', + 709: 'Hardwood', + 715: 'Lobster', + 716: 'Crayfish', + 717: 'Crab', + 718: 'Cockle', + 719: 'Mussel', + 720: 'Shrimp', + 721: 'Snail', + 722: 'Periwinkle', + 723: 'Oyster', + 724: 'Maple Syrup', + 725: 'Oak Resin', + 726: 'Pine Tar', + 727: 'Chowder', + 728: 'Fish Stew', + 729: 'Escargot', + 730: 'Lobster Bisque', + 731: 'Maple Bar', + 732: 'Crab Cakes', + 733: 'Shrimp Cocktail', + 734: 'Woodskip', + 766: 'Slime', + 767: 'Bat Wing', + 768: 'Solar Essence', + 769: 'Void Essence', + 771: 'Fiber', + 772: 'Oil of Garlic', + 773: 'Life Elixir', + 787: 'Battery Pack', + }; + save.cartPrices_1_4 = { + 'Acorn': 20, + 'Albacore': 75, + 'Algae Soup': 100, + 'Amaranth': 150, + 'Amaranth Seeds': 35, + 'Anchovy': 30, + 'Ancient Seeds': 30, + 'Apple': 100, + 'Apple Sapling': 1000, + 'Apricot': 50, + 'Apricot Sapling': 500, + 'Artichoke': 160, + 'Artichoke Dip': 210, + 'Artichoke Seeds': 15, + 'Autumn\'s Bounty': 350, + 'Bait': 1, + 'Baked Fish': 100, + 'Barbed Hook': 500, + 'Basic Fertilizer': 2, + 'Basic Retaining Soil': 4, + 'Bat Wing': 15, + 'Battery Pack': 500, + 'Bean Hotpot': 100, + 'Bean Starter': 30, + 'Beer': 200, + 'Beet': 100, + 'Beet Seeds': 10, + 'Blackberry': 20, + 'Blackberry Cobbler': 260, + 'Blue Jazz': 50, + 'Blueberry': 50, + 'Blueberry Seeds': 40, + 'Blueberry Tart': 150, + 'Bok Choy': 80, + 'Bok Choy Seeds': 25, + 'Bomb': 50, + 'Bread': 60, + 'Bream': 45, + 'Brick Floor': 1, + 'Bruschetta': 210, + 'Bug Meat': 8, + 'Bullhead': 75, + 'Cactus Fruit': 75, + 'Carp': 30, + 'Carp Surprise': 150, + 'Catfish': 200, + 'Cauliflower': 175, + 'Cauliflower Seeds': 40, + 'Cave Carrot': 25, + 'Caviar': 500, + 'Chanterelle': 160, + 'Cheese': 230, + 'Cheese Cauliflower': 300, + 'Cherry': 80, + 'Cherry Bomb': 50, + 'Cherry Sapling': 850, + 'Chocolate Cake': 200, + 'Chowder': 135, + 'Chub': 50, + 'Clam': 50, + 'Clay': 20, + 'Cloth': 470, + 'Coal': 15, + 'Cobblestone Path': 1, + 'Cockle': 50, + 'Coconut': 100, + 'Coffee Bean': 15, + 'Coleslaw': 345, + 'Common Mushroom': 40, + 'Complete Breakfast': 350, + 'Cookie': 140, + 'Copper Bar': 60, + 'Copper Ore': 5, + 'Coral': 80, + 'Cork Bobber': 250, + 'Corn': 50, + 'Corn Seeds': 75, + 'Crab': 100, + 'Crab Cakes': 275, + 'Cranberries': 75, + 'Cranberry Candy': 175, + 'Cranberry Sauce': 120, + 'Cranberry Seeds': 120, + 'Crayfish': 75, + 'Crispy Bass': 150, + 'Crocus': 60, + 'Crystal Floor': 1, + 'Crystal Fruit': 150, + 'Crystal Path': 1, + 'Daffodil': 30, + 'Dandelion': 40, + 'Deluxe Speed-Gro': 40, + 'Dish O\' The Sea': 220, + 'Dorado': 100, + 'Dressed Spinner': 500, + 'Duck Egg': 95, + 'Duck Feather': 125, + 'Duck Mayonnaise': 375, + 'Eel': 85, + 'Egg (Brown)': 50, + 'Eggplant': 60, + 'Eggplant Parmesan': 200, + 'Eggplant Seeds': 10, + 'Escargot': 125, + 'Fairy Rose': 290, + 'Fairy Seeds': 100, + 'Fall Seeds': 45, + 'Farmer\'s Lunch': 150, + 'Fiber': 1, + 'Fiddlehead Fern': 90, + 'Fiddlehead Risotto': 350, + 'Fish Stew': 175, + 'Fish Taco': 500, + 'Flounder': 100, + 'Fried Calamari': 150, + 'Fried Eel': 120, + 'Fried Egg': 35, + 'Fried Mushroom': 200, + 'Fruit Salad': 450, + 'Garlic': 60, + 'Garlic Seeds': 20, + 'Gate': 4, + 'Ghostfish': 45, + 'Glazed Yams': 200, + 'Goat Cheese': 400, + 'Goat Milk': 225, + 'Gold Bar': 250, + 'Gold Ore': 25, + 'Grape': 80, + 'Grape Starter': 30, + 'Gravel Path': 1, + 'Green Bean': 40, + 'Green Tea': 100, + 'Halibut': 80, + 'Hardwood': 15, + 'Hardwood Fence': 10, + 'Hashbrowns': 120, + 'Hazelnut': 90, + 'Herring': 30, + 'Holly': 80, + 'Honey': 100, + 'Hops': 25, + 'Hops Starter': 30, + 'Hot Pepper': 40, + 'Ice Cream': 120, + 'Iridium Bar': 1000, + 'Iridium Ore': 100, + 'Iron Bar': 120, + 'Iron Fence': 6, + 'Iron Ore': 10, + 'Jazz Seeds': 15, + 'Jelly': 160, + 'Joja Cola': 25, + 'Juice': 150, + 'Kale': 110, + 'Kale Seeds': 35, + 'L. Goat Milk': 345, + 'Large Egg (Brown)': 95, + 'Large Egg (White)': 95, + 'Large Milk': 190, + 'Largemouth Bass': 100, + 'Lead Bobber': 150, + 'Leek': 60, + 'Life Elixir': 500, + 'Lingcod': 120, + 'Lobster': 120, + 'Lobster Bisque': 205, + 'Lucky Lunch': 250, + 'Magnet': 15, + 'Maki Roll': 220, + 'Maple Bar': 300, + 'Maple Seed': 5, + 'Maple Syrup': 200, + 'Mayonnaise': 190, + 'Mead': 200, + 'Mega Bomb': 50, + 'Melon': 250, + 'Melon Seeds': 40, + 'Midnight Carp': 150, + 'Milk': 125, + 'Miner\'s Treat': 200, + 'Morel': 150, + 'Mussel': 30, + 'Nautilus Shell': 120, + 'Oak Resin': 150, + 'Octopus': 150, + 'Oil of Garlic': 1000, + 'Omelet': 125, + 'Orange': 100, + 'Orange Sapling': 1000, + 'Oyster': 40, + 'Pale Ale': 300, + 'Pale Broth': 150, + 'Pancakes': 80, + 'Parsnip': 35, + 'Parsnip Seeds': 10, + 'Parsnip Soup': 120, + 'Peach': 140, + 'Peach Sapling': 1500, + 'Pepper Poppers': 200, + 'Pepper Seeds': 20, + 'Perch': 55, + 'Periwinkle': 20, + 'Pickles': 100, + 'Pike': 100, + 'Pine Cone': 5, + 'Pine Tar': 100, + 'Pink Cake': 480, + 'Pizza': 300, + 'Plum Pudding': 260, + 'Pomegranate': 140, + 'Pomegranate Sapling': 1500, + 'Poppy': 140, + 'Poppy Seeds': 50, + 'Poppyseed Muffin': 250, + 'Potato': 80, + 'Potato Seeds': 25, + 'Pufferfish': 200, + 'Pumpkin': 320, + 'Pumpkin Pie': 385, + 'Pumpkin Seeds': 50, + 'Pumpkin Soup': 300, + 'Purple Mushroom': 250, + 'Quality Fertilizer': 10, + 'Quality Retaining Soil': 5, + 'Quality Sprinkler': 450, + 'Rabbit\'s Foot': 565, + 'Radish': 90, + 'Radish Salad': 300, + 'Radish Seeds': 20, + 'Rainbow Shell': 300, + 'Rainbow Trout': 65, + 'Rare Seed': 200, + 'Red Cabbage': 260, + 'Red Cabbage Seeds': 50, + 'Red Mullet': 75, + 'Red Mushroom': 75, + 'Red Plate': 400, + 'Red Snapper': 50, + 'Refined Quartz': 50, + 'Rhubarb': 220, + 'Rhubarb Pie': 400, + 'Rhubarb Seeds': 50, + 'Rice Pudding': 260, + 'Rice Shoot': 20, + 'Roasted Hazelnuts': 270, + 'Roots Platter': 100, + 'Salad': 110, + 'Salmon': 75, + 'Salmon Dinner': 300, + 'Salmonberry': 5, + 'Sandfish': 75, + 'Sap': 2, + 'Sardine': 40, + 'Sashimi': 75, + 'Scorpion Carp': 150, + 'Sea Cucumber': 75, + 'Sea Urchin': 160, + 'Seafoam Pudding': 300, + 'Shad': 60, + 'Shrimp': 60, + 'Shrimp Cocktail': 160, + 'Slime': 5, + 'Smallmouth Bass': 50, + 'Snail': 65, + 'Snow Yam': 100, + 'Solar Essence': 40, + 'Spaghetti': 120, + 'Spangle Seeds': 25, + 'Speed-Gro': 20, + 'Spice Berry': 80, + 'Spicy Eel': 175, + 'Spinner': 250, + 'Spring Onion': 8, + 'Spring Seeds': 35, + 'Sprinkler': 100, + 'Squid': 80, + 'Starfruit': 750, + 'Starfruit Seeds': 200, + 'Stepping Stone Path': 1, + 'Stir Fry': 335, + 'Stone': 2, + 'Stone Fence': 2, + 'Stone Floor': 1, + 'Strange Bun': 225, + 'Straw Floor': 1, + 'Strawberry': 120, + 'Stuffing': 165, + 'Sturgeon': 200, + 'Summer Seeds': 55, + 'Summer Spangle': 90, + 'Sunfish': 30, + 'Sunflower': 80, + 'Sunflower Seeds': 20, + 'Super Cucumber': 250, + 'Super Meal': 220, + 'Survival Burger': 180, + 'Sweet Pea': 50, + 'Tea Sapling': 500, + 'Tiger Trout': 150, + 'Tilapia': 75, + 'Tom Kha Soup': 250, + 'Tomato': 60, + 'Tomato Seeds': 25, + 'Tortilla': 50, + 'Trap Bobber': 200, + 'Treasure Hunter': 250, + 'Triple Shot Espresso': 450, + 'Trout Soup': 100, + 'Truffle': 625, + 'Truffle Oil': 1065, + 'Tulip': 30, + 'Tulip Bulb': 10, + 'Tuna': 100, + 'Unmilled Rice': 30, + 'Vegetable Medley': 120, + 'Void Essence': 50, + 'Walleye': 105, + 'Weathered Floor': 1, + 'Wheat': 25, + 'Wheat Seeds': 5, + 'Wild Horseradish': 50, + 'Wild Plum': 80, + 'Wine': 400, + 'Winter Root': 70, + 'Winter Seeds': 30, + 'Wood': 2, + 'Wood Fence': 1, + 'Wood Floor': 1, + 'Wood Path': 1, + 'Woodskip': 75, + 'Wool': 340, + 'Yam': 160, + 'Yam Seeds': 30, + }; save.cartFurniture = { 0: "Oak Chair", 3: "Walnut Chair", @@ -1310,17 +1983,41 @@ window.onload = function () { 536: [541, 544, 545, 546, 550, 551, 559, 560, 561, 564, 567, 572, 573, 577, 123], 537: [539, 540, 543, 547, 553, 554, 562, 563, 565, 570, 575, 578, 122], 749: [538, 542, 548, 549, 552, 555, 556, 557, 558, 566, 568, 569, 571, 574, 576, 541, 544, 545, 546, 550, 551, 559, - 560, 561, 564, 567, 572, 573, 577, 539, 540, 543, 547, 553, 554, 562, 563, 565, 570, 575, 578, 121, 122, 123] + 560, 561, 564, 567, 572, 573, 577, 539, 540, 543, 547, 553, 554, 562, 563, 565, 570, 575, 578, 121, 122, 123], + 275: [100, 101, 103, 104, 105, 106, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 166, 373, 797] }; save.minerals = { 74: 'Prismatic Shard', 82: 'Fire Quartz', 84: 'Frozen Tear', 86: 'Earth Crystal', + 100: 'Chipped Amphora', + 101: 'Arrowhead', + 103: 'Ancient Doll', + 104: 'Elvish Jewelry', + 105: 'Chewing Stick', + 106: 'Ornamental Fan', + 108: 'Rare Disc', + 109: 'Ancient Sword', + 110: 'Rusty Spoon', + 111: 'Rusty Spur', + 112: 'Rusty Cog', + 113: 'Chicken Statue', + 114: 'Ancient Seed', + 115: 'Prehistoric Tool', + 116: 'Dried Starfish', + 117: 'Anchor', + 118: 'Glass Shards', + 119: 'Bone Flute', + 120: 'Prehistoric Handaxe', 121: 'Dwarvish Helm', 122: 'Dwarf Gadget', 123: 'Ancient Drum', + 124: 'Golden Mask', + 125: 'Golden Relic', + 166: 'Treasure Chest', 330: 'Clay', + 373: 'Golden Pumpkin', 378: 'Copper Ore', 380: 'Iron Ore', 382: 'Coal', @@ -1367,7 +2064,8 @@ window.onload = function () { 575: 'Obsidian', 576: 'Slate', 577: 'Fairy Stone', - 578: 'Star Shards' + 578: 'Star Shards', + 797: 'Pearl', }; save.wallpaperEquiv = { 16: 'Wild Horseradish', @@ -1411,7 +2109,136 @@ window.onload = function () { 110: 'Rusty Spoon', 111: 'Rusty Spur', }; - + save.shirtItems = { + 1000: 'Classic Overalls (ID 1)', + 1001: 'Shirt (ID 2)', + 1002: 'Mint Blouse (ID 3)', + 1003: 'Dark Shirt (ID 4)', + 1004: 'Skull Shirt (ID 5)', + 1005: 'Light Blue Shirt (ID 6)', + 1006: 'Tan Striped Shirt (ID 7)', + 1007: 'Green Overalls (ID 8)', + 1008: 'Good Grief Shirt (ID 9)', + 1009: 'Aquamarine Shirt (ID 10)', + 1010: 'Suit Top (ID 11)', + 1011: 'Green Belted Shirt (ID 12)', + 1012: 'Lime Green Striped Shirt (ID 13)', + 1013: 'Red Striped Shirt (ID 14)', + 1014: 'Skeleton Shirt (ID 15)', + 1015: 'Orange Shirt (ID 16)', + 1016: 'Night Sky Shirt (ID 17)', + 1017: 'Mayoral Suspenders (ID 18)', + 1018: 'Brown Jacket (ID 19)', + 1019: 'Sailor Shirt (ID 20)', + 1020: 'Green Vest (ID 21)', + 1021: 'Yellow and Green Shirt (ID 22)', + 1022: 'Shirt (ID 23)', + 1023: 'Shirt (ID 24)', + 1024: 'Shirt (ID 25)', + 1025: 'Shirt (ID 26)', + 1026: 'Light Blue Striped Shirt (ID 27)', + 1027: 'Pink Striped Shirt (ID 28)', + 1028: 'Heart Shirt (ID 29)', + 1029: 'Work Shirt (ID 30)', + 1030: 'Store Owner\'s Jacket (ID 31)', + 1031: 'Shirt (ID 32)', + 1032: 'Shirt (ID 33)', + 1033: 'Shirt (ID 34)', + 1034: 'Green Tunic (ID 35)', + 1035: 'Fancy Red Blouse (ID 36)', + 1036: 'Shirt (ID 37)', + 1037: 'Shirt (ID 38)', + 1038: 'Plain Shirt (ID 39)', + 1039: 'Retro Rainbow Shirt (ID 40)', + 1040: 'Shirt (ID 41)', + 1041: 'Shirt (ID 42)', + 1042: 'Lime Green Tunic (ID 43)', + 1043: 'Shirt (ID 44)', + 1044: 'Shirt (ID 45)', + 1045: 'Shirt (ID 46)', + 1046: 'Shirt (ID 47)', + 1047: 'Shirt (ID 48)', + 1048: 'Shirt (ID 49)', + 1049: 'Shirt (ID 50)', + 1050: 'Shirt (ID 51)', + 1051: 'Shirt (ID 52)', + 1052: 'Shirt (ID 53)', + 1053: 'Shirt (ID 54)', + 1054: 'Shirt (ID 55)', + 1055: 'Shirt (ID 56)', + 1056: 'Shirt (ID 57)', + 1057: 'Shirt (ID 58)', + 1058: 'Shirt (ID 59)', + 1059: 'Shirt (ID 60)', + 1060: 'Shirt (ID 61)', + 1061: 'Shirt (ID 62)', + 1062: 'Shirt (ID 63)', + 1063: 'Shirt (ID 64)', + 1064: 'Shirt (ID 65)', + 1065: 'Shirt (ID 66)', + 1066: 'Shirt (ID 67)', + 1067: 'Shirt (ID 68)', + 1068: 'Shirt (ID 69)', + 1069: 'Shirt (ID 70)', + 1070: 'Shirt (ID 71)', + 1071: 'White Overalls Shirt (ID 72)', + 1072: 'Shirt (ID 73)', + 1073: 'Shirt (ID 74)', + 1074: 'Shirt (ID 75)', + 1075: 'Shirt (ID 76)', + 1076: 'Shirt (ID 77)', + 1077: 'Shirt (ID 78)', + 1078: 'Shirt (ID 79)', + 1079: 'Shirt (ID 80)', + 1080: 'Shirt (ID 81)', + 1081: 'Shirt (ID 82)', + 1082: 'Shirt (ID 83)', + 1083: 'Shirt (ID 84)', + 1084: 'Shirt (ID 85)', + 1085: 'Shirt (ID 86)', + 1086: 'Shirt (ID 87)', + 1087: 'Neat Bow Shirt (ID 88)', + 1088: 'Shirt (ID 89)', + 1089: 'Shirt (ID 90)', + 1090: 'Shirt (ID 91)', + 1091: 'Shirt (ID 92)', + 1092: 'Shirt (ID 93)', + 1093: 'Shirt (ID 94)', + 1094: 'Shirt (ID 95)', + 1095: 'Shirt (ID 96)', + 1096: 'Shirt (ID 97)', + 1097: 'Shirt (ID 98)', + 1098: 'Shirt (ID 99)', + 1099: 'Shirt (ID 100)', + 1100: 'Shirt (ID 101)', + 1101: 'Shirt (ID 102)', + 1102: 'Shirt (ID 103)', + 1103: 'Shirt (ID 104)', + 1104: 'Shirt (ID 105)', + 1105: 'Shirt (ID 106)', + 1106: 'Shirt (ID 107)', + 1107: 'Shirt (ID 108)', + 1108: 'Shirt (ID 109)', + 1109: 'Shirt (ID 110)', + 1110: 'Shirt (ID 111)', + 1111: 'Shirt (ID 112)', + 1112: 'Shirt (ID 113)', + 1113: 'Shirt (ID 114)', + 1114: 'Shirt (ID 115)', + 1115: 'Shirt (ID 116)', + 1116: 'Shirt (ID 117)', + 1117: 'Shirt (ID 118)', + 1118: 'Shirt (ID 119)', + 1119: 'Shirt (ID 120)', + 1120: 'Shirt (ID 121)', + 1121: 'Shirt (ID 122)', + 1122: 'Shirt (ID 123)', + 1123: 'Shirt And Tie (ID 124)', + 1124: 'Shirt (ID 125)', + 1125: 'Shirt (ID 126)', + 1126: 'Shirt (ID 127)', + }; + // Show input field immediately $(document.getElementById('input-container')).show(); @@ -1438,7 +2265,7 @@ window.onload = function () { function parseSummary(xmlDoc) { var output = '', - farmTypes = ['Standard', 'Riverland', 'Forest', 'Hill-top', 'Wilderness']; + farmTypes = ['Standard', 'Riverland', 'Forest', 'Hill-top', 'Wilderness', 'NEW ONE']; // This app doesn't actually need a whole lot from the save file, and can be run from just the gameID number. // Right now, that functionality is "secret" and accessed by adding "?id=123456789" (or similar) to the URL. // As a result, this is the only function that actually reads anything from the save file; it will store the @@ -1452,6 +2279,9 @@ window.onload = function () { 'Iron Ore': 1, 'Gold Ore': 1, 'Iridium Ore': 1, + 'Golden Pumpkin': 1, + 'Treasure Chest': 1, + 'Pearl': 1, }; // Large multiplayer games will sometimes get out of synch between the actual number of days played and the current date // The URL parameter 'days' lets this offset be defined. @@ -1462,9 +2292,15 @@ window.onload = function () { if (typeof xmlDoc !== 'undefined') { save.gameID = Number($(xmlDoc).find('uniqueIDForThisGame').text()); output += 'Game ID: ' + save.gameID + '
\n'; - save.is1_3 = ($(xmlDoc).find('hasApplied1_3_UpdateChanges').text() === 'true'); - output += 'Save is ' + (save.is1_3 ? '' : 'not ') + ' from version 1.3 or later
\n'; - if (save.is1_3) { + save.version = 1.2; + if ($(xmlDoc).find('hasApplied1_4_UpdateChanges').text() === 'true') { + save.version = 1.4; + } else if ($(xmlDoc).find('hasApplied1_3_UpdateChanges').text() === 'true') { + save.version = 1.3; + } + + output += 'Save is from version ' + save.version + '
\n'; + if (save.version >= 1.3) { $('#cart-title').html('Traveling Merchant Cart and Night Market Boat'); } else { $('#cart-title').html('Traveling Merchant Cart'); @@ -1480,7 +2316,7 @@ window.onload = function () { $(xmlDoc).find('SaveGame > player > farmName').html() + ' Farm (' + farmTypes[$(xmlDoc).find('whichFarm').text()] + ')
\n'; // In 1.2, stats are under SaveGame, but in 1.3 they are under SaveGame > player and the farmhand elements. - if (save.is1_3) { + if (save.version >= 1.3) { save.mp_ids.push($(xmlDoc).find('SaveGame > player > UniqueMultiplayerID').text()); save.geodesCracked.push(Number($(xmlDoc).find('SaveGame > player > stats > geodesCracked').text())); $(xmlDoc).find('farmhand').each(function(i) { @@ -1522,7 +2358,7 @@ window.onload = function () { }); // Need to know if the baby question is possible. For now, only doing for 1.2 save.canHaveChildren = false; - if (!save.is1_3) { + if (save.version < 1.3) { var spouse = $(xmlDoc).find('player > spouse').text(); var child_count = 0; if (typeof(spouse) !== 'undefined' && spouse !== '') { @@ -1538,7 +2374,14 @@ window.onload = function () { } } } - + // Check if Quarry is unlocked for mine level predictions. + save.quarryUnlocked = false; + $(xmlDoc).find('player > mailReceived > string').each(function () { + var id = $(this).text(); + if (id === 'ccCraftsRoom') { + save.quarryUnlocked = true; + } + }); } else if ($.QueryString.hasOwnProperty("id")) { save.gameID = parseInt($.QueryString.id); save.daysPlayed = 1; @@ -1546,10 +2389,11 @@ window.onload = function () { save.geodesCracked = [0]; save.deepestMineLevel = 0; save.canHaveChildren = false; - save.is1_3 = true; + save.quarryUnlocked = false; + save.version = 1.4; output += 'App run using supplied gameID ' + save.gameID + '.
' + 'No save information available so minimal progress assumed.
' + - 'Version 1.3 features will be included where possible.
\n'; + 'Newest version features will be included where possible.
\n'; } else { return 'Fatal Error: Problem reading save file and no ID passed via query string.'; } @@ -1584,6 +2428,8 @@ window.onload = function () { rainbowLights, infestedMonster, infestedSlime, + quarryLevel, + infestedQuarryLevel, mineLevel, day, weekDay, @@ -1621,59 +2467,129 @@ window.onload = function () { rainbowLights = []; infestedMonster = []; infestedSlime = []; + quarryLevel = []; day = 7 * week + weekDay + offset; - for (mineLevel = 1; mineLevel < 120; mineLevel++) { - if (mineLevel % 5 === 0) { - // skip elevator floors for everything - continue; - } - // Monster infestation seems to override mushroom spawns so that is checked first - rng = new CSRandom(day + save.dayAdjust + mineLevel + save.gameID / 2); - if (mineLevel % 40 > 5 && mineLevel % 40 < 30 && mineLevel % 40 !== 19) { - if (rng.NextDouble() < 0.05) { - if (rng.NextDouble() < 0.5) { - infestedMonster.push(mineLevel); - } else { - infestedSlime.push(mineLevel); + // This is unlike other pages because there is no search capability. Instead, we just have separate logic + // based on different versions since RNG seeding was changed in 1.4 + if (save.version <= 1.3) { + for (mineLevel = 1; mineLevel < 120; mineLevel++) { + if (mineLevel % 5 === 0) { + // skip elevator floors for everything + continue; + } + // Monster infestation seems to override mushroom spawns so that is checked first + rng = new CSRandom(day + save.dayAdjust + mineLevel + save.gameID / 2); + if (mineLevel % 40 > 5 && mineLevel % 40 < 30 && mineLevel % 40 !== 19) { + if (rng.NextDouble() < 0.044) { + if (rng.NextDouble() < 0.5) { + infestedMonster.push(mineLevel); + } else { + infestedSlime.push(mineLevel); + } + continue; // Skip mushroom checks } - continue; // skips Mushroom check + } + // Reset the seed for checking Mushrooms. Note, there are a couple checks related to + // darker than normal lighting. We don't care about the results but need to mimic them. + rng = new CSRandom(day + save.dayAdjust + mineLevel + save.gameID / 2); + if (rng.NextDouble() < 0.3 && mineLevel > 2) { + rng.NextDouble(); // checked vs < 0.3 again + } + rng.NextDouble(); // checked vs < 0.15 + if (rng.NextDouble() < 0.035 && mineLevel > 80) { + rainbowLights.push(mineLevel); } } - // Reset the seed for checking Mushrooms. Note, there are a couple checks related to - // darker than normal lighting. We don't care about the results but need to mimic them. - rng = new CSRandom(day + save.dayAdjust + mineLevel + save.gameID / 2); - if (rng.NextDouble() < 0.3 && mineLevel > 2) { - rng.NextDouble(); // checked vs < 0.3 again + if (day < save.daysPlayed) { + tclass = "past"; + } else if (day === save.daysPlayed) { + tclass = "current"; + } else { + tclass = "future"; } + } else { + for (mineLevel = 1; mineLevel < 120; mineLevel++) { + var skipMushroomCheck = false; + if (mineLevel % 5 === 0) { + // skip elevator floors for everything + continue; + } + // Monster infestation seems to override mushroom spawns so that is checked first + rng = new CSRandom(day + save.dayAdjust + mineLevel*100 + save.gameID / 2); + if (mineLevel % 40 > 5 && mineLevel % 40 < 30 && mineLevel % 40 !== 19) { + if (rng.NextDouble() < 0.044) { + if (rng.NextDouble() < 0.5) { + infestedMonster.push(mineLevel); + } else { + infestedSlime.push(mineLevel); + } + skipMushroomCheck = true; + } else if (save.quarryUnlocked) { + if (mineLevel % 40 > 1 && rng.NextDouble() < 0.044) { + if (rng.NextDouble() < 0.25) { + quarryLevel.push(mineLevel + '*'); + } else { + quarryLevel.push(mineLevel); + } + skipMushroomCheck = true; + } + } + } + if (skipMushroomCheck) { + continue; + } + // Reset the seed for checking Mushrooms. Note, there are a couple checks related to + // darker than normal lighting. We don't care about the results but need to mimic them. + rng = new CSRandom((day + save.dayAdjust) * mineLevel + (4 * mineLevel) + save.gameID / 2); + if (rng.NextDouble() < 0.3 && mineLevel > 2) { + rng.NextDouble(); // checked vs < 0.3 again + } rng.NextDouble(); // checked vs < 0.15 - if (rng.NextDouble() < 0.035 && mineLevel > 80) { - rainbowLights.push(mineLevel); + if (rng.NextDouble() < 0.035 && mineLevel > 80) { + rainbowLights.push(mineLevel); + } + } + if (day < save.daysPlayed) { + tclass = "past"; + } else if (day === save.daysPlayed) { + tclass = "current"; + } else { + tclass = "future"; } } - if (day < save.daysPlayed) { - tclass = "past"; - } else if (day === save.daysPlayed) { - tclass = "current"; - } else { - tclass = "future"; - } + var mushroomText = ""; if (rainbowLights.length === 0) { - rainbowLights.push("None"); + mushroomText = 'Mushroom None'; + } else { + mushroomText = 'Mushroom ' + rainbowLights.join(', '); } + var infestedText = ""; if (infestedMonster.length === 0) { - infestedMonster.push("None"); + infestedText = 'Sword None'; + } else { + infestedText = 'Sword ' + infestedMonster.join(', '); } + var slimeText = ""; if (infestedSlime.length === 0) { - infestedSlime.push("None"); + slimeText = 'Slime None'; + } else { + slimeText = 'Slime ' + infestedSlime.join(', '); + } + var quarryText = ""; + if (quarryLevel.length === 0) { + quarryText = 'Skull None'; + } else { + quarryText = 'Skull ' + quarryLevel.join(', '); } output += ' ' + (day - offset) + '
' + - 'Mushroom ' + rainbowLights.join(', ') + - '
Sword ' + infestedMonster.join(', ') + - '
Slime ' + infestedSlime.join(', ') + '
'; + '' + mushroomText + + '
' + infestedText + + '
' + slimeText + + '
' + quarryText + '
'; } output += "\n"; } - output += 'Legend: Mushroom Mushroom Level | Sword Monster Infestation | Slime Slime Infestation'; + output += 'Legend: Mushroom Mushroom Level | Sword Monster Infestation | Slime Slime Infestation | Skull Quarry Level (* = Infested)'; output += "\n"; return output; } @@ -1765,6 +2681,51 @@ window.onload = function () { return output; } + function getCartItem(rng, seenItems) { + /* Helper function for cart prediction that rolls the itemID, price, and quantity and + * (depending on version) makes sure this item does not duplicate something already seen. + * To save on the processing done by this script, the valid item lists were pre-calculated + * and listed as save.cartItems and save.cartItems_1_4; so this function doesn't necessarily + * do very much. + */ + var theItem = {}; + var itemID = rng.Next(2,790); + if (save.version >= 1.4) { + var keepGoing = true; + while (keepGoing) { + itemID++; + itemID %= 790; + if (save.cartItems_1_4.hasOwnProperty(itemID)) { + theItem.name = save.cartItems_1_4[itemID]; + theItem.price = Math.max(rng.Next(1,11)*100, getCartPrice(theItem.name)*rng.Next(3,6)); + theItem.qty = (rng.NextDouble() < 0.1) ? 5 : 1; + if (!(theItem.name in seenItems)) { + seenItems[theItem.name] = 1; + keepGoing = false; + } + } + } + } else { + theItem.name = save.cartItems[itemID]; + theItem.price = Math.max(rng.Next(1,11)*100, getCartPrice(theItem.name)*rng.Next(3,6)); + theItem.qty = (rng.NextDouble() < 0.1) ? 5 : 1; + } + return theItem; + } + /* + if (split[3].Contains('-') && Convert.ToInt32(split[1]) > 0 && !split[3].Contains("-13") && !split[3].Equals("Quest") && !split[0].Equals("Weeds") && !split[3].Contains("Minerals") && !split[3].Contains("Arch") && Utility.addToStock(stock, stockIndices, new Object(index, 1, false, -1, 0), new int[] + { + Math.Max(r.Next(1, 11) * 100, Convert.ToInt32(split[1]) * r.Next(3, 6)), + (r.NextDouble() < 0.1) ? 5 : 1 + })) + */ + function getCartPrice(itemName) { + /* Another helper function for cart prediction which basically just looks up the given itemName + * in the appropriate dictionary for the save version. + */ + return (save.version >= 1.4) ? save.cartPrices_1_4[itemName] : save.cartPrices[itemName]; + } + function predictCart(isSearch, offset) { // logic from StardewValley.Utility.getTravelingMerchantStock() var output = '', @@ -1808,24 +2769,23 @@ window.onload = function () { for (offset = searchStart; offset < searchStart + searchEnd; offset += 7) { // It might make more sense to only bother with the date stuff when matches are found. var days=[5,7]; - if (save.is1_3 && offset % 112 === 98) { + if (save.version >= 1.3 && offset % 112 === 98) { days = [1,2,3,5,7]; } month = Math.floor(offset / 28); monthName = save.seasonNames[month % 4]; year = 1 + Math.floor(offset / 112); for (var i = 0; i < days.length; i++) { + var seenItems = {}; dayOfMonth = offset % 28 + days[i]; dayOfWeek = save.dayNames[days[i]-1]; rngFirst = new CSRandom(save.gameID + offset + days[i] + save.dayAdjust); for (slot = 1; slot <= 10; slot++) { - item = save.cartItems[rngFirst.Next(2,790)]; - price = Math.max(rngFirst.Next(1,11)*100, save.cartPrices[item]*rngFirst.Next(3,6)); - qty = (rngFirst.NextDouble() < 0.1) ? 5 : 1; - if (searchTerm.test(item)) { + item = getCartItem(rngFirst, seenItems); + if (searchTerm.test(item.name)) { count++; output += '' + dayOfWeek + ' ' + monthName + ' ' + dayOfMonth + ', Year ' + year + '' + - wikify(item) + "" + qty + "" + price + "g"; + wikify(item.name) + "" + item.qty + "" + addCommas(item.price) + "g"; } } slot = -1; @@ -1837,7 +2797,7 @@ window.onload = function () { if (searchTerm.test(item)) { count++; output += '' + dayOfWeek + ' ' + monthName + ' ' + dayOfMonth + ', Year ' + year + '' + - wikify(item,'Furniture') + "" + qty + "" + price + "g"; + wikify(item,'Furniture') + "" + qty + "" + addCommas(price) + "g"; } if (month % 4 < 2) { item = 'Rare Seed'; @@ -1855,7 +2815,7 @@ window.onload = function () { if (searchTerm.test(item)) { count++; output += '' + dayOfWeek + ' ' + monthName + ' ' + dayOfMonth + ', Year ' + year + '' + - wikify(item) + "" + qty + "" + price + "g"; + wikify(item) + "" + qty + "" + addCommas(price) + "g"; } if (rngFirst.NextDouble() < 0.25) { item = 'Coffee Bean'; @@ -1876,7 +2836,7 @@ window.onload = function () { if (typeof(offset) === 'undefined' || offset === '') { offset = 7 * Math.floor((save.daysPlayed - 1) / 7); } - if (save.is1_3 && offset % 112 === 94) { + if (save.version >= 1.3 && offset % 112 === 94) { isNightMarket = true; $('#cart-next-week').val(offset + 4); $('#cart-prev-week').val(offset - 3); @@ -1884,11 +2844,11 @@ window.onload = function () { } else { isNightMarket = false; startDay = 4; - if (save.is1_3 && offset % 112 === 91) { + if (save.version >= 1.3 && offset % 112 === 91) { // weekend before night market $('#cart-next-week').val(offset + 3); $('#cart-prev-week').val(offset - 7); - } else if (save.is1_3 && offset % 112 === 98) { + } else if (save.version >= 1.3 && offset % 112 === 98) { // weekend after night market $('#cart-next-week').val(offset + 7); $('#cart-prev-week').val(offset - 4); @@ -1941,25 +2901,22 @@ window.onload = function () { rngMid = new CSRandom(save.gameID + offset + 6 + save.dayAdjust); } rngLast = new CSRandom(save.gameID + offset + 7 + save.dayAdjust); + var seenItemsFirst = {}; + var seenItemsMid = {}; + var seenItemsLast = {}; for (slot = 1; slot <= 10; slot++) { output += "Basic Item " + slot + ""; - item = save.cartItems[rngFirst.Next(2,790)]; - price = Math.max(rngFirst.Next(1,11)*100, save.cartPrices[item]*rngFirst.Next(3,6)); - qty = (rngFirst.NextDouble() < 0.1) ? 5 : 1; - output += '' + wikify(item) + "" + qty + "" + addCommas(price) + "g"; + item = getCartItem(rngFirst, seenItemsFirst); + output += '' + wikify(item.name) + "" + item.qty + "" + addCommas(item.price) + "g"; if (isNightMarket) { - item = save.cartItems[rngMid.Next(2,790)]; - price = Math.max(rngMid.Next(1,11)*100, save.cartPrices[item]*rngMid.Next(3,6)); - qty = (rngMid.NextDouble() < 0.1) ? 5 : 1; - output += '' + wikify(item) + "" + qty + "" + addCommas(price) + "g"; + item = getCartItem(rngMid, seenItemsMid); + output += '' + wikify(item.name) + "" + item.qty + "" + addCommas(item.price) + "g"; } - item = save.cartItems[rngLast.Next(2,790)]; - price = Math.max(rngLast.Next(1,11)*100, save.cartPrices[item]*rngLast.Next(3,6)); - qty = (rngLast.NextDouble() < 0.1) ? 5 : 1; - output += '' + wikify(item) + "" + qty + "" + addCommas(price) + "g"; + item = getCartItem(rngLast, seenItemsLast); + output += '' + wikify(item.name) + "" + item.qty + "" + addCommas(item.price) + "g"; } // Furniture uses StardewValley.Utility.getRandomFurniture() & StardewValley.Utility.isFurnitureOffLimitsForSale() - // Rather than fully emulating both of those functions, we will simply make sure the save.cartFurniture() structure + // Rather than fully emulating both of those functions, we will simply make sure the save.cartFurniture structure // only contains items which are valid for sale. slot = -1; while (!save.cartFurniture.hasOwnProperty(slot)) { @@ -2004,8 +2961,8 @@ window.onload = function () { price = '4000g'; } else { item = '(None)'; - qty = '(N/A)'; - price = '(N/A)'; + qty = '--'; + price = '--'; } output += '' + item + "" + qty + "" + addCommas(price) + ""; if (isNightMarket) { @@ -2015,8 +2972,8 @@ window.onload = function () { price = '4000g'; } else { item = '(None)'; - qty = '(N/A)'; - price = '(N/A)'; + qty = '--'; + price = '--'; } output += '' + item + "" + qty + "" + addCommas(price) + ""; } @@ -2026,8 +2983,8 @@ window.onload = function () { price = '4000g'; } else { item = '(None)'; - qty = '(N/A)'; - price = '(N/A)'; + qty = '--'; + price = '--'; } output += '' + item + "" + qty + "" + addCommas(price) + ""; } @@ -2039,8 +2996,8 @@ window.onload = function () { price = '2500g'; } else { item = '(None)'; - qty = '(N/A)'; - price = '(N/A)'; + qty = '--'; + price = '--'; } output += '' + item + "" + qty + "" + addCommas(price) + ""; if (isNightMarket) { @@ -2050,8 +3007,8 @@ window.onload = function () { price = '2500g'; } else { item = '(None)'; - qty = '(N/A)'; - price = '(N/A)'; + qty = '--'; + price = '--'; } output += '' + item + "" + qty + "" + addCommas(price) + ""; } @@ -2061,8 +3018,8 @@ window.onload = function () { price = '2500g'; } else { item = '(None)'; - qty = '(N/A)'; - price = '(N/A)'; + qty = '--'; + price = '--'; } output += '' + item + "" + qty + "" + addCommas(price) + ""; output += '\n'; @@ -2112,15 +3069,19 @@ window.onload = function () { var days=[3,6]; for (var i = 0; i < days.length; i++) { rngFirst = new CSRandom((save.gameID / 2) + offset + days[i] + save.dayAdjust); + // Note that we are using getCartItem which is a special filtered version of the object list. + // Since getCartItem has a built-in increment, we counter that with the -1 after rolling the + // random number. Luckily for us, neither of the ranges we care about has any disallowed items + // so we can get away with using the cart list for this situation. if (days[i] === 3) { // Wednesday Fish - item = save.cartItems[rngFirst.Next(698,709) - 1]; + item = getCartItem(rngFirst.Next(698,709) - 1, {}); price = 200; } else if (days[i] === 6) { // Saturday Cooking thisRoll = rngFirst.Next(194,245) - 1; if (thisRoll === 216) { thisRoll = 215; } - item = save.cartItems[thisRoll]; + item = getCartItem(thisRoll, {}); price = rngFirst.Next(5,51) * 10; } if (searchTerm.test(item)) { @@ -2191,6 +3152,120 @@ window.onload = function () { return output; } + function predictSandy(isSearch, offset) { + // logic from StardewValley.GameLocation.sandyShopStock() + var output = '', + month, + monthName, + year, + day, + dayOfMonth, + dayOfWeek, + item, + searchTerm, + searchStart, + searchEnd, + count, + rng, + thisRoll, + tclass; + // Hitting search without an actual search term will fall through to the default browse function; we might want + // to add some sort of error message or other feedback. + if (isSearch && typeof(offset) !== 'undefined' && offset !== '') { + $('#sandy-prev-year').prop("disabled", true); + $('#sandy-prev-week').prop("disabled", true); + $('#sandy-next-week').prop("disabled", true); + $('#sandy-next-year').prop("disabled", true); + $('#sandy-reset').html("Clear Search Results & Reset Browsing"); + // Note we are using the regexp matcher due to wanting to ignore case. The table header references offset still + // so that it appears exactly as was typed in by the user. + searchTerm = new RegExp(offset, "i"); + searchStart = ($('#sandy-search-all').prop('checked')) ? 0 : 7 * Math.floor((save.daysPlayed - 1) / 7); + searchEnd = 112 * $('#sandy-search-range').val(); + output += '\n'; + output += '\n'; + count = 0; + // Much of the logic here is duplicated from the browsing section, but comments related to it have been removed. + for (offset = searchStart; offset < searchStart + searchEnd; offset += 7) { + for (var i = 0; i < 7; i++) { + day = offset + i; + rng = new CSRandom((save.gameID / 2) + day + save.dayAdjust); + thisRoll = 1000 + rng.Next(127); + item = save.shirtItems[thisRoll]; + if (searchTerm.test(item)) { + count++; + month = Math.floor(offset / 28); + monthName = save.seasonNames[month % 4]; + year = 1 + Math.floor(offset / 112); + dayOfMonth = offset % 28 + i; + // Hack to fix day name problems + if (i === 0) { i = 7; } + dayOfWeek = save.dayNames[i-1]; + output += '' + + '"; + } + } + } + output += '
Search results for "' + offset + '" over the ' + + (($('#sandy-search-all').prop('checked')) ? 'first ' : 'next ') + $('#sandy-search-range').val() + ' year(s)
DayImageName
' + dayOfWeek + ' ' + monthName + ' ' + dayOfMonth + ', Year ' + year + '' + + '' + item + "
Found ' + count + ' matching item(s)
\n'; + } else { + if (typeof(offset) === 'undefined' || offset === '') { + offset = 7 * Math.floor((save.daysPlayed - 1) / 7); + } + $('#sandy-prev-year').val(offset - 112); + $('#sandy-prev-week').val(offset - 7); + $('#sandy-next-week').val(offset + 7); + $('#sandy-next-year').val(offset + 112); + if (offset < 7) { + $('#sandy-prev-week').prop("disabled", true); + } else { + $('#sandy-prev-week').prop("disabled", false); + } + if (offset < 112) { + $('#sandy-prev-year').prop("disabled", true); + } else { + $('#sandy-prev-year').prop("disabled", false); + } + $('#sandy-reset').val('reset'); + $('#sandy-reset').html("Reset Browsing"); + $('#sandy-next-week').prop("disabled", false); + $('#sandy-next-year').prop("disabled", false); + // Reset search fields too + $('#sandy-search-text').val(''); + $('#sandy-search-range').val(2); + $('#sandy-search-all').prop('checked', false); + month = Math.floor(offset / 28); + monthName = save.seasonNames[month % 4]; + year = 1 + Math.floor(offset / 112); + dayOfMonth = offset % 28; + + output += '' + + ''; + + for (dayOfWeek = 1; dayOfWeek < 8; dayOfWeek++) { + day = dayOfWeek + offset; + rng = new CSRandom((save.gameID / 2) + day + save.dayAdjust); + thisRoll = 1000 + rng.Next(127); + item = save.shirtItems[thisRoll]; + + if (day < save.daysPlayed) { + tclass = "past"; + } else if (day === save.daysPlayed) { + tclass = "current"; + } else { + tclass = "future"; + } + output += '' + + '' + + ''; + } + output += '
DateImageName
' + save.dayNames[(day - 1) % 7] + '
' + + monthName + ' ' + ((day - 1) % 28 + 1) +', Year ' + year + '
' + item + '
\n'; + } + return output; + } + function predictGeodes(isSearch, offset) { // logic from StardewValley.Utility.getTreasureFromGeode() var output = '', @@ -2209,7 +3284,9 @@ window.onload = function () { searchResults, count, pageSize = 20, - rng; + numColumns = (save.version >= 1.4) ? 5 : 4, + rng, + rngTrove; if (isSearch && typeof(offset) !== 'undefined' && offset !== '') { $('#geode-prev-100').prop("disabled", true); @@ -2222,7 +3299,8 @@ window.onload = function () { searchTerm = new RegExp(offset, "i"); searchStart = Math.max(1, ($('#geode-search-all').prop('checked')) ? 1 : save.geodesCracked[0]); searchEnd = parseInt($('#geode-search-range').val()) + searchStart; - output += '
Search results for "' + offset + '" over the ' + + output += '\n'; output += '' + '' + '\n'; + ''; + if (save.version >= 1.4) { + output += ''; + } + output += '\n'; count = 0; searchResults = {}; - console.log('searching from ' + searchStart + ' to ' + searchEnd); + //console.log('searching from ' + searchStart + ' to ' + searchEnd); for (numCracked = searchStart; numCracked < searchEnd; numCracked++) { // Nearly an exact copy of the browsing code within the for loop. We don't use the qty stuff right now, // but I'd rather leave it in place in case that gets added to the search results later. item = ['Stone', 'Stone', 'Stone', 'Stone']; itemQty = [1, 1, 1, 1]; rng = new CSRandom(numCracked + save.gameID / 2); + // Artifact Troves need their own RNG because the way the conditionals are set up means their content roll + // happens at the same time as the rng.NextDouble() < 0.5 check. Unfortunately, that also means we have to + // do all the warmups on both RNGs. + rngTrove = new CSRandom(numCracked + save.gameID / 2); + if (save.version >= 1.4) { + // extending arrays to support artifact troves + item.push('Stone'); + itemQty.push(1); + // 1.4 added a bunch of extra random calls to prime the RNG to counter repeating patterns + var i, j, prewarm_amount2 = rng.Next(1,10); + rngTrove.Next(); + for (j = 0; j < prewarm_amount2; j++) { + rng.NextDouble(); + rngTrove.NextDouble(); + } + prewarm_amount2 = rng.Next(1,10); + rngTrove.Next(); + for (i = 0; i < prewarm_amount2; i++) { + rng.NextDouble(); + rngTrove.NextDouble(); + } + // Might as well just roll the troves now since the separate RNGs mean the order doesn't matter. + item[4] = save.minerals[save.geodeContents[275][Math.floor(rngTrove.NextDouble()*save.geodeContents[275].length)]]; + } if (rng.NextDouble() < 0.5) { qty = rng.Next(3)*2 + 1; if (rng.NextDouble() < 0.1) { qty = 10; } @@ -2268,7 +3375,7 @@ window.onload = function () { itemQty[0] = 1; item[1] = save.minerals[84]; item[2] = save.minerals[82]; - item[3] = save.minerals[(save.is1_3) ? (82 + rng.Next(3) * 2): 82]; + item[3] = save.minerals[(save.version >= 1.3) ? (82 + rng.Next(3) * 2): 82]; } } else { next = rng.NextDouble(); @@ -2339,10 +3446,13 @@ window.onload = function () { item[3] = save.minerals[save.geodeContents[749][Math.floor(next*save.geodeContents[749].length)]]; } } - for (c = 0; c < 4; c++) { + for (c = 0; c < numColumns; c++) { if (searchTerm.test(item[c])) { if (!searchResults.hasOwnProperty(item[c])) { searchResults[item[c]] = [ [], [], [], [] ]; + if (save.version >= 1.4) { + searchResults[item[c]].push([]); + } } searchResults[item[c]][c].push(numCracked); count++; @@ -2356,9 +3466,9 @@ window.onload = function () { itemIcon = ' Need to Donate'; } output += ''; - for (c = 0; c < 4; c++) { + for (c = 0; c < numColumns; c++) { if (searchResults[key][c].length > 0) { - // Limit to first 5 results actually showin in table with ellipsis & tooltip for others + // Limit to first 5 results actually shown in table with ellipsis & tooltip for others output += ''; }); - output += '\n'; } else { if (typeof(offset) === 'undefined') { @@ -2398,7 +3508,7 @@ window.onload = function () { $('#geode-search-text').val(''); $('#geode-search-range').val(200); $('#geode-search-all').prop('checked', false); - output += '
Search results for "' + offset + '" over the ' + (($('#geode-search-all').prop('checked')) ? 'first ' : 'next ') + $('#geode-search-range').val() + ' geodes
ItemGeode ' + @@ -2232,16 +3310,45 @@ window.onload = function () { 'Magma Geode ' + 'Omni Geode ' + - '
Artifact Trove ' + + '
' + wikify(key) + itemIcon + '' + searchResults[key][c].slice(0,5); if (searchResults[key][c].length > 5) { output += ',...'; @@ -2370,7 +3480,7 @@ window.onload = function () { } output += '
Found ' + count + ' matching instance(s) of ' + + output += '
Found ' + count + ' matching instance(s) of ' + Object.keys(searchResults).length + ' matching item(s)
' + + output += '
Number Opened
' + '' + '' + '\n'; - output += '' + - '\n'; + ''; + if (save.version >= 1.4) { + output += ''; + } + output += '\n'; + for (c = 0; c < numColumns; c++) { + output += ''; + } + output += '\n'; // We are going to predict all 4 types of geodes at once, so we have multiple variables and in several cases will // use rng.Double() & scale things ourselves where the source does rng.Next() with various different integers. + // Artifact Troves sill require special handling though due to the precedence of conditionals for (g = 1; g <= pageSize; g++) { numCracked = offset + g; item = ['Stone', 'Stone', 'Stone', 'Stone']; itemQty = [1, 1, 1, 1]; rng = new CSRandom(numCracked + save.gameID / 2); + rngTrove = new CSRandom(numCracked + save.gameID / 2); + if (save.version >= 1.4) { + // extending arrays to support artifact troves + item.push('Stone'); + itemQty.push(1); + // 1.4 added a bunch of extra random calls to prime the RNG to counter repeating patterns + var i, j, prewarm_amount2 = rng.Next(1,10); + rngTrove.Next(); + for (j = 0; j < prewarm_amount2; j++) { + rng.NextDouble(); + rngTrove.NextDouble(); + } + prewarm_amount2 = rng.Next(1,10); + rngTrove.Next(); + for (i = 0; i < prewarm_amount2; i++) { + rng.NextDouble(); + rngTrove.NextDouble(); + } + // Might as well just roll the troves now since the separate RNGs mean the order doesn't matter. + item[4] = save.minerals[save.geodeContents[275][Math.floor(rngTrove.NextDouble()*save.geodeContents[275].length)]]; + } if (rng.NextDouble() < 0.5) { qty = rng.Next(3)*2 + 1; if (rng.NextDouble() < 0.1) { qty = 10; } @@ -2442,7 +3581,7 @@ window.onload = function () { itemQty[0] = 1; item[1] = save.minerals[84]; item[2] = save.minerals[82]; - item[3] = save.minerals[(save.is1_3) ? (82 + rng.Next(3) * 2): 82]; + item[3] = save.minerals[(save.version >= 1.3) ? (82 + rng.Next(3) * 2): 82]; } } else { next = rng.NextDouble(); @@ -2521,7 +3660,7 @@ window.onload = function () { tclass = "future"; } output += ''; - for (c = 0; c < 4; c++) { + for (c = 0; c < numColumns; c++) { itemIcon = ''; if (!save.donatedItems.hasOwnProperty(item[c])) { itemIcon = ' Need to Donate'; @@ -2531,7 +3670,8 @@ window.onload = function () { output += ''; } } - output += ''; output += '
Num OpenGeode ' + 'Frozen Geode ' + @@ -2406,16 +3516,45 @@ window.onload = function () { 'Magma Geode ' + 'Omni Geode ' + - '
ItemQtyItemQtyItemQtyItemQty
Artifact Trove ' + + '
ItemQty
' + addCommas(numCracked) + '
Note: Need to Donate denotes items ' + + output += '
Note: Need to Donate denotes items ' + 'which need to be donated to the ' + wikify('Museum') + '
'; return output; @@ -2588,9 +3728,9 @@ window.onload = function () { day = 7 * week + weekDay + offset; rng = new CSRandom(save.gameID / 2 + day + save.dayAdjust); if (day < 31) { - thisTrain = 'Railroad
not yet
accessible'; + thisTrain = 'Railroad
not yet
accessible
'; } else { - thisTrain = ' 
(No train)
 '; + thisTrain = ' 
(No train)
 
'; if (rng.NextDouble() < 0.2) { trainTime = rng.Next(900,1800); trainTime -= trainTime % 10; @@ -2680,7 +3820,7 @@ window.onload = function () { rng = new CSRandom(save.gameID / 2 + day + 1 + save.dayAdjust); if (day + save.dayAdjust === 30) { thisEvent = '
Earthquake'; - } else if (!save.is1_3 && save.canHaveChildren && rng.NextDouble() < 0.05) { + } else if (save.version < 1.3 && save.canHaveChildren && rng.NextDouble() < 0.05) { thisEvent = '
"Want a Baby?"'; } else if (rng.NextDouble() < 0.01 && (month%4) < 3) { thisEvent = '
Fairy'; @@ -2693,7 +3833,7 @@ window.onload = function () { } else if (rng.NextDouble() < 0.01) { thisEvent = '
Stone Owl'; } else { - thisEvent = ' 
(No event)
 '; + thisEvent = ' 
(No event)
 
'; } if (day < save.daysPlayed) { @@ -2713,6 +3853,77 @@ window.onload = function () { return output; }; + function predictCrane(isSearch, offset) { + // logic from StardewValley.Locations.MovieTheater.addRandomNPCs() + var output = '', + thisEvent, + day, + week, + weekDay, + monthName, + month, + year, + tclass, + rng; + + if (isSearch && typeof(offset) !== 'undefined' && offset !== '') { + $('#crane-prev').prop("disabled", true); + $('#crane-next').prop("disabled", true); + $('#crane-reset').html("Clear Search Results & Reset Browsing"); + } else { + if (typeof(offset) === 'undefined') { + offset = 28 * Math.floor(save.daysPlayed/28); + } + if (offset < 112) { + $('#crane-prev-year').prop("disabled", true); + } else { + $('#crane-prev-year').val(offset - 112); + $('#crane-prev-year').prop("disabled", false); + } + if (offset < 28) { + $('#crane-prev-month').prop("disabled", true); + } else { + $('#crane-prev-month').val(offset - 28); + $('#crane-prev-month').prop("disabled", false); + } + $('#crane-reset').val('reset'); + $('#crane-next-month').val(offset + 28); + $('#crane-next-year').val(offset + 112); + month = Math.floor(offset / 28); + monthName = save.seasonNames[month % 4]; + year = 1 + Math.floor(offset / 112); + output += '\n'; + output += '\n'; + for (week = 0; week < 4; week++) { + output += ""; + for (weekDay = 1; weekDay < 8; weekDay++) { + day = 7 * week + weekDay + offset; + // Game1.Date.TotalDays does not does not include today, so the RNG seed must be offset by 1 + rng = new CSRandom(save.gameID + day + save.dayAdjust - 1); + //var TEMP = "" + (day + save.dayAdjust -1) + "
"; + if (save.version >= 1.4 && rng.NextDouble() < 0.25) { + thisEvent = '
Crane In Use'; + } else { + thisEvent = ' 
(Crane Free)
 '; + } + + if (day < save.daysPlayed) { + tclass = "past"; + } else if (day === save.daysPlayed) { + tclass = "current"; + } else { + tclass = "future"; + } + output += ''; + } + output += "\n"; + } + output += "
' + monthName + ' Year ' + year + '
MTWThFSaSu
' + (day - offset) + '
' + + '' + thisEvent+ '
\n"; + } + return output; + }; + function predictWinterStar(isSearch, offset) { var output = "", // NPC list from Data\NPCDispositions @@ -2755,9 +3966,10 @@ window.onload = function () { if (typeof save.names !== "undefined") { numPlayers = save.names.length; + $('#winterstar-note').html(''); } else { forceOldLogic = true; - $('#winterstar-intro').after('

Note: No players found; predictions may be wrong in game version 1.3

'); + $('#winterstar-note').html('Note: No players found; predictions will not be reliable for game version > 1.2'); } output += ''; @@ -2767,7 +3979,7 @@ window.onload = function () { // While it looks like the gift itself might be predictable from StardewValley.Utility.getGiftFromNPC(), the RNG there gets seeded // by an expression that includes the NPC's X coordinate, and (based on in-game testing) that seems to be from a pre-festival // position which is not easily predictable. - if (forceOldLogic || !save.is1_3) { + if (forceOldLogic || save.version < 1.3) { rng = new CSRandom(save.gameID / 2 - year); } else { // Using BigInteger Library to convert the UniqueMultiplayerID to integer since these IDs can exceed JS' integer storage @@ -2818,8 +4030,12 @@ window.onload = function () { output = predictTrains(isSearch, extra); } else if (tabID === 'night') { output = predictNight(isSearch, extra); + } else if (tabID === 'crane') { + output = predictCrane(isSearch, extra); } else if (tabID === 'krobus') { output = predictKrobus(isSearch, extra); + } else if (tabID === 'sandy') { + output = predictSandy(isSearch, extra); } else if (tabID === 'wallpaper') { output = predictWallpaper(isSearch, extra); } else if (tabID === 'winterstar') {
Year ' + year + '