11use crate :: game;
22use crate :: item:: equipment:: { Shield , Sword } ;
3- use crate :: item:: { equipment:: Equipment , Item , Potion } ;
3+ use crate :: item:: { equipment:: Equipment , Escape , Item , Potion , Remedy } ;
44use crate :: randomizer:: random;
55use crate :: randomizer:: Randomizer ;
66use serde:: { Deserialize , Serialize } ;
@@ -20,34 +20,35 @@ pub struct Chest {
2020impl Chest {
2121 /// Randomly generate a chest at the current location.
2222 pub fn generate ( game : & game:: Game ) -> Option < Self > {
23- // FIXME improve random generation logic
24- // FIXME inlcude other items
25-
26- match random ( ) . range ( 6 ) {
27- 0 => {
28- let gold = random ( ) . gold_gained ( game. player . level * 200 ) ;
29- Some ( Self {
30- items : HashMap :: new ( ) ,
31- sword : None ,
32- shield : None ,
33- gold,
34- } )
35- }
36- 1 => {
37- let potion = Box :: new ( Potion :: new ( game. player . level ) ) ;
38- let potions: Vec < Box < dyn Item > > = vec ! [ potion] ;
39-
40- let mut items = HashMap :: new ( ) ;
41- items. insert ( "potion" . to_string ( ) , potions) ;
42-
43- Some ( Self {
44- items,
45- sword : None ,
46- shield : None ,
47- gold : 0 ,
48- } )
49- }
50- _ => None ,
23+ // To give the impression of "dynamic" chest contents, each content type
24+ // is randomized separately, and what's found is combined into a single
25+ // chest at the end
26+ let distance = & game. location . distance_from_home ( ) ;
27+ let gold_chest = random ( ) . gold_chest ( distance) ;
28+ let equipment_chest = random ( ) . equipment_chest ( distance) ;
29+ let item_chest = random ( ) . item_chest ( distance) ;
30+
31+ let mut chest = Self :: default ( ) ;
32+
33+ if gold_chest {
34+ chest. gold = random ( ) . gold_gained ( game. player . level * 200 )
35+ }
36+
37+ if equipment_chest {
38+ let ( sword, shield) = random_equipment ( game. player . rounded_level ( ) ) ;
39+ chest. sword = sword;
40+ chest. shield = shield;
41+ }
42+
43+ if item_chest {
44+ chest. items = random_items ( game. player . rounded_level ( ) ) ;
45+ }
46+
47+ // Return None instead of an empty chest if none was found
48+ if gold_chest || equipment_chest || item_chest {
49+ Some ( chest)
50+ } else {
51+ None
5152 }
5253 }
5354
@@ -125,6 +126,42 @@ impl Chest {
125126 }
126127}
127128
129+ // TODO consider using weighted random instead of these matches
130+ fn random_equipment ( level : i32 ) -> ( Option < Sword > , Option < Shield > ) {
131+ match random ( ) . range ( 15 ) {
132+ n if n < 8 => ( Some ( Sword :: new ( level) ) , None ) ,
133+ n if n < 13 => ( None , Some ( Shield :: new ( level) ) ) ,
134+ 14 => ( Some ( Sword :: new ( level + 5 ) ) , None ) ,
135+ _ => ( None , Some ( Shield :: new ( level + 5 ) ) ) ,
136+ }
137+ }
138+
139+ fn random_items ( level : i32 ) -> HashMap < String , Vec < Box < dyn Item > > > {
140+ let mut map = HashMap :: new ( ) ;
141+ let potion = || Box :: new ( Potion :: new ( level) ) ;
142+
143+ let ( key, items) : ( & str , Vec < Box < dyn Item > > ) = match random ( ) . range ( 15 ) {
144+ n if n < 7 => ( "potion" , vec ! [ potion( ) ] ) ,
145+ n if n < 11 => ( "potion" , vec ! [ potion( ) , potion( ) ] ) ,
146+ n if n < 13 => ( "potion" , vec ! [ potion( ) , potion( ) , potion( ) ] ) ,
147+ 13 => ( "remedy" , vec ! [ Box :: new( Remedy :: new( ) ) ] ) ,
148+ _ => ( "escape" , vec ! [ Box :: new( Escape :: new( ) ) ] ) ,
149+ } ;
150+ map. insert ( key. to_string ( ) , items) ;
151+ map
152+ }
153+
154+ impl Default for Chest {
155+ fn default ( ) -> Self {
156+ Self {
157+ gold : 0 ,
158+ sword : None ,
159+ shield : None ,
160+ items : HashMap :: new ( ) ,
161+ }
162+ }
163+ }
164+
128165#[ cfg( test) ]
129166mod tests {
130167 use super :: * ;
0 commit comments