From 1f6c04a52604e934e18b04975c9d1982428b8d71 Mon Sep 17 00:00:00 2001 From: Llywelwyn Date: Sun, 23 Jul 2023 22:41:24 +0100 Subject: [PATCH] atomises item table into sub-tables, adds table rolls to vaults --- README.md | 48 ++++++++++++++++--------- image.png | Bin 0 -> 24850 bytes src/map_builders/mod.rs | 1 + src/map_builders/prefab_builder/mod.rs | 36 +++++++++++-------- src/random_table.rs | 12 +++++++ src/spawner.rs | 41 +++++++++++++-------- 6 files changed, 92 insertions(+), 46 deletions(-) create mode 100644 image.png diff --git a/README.md b/README.md index 44d8568..0c7aa3f 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,37 @@ -## a roguelike in rust, playable @ [llywelwyn.github.io](https://llywelwyn.github.io/) -#### using _rltk/bracket-lib_, and _specs_ -![image](https://github.com/Llywelwyn/rust-rl/assets/82828093/b05e4f0b-2062-4abe-9fee-c679f9ef420d) +## a roguelike in rust, playable @ [llywelwyn.github.io](https://llywelwyn.github.io/) +#### using _rltk/bracket-lib_, and _specs_ + +![image](https://github.com/Llywelwyn/rust-rl/assets/82828093/b05e4f0b-2062-4abe-9fee-c679f9ef420d) this year for roguelikedev does the complete tutorial, i'm following along with thebracket's [_roguelike tutorial - in rust_](https://bfnightly.bracketproductions.com). for most of the 8 weeks, i'll probably just be working through the content rather than diverging too much into doing my own thing, since it's lengthy and i'd rather finish in time. that said, the ultimate aim here is to strip out the vast majority of the existing entities and replace them with my own, using the systems and components from the tutorial as a jumping-off point for something of my own making. i'll try to remember to update the web version on my page at the end of every week -- - - +---
week 1 - brogue-like colours - - i was staring at a horrible-looking game for a while as i tried to figure out how to make it look nice, before deciding to try the brogue method of colour offsets. when a map is generated, it also generates a red, green, and blue offset value for every tile on the map, and applies them during rendering. after making that change i started to miss the previous hue, so i combined the two. as it stands, every tile starts off a subtle green/blue, has rgb offsets applied on top of that, and then has the actual tile colour applied. and it ends up making something like this +- i was staring at a horrible-looking game for a while as i tried to figure out how to make it look nice, before deciding to try the brogue method of colour offsets. when a map is generated, it also generates a red, green, and blue offset value for every tile on the map, and applies them during rendering. after making that change i started to miss the previous hue, so i combined the two. as it stands, every tile starts off a subtle green/blue, has rgb offsets applied on top of that, and then has the actual tile colour applied. and it ends up making something like this ![image](https://github.com/Llywelwyn/rust-rl/assets/82828093/2ded4eb7-b758-4022-8fee-fdf12673cf0e) -- fov - - decided to use bracket-lib's symmetric shadowcasting for common viewsheds (i.e. sight) - - and implemented elig's [raycasting](https://www.roguebasin.com/index.php/Eligloscode) algorithm for any viewsheds that _dont_ need that level of detail. symmetric is great, but when it comes to viewsheds that often _aren't_ symmetric in the first place, it's not really necessary (i.e. it's not often you've got two people with: the same additional viewshed, both within range, etc.). doing it this way comes with the benefit of being able to easily define what blocks a viewshed, rather than having to make a whole new BaseMap to work through bracket-lib +- fov + + - decided to use bracket-lib's symmetric shadowcasting for common viewsheds (i.e. sight) + - and implemented elig's [raycasting](https://www.roguebasin.com/index.php/Eligloscode) algorithm for any viewsheds that _dont_ need that level of detail. symmetric is great, but when it comes to viewsheds that often _aren't_ symmetric in the first place, it's not really necessary (i.e. it's not often you've got two people with: the same additional viewshed, both within range, etc.). doing it this way comes with the benefit of being able to easily define what blocks a viewshed, rather than having to make a whole new BaseMap to work through bracket-lib + +- telepaths and having brains + - telepathy! a personal favourite rl feature, so i thought it'd be a cool test of the raycasting. right now it's simple, since the point was really just making sure the raycasting worked: there's a component for _being a telepath_, and for _having a mind_. if someone has telepathy, they'll see every entity with a mind within a given radius (defined by their telepath component), even through walls. + ![image](https://github.com/Llywelwyn/rust-rl/assets/82828093/d55d5df4-267c-4dd5-b166-8417f58365af) +- atomised spawn tables + - i tried figuring out how often things would spawn by just looking at the weighted tables, and i had no idea at a glance, so i replaced it with category tables. right now it's just rolling for an entity or a mob, and then rolling on the right table from there, but at least it means easily being able to see how often something will spawn. on average right now, there's 1 item : 3 mobs -- telepaths and having brains - - telepathy! a personal favourite rl feature, so i thought it'd be a cool test of the raycasting. right now it's simple, since the point was really just making sure the raycasting worked: there's a component for _being a telepath_, and for _having a mind_. if someone has telepathy, they'll see every entity with a mind within a given radius (defined by their telepath component), even through walls. - ![image](https://github.com/Llywelwyn/rust-rl/assets/82828093/d55d5df4-267c-4dd5-b166-8417f58365af) - -- atomised spawn tables - - i tried figuring out how often things would spawn by just looking at the weighted tables, and i had no idea at a glance, so i replaced it with category tables. right now it's just rolling for an entity or a mob, and then rolling on the right table from there, but at least it means easily being able to see how often something will spawn. on average right now, there's 1 item : 3 mobs -
-- - - +---
week 2 @@ -41,5 +42,18 @@ i'll try to remember to update the web version on my page at the end of every we - 8-bit walls - i wasn't happy with how the walls looked, so i made the masks 8-bit instead of just 4-, which means being able to be a lot more specific with which glyphs are used. mainly it means no more grids of ╬. this comes with a side-effect of magic mapping looking a lot better. - ![image](https://github.com/Llywelwyn/rust-rl/assets/82828093/6568d203-e0b0-4c68-ad81-fe2d5c2f0ac3) + ![wall bitmask before-and-after](https://github.com/Llywelwyn/rust-rl/assets/82828093/6568d203-e0b0-4c68-ad81-fe2d5c2f0ac3) + +
+ +--- + +
+ week 3 + +- stacking items + - finally, identical items in the inventory stack. i waited with this until figuring out a way that would work with extra parameters in the future like BUC. current solution is using a BTreeMap with a tuple containing the parameters that need to be the same (right now just the name) as the key, and the number of those items in the inventory as the value. + + ![inventory screenshot](image.png) +
diff --git a/image.png b/image.png new file mode 100644 index 0000000000000000000000000000000000000000..8a34c2546cc04a40511ba6e69e5fb20b9fe9e425 GIT binary patch literal 24850 zcmeAS@N?(olHy`uVBq!ia0y~yVEWC#z;K9zje&vTdNwZ;0|NtNage(c!@6@aFBupZ zSkfJR9T^xl_H+M9WMyDr;4JWnEM{QfI}E~%$MaXDFfb%6_jGX#sfc^Kw{lBrXzlym z{=u%HjW-q@Gi%~1O`g44!6W>O#BGhjJrbsZqObSunf~PId~2g)zu))%j$e1OdSO6- zkZK{%;(Iq#%N#D6zv^AY=ptZhR=JJCDKKPi@vC>6pS|DA_q+U6dD`}CcE8{Js+G8M zC;gr2^*7Idt$jc1_x3cl=7Wt7|MzWHteDesnz?-DZQM4yZIx*@uJ zj)c>)|Geg>+qu%%qUD6oneSiD*%cWvg}>ECDN%E4_~V&}pE@hDA6lT9_S)`ez=^{` z(>A4c*Jz|XQ~Vj=k@PeE|Dl9;=TBVyHGiMtvwtkMwQ` zos_cRyFX%Y#qVagzE-}ty<1fNVXpPOt1WBf<#+x1?YrYgz|&uQmlpjw>!~rP>e%E% z?3veIm;JY?*-%ljx+Y?FaQw#}wI%U;oO_3%>ic{A1(UPb(+vJoQU{g-?9spM&>K37@&P_5YoEuFqD->VHp8o+Xg{ zfB(Mk^VI%4;eWj@{{QXCPn{L{?JTPP{@#CI?o_3NSi}G4&fm=zo?Kt2B%d=aJ7%|+ zd5*1*ok&s2(vAOvIlWirwk-FKIOVf+?dQX%vdfWSw zzBf+#R`$|&>^=e0ivFpUq~3oid`$Ai#}fwg?yAXiS*|Mjr|gQj2Uf%%`7nXeLY@1-60P6rvhq&X zG^d%aJeT13?(FmD&Og>~Z!OTZ3jTJ!e(95_6BECmZg;BNwO#N3UC!REV)>OngP#OT z8vCyZ-nQ?*{HKKz((R}68m0bubKv`|f}=P14`=1&mF$dYV8}_0XIDyX`Cd zgoEnR-mmdlz z;Tu!`TE*N9-?3?atM>2G9h>5h9}D~T-=|KI;eGEPr-WPI-t5nAuMyC@tt-z{bADfq z#*%G+qHfloU-`+g;(qC;hYvDR|9|}dJOA|Z1GVD+SJzlste*0B`#fFw7VUj1@;X=k z{B$<`_b>15$K_N19)EA+YsdO-_milfB?V!czu!yv+V7}&`z7jT{h8fyTJxFq?|YN| z>7;}9@BGl8@q#DgHGjMR`N{c7^TUJqds_2(rfizu`~K#1|D`?Y=l8XSeUtzH&G>2X zgMIw5svt@G&-HUu<}Hm-O{mb^h-?Q~{cB=jCRkpvEdvIL$u2}J* zru5^FZ*0Zw1uH%?ZT;~l`dN}u)@%O!xvD--XFfEnIn8ghbpHL2$M36WK7Q}&wd$wa zv%6m|W_{dg9$a0yujNB7XK>{*t^fQ+r`Nxm$~LjO^tj~1oBdPy4}CZOK677;(~qte z>DI|t|Lk8FR$V&hq^tJt`k(cCBkWlJ{n@`>ckcS2Z~F4S_7(0=?LP7qq{{oG#{1RW zG*#SEkQ#3%a%uJ^{lDM$ZG9hB9sI3w-jDC$Z5Le5&JVje{Y(A!Z$FHu)}H*o@5E%@ z;G5Imu-EVQJ5_hPE_?d@kZ-C#oB!XP`gdzV>VEaq_*qX6+|LO}jX(AOLj3Mc`^DD1 z@n5+~|M7qQdvPn?c>nr+t~|BAD06CU=DhrHsmvX`uDrharskrKkJQ)4P9?ef71MZ-Q-R8x5cOZyt?<2Lectn7Rn_{-x&Yfr}fZ{ zF?If@`!7!IGu}Jn)V`ko_VxJ-H=X~pvtZKHzcF_}iS5YLzgBmo`)jlg@BO%AQ~cpA zn!j&qq}tEs4F0ycFg4yz^wFt(H5!YvQ|+g(+xs!a*w6m=O*p`6KhS_jTqi(>DH}^WfhV&z5gr%8J42 zUd}lCQ9XOz8|eie?qSlzKPTzuBl*a%sU| zgKzzoYAt<_E&3i?__^=c6n~E0PQd9NgY@lUx!bShgLioHTkSus$E}#}v-#BLgZyn1 z%-3hGDcSmzJ(mB$p8ga38_VDEEOcA09{(zN>i7mU8Gg&2C_*uQUY6|cPb za_h$SQ~z@d-*#*~U%0WXHRAWOc~C#(?AR3Vw{BAP&!?XpD~?+~X+H2%?5OiK>-&;U z3bwb~?tPvo;IzhO<9kbcw>@8NpG=ECEX>yP=f=l5_p6#F{94lbS*JsNXZc?3mL=-< zvf}6RCEd3M2Y`>#x;N5Kw03^Kr!C*IxPKZy)APd-b?=IuIhGv1VgGIi%+zE1;HLgX zGBsg}{rgHq72A(tzh9LI{JsZ@Ihph~K24kcJ5H=fPnAEmD59JF2-lqS`$w!kyL)N5 z&1}07_w|bH{b@yKzc08PEZ0-IcF*fSx;m-*w*8rPV={~Hntv-#?Yf$OB)a;4^4G<+ zEBf>H@4qTq0FG_X6i1#pl7=SFT<*pQ9e#3X#>1^_d$oVx7wjy*)cg0j#!87)sif+w z!QX45Pnq6t_VceaFx&cR-RJEaru@A1DLctu$YkTMlU4Qqj z)_wqvwaFf5ZrC3)+7olr|M=9u0@J3&KR&e&l1!vtFMjjh#@UWzdW^0-kI>ZGwF~^U z43|buUgfj7Jy!d3gHqo`&Xv_y6}Qjatz}=Zr*4wN9=rNCo(!jK-X>}O-ul+TQm)RT z<@UDLfXUTy;7FYla>H=K>NocP_V01BV|{nK^t9Bef8Bjk_z#)OP2jK0NR8KiG1>FL;}rkKy`N^q?sm$M{5@@v|4&Eh-&?(GzAmr*GOreD$Bq~t>7Kt&<{E1J zzVUwh+NE!vCu#h?GxNX_?ce7Xc80yXX|{A_trH~vgq3gB&11>8x#fM*?dIn9;5dD3 z@!E0vx6fx1KKDrco@%y#&(F}sfBW-A@@>HB-=c4`+>+bpkL`Qw{tz11eUE(v*1dsb z4~uhC9wCG^SAH`-$Ii+4xX$w4REPs*j?YvGdiz}V|IeAeD`NXT3tf5oIjFZ z7>cFVf3B}wS;O___E*!NDG%Td3klwp{=a9s?NZK<$2R}2U$^wleix@7yIN-M*aQoN zms4(?*Z3{*{d8i?&FL>{rz2!)jpI`1=jTs05DtgAPJQl1jo&vBO0Hhf`d#2r@mR9_ z-}~zOzh`~eaF{RKseaPKD+`Xl-&gx^-LFO6dRb1(qwoFy_uc&Av$Ko4(_h)F^R?G} zQMi4_jvFoSYo1%r-jJX5IsW}?KfPUh4Bi#}{!u>X%{FG~D2Zg!v55yZ#y z?ey}%Ytc95%m3Z~b#41Bf#m!1Q!GqheC!65-p9=rUe+zYTN*B$m8@DScYWD>kuN(A z->7?6JpXn}ZSd^>Rq^FT7oWBkPQQJ8|6?D%m#=TkX=%LNS05xS7r_Q|6@c;&-O-uk(Rd}o`PF6>`kmv}cSD(YK+ zjYLev!&cuJUb(-%HkEc4?f-rEeV)^+Vu!kqU;E1UEau#uw)b;A~h-;9k<^H0oaRsW`rho_?JGekNLb}+rOoM?}z?>`S|4gUrV-0KUMr&eed%;kke~5w|?OShrUn{aP z{j$+F9aOg(CSSj0owKz4(ntGsl_AmpFa3MJ>i?IIPyB0xZufqg@NdWCzU)IGS(m2= zMgK4CHSWIk=c^Ud#?N2&);Z|>y<2|&Zi`{^eJlQ_Yh(YLgJLjKsrIjA?0di3pTe^z zyv|$vhTqvR`FG+SuZdw{yH5GbN$^PPx2#+)TmDzK;*sO?^4PwpRJ(7ruksBn_{FXJ z7S|lfUb=GI{GikF+4Z$@dY7)u{`U44r*H1x?BDVh>$dzi(EB3X67kby>)XKDcGGYD zGPd(PU)ZyM-+Yd;x%DfzdN0>bqtA6IkuvbL&gr;}YxsHsktT z-`-@WE?xcSNuy)-o8|M$?tI<;pp(73_WIKmq0f@*Y=No(euA6`1%C-I5{x?4M`puslXz$oD)j#;Q+}4-HE7#gTzHGGZcb(FW(tk_i zE@mBi^95zc^=w?vq&g(|L`ZVb*2qzRAY@hpIT+ugv=!!s_f?m?|EyyVrf4 zt?tU@OaH|j`Ss1qp)M!+k)fWr|BGGnS-)@IGUi_S{P{_~3-?y~*XZs0@ks5?w%lmp zq?`-&*W$ij*}Ki~D4*Bsc<;==yS3so|1aGp?-t}E*xP$tV(ZJ|m#hBCY_G2u$p7`P zcjB_ue=PYQn}0p&@bbs%x&H-DtAFK;di!ht#bf&)`zW7DUoq1?cz62#kb}#17$)0) z-Eu`fd1d~3mM7xTZ+5?Hzg4&G+Zm@t3Lo{pQ0DfhG>~!?QaCqz?IT z|6H!sxpj$_{~eadQ$d6H82{OR~J<);TSPJLAinr%0I-K+Rp|6(tO{+pv-{#WbAMijd{Z>9=exgMzh$<0`-GGl{p)8Mg)DCW*|8#v zbxZz<`uQOVn?o9^zQ@|#y?5ZK=C<-_Ut*RR=6R;SWp(|?q?aY|y8O@K%W)EGpZHhH zRlnKjo3Zfx;?Dm47r$NpIju^v^J3^<@8A2k{eRGB{VwCs+UV_Oic7TiRMliAJze1o zO0+M{x1GQ6ep=8S)7Yo$kITQ7dA#!d$8f9vn&$UEn}42A=2^Qba@W87FCX7qTyBXw z|K_LgwbNT8;w~Nv-J!l~kFh4m`O~)+?TBcbkW$mXaXrg9GkdY%wGH2Rf4)9C5908K zjpDbhy{mHHOg?vuyXz6_uO!*m<@f$b8JFqrda+1t%Z29`j~u_B@%v?s_uuy8FIN3q z`ZxY|{f6x8>(sXN+?{b~_3`OyVSTs0Y@Kp#e%q zJ~=)4!u(pyEq~qhRkyypW_>O8^cnBAE9zHQPkU_97wo=n)BkOk=ih5FPI_1WVwSww zL-{$swuq+2YkwC@V&0zkCVlVYuy281KEB^w{QR7n4{QCeiQeV-%#ea?zL<7o(`+hkp2I7 zmiyMP`fvZQ`{gh1|8HkptOozf;=e7|m+3Fv_V2M&`m7uIvKrxUwlm9w@A~($(BrpZ z-q)XBg_HO8Ic8Z;J2yY-+U~!e+sohIQ+u+@*XCs=``vk4UoPD8e@%@;zjF7jkI#L? zznDin{&4f4=k)mcPj2Cl-=X{Fr97WKr@Q3#P5q1d zH}0?f{Y}lM)%+=_2+jJC<}BtpWz)U|d)FS_mhakUd-~eZQ$ZHV{!U4cPXz5NK0d!r zSoK2PmE*T|<8BAoedk@U^`-a9wf}bZ;&XGd8!{#0DN+9-e!u=)@NvPtE&12| zGqPUC-~NA~aOvOUM+3DpHh#*?y1m$}R&VRq^qNWY9!u2M+6ylE(_vy^(a7cTRt z`1{xKS)UVoc7D*c-G3X+{}xSN(BD0GxtZ;gZ}QJsZR0cIuND4HS@Qbh zb07AdpXKsxHs$c|Q)l}#>t2P$kI&m%_x_YVD%~HZR{VMK4vF7U2Od=S{Jqipb;s-c zgTnk`-Rn*Vu0C^opZ{N8w&xd5v7#wvZ->V3CceG8mWu=h%vJcH{8H#&%RcjM-~LJ~Z#Z>+&!Rmmzn+Qx zA0(Q*Z~HUG0KRAbZ%%rwiL2dw?=Anz=k4qEv-f$w-G4)v^<4T*#@K%QbSn?p^%}o^ zA8ok5yag25UFU zTibq3pY(m}bD=Fx$-3KOzdPsJM^|rC{Q5^j#i4q+-N&7OzulgF!KiHQ-c?b7n>I|D z8}T>mw)xaczsf`RUHa#pYybDi0VOlJm$WzAPU{&Iib z{{7vX>Dx1NXUWgaKVAKOy1Sq8tk|3UJ2u6;<=XGww7(2wzi9Y3=5=p&Z~C86G&S_p z!9Rtm_HTbphMRi%_5P{rQ`vWHdN0#+<>RcSZ;pR^Gyn6Vzy>ks4}(pXup78^V( z-{)p}w%prf&aY4AuTr@t6m0i9jBQ2!+M?X0`#+zvo?UQy+Jod%ug+hp>pxhjo4U*_ z{@a_P!i$ep%g$|NU+-20UAPJki%R+1 zo9!Q^%Ln}EJR;2>tj4o->JJIH!>3DsxOQWEOVy|JjUAeuTaQdRzi%qztJ6x+t74*G z>hF9qN$tpu?Y}l}xu0ARClqOaHR}`corc4R!pT%KXa5mDdsv?@ZVH?OkM2_r<)+T+TP^ zpUkoAe{{2tIn2xA?>#FJbydM$ZF!i(@3udYTO!}AD8K*o@>^a*S7wj%O5KItR@wR{ zUBU%{jW>2&u3NSm-j^Mw!Z9{wLd$xKJeJh`|;l&tI2#k#vG$` zXkqg=-kpo5_x*^gJ+asL<*X}zEPK}fylwPtZPo<&$x+%1YMuX8ZCrY z3VY4Id8WT@bt`1b)j4)-Ze8<|)HhLG;t~0aS|Th%3XihO)mgT#e%6(*o2Oj*ReJZ| z*4NRwRk8c8{7e3uboa_KrMg?sN$wCa0o zF}Wu!Zs*C1KfbP(|B^px%MF3-@3+3Z?(MgKp72ELclJiFoAol~Y>Df5>SXgS+^+Z$ zx@gP(d*w?WpWCnh^r~j_zw>X|!-HO3czgWl^;@f>xlcvC6mIl9SY;Fv)qmxqz3$uR z7JbILPi`q5>^;}@rEY)A>5qq}))pIl+umk<>iT@SZ_o7g+FqQGk^lBan1B8rx3@Rz zD~zW8Eqs3e`n;^=JiqL>%U`R$lUGvmJMm3)oJCgR`88%?=2812_y5#b>;CG;{Au5c zWsc7jyYzO#hTJc``gKLZdMAVz`PUlXczOEcY3<*RQ)?X#+8fC5Fyb$yR5r00J5YN7Fa|L;eOXBD`e`Rjk@+hw(yzufb;EP5la8JBisn)$@+ zQfa$}-Ez%^+O8VEer&(Dvp8LNnNoBbyT#=R*-smm-`>e&_^*BE%NM_Ur))95Xa3>3 zwVrF~`+E;=Of6HN_vfZsLNBvzoIzO7-li8{9R6)ATsZ%P!>b*-y8EyC9jN?O=XA03 z_wsGg;S=`PF|WDDoj=w4H#evVEBp8GYWp|;ub0=~mJd2!pZnXg@3F<*301bWds=?2 z5)J$7b~QfmTYPn##T@e|Q723n@2#l$WmOqqpm@rz%T9bNWLRZW>NS&Z#jHv9bUHTn zU%Dy2WqWGQm$fUB-duinBwP8K6noX4jAUc8x{|63$NEh5z6eWt$ZlWybo(t%3;+B6 z8oToTUnzUd_33y->b{qIKZNc4GTXs)<=6Fd>wmmn{x|skfz{}aJKR0*TFb3EdxdBJ zHeK5P$M=VK!ufqM(rn*POYbo2GM{&2_8%RW3$jv`idRlKU=snSZ91LzI4C)6PJvf8Mvx7s?!eIqlAChIp9FDM#x>oh(-C z@8$wM%sq2s^bK~#P0c#(zq=u84`THbEuP_L7vl8SuxU%uEz>8*A6NC{_2|D?EuZu^w|4$4y>Ds(D?*b__InBk zFUvkHU#;ZyYu~ZJ+@Fn)uX+Det$*FSe~a;BrlZl@_q)D1$@TO4Is4bX7gtVM-<%4{ z>yJygUmHfew&<$c@ha)p`VBcpdBWc;c(ecCp9fpg6BQ+dKYw0)s?T-x2mc%MYW3V! z{#=>Fcll(y*NXj1XO!{&JlfN{@0a4W``5m&d}&~od~a&j)QC^sq1YVKb2Yi&b{l77 zSz643ir3$^ci2WM{=fC}_`KSlzjr=H@8g@c6%sIOjwh^roL}?t^6Y~BG4stH`6Do?@K-O=gsqHrymyS%4Zz(nwwW)_cOuapP}j&?WFyo64RBA-^=S!`l}Xj z;c2(##4G&C=GW$J*&mkF+27y3=ih4G`kXzVYF|xmi`kX+Kf?Bx+BLs0+w9YKzy4Wl zbH`*!)nmp>&-?yg0+nioyHx+~5;^nx>E55GH`HxTuRn6$F5+HAMf18fA9HJZu2@fA zck=A?!-n6sCM@+cx0zpKpzuFLb=R~R;)@D3iz;$EC4cX#F;t!OD>3KTt;yf6d+B`q z_;JGZ`mIm#C=v z&(F%g5F5pH@yEsc8uuzpcK%#_IC{s=PR>hLe&O`=DZKjPqmy;wog*S&FMV1(BWX_LsrvhmS8e$-rT?vUPPAw4_ox%{%2UfO zaqW5+AFKD)KW?&Bo!9gE_g1eznS1=o$NQnsGHKtvKCR!~o9gO6%|7TC-xt3v;Ybhf zf;Xo(zLhRA@0wrhoA>Y42eF>NGZuK&#pwLLw|Ko`eQr*``+cXUzn&kr88U2f_Kv!W z;nC2=$GP9OY?Kea;(fY5scU1t=KtFM)t5d6+nv7gzWUze{bpfXo7VjfpYFY1GYp4w zcHdj{hFv=;W4ZSZnf+cj_0ycD)~4HJ*uRlj`{(W7ci?HF$!_a5?H5};<*%+#QTl=P z)t%211ph2vvt1};_I@urfrEJq=7emyqIG-gi(l_*djb-!8t)IFx(rv838 z?UwE9tuKCMCI1fyzLDAgr!M|}VfBi|x8;-VpB-H$mVF*wjLC{k?x%o->(XEpOhvssHc44vAN@+t~hSrT&iz{9yUx z^P&gyKR;S-`tXg9!S~Mi#7$HF&M2Q8bHvjv`_1uH3%_sp9-DtlGP(Zux3|6=HRm7M z=jl5>eipF${gwlJ7T&L5*8khN@&4CpHWRx#Gqo?$IU%XL%Kl7U25Mi-$yBJc>)KS` zr}6v7`}hgp6X({R5}v)|*{hXPs~x_n8`cY-S|>KSP@7fb*N^4bxXLwGNgb{_(auqE z_`$N|JNY85`+hfmeyz@ZBzeil88=SFPoLZ!y3$-$;s1>t7yMHcw(j*hoxfGMVBQR; z<$KpNJb!9a`?mJ|uKz)|_hx_Sa{d|o_fGU%{*Aw`e2b5Nc~EQr*JJk!zpMXzX>Rd) z?}sqUtl#~AKE@rDo|5_hih9t}$Nt;RC)DTsU7c%xyl|It>ED>JchfJP4x91yU|s&y zzr}G9W(&`2DL=l*u77w@p~JqPk8%_J|1Z(_t?^4IE9#-1YiM!=|4#u%`|A?FHKsmZ z_P*}HpI;}LKK4lWL}=H?`sL~zJGJ(5^S&FAkDcr%HeUIrr3-FgByr9CwwiCw^v_?m ze&750pLg!}bqDX;KJ~9{UV9ZZ%aHs3>r}ZlpI(1wF1WMf_vAkYVUhV?<0@9mZ2jy0 zZ`#KBzg$+8{CW9}?Nk5S`mJxDTlkePSvNm@{^u!bbLMV+`~B(vpSur!+U0C>B5VP; z@3wI2%sYR!7${{wxe&pxXqND5+QY)@@>?z)b6xy?@9{mFQ@ZD@TmS9Xf$v);hn~B0 z-@$-y$BCy}H{rD_gQdNB?=HL7rso!{{xx~t;}`q)eSeW%blbRWkLkOG+y4LZlh?K{ zkoz0Q_G|u+JLXKwlQch1nf@vB>8{nkqMs~V|MvW)uj{w`l{x-$-knm0e486`(15=a6c0tHD+IwIBSi&8rn#eC1DwVf)J`%>uvgHT^2y_%=Fn`vRGmKfi;#lEG~n zKkYWLtzI#C)mNw4e{*U1vazt__uK8}3sc`bwR`BXblblJg|FAjL{(qVE%_fA;h(l( z+kdai<)L*`5)Gi392lxB?iU+qHoch-&b;lG?xb55& zf31GqoA!^7K@&KAn=dBXHJSErp7Qs`yuX{{Tju{YI0-7$KrIjLXs`CKd<(_oe$F$s z4&(jQuQ@YTFgPY_>bsB|vroGQ?&lOL`M2@YLY|~8N5Iv-%*Tzto_~wa{h9oD>ie0~ zclxJ!`2UNwPkgoNf6`k6Jq>oZ=$!t_`%l-s-5>F5-KqQK)<+cTv*$%T-m>k^Ms@L> zT^3J0W$UFbJzrP96qGZ+tE~5)X!|?e_SSj31s{YZr_DO3@PhmBQ6HyA$xH6dEb-0T z_ef_^u8OYBC);CnYowmcU*&RL2;#yIy9ABDl|r1i28;7{ay+-2sc8GN{BO(3)&IJ; z9^SD)Mk3nqy6!p?SnI7|Z|C5n0uC1I4!OfS;FFrci7ZrAEzFPX!-SF|F9L@jNc3i64V99@* z{n*w2OTR9!%elYs&dR^nM61?sQ48a{%P*x}oBO}?>i?xr)BiAiX8rZf{usZT)@K*( z7gcqyi>6+ax3GVEw$ej<_VKNc{U`7GS9-Os?{SIyr<&kX|1Jg1I3B<2pSZ8Zqh9ua zZ-+zV^`eA?_$R+gkDokyzg5kSjP?m@yC2o`;_0S{kP5g^o`fM|9ijo|ErT+AM?8H zp7z?#sm|YxO!4j@y>6K1 z?}H}NQrSXG9~bQ39+9t_aOdZBt=mfj{6$oIa-4d#js7NRHm`geJG(CG!sltTkKc$( zSKn~&^}DLg7hlIm)y3P(q*tAYWtY=d{AMWxZtc9|F#P8HWu`ita%teVKQYDG26CP4 ztMkI_FPg6T|26E}@wxSH=Y0)Yk-lo z_FefOyqY)OE#Lb!E;GF7n)tE3Tg}bga)0%5eQv9IoId~ChCMA7eVY$0+*N0B&SYY? z{o*%={|f2dXPR-!Zoz~17K%qc?>_Gt{w?{Q?=QFaiSKvcyT#q5ZTXt}@x1Qb>vb2G zi$3H1`iyti7WQLx@2c5ko(DV%h_#nVpXIS_{?twU^LM?x?)zhwu>8_D*-!5^9cQrG za$>LXN>*3-|3|FL-{l+%Ia29Z9lZFYuH8L>9WP(J?p?6OIL`k=+rcFM+Wix+d}0&% z{E6X>Zu@&|Z=6HUdgG^E zf6E#k&uHJ{X!^CYMeg_(`Nwhd4yK#%`_8{hR&VKlj(IPtMn0vp<=3 zxNuHU(E9cnKiKDQZ4A44|6|vKPkCai?>k&uV|J%@wZDhee z`d5>!>NNRZ7T<4(oV9P;-|UsS`wQ2L3*3I~vvbDru-}DqQr{e(=kcP@=eOZI(A1Lm zUl+lk+Z#*%UA+|g`}(#0W&f@$Td!+hvbWA5&r;^Nrux^aAG3m+cJ80{ci*4Th*p+q z5=w`IHVW~#=R#cN-lK^lpjy99Ele`0iJ9#HsS|E7y#_Nw;75Ts$Yt z&eHy4)Jr8h z$DOaOzY@w{RJ|Ka_$O$vgh*dwb2{D%!*l`5PNoi(5}4ycYa;Jw*K|Em0w>? zi=KDm7E&2(X1jqL5$JWHQCE3)3QUN-00r{~pwj~rO^ z&C@IM*QIakm#+Ty*y7%j+u!qj4ru*P-DD7O?@CqDmQB6v%2Fk}ZtQZk{9$*ke~R)~ z@chW-(BIB^e;1bhD>41=b((+YYxcKiDzRL=bcCF`+zTpuWsG?*F9tEUt@nB z@$0Rv8+M#I-`?%j6ZUYvKxXdtorI3{+55|*Y;aKFJJwmt!w{eo&1*KRU7YLw|Zo8 zj%(%XyRZFtyZ#+YiqL=dJnZ)S(}riauY1#OaP@}2sM!kp?!DT-+4Fa-JM|pYT2PI6 zTAo^OQ*!m+gy{N$RQt0x-e=1e%A~hm`KsrAQ{Lsxc|0@ddr0uVi25MDo_#U-Hd|ks z&N-h2^5XtW|5mrwR5E|vcwFvwi`1)=d!C(ieseN#l~KOtnk~^e>#YLjU-{bpIQx0L z=GM3CPp+-!IR_rFx;}0GmzA&Ck00LgZ0=(Vvy-2L^u({t`y0OhQHN-%{Dt`)Jl>Z- z#U9M?fDLYh%i7t**@-y))467-x{wbEi);pQqgRfA*<;YdqJ= z|LuL$>ppq*{vFkF8s+D8H$Ivy-(7W`D~o5YeSf2_zI5!uzbVNk6Y_mFGT)7NuwAov zwMJb-AnVGn*YcIv^mZAx`o5of^~kBfS^HLhTfcP6-xuZE-Uqx3QQbZ5N~uzr%>B#v zzOWzvSo`i*K$P95M7t;N?zZ3fvwG>4y0G8QS)dX&{&jrb|Euflul?pzn061Gs>Qc% z+OHD!H$Bg;!(5KT`|^hb&YzPi403*luGyY6VfOw#Ckl7mXblk7w)?r!qkQdxV;6pA zC+}bX?Rx%!EqZ0slGu0cSJ+lAdX#_5#`#h2V|SOCE=*l>OvG$&c$l!Zd*-ioxAsrF z^lkBd-Q@l23%OUmc6np5@A@%2m@{*Ap5b=o>>fp!D;ZPm*GM%x8hquo|9w>ATK%WF zosTWfDRRA5IVNjvDHU%od%Td3w`l6$tr1J#*n2wVeE+SYGOxK_F|p^4_2hM$XX90+ zI|7V5EEOZ(@BQev{l!OPz1BTqv3XP6zAu`1tZIGq>$7g_>ob4qrry3@c)akK?>nn) zsuqmWI$c}#*XMIgf1J)X`)FOf%_h!!C66}GE(jD^R=?{jyRXL|M$13K=KrgOx9NNe zZUC(n)ju1rn%(<%M)kw5->M#$bj#UG{H}dpyWVxWbn^Yb$;&t1dwmVsFv&i@YyIQC z$33&3l=}4i`RvCpUAf`@{*u@mCpLpdhUyo!B(^_D_)yF#ub+6Mafgt6@?H_8Zw1Lu zmQ)%4IKb|tk!yP3CiB$^YgaelxOSEQMCXmoFHc=Kw(QFLzdI7|Y?NimS3Prh-}gA{ zb0!CB66Jb-Zx8Q#Y_ap(=ftEh$@|~m6+Uz}ns3pY{U48{n?CH3`S)@5{`)QY77u6o zTBuihcIMACNR@xDBk_A>UDx}a>Mat#cN8v@lK5@7T;%(SPJ?f;lDBS5*>|z*j=?*F zpbyUHY~I{n-Syaq`Hc9pi#ztNvg~{OMCwNJwG()2aNe59()w7LagwRX_gAQh!(a z>}7@^?|~rkHhU7FaBM3e*OF( zFV%}C-0IuXd~W{78`GWcfGY3!{rL

~9@Dwco5Z_h)0FOWn%%J2#x--}$sa=J^HH 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 literal 0 HcmV?d00001 diff --git a/src/map_builders/mod.rs b/src/map_builders/mod.rs index eb9c76e..b55fe36 100644 --- a/src/map_builders/mod.rs +++ b/src/map_builders/mod.rs @@ -304,6 +304,7 @@ pub fn random_builder(new_depth: i32, rng: &mut rltk::RandomNumberGenerator) -> builder.start_with(BspInteriorBuilder::new()); builder.with(DoorPlacement::new()); builder.with(RoomBasedSpawner::new()); + builder.with(PrefabBuilder::vaults()); builder.with(RoomBasedStairs::new()); builder.with(RoomBasedStartingPosition::new()); builder diff --git a/src/map_builders/prefab_builder/mod.rs b/src/map_builders/prefab_builder/mod.rs index b285029..e8ab04d 100644 --- a/src/map_builders/prefab_builder/mod.rs +++ b/src/map_builders/prefab_builder/mod.rs @@ -1,4 +1,7 @@ -use super::{BuilderMap, InitialMapBuilder, MetaMapBuilder, Position, TileType}; +use super::{ + spawner::equipment_table, spawner::food_table, spawner::potion_table, spawner::scroll_table, spawner::wand_table, + BuilderMap, InitialMapBuilder, MetaMapBuilder, Position, TileType, +}; use rltk::RandomNumberGenerator; pub mod prefab_levels; pub mod prefab_sections; @@ -60,15 +63,15 @@ impl PrefabBuilder { fn build(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { match self.mode { - PrefabMode::RexLevel { template } => self.load_rex_map(&template, build_data), - PrefabMode::Constant { level } => self.load_ascii_map(&level, build_data), + PrefabMode::RexLevel { template } => self.load_rex_map(&template, rng, build_data), + PrefabMode::Constant { level } => self.load_ascii_map(&level, rng, build_data), PrefabMode::Sectional { section } => self.apply_sectional(§ion, rng, build_data), PrefabMode::RoomVaults => self.apply_room_vaults(rng, build_data), } build_data.take_snapshot(); } - fn char_to_map(&mut self, ch: char, idx: usize, build_data: &mut BuilderMap) { + fn char_to_map(&mut self, ch: char, idx: usize, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { match ch { ' ' => build_data.map.tiles[idx] = TileType::Floor, '#' => build_data.map.tiles[idx] = TileType::Wall, @@ -98,20 +101,20 @@ impl PrefabBuilder { } '%' => { build_data.map.tiles[idx] = TileType::Floor; - build_data.spawn_list.push((idx, "rations".to_string())); + build_data.spawn_list.push((idx, food_table(build_data.map.depth).roll(rng))); } '!' => { build_data.map.tiles[idx] = TileType::Floor; - build_data.spawn_list.push((idx, "health potion".to_string())); + build_data.spawn_list.push((idx, potion_table(build_data.map.depth).roll(rng))); } '/' => { build_data.map.tiles[idx] = TileType::Floor; - build_data.spawn_list.push((idx, "magic missile wand".to_string())); + build_data.spawn_list.push((idx, wand_table(build_data.map.depth).roll(rng))); // Placeholder for wand spawn } '?' => { build_data.map.tiles[idx] = TileType::Floor; - build_data.spawn_list.push((idx, "fireball scroll".to_string())); + build_data.spawn_list.push((idx, scroll_table(build_data.map.depth).roll(rng))); // Placeholder for scroll spawn } _ => { @@ -121,7 +124,7 @@ impl PrefabBuilder { } #[allow(dead_code)] - fn load_rex_map(&mut self, path: &str, build_data: &mut BuilderMap) { + fn load_rex_map(&mut self, path: &str, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { let xp_file = rltk::rex::XpFile::from_resource(path).unwrap(); for layer in &xp_file.layers { @@ -131,7 +134,7 @@ impl PrefabBuilder { if x < build_data.map.width as usize && y < build_data.map.height as usize { let idx = build_data.map.xy_idx(x as i32, y as i32); // We're doing some nasty casting to make it easier to type things like '#' in the match - self.char_to_map(cell.ch as u8 as char, idx, build_data); + self.char_to_map(cell.ch as u8 as char, idx, rng, build_data); } } } @@ -149,7 +152,12 @@ impl PrefabBuilder { } #[allow(dead_code)] - fn load_ascii_map(&mut self, level: &prefab_levels::PrefabLevel, build_data: &mut BuilderMap) { + fn load_ascii_map( + &mut self, + level: &prefab_levels::PrefabLevel, + rng: &mut RandomNumberGenerator, + build_data: &mut BuilderMap, + ) { let string_vec = PrefabBuilder::read_ascii_to_vec(level.template); let mut i = 0; @@ -158,7 +166,7 @@ impl PrefabBuilder { if tx < build_data.map.width as usize && ty < build_data.map.height as usize { let idx = build_data.map.xy_idx(tx as i32, ty as i32); if i < string_vec.len() { - self.char_to_map(string_vec[i], idx, build_data); + self.char_to_map(string_vec[i], idx, rng, build_data); } } i += 1; @@ -228,7 +236,7 @@ impl PrefabBuilder { { let idx = build_data.map.xy_idx(tx as i32 + chunk_x, ty as i32 + chunk_y); if i < string_vec.len() { - self.char_to_map(string_vec[i], idx, build_data); + self.char_to_map(string_vec[i], idx, rng, build_data); } } i += 1; @@ -385,7 +393,7 @@ impl PrefabBuilder { } let idx = build_data.map.xy_idx(x_ + chunk_x, y_ + chunk_y); if i < string_vec.len() { - self.char_to_map(string_vec[i], idx, build_data); + self.char_to_map(string_vec[i], idx, rng, build_data); } used_tiles.insert(idx); i += 1; diff --git a/src/random_table.rs b/src/random_table.rs index 6d14f79..4baa921 100644 --- a/src/random_table.rs +++ b/src/random_table.rs @@ -37,6 +37,18 @@ impl RandomTable { return self; } + /// Adds two RandomTables together + pub fn add_table(mut self, table: RandomTable) -> RandomTable { + // For every entry in the table being added, push to self. + for entry in table.entries { + self.entries.push(entry); + } + // Add the total weight of the added table to self. + self.total_weight += table.total_weight; + // Return + return self; + } + /// Rolls on an existing RandomTable pub fn roll(&self, rng: &mut RandomNumberGenerator) -> String { // If the table has no weight, return nothing. diff --git a/src/spawner.rs b/src/spawner.rs index 2d5aecf..3bf7891 100644 --- a/src/spawner.rs +++ b/src/spawner.rs @@ -199,35 +199,46 @@ fn mob_table(map_depth: i32) -> RandomTable { } // 6 equipment : 10 potions : 10 scrolls : 2 cursed scrolls -fn item_table(_map_depth: i32) -> RandomTable { +fn item_table(map_depth: i32) -> RandomTable { return RandomTable::new() // Equipment - .add("dagger", 4) - .add("shortsword", 4) - .add("buckler", 4) - .add("shield", 2) + .add_table(equipment_table(map_depth)) // Potions - .add("weak health potion", 14) - .add("health potion", 6) + .add_table(potion_table(map_depth)) // Scrolls + .add_table(scroll_table(map_depth)) + // Wands + .add_table(wand_table(map_depth)); +} + +pub fn equipment_table(_map_depth: i32) -> RandomTable { + return RandomTable::new().add("dagger", 4).add("shortsword", 2).add("buckler", 4).add("shield", 2); +} + +pub fn potion_table(_map_depth: i32) -> RandomTable { + return RandomTable::new().add("weak health potion", 14).add("health potion", 6); +} + +pub fn scroll_table(_map_depth: i32) -> RandomTable { + return RandomTable::new() .add("fireball scroll", 2) .add("cursed fireball scroll", 2) .add("confusion scroll", 4) .add("magic missile scroll", 10) .add("magic map scroll", 4) - .add("cursed magic map scroll", 2) - // Wands - .add("magic missile wand", 1) - .add("fireball wand", 1) - .add("confusion wand", 1); + .add("cursed magic map scroll", 2); } -fn food_table(_map_depth: i32) -> RandomTable { +pub fn wand_table(_map_depth: i32) -> RandomTable { + return RandomTable::new().add("magic missile wand", 1).add("fireball wand", 1).add("confusion wand", 1); +} + +pub fn food_table(_map_depth: i32) -> RandomTable { return RandomTable::new().add("rations", 1).add("apple", 1); } -fn trap_table(_map_depth: i32) -> RandomTable { - return RandomTable::new().add("bear trap", 0).add("confusion trap", 1); +pub fn trap_table(_map_depth: i32) -> RandomTable { + return RandomTable::new().add("bear trap", 2).add("confusion trap", 1); } fn door(ecs: &mut World, x: i32, y: i32) {