1	use rltk::{ RGB, RandomNumberGenerator };
2	use specs::prelude::*;
3	use super::{CombatStats, Player, Renderable, 
4	            Name, Position, Viewshed, 
5	            Monster, BlocksTile, Rect, 
6	            MAPWIDTH, Item, ProvidesHealing,
7	            Consumable, InflictsDamage, Ranged,
8	            AreaOfEffect,SerializeMe,RandomTable,
9	            EquipmentSlot,Equippable, MeleePowerBonus,
10	            DefenseBonus};
11	use specs::saveload::{MarkedBuilder, SimpleMarker};
12	use std::collections::HashMap;
13	
14	const MAX_MONSTERS : i32 = 4;
15	
16	fn room_table(map_depth: i32) -> RandomTable {
17	    RandomTable::new()
18	        .add("Goblin", 10)
19	        .add("Orc", 1 + map_depth)
20	        .add("Health Potion", 7)
21	        .add("Fireball Scroll", 2 + map_depth)
22	        .add("Magic Missile Scroll", 4 + map_depth)
23	        .add("Staff", 3)
24	        .add("Holy Cross", 3)
25	        .add("Shepards Staff", map_depth - 1)
26	        .add("Tower Shield", map_depth - 1)
27	}
28	
29	#[allow(clippy::map_entry)]
30	pub fn spawn_room(ecs: &mut World, room : &Rect, map_depth: i32) {
31	    let spawn_table = room_table(map_depth);
32	    let mut spawn_points : HashMap<usize, String> = HashMap::new();
33	
34	    // Scope to keep the borrow checker happy
35	    {
36	        let mut rng = ecs.write_resource::<RandomNumberGenerator>();
37	        let num_spawns = rng.roll_dice(1, MAX_MONSTERS + 3) + (map_depth - 1) - 3;
38	
39	        for _i in 0 .. num_spawns {
40	            let mut added = false;
41	            let mut tries = 0;
42	            while !added && tries < 20 {
43	                let x = (room.x1 + rng.roll_dice(1, i32::abs(room.x2 - room.x1))) as usize;
44	                let y = (room.y1 + rng.roll_dice(1, i32::abs(room.y2 - room.y1))) as usize;
45	                let idx = (y * MAPWIDTH) + x;
46	                if !spawn_points.contains_key(&idx) {
47	                    spawn_points.insert(idx, spawn_table.roll(&mut rng));
48	                    added = true;
49	                } else {
50	                    tries += 1;
51	                }
52	            }
53	        }
54	    }
55	
56	    // Actually spawn the monsters
57	    for spawn in spawn_points.iter() {
58	        let x = (*spawn.0 % MAPWIDTH) as i32;
59	        let y = (*spawn.0 / MAPWIDTH) as i32;
60	
61	        match spawn.1.as_ref() {
62	            "Goblin" => goblin(ecs, x, y),
63	            "Orc" => orc(ecs, x, y),
64	            "Health Potion" => health_potion(ecs, x, y),
65	            "Fireball Scroll" => fireball_scroll(ecs, x, y),
66	            "Magic Missile Scroll" => magic_missile_scroll(ecs, x, y),
67	            "Staff" => staff(ecs, x, y),
68	            "Holy Cross" => holy_cross(ecs, x, y),
69	            "Shepards Staff" => shepards_staff(ecs, x, y),
70	            "Tower Sheild" => tower_shield(ecs, x, y),
71	            _ => {}
72	        }
73	    }
74	}
75	
76	fn shepards_staff(ecs: &mut World, x: i32, y: i32) {
77	    ecs.create_entity()
78	        .with(Position{ x, y })
79	        .with(Renderable{
80	            glyph: rltk::to_cp437('/'),
81	            fg: RGB::named(rltk::YELLOW),
82	            bg: RGB::named(rltk::BLACK),
83	            render_order: 2
84	        })
85	        .with(Name{ name : "Shepards Staff".to_string() })
86	        .with(Item{})
87	        .with(Equippable{ slot: EquipmentSlot::Melee })
88	        .with(MeleePowerBonus{ power: 4 })
89	        .marked::<SimpleMarker<SerializeMe>>()
90	        .build();
91	}
92	
93	fn tower_shield(ecs: &mut World, x: i32, y: i32) {
94	    ecs.create_entity()
95	        .with(Position{ x, y })
96	        .with(Renderable{
97	            glyph: rltk::to_cp437('('),
98	            fg: RGB::named(rltk::YELLOW),
99	            bg: RGB::named(rltk::BLACK),
100	            render_order: 2
101	        })
102	        .with(Name{ name : "Tower Shield".to_string() })
103	        .with(Item{})
104	        .with(Equippable{ slot: EquipmentSlot::Shield })
105	        .with(DefenseBonus{ defense: 3 })
106	        .marked::<SimpleMarker<SerializeMe>>()
107	        .build();
108	}
109	
110	fn staff(ecs: &mut World, x: i32, y: i32) {
111	    ecs.create_entity()
112	        .with(Position{ x, y })
113	        .with(Renderable{
114	            glyph: rltk::to_cp437('|'),
115	            fg: RGB::named(rltk::CYAN),
116	            bg: RGB::named(rltk::BLACK),
117	            render_order: 2
118	        })
119	        .with(Name{ name : "Staff".to_string() })
120	        .with(Item{})
121	        .marked::<SimpleMarker<SerializeMe>>()
122	        .with(Equippable{ slot: EquipmentSlot::Melee })
123	        .with(MeleePowerBonus{power: 2})
124	        .build();
125	}
126	
127	fn holy_cross(ecs: &mut World, x: i32, y: i32) {
128	    ecs.create_entity()
129	        .with(Position{ x, y })
130	        .with(Renderable{
131	            glyph: rltk::to_cp437('+'),
132	            fg: RGB::named(rltk::CYAN),
133	            bg: RGB::named(rltk::BLACK),
134	            render_order: 2
135	        })
136	        .with(Name{ name : "Holy Cross".to_string() })
137	        .with(Item{})
138	        .marked::<SimpleMarker<SerializeMe>>()
139	        .with(Equippable{ slot: EquipmentSlot::Shield })
140	        .with(DefenseBonus{ defense: 1})
141	        .build();
142	}
143	
144	fn fireball_scroll(ecs: &mut World, x: i32, y: i32) {
145	    ecs.create_entity()
146	        .with(Position{ x, y })
147	        .with(Renderable{
148	            glyph: rltk::to_cp437(')'),
149	            fg: RGB::named(rltk::ORANGE),
150	            bg: RGB::named(rltk::BLACK),
151	            render_order: 2
152	        })
153	        .with(Name{ name : "Fireball Scroll".to_string() })
154	        .with(Item{})
155	        .with(Consumable{})
156	        .with(Ranged{ range: 6 })
157	        .with(InflictsDamage{ damage: 20 })
158	        .with(AreaOfEffect{ radius: 3 })
159	        .marked::<SimpleMarker<SerializeMe>>()
160	        .build();
161	}
162	
163	fn magic_missile_scroll(ecs: &mut World, x: i32, y: i32) {
164	    ecs.create_entity()
165	        .with(Position{ x, y })
166	        .with(Renderable{
167	            glyph: rltk::to_cp437(')'),
168	            fg: RGB::named(rltk::CYAN),
169	            bg: RGB::named(rltk::BLACK),
170	            render_order: 2
171	        })
172	        .with(Name{ name : "Magic Missile Scroll".to_string() })
173	        .with(Item{})
174	        .with(Consumable{})
175	        .with(Ranged{ range: 6 })
176	        .with(InflictsDamage{ damage: 8 })
177	        .marked::<SimpleMarker<SerializeMe>>()
178	        .build();
179	}
180	
181	fn health_potion(ecs: &mut World, x: i32, y: i32) {
182	    ecs.create_entity()
183	        .with(Position{ x, y })
184	        .with(Renderable{
185	            glyph: rltk::to_cp437('ยก'),
186	            fg: RGB::named(rltk::MAGENTA),
187	            bg: RGB::named(rltk::BLACK),
188	            render_order: 2
189	        })
190	        .with(Name{ name : "Health Potion".to_string() })
191	        .with(Item{})
192	        .with(Consumable{})
193	        .with(ProvidesHealing{ heal_amount: 8 })
194	        .marked::<SimpleMarker<SerializeMe>>()
195	        .build();
196	}
197	
198	/// Spawns the player and returns his/her entity object.
199	pub fn player(ecs : &mut World, player_x : i32, player_y : i32) -> Entity {
200	    ecs
201	        .create_entity()
202	        .with(Position { x: player_x, y: player_y })
203	        .with(Renderable {
204	            glyph: rltk::to_cp437('@'),
205	            fg: RGB::named(rltk::YELLOW),
206	            bg: RGB::named(rltk::BLACK),
207	            render_order: 0
208	        })
209	        .with(Player{})
210	        .with(Viewshed{ visible_tiles : Vec::new(), range: 8, dirty: true })
211	        .with(Name{name: "Player".to_string() })
212	        .with(CombatStats{ max_hp: 30, hp: 30, defense: 2, power: 5 })
213	        .marked::<SimpleMarker<SerializeMe>>()
214	        .build()
215	}
216	
217	fn orc(ecs: &mut World, x: i32, y: i32) { monster(ecs, x, y, rltk::to_cp437('o'), "Orc"); }
218	fn goblin(ecs: &mut World, x: i32, y: i32) { monster(ecs, x, y, rltk::to_cp437('g'), "Goblin"); }
219	
220	fn monster<S : ToString>(ecs: &mut World, x: i32, y: i32, glyph : rltk::FontCharType, name : S) {
221	    ecs.create_entity()
222	        .with(Position{ x, y })
223	        .with(Renderable{
224	            glyph,
225	            fg: RGB::named(rltk::RED),
226	            bg: RGB::named(rltk::BLACK),
227	            render_order: 1
228	        })
229	        .with(Viewshed{ visible_tiles : Vec::new(), range: 8, dirty: true })
230	        .with(Monster{})
231	        .with(Name{ name : name.to_string() })
232	        .with(BlocksTile{})
233	        .with(CombatStats{ max_hp: 16, hp: 16, defense: 1, power: 4 })
234	        .marked::<SimpleMarker<SerializeMe>>()
235	        .build();
236	}