1036 lines
53 KiB
HTML
1036 lines
53 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en" style="color-scheme: dark;"><head>
|
||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||
<title>
|
||
Part 12 - Increasing Difficulty · Roguelike Tutorials
|
||
</title>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="color-scheme" content="light dark">
|
||
|
||
|
||
|
||
|
||
<meta name="description" content="Despite the fact that we can go down floors now, the dungeon doesn’t get progressively more difficult as the player descends. This is because the method in which we place monsters and items is the same on each floor. In this chapter, we’ll adjust how we place things in the dungeon, so things get more difficult with each floor.
|
||
Currently, we pass maximum_monsters and maximum_items into the place_entities function, and this number does not change.">
|
||
<meta name="keywords" content="">
|
||
|
||
<meta name="twitter:card" content="summary">
|
||
<meta name="twitter:title" content="Part 12 - Increasing Difficulty">
|
||
<meta name="twitter:description" content="Despite the fact that we can go down floors now, the dungeon doesn’t get progressively more difficult as the player descends. This is because the method in which we place monsters and items is the same on each floor. In this chapter, we’ll adjust how we place things in the dungeon, so things get more difficult with each floor.
|
||
Currently, we pass maximum_monsters and maximum_items into the place_entities function, and this number does not change.">
|
||
|
||
<meta property="og:title" content="Part 12 - Increasing Difficulty">
|
||
<meta property="og:description" content="Despite the fact that we can go down floors now, the dungeon doesn’t get progressively more difficult as the player descends. This is because the method in which we place monsters and items is the same on each floor. In this chapter, we’ll adjust how we place things in the dungeon, so things get more difficult with each floor.
|
||
Currently, we pass maximum_monsters and maximum_items into the place_entities function, and this number does not change.">
|
||
<meta property="og:type" content="article">
|
||
<meta property="og:url" content="https://rogueliketutorials.com/tutorials/tcod/v2/part-12/"><meta property="article:section" content="tutorials">
|
||
<meta property="article:published_time" content="2020-07-28T00:00:00+00:00">
|
||
<meta property="article:modified_time" content="2020-07-28T00:00:00+00:00">
|
||
|
||
|
||
|
||
|
||
<link rel="canonical" href="https://rogueliketutorials.com/tutorials/tcod/v2/part-12/">
|
||
|
||
|
||
<link rel="preload" href="https://rogueliketutorials.com/fonts/forkawesome-webfont.woff2?v=1.2.0" as="font" type="font/woff2" crossorigin="">
|
||
|
||
|
||
|
||
|
||
<link rel="stylesheet" href="Part%2012%20-%20Increasing%20Difficulty%20%C2%B7%20Roguelike%20Tutorials_files/coder.min.c4d7e93a158eda5a65b3df343745d2092a0a1e2170feeec909.css" integrity="sha256-xNfpOhWO2lpls980N0XSCSoKHiFw/u7JCbiolEOQPGo=" crossorigin="anonymous" media="screen">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<link rel="stylesheet" href="Part%2012%20-%20Increasing%20Difficulty%20%C2%B7%20Roguelike%20Tutorials_files/coder-dark.min.78b5fe3864945faf5207fb8fe3ab2320d49c3365def0e.css" integrity="sha256-eLX+OGSUX69SB/uP46sjINScM2Xe8OiKwd8N2txUoDw=" crossorigin="anonymous" media="screen">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<link rel="stylesheet" href="Part%2012%20-%20Increasing%20Difficulty%20%C2%B7%20Roguelike%20Tutorials_files/style.min.9d3eb202952dddb888856ff12c83bc88de866c596286bfb4c1.css" integrity="sha256-nT6yApUt3biIhW/xLIO8iN6GbFlihr+0wfjmvq2a42Y=" crossorigin="anonymous" media="screen">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<link rel="icon" type="image/png" href="https://rogueliketutorials.com/images/favicon-32x32.png" sizes="32x32">
|
||
<link rel="icon" type="image/png" href="https://rogueliketutorials.com/images/favicon-16x16.png" sizes="16x16">
|
||
|
||
<link rel="apple-touch-icon" href="https://rogueliketutorials.com/images/apple-touch-icon.png">
|
||
<link rel="apple-touch-icon" sizes="180x180" href="https://rogueliketutorials.com/images/apple-touch-icon.png">
|
||
|
||
<link rel="manifest" href="https://rogueliketutorials.com/site.webmanifest">
|
||
<link rel="mask-icon" href="https://rogueliketutorials.com/images/safari-pinned-tab.svg" color="#5bbad5">
|
||
|
||
|
||
|
||
|
||
<meta name="generator" content="Hugo 0.110.0">
|
||
|
||
|
||
|
||
|
||
|
||
<style>:is([id*='google_ads_iframe'],[id*='taboola-'],.taboolaHeight,.taboola-placeholder,#top-ad,#credential_picker_container,#credentials-picker-container,#credential_picker_iframe,[id*='google-one-tap-iframe'],#google-one-tap-popup-container,.google-one-tap__module,.google-one-tap-modal-div,#amp_floatingAdDiv,#ez-content-blocker-container) {display:none!important;min-height:0!important;height:0!important;}</style></head>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<body class="colorscheme-dark vsc-initialized">
|
||
|
||
<div class="float-container">
|
||
<a id="dark-mode-toggle" class="colorscheme-toggle">
|
||
<i class="fa fa-adjust fa-fw" aria-hidden="true"></i>
|
||
</a>
|
||
</div>
|
||
|
||
|
||
<main class="wrapper">
|
||
<nav class="navigation">
|
||
<section class="container">
|
||
<a class="navigation-title" href="https://rogueliketutorials.com/">
|
||
Roguelike Tutorials
|
||
</a>
|
||
|
||
<input type="checkbox" id="menu-toggle">
|
||
<label class="menu-button float-right" for="menu-toggle">
|
||
<i class="fa fa-bars fa-fw" aria-hidden="true"></i>
|
||
</label>
|
||
<ul class="navigation-list">
|
||
|
||
|
||
<li class="navigation-item">
|
||
<a class="navigation-link" href="https://rogueliketutorials.com/">Home</a>
|
||
</li>
|
||
|
||
<li class="navigation-item">
|
||
<a class="navigation-link" href="https://rogueliketutorials.com/tutorials/tcod/v2/">TCOD Tutorial (2020)</a>
|
||
</li>
|
||
|
||
<li class="navigation-item">
|
||
<a class="navigation-link" href="https://rogueliketutorials.com/tutorials/tcod/2019/">TCOD Tutorial (2019)</a>
|
||
</li>
|
||
|
||
<li class="navigation-item">
|
||
<a class="navigation-link" href="https://rogueliketutorials.com/about/">About</a>
|
||
</li>
|
||
|
||
|
||
|
||
</ul>
|
||
|
||
</section>
|
||
</nav>
|
||
|
||
|
||
<div class="content">
|
||
|
||
<section class="container page">
|
||
<article>
|
||
<header>
|
||
<h1 class="title">
|
||
<a class="title-link" href="https://rogueliketutorials.com/tutorials/tcod/v2/part-12/">
|
||
Part 12 - Increasing Difficulty
|
||
</a>
|
||
</h1>
|
||
</header>
|
||
|
||
<p>Despite the fact that we can go down floors now, the dungeon
|
||
doesn’t get progressively more difficult as the player descends. This is
|
||
because the method in which we place monsters and items is the same on
|
||
each floor. In this chapter, we’ll adjust how we place things in the
|
||
dungeon, so things get more difficult with each floor.</p>
|
||
<p>Currently, we pass <code>maximum_monsters</code> and <code>maximum_items</code> into the <code>place_entities</code>
|
||
function, and this number does not change. To adjust the difficulty of
|
||
our game, we can change these numbers based on the floor number. The way
|
||
we’ll accomplish this is by setting up a list of tuples, which will
|
||
contain two integers: the floor number, and the number of
|
||
items/monsters.</p>
|
||
<p>Add the following to <code>procgen.py</code>:</p>
|
||
<div>
|
||
<button class="btn btn-primary data-toggle-tab active" data-toggle-tab="diff">
|
||
Diff
|
||
</button>
|
||
<button class="btn btn-secondary data-toggle-tab" data-toggle-tab="original">
|
||
Original
|
||
</button>
|
||
|
||
|
||
<div class="data-pane active" data-pane="diff">
|
||
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-diff" data-lang="diff"><span style="display:flex;"><span>...
|
||
</span></span><span style="display:flex;"><span>if TYPE_CHECKING:
|
||
</span></span><span style="display:flex;"><span> from engine import Engine
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+max_items_by_floor = [
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ (1, 1),
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ (4, 2),
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+]
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+max_monsters_by_floor = [
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ (1, 2),
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ (4, 3),
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ (6, 5),
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+]
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>class RectangularRoom:
|
||
</span></span><span style="display:flex;"><span> ...
|
||
</span></span></code></pre></div>
|
||
|
||
</div>
|
||
<div class="data-pane" data-pane="original">
|
||
|
||
<pre>...
|
||
if TYPE_CHECKING:
|
||
from engine import Engine
|
||
|
||
|
||
<span class="new-text">max_items_by_floor = [
|
||
(1, 1),
|
||
(4, 2),
|
||
]
|
||
|
||
max_monsters_by_floor = [
|
||
(1, 2),
|
||
(4, 3),
|
||
(6, 5),
|
||
]</span>
|
||
|
||
|
||
class RectangularRoom:
|
||
...</pre>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<p>As mentioned, the first number in these tuples represents the floor
|
||
number, and the second represents the maximum of either the items or the
|
||
monsters.</p>
|
||
<p>You might be wondering why we’ve only supplied values for only
|
||
certain floors. Rather than having to type out each floor number, we’ll
|
||
provide the floor numbers that have a different value, so that we can
|
||
loop through the list and stop when we hit a floor number higher than
|
||
the one we’re on. For example, if we’re on floor 3, we’ll take the floor
|
||
1 entry for both items and monsters, and stop iteration when we reach
|
||
the second item in the list, since floor 4 is higher than floor 3.</p>
|
||
<p>Let’s write the function to take care of this. We’ll call it <code>get_max_value_for_floor</code>, as we’re getting the maximum value for either the items or monsters. It looks like this:</p>
|
||
<div>
|
||
<button class="btn btn-primary data-toggle-tab active" data-toggle-tab="diff">
|
||
Diff
|
||
</button>
|
||
<button class="btn btn-secondary data-toggle-tab" data-toggle-tab="original">
|
||
Original
|
||
</button>
|
||
|
||
|
||
<div class="data-pane active" data-pane="diff">
|
||
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-diff" data-lang="diff"><span style="display:flex;"><span>...
|
||
</span></span><span style="display:flex;"><span>max_items_by_floor = [
|
||
</span></span><span style="display:flex;"><span> (1, 1),
|
||
</span></span><span style="display:flex;"><span> (4, 2),
|
||
</span></span><span style="display:flex;"><span>]
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>max_monsters_by_floor = [
|
||
</span></span><span style="display:flex;"><span> (1, 2),
|
||
</span></span><span style="display:flex;"><span> (4, 3),
|
||
</span></span><span style="display:flex;"><span> (6, 5),
|
||
</span></span><span style="display:flex;"><span>]
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+def get_max_value_for_floor(
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ weighted_chances_by_floor: List[Tuple[int, int]], floor: int
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+) -> int:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ current_value = 0
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ for floor_minimum, value in weighted_chances_by_floor:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ if floor_minimum > floor:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ break
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ else:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ current_value = value
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ return current_value
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>class RectangularRoom:
|
||
</span></span><span style="display:flex;"><span> ...
|
||
</span></span></code></pre></div>
|
||
|
||
</div>
|
||
<div class="data-pane" data-pane="original">
|
||
|
||
<pre>...
|
||
max_items_by_floor = [
|
||
(1, 1),
|
||
(4, 2),
|
||
]
|
||
|
||
max_monsters_by_floor = [
|
||
(1, 2),
|
||
(4, 3),
|
||
(6, 5),
|
||
]
|
||
|
||
|
||
<span class="new-text">def get_max_value_for_floor(
|
||
max_value_by_floor: List[Tuple[int, int]], floor: int
|
||
) -> int:
|
||
current_value = 0
|
||
|
||
for floor_minimum, value in max_value_by_floor:
|
||
if floor_minimum > floor:
|
||
break
|
||
else:
|
||
current_value = value
|
||
|
||
return current_value</span>
|
||
|
||
|
||
class RectangularRoom:
|
||
...</pre>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<p>Using this function is quite simple: we simply remove the <code>maximum_monsters</code> and <code>maximum_items</code> parameters from the <code>place_entities</code> function, pass the <code>floor_number</code> instead, and use that to get our maximum values from the <code>get_max_value_for_floor</code> function.</p>
|
||
<div>
|
||
<button class="btn btn-primary data-toggle-tab active" data-toggle-tab="diff">
|
||
Diff
|
||
</button>
|
||
<button class="btn btn-secondary data-toggle-tab" data-toggle-tab="original">
|
||
Original
|
||
</button>
|
||
|
||
|
||
<div class="data-pane active" data-pane="diff">
|
||
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-diff" data-lang="diff"><span style="display:flex;"><span><span style="color:#f92672">-def place_entities(
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- room: RectangularRoom, dungeon: GameMap, maximum_monsters: int, maximum_items: int
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">-) -> None:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- number_of_monsters = random.randint(0, maximum_monsters)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- number_of_items = random.randint(0, maximum_items)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span><span style="color:#a6e22e">+def place_entities(room: RectangularRoom, dungeon: GameMap, floor_number: int,) -> None:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ number_of_monsters = random.randint(
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ 0, get_max_value_for_floor(max_monsters_by_floor, floor_number)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ )
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ number_of_items = random.randint(
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ 0, get_max_value_for_floor(max_items_by_floor, floor_number)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ )
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span> for i in range(number_of_monsters):
|
||
</span></span><span style="display:flex;"><span> ...
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>...
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>def generate_dungeon(
|
||
</span></span><span style="display:flex;"><span> max_rooms: int,
|
||
</span></span><span style="display:flex;"><span> room_min_size: int,
|
||
</span></span><span style="display:flex;"><span> room_max_size: int,
|
||
</span></span><span style="display:flex;"><span> map_width: int,
|
||
</span></span><span style="display:flex;"><span> map_height: int,
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">- max_monsters_per_room: int,
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- max_items_per_room: int,
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span> engine: Engine,
|
||
</span></span><span style="display:flex;"><span>) -> GameMap:
|
||
</span></span><span style="display:flex;"><span> ...
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> ...
|
||
</span></span><span style="display:flex;"><span> center_of_last_room = new_room.center
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">- place_entities(new_room, dungeon, max_monsters_per_room, max_items_per_room)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span><span style="color:#a6e22e">+ place_entities(new_room, dungeon, engine.game_world.current_floor)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span> dungeon.tiles[center_of_last_room] = tile_types.down_stairs
|
||
</span></span><span style="display:flex;"><span> dungeon.downstairs_location = center_of_last_room
|
||
</span></span></code></pre></div>
|
||
|
||
</div>
|
||
<div class="data-pane" data-pane="original">
|
||
|
||
<pre><span class="crossed-out-text">def place_entities(</span>
|
||
<span class="crossed-out-text">room: RectangularRoom, dungeon: GameMap, maximum_monsters: int, maximum_items: int</span>
|
||
<span class="crossed-out-text">) -> None:</span>
|
||
<span class="crossed-out-text">number_of_monsters = random.randint(0, maximum_monsters)</span>
|
||
<span class="crossed-out-text">number_of_items = random.randint(0, maximum_items)</span>
|
||
<span class="new-text">def place_entities(room: RectangularRoom, dungeon: GameMap, floor_number: int,) -> None:
|
||
number_of_monsters = random.randint(
|
||
0, get_max_value_for_floor(max_monsters_by_floor, floor_number)
|
||
)
|
||
number_of_items = random.randint(
|
||
0, get_max_value_for_floor(max_items_by_floor, floor_number)
|
||
)</span>
|
||
|
||
for i in range(number_of_monsters):
|
||
...
|
||
|
||
...
|
||
|
||
def generate_dungeon(
|
||
max_rooms: int,
|
||
room_min_size: int,
|
||
room_max_size: int,
|
||
map_width: int,
|
||
map_height: int,
|
||
<span class="crossed-out-text">max_monsters_per_room: int,</span>
|
||
<span class="crossed-out-text">max_items_per_room: int,</span>
|
||
engine: Engine,
|
||
) -> GameMap:
|
||
...
|
||
|
||
...
|
||
center_of_last_room = new_room.center
|
||
|
||
<span class="crossed-out-text">place_entities(new_room, dungeon, max_monsters_per_room, max_items_per_room)</span>
|
||
<span class="new-text">place_entities(new_room, dungeon, engine.game_world.current_floor)</span>
|
||
|
||
dungeon.tiles[center_of_last_room] = tile_types.down_stairs
|
||
dungeon.downstairs_location = center_of_last_room</pre>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<p>We can also remove <code>max_monsters_per_room</code> and <code>max_items_per_room</code> from <code>GameWorld</code>. Remove these lines from <code>game_map.py</code>:</p>
|
||
<div>
|
||
<button class="btn btn-primary data-toggle-tab active" data-toggle-tab="diff">
|
||
Diff
|
||
</button>
|
||
<button class="btn btn-secondary data-toggle-tab" data-toggle-tab="original">
|
||
Original
|
||
</button>
|
||
|
||
|
||
<div class="data-pane active" data-pane="diff">
|
||
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-diff" data-lang="diff"><span style="display:flex;"><span>class GameWorld:
|
||
</span></span><span style="display:flex;"><span> """
|
||
</span></span><span style="display:flex;"><span> Holds the settings for the GameMap, and generates new maps when moving down the stairs.
|
||
</span></span><span style="display:flex;"><span> """
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> def __init__(
|
||
</span></span><span style="display:flex;"><span> self,
|
||
</span></span><span style="display:flex;"><span> *,
|
||
</span></span><span style="display:flex;"><span> engine: Engine,
|
||
</span></span><span style="display:flex;"><span> map_width: int,
|
||
</span></span><span style="display:flex;"><span> map_height: int,
|
||
</span></span><span style="display:flex;"><span> max_rooms: int,
|
||
</span></span><span style="display:flex;"><span> room_min_size: int,
|
||
</span></span><span style="display:flex;"><span> room_max_size: int,
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">- max_monsters_per_room: int,
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- max_items_per_room: int,
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span> current_floor: int = 0
|
||
</span></span><span style="display:flex;"><span> ):
|
||
</span></span><span style="display:flex;"><span> self.engine = engine
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> self.map_width = map_width
|
||
</span></span><span style="display:flex;"><span> self.map_height = map_height
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> self.max_rooms = max_rooms
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> self.room_min_size = room_min_size
|
||
</span></span><span style="display:flex;"><span> self.room_max_size = room_max_size
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">- self.max_monsters_per_room = max_monsters_per_room
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- self.max_items_per_room = max_items_per_room
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span>
|
||
</span></span><span style="display:flex;"><span> self.current_floor = current_floor
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> def generate_floor(self) -> None:
|
||
</span></span><span style="display:flex;"><span> from procgen import generate_dungeon
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> self.current_floor += 1
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> self.engine.game_map = generate_dungeon(
|
||
</span></span><span style="display:flex;"><span> max_rooms=self.max_rooms,
|
||
</span></span><span style="display:flex;"><span> room_min_size=self.room_min_size,
|
||
</span></span><span style="display:flex;"><span> room_max_size=self.room_max_size,
|
||
</span></span><span style="display:flex;"><span> map_width=self.map_width,
|
||
</span></span><span style="display:flex;"><span> map_height=self.map_height,
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">- max_monsters_per_room=self.max_monsters_per_room,
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- max_items_per_room=self.max_items_per_room,
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span> engine=self.engine,
|
||
</span></span><span style="display:flex;"><span> )
|
||
</span></span></code></pre></div>
|
||
|
||
</div>
|
||
<div class="data-pane" data-pane="original">
|
||
|
||
<pre>class GameWorld:
|
||
"""
|
||
Holds the settings for the GameMap, and generates new maps when moving down the stairs.
|
||
"""
|
||
|
||
def __init__(
|
||
self,
|
||
*,
|
||
engine: Engine,
|
||
map_width: int,
|
||
map_height: int,
|
||
max_rooms: int,
|
||
room_min_size: int,
|
||
room_max_size: int,
|
||
<span class="crossed-out-text">max_monsters_per_room: int,</span>
|
||
<span class="crossed-out-text">max_items_per_room: int,</span>
|
||
current_floor: int = 0
|
||
):
|
||
self.engine = engine
|
||
|
||
self.map_width = map_width
|
||
self.map_height = map_height
|
||
|
||
self.max_rooms = max_rooms
|
||
|
||
self.room_min_size = room_min_size
|
||
self.room_max_size = room_max_size
|
||
|
||
<span class="crossed-out-text">self.max_monsters_per_room = max_monsters_per_room</span>
|
||
<span class="crossed-out-text">self.max_items_per_room = max_items_per_room</span>
|
||
|
||
self.current_floor = current_floor
|
||
|
||
def generate_floor(self) -> None:
|
||
from procgen import generate_dungeon
|
||
|
||
self.current_floor += 1
|
||
|
||
self.engine.game_map = generate_dungeon(
|
||
max_rooms=self.max_rooms,
|
||
room_min_size=self.room_min_size,
|
||
room_max_size=self.room_max_size,
|
||
map_width=self.map_width,
|
||
map_height=self.map_height,
|
||
<span class="crossed-out-text">max_monsters_per_room=self.max_monsters_per_room,</span>
|
||
<span class="crossed-out-text">max_items_per_room=self.max_items_per_room,</span>
|
||
engine=self.engine,
|
||
)</pre>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<p>Also remove the same variables from <code>setup_game.py</code>:</p>
|
||
<div>
|
||
<button class="btn btn-primary data-toggle-tab active" data-toggle-tab="diff">
|
||
Diff
|
||
</button>
|
||
<button class="btn btn-secondary data-toggle-tab" data-toggle-tab="original">
|
||
Original
|
||
</button>
|
||
|
||
|
||
<div class="data-pane active" data-pane="diff">
|
||
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-diff" data-lang="diff"><span style="display:flex;"><span>def new_game() -> Engine:
|
||
</span></span><span style="display:flex;"><span> """Return a brand new game session as an Engine instance."""
|
||
</span></span><span style="display:flex;"><span> map_width = 80
|
||
</span></span><span style="display:flex;"><span> map_height = 43
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> room_max_size = 10
|
||
</span></span><span style="display:flex;"><span> room_min_size = 6
|
||
</span></span><span style="display:flex;"><span> max_rooms = 30
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">- max_monsters_per_room = 2
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- max_items_per_room = 2
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span>
|
||
</span></span><span style="display:flex;"><span> player = copy.deepcopy(entity_factories.player)
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> engine = Engine(player=player)
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> engine.game_world = GameWorld(
|
||
</span></span><span style="display:flex;"><span> engine=engine,
|
||
</span></span><span style="display:flex;"><span> max_rooms=max_rooms,
|
||
</span></span><span style="display:flex;"><span> room_min_size=room_min_size,
|
||
</span></span><span style="display:flex;"><span> room_max_size=room_max_size,
|
||
</span></span><span style="display:flex;"><span> map_width=map_width,
|
||
</span></span><span style="display:flex;"><span> map_height=map_height,
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">- max_monsters_per_room=max_monsters_per_room,
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- max_items_per_room=max_items_per_room,
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span> )
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> engine.game_world.generate_floor()
|
||
</span></span><span style="display:flex;"><span> engine.update_fov()
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> engine.message_log.add_message(
|
||
</span></span><span style="display:flex;"><span> "Hello and welcome, adventurer, to yet another dungeon!", color.welcome_text
|
||
</span></span><span style="display:flex;"><span> )
|
||
</span></span><span style="display:flex;"><span> return engine
|
||
</span></span></code></pre></div>
|
||
|
||
</div>
|
||
<div class="data-pane" data-pane="original">
|
||
|
||
<pre>def new_game() -> Engine:
|
||
"""Return a brand new game session as an Engine instance."""
|
||
map_width = 80
|
||
map_height = 43
|
||
|
||
room_max_size = 10
|
||
room_min_size = 6
|
||
max_rooms = 30
|
||
|
||
<span class="crossed-out-text">max_monsters_per_room = 2</span>
|
||
<span class="crossed-out-text">max_items_per_room = 2</span>
|
||
|
||
player = copy.deepcopy(entity_factories.player)
|
||
|
||
engine = Engine(player=player)
|
||
|
||
engine.game_world = GameWorld(
|
||
engine=engine,
|
||
max_rooms=max_rooms,
|
||
room_min_size=room_min_size,
|
||
room_max_size=room_max_size,
|
||
map_width=map_width,
|
||
map_height=map_height,
|
||
<span class="crossed-out-text">max_monsters_per_room=max_monsters_per_room,</span>
|
||
<span class="crossed-out-text">max_items_per_room=max_items_per_room,</span>
|
||
)
|
||
|
||
engine.game_world.generate_floor()
|
||
engine.update_fov()
|
||
|
||
engine.message_log.add_message(
|
||
"Hello and welcome, adventurer, to yet another dungeon!", color.welcome_text
|
||
)
|
||
return engine</pre>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<p>Now we’re adjusting the number of items and monsters based on the
|
||
floor. The next step is to control which entities appear on which floor,
|
||
instead of allowing any entity to appear on any floor. The first floor
|
||
will only have health potions and orcs, and we’ll gradually add
|
||
different items and enemies as the player goes deeper into the dungeon.</p>
|
||
<p>We need a function that allows us to get these entities at random,
|
||
based on a set of weights. We also need to define the weights
|
||
themselves.</p>
|
||
<p>What are “weights” in this context? Basically, we could define all of
|
||
the odds of generating a type of entity the way we have already, by
|
||
getting a random number and comparing against a set of values, but that
|
||
will quickly become cumbersome as we add more entities. Imagine wanting
|
||
to add a new enemy type, but needing to adjust the values for dozens, or
|
||
perhaps <strong>hundreds</strong>, of other entities.</p>
|
||
<p>Instead, we’ll just give each entity a value, or a “weight”, which
|
||
we’ll use to determine how common that entity should be. We’ll use
|
||
Python’s <code>random.choices</code> function, which allows the user to
|
||
pass a list of items and a set of weights. It returns a number of items
|
||
that you specify, based on the weights you give it.</p>
|
||
<p>First, we need to define our weights for the entity types, along with
|
||
the minimum floor that the item or monster will appear on. Add the
|
||
following to <code>procgen.py</code>:</p>
|
||
<div>
|
||
<button class="btn btn-primary data-toggle-tab active" data-toggle-tab="diff">
|
||
Diff
|
||
</button>
|
||
<button class="btn btn-secondary data-toggle-tab" data-toggle-tab="original">
|
||
Original
|
||
</button>
|
||
|
||
|
||
<div class="data-pane active" data-pane="diff">
|
||
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-diff" data-lang="diff"><span style="display:flex;"><span>from __future__ import annotations
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>import random
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">-from typing import Iterator, List, Tuple, TYPE_CHECKING
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span><span style="color:#a6e22e">+from typing import Dict, Iterator, List, Tuple, TYPE_CHECKING
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span>import tcod
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>import entity_factories
|
||
</span></span><span style="display:flex;"><span>from game_map import GameMap
|
||
</span></span><span style="display:flex;"><span>import tile_types
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>if TYPE_CHECKING:
|
||
</span></span><span style="display:flex;"><span> from engine import Engine
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ from entity import Entity
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>max_items_by_floor = [
|
||
</span></span><span style="display:flex;"><span> (1, 1),
|
||
</span></span><span style="display:flex;"><span> (4, 2),
|
||
</span></span><span style="display:flex;"><span>]
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>max_monsters_by_floor = [
|
||
</span></span><span style="display:flex;"><span> (1, 2),
|
||
</span></span><span style="display:flex;"><span> (4, 3),
|
||
</span></span><span style="display:flex;"><span> (6, 5),
|
||
</span></span><span style="display:flex;"><span>]
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+item_chances: Dict[int, List[Tuple[Entity, int]]] = {
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ 0: [(entity_factories.health_potion, 35)],
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ 2: [(entity_factories.confusion_scroll, 10)],
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ 4: [(entity_factories.lightning_scroll, 25)],
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ 6: [(entity_factories.fireball_scroll, 25)],
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+}
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+enemy_chances: Dict[int, List[Tuple[Entity, int]]] = {
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ 0: [(entity_factories.orc, 80)],
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ 3: [(entity_factories.troll, 15)],
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ 5: [(entity_factories.troll, 30)],
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ 7: [(entity_factories.troll, 60)],
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+}
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>def get_max_value_for_floor(
|
||
</span></span><span style="display:flex;"><span> ...
|
||
</span></span></code></pre></div>
|
||
|
||
</div>
|
||
<div class="data-pane" data-pane="original">
|
||
|
||
<pre>from __future__ import annotations
|
||
|
||
import random
|
||
<span class="crossed-out-text">from typing import Iterator, List, Tuple, TYPE_CHECKING</span>
|
||
<span class="new-text">from typing import Dict, Iterator, List, Tuple, TYPE_CHECKING</span>
|
||
|
||
import tcod
|
||
|
||
import entity_factories
|
||
from game_map import GameMap
|
||
import tile_types
|
||
|
||
if TYPE_CHECKING:
|
||
from engine import Engine
|
||
<span class="new-text">from entity import Entity</span>
|
||
|
||
|
||
max_items_by_floor = [
|
||
(1, 1),
|
||
(4, 2),
|
||
]
|
||
|
||
max_monsters_by_floor = [
|
||
(1, 2),
|
||
(4, 3),
|
||
(6, 5),
|
||
]
|
||
|
||
<span class="new-text">item_chances: Dict[int, List[Tuple[Entity, int]]] = {
|
||
0: [(entity_factories.health_potion, 35)],
|
||
2: [(entity_factories.confusion_scroll, 10)],
|
||
4: [(entity_factories.lightning_scroll, 25)],
|
||
6: [(entity_factories.fireball_scroll, 25)],
|
||
}
|
||
|
||
enemy_chances: Dict[int, List[Tuple[Entity, int]]] = {
|
||
0: [(entity_factories.orc, 80)],
|
||
3: [(entity_factories.troll, 15)],
|
||
5: [(entity_factories.troll, 30)],
|
||
7: [(entity_factories.troll, 60)],
|
||
}</span>
|
||
|
||
|
||
def get_max_value_for_floor(
|
||
...</pre>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<p>They keys in the dictionary represent the floor number, and the value
|
||
is a list of tuples. The tuples contain an entity and the weights at
|
||
which they’ll be generated. Notice that Trolls get defined multiple
|
||
times in <code>enemy_chances</code>, and their weights grow higher when
|
||
the floor number increases. This will allow Trolls to be generated more
|
||
frequently as the player dives into the dungeon, thus making the dungeon
|
||
more dangerous with each passing floor.</p>
|
||
<p>Why a <em>list</em> of tuples, though? While there isn’t any examples
|
||
here, we want it to be possible to define many entity types and weights
|
||
for each floor. For example, imagine we added a new enemy type that
|
||
appears on floor 5. We could put that as a tuple inside the list,
|
||
alongside the Troll’s tuple. We’ll see an example of this in the next
|
||
chapter, when we start adding equipment.</p>
|
||
<p>With our weights defined, we need a function to actually pick which entities we want to create. As mentioned, it will utilize <code>random.choices</code> from the Python standard library to choose the entities. Add this function to <code>procgen.py</code>:</p>
|
||
<div>
|
||
<button class="btn btn-primary data-toggle-tab active" data-toggle-tab="diff">
|
||
Diff
|
||
</button>
|
||
<button class="btn btn-secondary data-toggle-tab" data-toggle-tab="original">
|
||
Original
|
||
</button>
|
||
|
||
|
||
<div class="data-pane active" data-pane="diff">
|
||
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-diff" data-lang="diff"><span style="display:flex;"><span>def get_max_value_for_floor(
|
||
</span></span><span style="display:flex;"><span> weighted_chances_by_floor: List[Tuple[int, int]], floor: int
|
||
</span></span><span style="display:flex;"><span>) -> int:
|
||
</span></span><span style="display:flex;"><span> ...
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+def get_entities_at_random(
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ weighted_chances_by_floor: Dict[int, List[Tuple[Entity, int]]],
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ number_of_entities: int,
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ floor: int,
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+) -> List[Entity]:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ entity_weighted_chances = {}
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ for key, values in weighted_chances_by_floor.items():
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ if key > floor:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ break
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ else:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ for value in values:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ entity = value[0]
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ weighted_chance = value[1]
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ entity_weighted_chances[entity] = weighted_chance
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ entities = list(entity_weighted_chances.keys())
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ entity_weighted_chance_values = list(entity_weighted_chances.values())
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ chosen_entities = random.choices(
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ entities, weights=entity_weighted_chance_values, k=number_of_entities
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ )
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ return chosen_entities
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>class RectangularRoom:
|
||
</span></span><span style="display:flex;"><span> ...
|
||
</span></span></code></pre></div>
|
||
|
||
</div>
|
||
<div class="data-pane" data-pane="original">
|
||
|
||
<pre>def get_max_value_for_floor(
|
||
weighted_chances_by_floor: List[Tuple[int, int]], floor: int
|
||
) -> int:
|
||
...
|
||
|
||
|
||
<span class="new-text">def get_entities_at_random(
|
||
weighted_chances_by_floor: Dict[int, List[Tuple[Entity, int]]],
|
||
number_of_entities: int,
|
||
floor: int,
|
||
) -> List[Entity]:
|
||
entity_weighted_chances = {}
|
||
|
||
for key, values in weighted_chances_by_floor.items():
|
||
if key > floor:
|
||
break
|
||
else:
|
||
for value in values:
|
||
entity = value[0]
|
||
weighted_chance = value[1]
|
||
|
||
entity_weighted_chances[entity] = weighted_chance
|
||
|
||
entities = list(entity_weighted_chances.keys())
|
||
entity_weighted_chance_values = list(entity_weighted_chances.values())
|
||
|
||
chosen_entities = random.choices(
|
||
entities, weights=entity_weighted_chance_values, k=number_of_entities
|
||
)
|
||
|
||
return chosen_entities</span>
|
||
|
||
|
||
class RectangularRoom:
|
||
...</pre>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<p>This function goes through they keys (floor numbers) and values (list
|
||
of weighted entities), stopping when the key is higher than the given
|
||
floor number. It sets up a dictionary of the weights for each entity,
|
||
based on which floor the player is currently on. So if we were trying to
|
||
get the weights for floor 6, <code>entity_weighted_chances</code> would look like this: <code>{ orc: 80, troll: 30 }</code>.</p>
|
||
<p>Then, we get both the keys and values in list format, so that they can be passed to <code>random.choices</code> (it accepts choices and weights as lists). <code>k</code> represents the number of items that <code>random.choices</code>
|
||
should pick, so we can simply pass the number of entities we’ve decided
|
||
to generate. Finally, we return the list of chosen entities.</p>
|
||
<p>Putting this function to use is quite simple. In fact, it will reduce the amount of code in our <code>place_entities</code> function quite nicely:</p>
|
||
<div>
|
||
<button class="btn btn-primary data-toggle-tab active" data-toggle-tab="diff">
|
||
Diff
|
||
</button>
|
||
<button class="btn btn-secondary data-toggle-tab" data-toggle-tab="original">
|
||
Original
|
||
</button>
|
||
|
||
|
||
<div class="data-pane active" data-pane="diff">
|
||
|
||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-diff" data-lang="diff"><span style="display:flex;"><span>def place_entities(room: RectangularRoom, dungeon: GameMap, floor_number: int,) -> None:
|
||
</span></span><span style="display:flex;"><span> number_of_monsters = random.randint(
|
||
</span></span><span style="display:flex;"><span> 0, get_weight_for_floor(max_monsters_by_floor, floor_number)
|
||
</span></span><span style="display:flex;"><span> )
|
||
</span></span><span style="display:flex;"><span> number_of_items = random.randint(
|
||
</span></span><span style="display:flex;"><span> 0, get_weight_for_floor(max_items_by_floor, floor_number)
|
||
</span></span><span style="display:flex;"><span> )
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ monsters: List[Entity] = get_entities_at_random(
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ enemy_chances, number_of_monsters, floor_number
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ )
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ items: List[Entity] = get_entities_at_random(
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ item_chances, number_of_items, floor_number
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ )
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">- for i in range(number_of_monsters):
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- x = random.randint(room.x1 + 1, room.x2 - 1)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- y = random.randint(room.y1 + 1, room.y2 - 1)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">- if not any(entity.x == x and entity.y == y for entity in dungeon.entities):
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- if random.random() < 0.8:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- entity_factories.orc.spawn(dungeon, x, y)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- else:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- entity_factories.troll.spawn(dungeon, x, y)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">- for i in range(number_of_items):
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span><span style="color:#a6e22e">+ for entity in monsters + items:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span> x = random.randint(room.x1 + 1, room.x2 - 1)
|
||
</span></span><span style="display:flex;"><span> y = random.randint(room.y1 + 1, room.y2 - 1)
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> if not any(entity.x == x and entity.y == y for entity in dungeon.entities):
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">- item_chance = random.random()
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">- if item_chance < 0.7:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- entity_factories.health_potion.spawn(dungeon, x, y)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- elif item_chance < 0.80:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- entity_factories.fireball_scroll.spawn(dungeon, x, y)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- elif item_chance < 0.90:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- entity_factories.confusion_scroll.spawn(dungeon, x, y)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- else:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- entity_factories.lightning_scroll.spawn(dungeon, x, y)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span><span style="color:#a6e22e">+ entity.spawn(dungeon, x, y)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span>...
|
||
</span></span></code></pre></div>
|
||
|
||
</div>
|
||
<div class="data-pane" data-pane="original">
|
||
|
||
<pre>def place_entities(room: RectangularRoom, dungeon: GameMap, floor_number: int,) -> None:
|
||
number_of_monsters = random.randint(
|
||
0, get_weight_for_floor(max_monsters_by_floor, floor_number)
|
||
)
|
||
number_of_items = random.randint(
|
||
0, get_weight_for_floor(max_items_by_floor, floor_number)
|
||
)
|
||
|
||
<span class="new-text">monsters: List[Entity] = get_entities_at_random(
|
||
enemy_chances, number_of_monsters, floor_number
|
||
)
|
||
items: List[Entity] = get_entities_at_random(
|
||
item_chances, number_of_items, floor_number
|
||
)</span>
|
||
|
||
<span class="crossed-out-text">for i in range(number_of_monsters):</span>
|
||
<span class="crossed-out-text">x = random.randint(room.x1 + 1, room.x2 - 1)</span>
|
||
<span class="crossed-out-text">y = random.randint(room.y1 + 1, room.y2 - 1)</span>
|
||
|
||
<span class="crossed-out-text">if not any(entity.x == x and entity.y == y for entity in dungeon.entities):</span>
|
||
<span class="crossed-out-text">if random.random() < 0.8:</span>
|
||
<span class="crossed-out-text">entity_factories.orc.spawn(dungeon, x, y)</span>
|
||
<span class="crossed-out-text">else:</span>
|
||
<span class="crossed-out-text">entity_factories.troll.spawn(dungeon, x, y)</span>
|
||
|
||
<span class="crossed-out-text">for i in range(number_of_items):</span>
|
||
<span class="new-text">for entity in monsters + items:</span>
|
||
x = random.randint(room.x1 + 1, room.x2 - 1)
|
||
y = random.randint(room.y1 + 1, room.y2 - 1)
|
||
|
||
if not any(entity.x == x and entity.y == y for entity in dungeon.entities):
|
||
<span class="crossed-out-text">item_chance = random.random()</span>
|
||
|
||
<span class="crossed-out-text">if item_chance < 0.7:</span>
|
||
<span class="crossed-out-text">entity_factories.health_potion.spawn(dungeon, x, y)</span>
|
||
<span class="crossed-out-text">elif item_chance < 0.80:</span>
|
||
<span class="crossed-out-text">entity_factories.fireball_scroll.spawn(dungeon, x, y)</span>
|
||
<span class="crossed-out-text">elif item_chance < 0.90:</span>
|
||
<span class="crossed-out-text">entity_factories.confusion_scroll.spawn(dungeon, x, y)</span>
|
||
<span class="crossed-out-text">else:</span>
|
||
<span class="crossed-out-text">entity_factories.lightning_scroll.spawn(dungeon, x, y)</span>
|
||
<span class="new-text">entity.spawn(dungeon, x, y)</span>
|
||
|
||
...</pre>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<p>Now <code>place_entities</code> is just getting the amount of monsters and items to generate, and leaving it up to <code>get_entities_at_random</code> to determine which ones to create.</p>
|
||
<p>With those changes, the dungeon will get progressively more
|
||
difficult! You may want to tweak certain numbers, like the strength of
|
||
the enemies or how much health you recover with potions, to get a more
|
||
challenging experience (our game is still not <em>that</em> difficult, if you increase your defense by just 1, Orcs are no longer a threat).</p>
|
||
<p>If you want to see the code so far in its entirety, <a href="https://github.com/TStand90/tcod_tutorial_v2/tree/2020/part-12">click here</a>.</p>
|
||
<p><a href="https://rogueliketutorials.com/tutorials/tcod/v2/part-13">Click here to move on to the next part of this tutorial.</a></p>
|
||
|
||
</article>
|
||
</section>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
<footer class="footer">
|
||
<section class="container">
|
||
©
|
||
|
||
2023
|
||
|
||
·
|
||
|
||
Powered by <a href="https://gohugo.io/">Hugo</a> & <a href="https://github.com/luizdepra/hugo-coder/">Coder</a>.
|
||
|
||
</section>
|
||
</footer>
|
||
|
||
</main>
|
||
|
||
|
||
|
||
|
||
|
||
<script src="Part%2012%20-%20Increasing%20Difficulty%20%C2%B7%20Roguelike%20Tutorials_files/coder.min.236049395dc3682fb2719640872958e12f1f24067bb09c327b2.js" integrity="sha256-I2BJOV3DaC+ycZZAhylY4S8fJAZ7sJwyeyM+YpDH7aw="></script>
|
||
|
||
|
||
|
||
|
||
|
||
<script src="Part%2012%20-%20Increasing%20Difficulty%20%C2%B7%20Roguelike%20Tutorials_files/codetabs.min.cc52451e7f25e50f64c1c893826f606d58410d742c214dce.js" integrity="sha256-zFJFHn8l5Q9kwciTgm9gbVhBDXQsIU3OI/tEfJlh8rA="></script>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</body></html> |