11from __future__ import annotations
22
3- from typing import TYPE_CHECKING , Iterator , List , Tuple
3+ from typing import TYPE_CHECKING , Dict , Iterator , List , Tuple
44import copy
55import random
66
77import tcod
88
9- from game .entity import Entity
10- from game .entity_factories import confusion_scroll , fireball_scroll , health_potion , lightning_scroll , orc , troll
11- from game .game_map import GameMap
12- from game .tiles import floor
9+ import game .entity_factories
10+ import game .tiles
1311
1412if TYPE_CHECKING :
1513 import game .engine
14+ import game .entity
15+ import game .game_map
16+
17+
18+ max_items_by_floor = [
19+ (1 , 1 ),
20+ (4 , 2 ),
21+ ]
22+
23+ max_monsters_by_floor = [
24+ (1 , 2 ),
25+ (4 , 3 ),
26+ (6 , 5 ),
27+ ]
28+
29+ item_chances : Dict [int , List [Tuple [game .entity .Entity , int ]]] = {
30+ 0 : [(game .entity_factories .health_potion , 35 )],
31+ 2 : [(game .entity_factories .confusion_scroll , 10 )],
32+ 4 : [(game .entity_factories .lightning_scroll , 25 )],
33+ 6 : [(game .entity_factories .fireball_scroll , 25 )],
34+ }
35+
36+ enemy_chances : Dict [int , List [Tuple [game .entity .Entity , int ]]] = {
37+ 0 : [(game .entity_factories .orc , 80 )],
38+ 3 : [(game .entity_factories .troll , 15 )],
39+ 5 : [(game .entity_factories .troll , 30 )],
40+ 7 : [(game .entity_factories .troll , 60 )],
41+ }
42+
43+
44+ def get_max_value_for_floor (weighted_chances_by_floor : List [Tuple [int , int ]], floor : int ) -> int :
45+ current_value = 0
46+
47+ for floor_minimum , value in weighted_chances_by_floor :
48+ if floor_minimum > floor :
49+ break
50+ else :
51+ current_value = value
52+
53+ return current_value
54+
55+
56+ def get_entities_at_random (
57+ weighted_chances_by_floor : Dict [int , List [Tuple [game .entity .Entity , int ]]],
58+ number_of_entities : int ,
59+ floor : int ,
60+ ) -> List [game .entity .Entity ]:
61+ entity_weighted_chances = {}
62+
63+ for key , values in weighted_chances_by_floor .items ():
64+ if key > floor :
65+ break
66+ else :
67+ for value in values :
68+ entity = value [0 ]
69+ weighted_chance = value [1 ]
70+
71+ entity_weighted_chances [entity ] = weighted_chance
72+
73+ entities = list (entity_weighted_chances .keys ())
74+ entity_weighted_chance_values = list (entity_weighted_chances .values ())
75+
76+ chosen_entities = random .choices (entities , weights = entity_weighted_chance_values , k = number_of_entities )
77+
78+ return chosen_entities
1679
1780
1881class RectangularRoom :
@@ -60,41 +123,21 @@ def tunnel_between(start: Tuple[int, int], end: Tuple[int, int]) -> Iterator[Tup
60123def place_entities (
61124 room : RectangularRoom ,
62125 dungeon : game .game_map .GameMap ,
63- maximum_monsters : int ,
64- maximum_items : int ,
126+ floor_number : int ,
65127) -> None :
66- number_of_monsters = random .randint (0 , maximum_monsters )
67- number_of_items = random .randint (0 , maximum_items )
128+ number_of_monsters = random .randint (0 , get_max_value_for_floor ( max_monsters_by_floor , floor_number ) )
129+ number_of_items = random .randint (0 , get_max_value_for_floor ( max_items_by_floor , floor_number ) )
68130
69- for _ in range (number_of_monsters ):
70- x = random .randint (room .x1 + 1 , room .x2 - 1 )
71- y = random .randint (room .y1 + 1 , room .y2 - 1 )
131+ monsters : List [game .entity .Entity ] = get_entities_at_random (enemy_chances , number_of_monsters , floor_number )
132+ items : List [game .entity .Entity ] = get_entities_at_random (item_chances , number_of_items , floor_number )
72133
73- if not any (entity .x == x and entity .y == y for entity in dungeon .entities ):
74- if random .random () < 0.8 :
75- monster = copy .deepcopy (orc )
76- else :
77- monster = copy .deepcopy (troll )
78-
79- monster .place (x , y , dungeon )
80-
81- for _ in range (number_of_items ):
134+ for entity in monsters + items :
82135 x = random .randint (room .x1 + 1 , room .x2 - 1 )
83136 y = random .randint (room .y1 + 1 , room .y2 - 1 )
84137
85138 if not any (entity .x == x and entity .y == y for entity in dungeon .entities ):
86- item_chance = random .random ()
87-
88- if item_chance < 0.7 :
89- item = copy .deepcopy (health_potion )
90- elif item_chance < 0.8 :
91- item = copy .deepcopy (fireball_scroll )
92- elif item_chance < 0.9 :
93- item = copy .deepcopy (confusion_scroll )
94- else :
95- item = copy .deepcopy (lightning_scroll )
96-
97- item .place (x , y , dungeon )
139+ entity_copy = copy .deepcopy (entity )
140+ entity_copy .place (x , y , dungeon )
98141
99142
100143def generate_dungeon (
@@ -103,13 +146,12 @@ def generate_dungeon(
103146 room_max_size : int ,
104147 map_width : int ,
105148 map_height : int ,
106- max_monsters_per_room : int ,
107- max_items_per_room : int ,
149+ current_floor : int ,
108150 engine : game .engine .Engine ,
109151) -> game .game_map .GameMap :
110152 """Generate a new dungeon map."""
111153 player = engine .player
112- dungeon = GameMap (engine , map_width , map_height )
154+ dungeon = game . game_map . GameMap (engine , map_width , map_height )
113155
114156 rooms : List [RectangularRoom ] = []
115157
@@ -129,17 +171,17 @@ def generate_dungeon(
129171 # If there are no intersections then the room is valid.
130172
131173 # Dig out this rooms inner area.
132- dungeon .tiles [new_room .inner ] = floor
174+ dungeon .tiles [new_room .inner ] = game . tiles . floor
133175
134176 if len (rooms ) == 0 :
135177 # The first room, where the player starts.
136178 player .place (* new_room .center , dungeon )
137179 else : # All rooms after the first.
138180 # Dig out a tunnel between this room and the previous one.
139181 for x , y in tunnel_between (rooms [- 1 ].center , new_room .center ):
140- dungeon .tiles [x , y ] = floor
182+ dungeon .tiles [x , y ] = game . tiles . floor
141183
142- place_entities (new_room , dungeon , max_monsters_per_room , max_items_per_room )
184+ place_entities (new_room , dungeon , current_floor )
143185
144186 # Finally, append the new room to the list.
145187 rooms .append (new_room )
0 commit comments