1524 lines
101 KiB
HTML
1524 lines
101 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 13 - Gearing up · 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="For the final part of this tutorial, we’ll implement something that most roguelikes have: equipment. Our implementation will be extremely simple: equipping a weapon increases attack power, and equipping armor increases defense. Many roguelikes have more equipment types than just these two, and the effects of equipment can go much further than this, but this should be enough to get you started.
|
||
First, we’ll want to define the types of equipment that can be found in the dungeon.">
|
||
<meta name="keywords" content="">
|
||
|
||
<meta name="twitter:card" content="summary">
|
||
<meta name="twitter:title" content="Part 13 - Gearing up">
|
||
<meta name="twitter:description" content="For the final part of this tutorial, we’ll implement something that most roguelikes have: equipment. Our implementation will be extremely simple: equipping a weapon increases attack power, and equipping armor increases defense. Many roguelikes have more equipment types than just these two, and the effects of equipment can go much further than this, but this should be enough to get you started.
|
||
First, we’ll want to define the types of equipment that can be found in the dungeon.">
|
||
|
||
<meta property="og:title" content="Part 13 - Gearing up">
|
||
<meta property="og:description" content="For the final part of this tutorial, we’ll implement something that most roguelikes have: equipment. Our implementation will be extremely simple: equipping a weapon increases attack power, and equipping armor increases defense. Many roguelikes have more equipment types than just these two, and the effects of equipment can go much further than this, but this should be enough to get you started.
|
||
First, we’ll want to define the types of equipment that can be found in the dungeon.">
|
||
<meta property="og:type" content="article">
|
||
<meta property="og:url" content="https://rogueliketutorials.com/tutorials/tcod/v2/part-13/"><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-13/">
|
||
|
||
|
||
<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%2013%20-%20Gearing%20up%20%C2%B7%20Roguelike%20Tutorials_files/coder.min.c4d7e93a158eda5a65b3df343745d2092a0a1e2170feeec909.css" integrity="sha256-xNfpOhWO2lpls980N0XSCSoKHiFw/u7JCbiolEOQPGo=" crossorigin="anonymous" media="screen">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<link rel="stylesheet" href="Part%2013%20-%20Gearing%20up%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%2013%20-%20Gearing%20up%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-13/">
|
||
Part 13 - Gearing up
|
||
</a>
|
||
</h1>
|
||
</header>
|
||
|
||
<p>For the final part of this tutorial, we’ll implement something that <em>most</em>
|
||
roguelikes have: equipment. Our implementation will be extremely
|
||
simple: equipping a weapon increases attack power, and equipping armor
|
||
increases defense. Many roguelikes have more equipment types than just
|
||
these two, and the effects of equipment can go much further than this,
|
||
but this should be enough to get you started.</p>
|
||
<p>First, we’ll want to define the types of equipment that can be found in the dungeon. As with the <code>RenderOrder</code> class, we can use <code>Enum</code> to define the types. For now, we’ll leave it at weapons and armor, but feel free to add more types as you see fit.</p>
|
||
<p>Create a new file, <code>equipment_types.py</code>, and put the following contents in it:</p>
|
||
<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-py3" data-lang="py3"><span style="display:flex;"><span><span style="color:#f92672">from</span> enum <span style="color:#f92672">import</span> auto, Enum
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">EquipmentType</span>(Enum):
|
||
</span></span><span style="display:flex;"><span> WEAPON <span style="color:#f92672">=</span> auto()
|
||
</span></span><span style="display:flex;"><span> ARMOR <span style="color:#f92672">=</span> auto()
|
||
</span></span></code></pre></div><p>Now it’s time to create the component that we’ll attach to the equipment. We’ll call the component <code>Equippable</code>, which will have a few different attributes:</p>
|
||
<ul>
|
||
<li><code>equipment_type</code>: The type of equipment, using the <code>EquipmentType</code> enum.</li>
|
||
<li><code>power_bonus</code>: How much the wielder’s attack power will be increased. Currently used for just weapons.</li>
|
||
<li><code>defense_bonus</code>: How much the wearer’s defense will be increased. Currently just for armor.</li>
|
||
</ul>
|
||
<p>Create the file <code>equippable.py</code> in the <code>components</code> directory, and fill it with the following:</p>
|
||
<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-py3" data-lang="py3"><span style="display:flex;"><span><span style="color:#f92672">from</span> __future__ <span style="color:#f92672">import</span> annotations
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> typing <span style="color:#f92672">import</span> TYPE_CHECKING
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> components.base_component <span style="color:#f92672">import</span> BaseComponent
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> equipment_types <span style="color:#f92672">import</span> EquipmentType
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> TYPE_CHECKING:
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">from</span> entity <span style="color:#f92672">import</span> Item
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Equippable</span>(BaseComponent):
|
||
</span></span><span style="display:flex;"><span> parent: Item
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(
|
||
</span></span><span style="display:flex;"><span> self,
|
||
</span></span><span style="display:flex;"><span> equipment_type: EquipmentType,
|
||
</span></span><span style="display:flex;"><span> power_bonus: int <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>,
|
||
</span></span><span style="display:flex;"><span> defense_bonus: int <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>,
|
||
</span></span><span style="display:flex;"><span> ):
|
||
</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>equipment_type <span style="color:#f92672">=</span> equipment_type
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>power_bonus <span style="color:#f92672">=</span> power_bonus
|
||
</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>defense_bonus <span style="color:#f92672">=</span> defense_bonus
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Dagger</span>(Equippable):
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self) <span style="color:#f92672">-></span> <span style="color:#66d9ef">None</span>:
|
||
</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(equipment_type<span style="color:#f92672">=</span>EquipmentType<span style="color:#f92672">.</span>WEAPON, power_bonus<span style="color:#f92672">=</span><span style="color:#ae81ff">2</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:#66d9ef">class</span> <span style="color:#a6e22e">Sword</span>(Equippable):
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self) <span style="color:#f92672">-></span> <span style="color:#66d9ef">None</span>:
|
||
</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(equipment_type<span style="color:#f92672">=</span>EquipmentType<span style="color:#f92672">.</span>WEAPON, power_bonus<span style="color:#f92672">=</span><span style="color:#ae81ff">4</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:#66d9ef">class</span> <span style="color:#a6e22e">LeatherArmor</span>(Equippable):
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self) <span style="color:#f92672">-></span> <span style="color:#66d9ef">None</span>:
|
||
</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(equipment_type<span style="color:#f92672">=</span>EquipmentType<span style="color:#f92672">.</span>ARMOR, defense_bonus<span style="color:#f92672">=</span><span style="color:#ae81ff">1</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:#66d9ef">class</span> <span style="color:#a6e22e">ChainMail</span>(Equippable):
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self) <span style="color:#f92672">-></span> <span style="color:#66d9ef">None</span>:
|
||
</span></span><span style="display:flex;"><span> super()<span style="color:#f92672">.</span>__init__(equipment_type<span style="color:#f92672">=</span>EquipmentType<span style="color:#f92672">.</span>ARMOR, defense_bonus<span style="color:#f92672">=</span><span style="color:#ae81ff">3</span>)
|
||
</span></span></code></pre></div><p>Aside from creating the <code>Equippable</code>
|
||
class, as described earlier, we’ve also created a few types of
|
||
equippable components, for each equippable entity that we’ll end up
|
||
creating, similar to what we did with the <code>Consumable</code>
|
||
classes. You don’t have to do it this way, you could just define these
|
||
when creating the entities, but you might want to add additional
|
||
functionality to weapons and armor at some point, and defining the <code>Equippable</code>
|
||
classes this way might make that easier. You might also want to move
|
||
these classes to their own file, but that’s outside the scope of this
|
||
tutorial.</p>
|
||
<p>To create the actual equippable entities, we’ll want to adjust our <code>Item</code>
|
||
class. We can use the same class that we used for our consumables, and
|
||
just handle them slightly differently. Another approach would be to
|
||
create another subclass of <code>Entity</code>, but for the sake of keeping the number of <code>Entity</code> subclasses in this tutorial short, we’ll adjust <code>Item</code>. Make the following adjustments to <code>entity.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 components.ai import BaseAI
|
||
</span></span><span style="display:flex;"><span> from components.consumable import Consumable
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ from components.equippable import Equippable
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span> from components.fighter import Fighter
|
||
</span></span><span style="display:flex;"><span> from components.inventory import Inventory
|
||
</span></span><span style="display:flex;"><span> from components.level import Level
|
||
</span></span><span style="display:flex;"><span> from game_map import GameMap
|
||
</span></span><span style="display:flex;"><span>...
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>class Item(Entity):
|
||
</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> x: int = 0,
|
||
</span></span><span style="display:flex;"><span> y: int = 0,
|
||
</span></span><span style="display:flex;"><span> char: str = "?",
|
||
</span></span><span style="display:flex;"><span> color: Tuple[int, int, int] = (255, 255, 255),
|
||
</span></span><span style="display:flex;"><span> name: str = "<Unnamed>",
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">- consumable: Consumable,
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span><span style="color:#a6e22e">+ consumable: Optional[Consumable] = None,
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ equippable: Optional[Equippable] = None,
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span> ):
|
||
</span></span><span style="display:flex;"><span> super().__init__(
|
||
</span></span><span style="display:flex;"><span> x=x,
|
||
</span></span><span style="display:flex;"><span> y=y,
|
||
</span></span><span style="display:flex;"><span> char=char,
|
||
</span></span><span style="display:flex;"><span> color=color,
|
||
</span></span><span style="display:flex;"><span> name=name,
|
||
</span></span><span style="display:flex;"><span> blocks_movement=False,
|
||
</span></span><span style="display:flex;"><span> render_order=RenderOrder.ITEM,
|
||
</span></span><span style="display:flex;"><span> )
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> self.consumable = consumable
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">- self.consumable.parent = self
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ if self.consumable:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ self.consumable.parent = self
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ self.equippable = equippable
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ if self.equippable:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ self.equippable.parent = self
|
||
</span></span></span></code></pre></div>
|
||
|
||
</div>
|
||
<div class="data-pane" data-pane="original">
|
||
|
||
<pre>...
|
||
if TYPE_CHECKING:
|
||
from components.ai import BaseAI
|
||
from components.consumable import Consumable
|
||
<span class="new-text">from components.equippable import Equippable</span>
|
||
from components.fighter import Fighter
|
||
from components.inventory import Inventory
|
||
from components.level import Level
|
||
from game_map import GameMap
|
||
...
|
||
|
||
class Item(Entity):
|
||
def __init__(
|
||
self,
|
||
*,
|
||
x: int = 0,
|
||
y: int = 0,
|
||
char: str = "?",
|
||
color: Tuple[int, int, int] = (255, 255, 255),
|
||
name: str = "<Unnamed>",
|
||
<span class="crossed-out-text">consumable: Consumable,</span>
|
||
<span class="new-text">consumable: Optional[Consumable] = None,
|
||
equippable: Optional[Equippable] = None,</span>
|
||
):
|
||
super().__init__(
|
||
x=x,
|
||
y=y,
|
||
char=char,
|
||
color=color,
|
||
name=name,
|
||
blocks_movement=False,
|
||
render_order=RenderOrder.ITEM,
|
||
)
|
||
|
||
self.consumable = consumable
|
||
<span class="crossed-out-text">self.consumable.parent = self</span>
|
||
|
||
<span class="new-text">if self.consumable:
|
||
self.consumable.parent = self
|
||
|
||
self.equippable = equippable
|
||
|
||
if self.equippable:
|
||
self.equippable.parent = self</span></pre>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<p>We’ve added <code>Equippable</code> as an optional component for the <code>Item</code> class, and also made <code>Consumable</code> optional, so that not all <code>Item</code> instances will be consumable.</p>
|
||
<p>Because <code>consumable</code> is now an optional attribute, we need to adjust <code>actions.py</code> to take this into account:</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 ItemAction(Action):
|
||
</span></span><span style="display:flex;"><span> ...
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> def perform(self) -> None:
|
||
</span></span><span style="display:flex;"><span> """Invoke the items ability, this action will be given to provide context."""
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">- self.item.consumable.activate(self)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span><span style="color:#a6e22e">+ if self.item.consumable:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ self.item.consumable.activate(self)
|
||
</span></span></span></code></pre></div>
|
||
|
||
</div>
|
||
<div class="data-pane" data-pane="original">
|
||
|
||
<pre>class ItemAction(Action):
|
||
...
|
||
|
||
def perform(self) -> None:
|
||
"""Invoke the items ability, this action will be given to provide context."""
|
||
<span class="crossed-out-text">self.item.consumable.activate(self)</span>
|
||
<span class="new-text">if self.item.consumable:
|
||
self.item.consumable.activate(self)</span></pre>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<p>In order to actually create the equippable entities, we’ll want to add a few examples to <code>entity_factories.py</code>. The entities we will add will correspond to the <code>Equippable</code> subclasses we already made. Edit <code>entity_factories.py</code> 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>from components.ai import HostileEnemy
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">-from components import consumable
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span><span style="color:#a6e22e">+from components import consumable, equippable
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>from components.fighter import Fighter
|
||
</span></span><span style="display:flex;"><span>from components.inventory import Inventory
|
||
</span></span><span style="display:flex;"><span>from components.level import Level
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>...
|
||
</span></span><span style="display:flex;"><span>lightning_scroll = Item(
|
||
</span></span><span style="display:flex;"><span> char="~",
|
||
</span></span><span style="display:flex;"><span> color=(255, 255, 0),
|
||
</span></span><span style="display:flex;"><span> name="Lightning Scroll",
|
||
</span></span><span style="display:flex;"><span> consumable=consumable.LightningDamageConsumable(damage=20, maximum_range=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">+dagger = Item(
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ char="/", color=(0, 191, 255), name="Dagger", equippable=equippable.Dagger()
|
||
</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">+sword = Item(char="/", color=(0, 191, 255), name="Sword", equippable=equippable.Sword())
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+leather_armor = Item(
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ char="[",
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ color=(139, 69, 19),
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ name="Leather Armor",
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ equippable=equippable.LeatherArmor(),
|
||
</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">+chain_mail = Item(
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ char="[", color=(139, 69, 19), name="Chain Mail", equippable=equippable.ChainMail()
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+)
|
||
</span></span></span></code></pre></div>
|
||
|
||
</div>
|
||
<div class="data-pane" data-pane="original">
|
||
|
||
<pre>from components.ai import HostileEnemy
|
||
<span class="crossed-out-text">from components import consumable</span>
|
||
<span class="new-text">from components import consumable, equippable</span>
|
||
from components.fighter import Fighter
|
||
from components.inventory import Inventory
|
||
from components.level import Level
|
||
|
||
...
|
||
lightning_scroll = Item(
|
||
char="~",
|
||
color=(255, 255, 0),
|
||
name="Lightning Scroll",
|
||
consumable=consumable.LightningDamageConsumable(damage=20, maximum_range=5),
|
||
)
|
||
|
||
<span class="new-text">dagger = Item(
|
||
char="/", color=(0, 191, 255), name="Dagger", equippable=equippable.Dagger()
|
||
)
|
||
|
||
sword = Item(char="/", color=(0, 191, 255), name="Sword", equippable=equippable.Sword())
|
||
|
||
leather_armor = Item(
|
||
char="[",
|
||
color=(139, 69, 19),
|
||
name="Leather Armor",
|
||
equippable=equippable.LeatherArmor(),
|
||
)
|
||
|
||
chain_mail = Item(
|
||
char="[", color=(139, 69, 19), name="Chain Mail", equippable=equippable.ChainMail()
|
||
)</span></pre>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<p>The creation of these entities is very similar to the consumables, except we give them the <code>Equippable</code> component instead of <code>Consumable</code>.
|
||
This is all we need to do to create the entities themselves, but we’re
|
||
far from finished. We still need to make these entities appear on the
|
||
map, make them equippable (there’s nothing for them to attach <em>to</em> on the player right now), and make equipping them actually do something.</p>
|
||
<p>To handle the equipment that the player has equipped at the moment,
|
||
we can create yet another component to handle the player’s (or the
|
||
monster’s, for that matter) equipment. Create a new file called <code>equipment.py</code> in the <code>components</code> folder, and add these contents:</p>
|
||
<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-py3" data-lang="py3"><span style="display:flex;"><span><span style="color:#f92672">from</span> __future__ <span style="color:#f92672">import</span> annotations
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> typing <span style="color:#f92672">import</span> Optional, TYPE_CHECKING
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> components.base_component <span style="color:#f92672">import</span> BaseComponent
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> equipment_types <span style="color:#f92672">import</span> EquipmentType
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> TYPE_CHECKING:
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">from</span> entity <span style="color:#f92672">import</span> Actor, Item
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Equipment</span>(BaseComponent):
|
||
</span></span><span style="display:flex;"><span> parent: Actor
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, weapon: Optional[Item] <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>, armor: Optional[Item] <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>):
|
||
</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>weapon <span style="color:#f92672">=</span> weapon
|
||
</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>armor <span style="color:#f92672">=</span> armor
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">@property</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">defense_bonus</span>(self) <span style="color:#f92672">-></span> int:
|
||
</span></span><span style="display:flex;"><span> bonus <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> self<span style="color:#f92672">.</span>weapon <span style="color:#f92672">is</span> <span style="color:#f92672">not</span> <span style="color:#66d9ef">None</span> <span style="color:#f92672">and</span> self<span style="color:#f92672">.</span>weapon<span style="color:#f92672">.</span>equippable <span style="color:#f92672">is</span> <span style="color:#f92672">not</span> <span style="color:#66d9ef">None</span>:
|
||
</span></span><span style="display:flex;"><span> bonus <span style="color:#f92672">+=</span> self<span style="color:#f92672">.</span>weapon<span style="color:#f92672">.</span>equippable<span style="color:#f92672">.</span>defense_bonus
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> self<span style="color:#f92672">.</span>armor <span style="color:#f92672">is</span> <span style="color:#f92672">not</span> <span style="color:#66d9ef">None</span> <span style="color:#f92672">and</span> self<span style="color:#f92672">.</span>armor<span style="color:#f92672">.</span>equippable <span style="color:#f92672">is</span> <span style="color:#f92672">not</span> <span style="color:#66d9ef">None</span>:
|
||
</span></span><span style="display:flex;"><span> bonus <span style="color:#f92672">+=</span> self<span style="color:#f92672">.</span>armor<span style="color:#f92672">.</span>equippable<span style="color:#f92672">.</span>defense_bonus
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> bonus
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">@property</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">power_bonus</span>(self) <span style="color:#f92672">-></span> int:
|
||
</span></span><span style="display:flex;"><span> bonus <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> self<span style="color:#f92672">.</span>weapon <span style="color:#f92672">is</span> <span style="color:#f92672">not</span> <span style="color:#66d9ef">None</span> <span style="color:#f92672">and</span> self<span style="color:#f92672">.</span>weapon<span style="color:#f92672">.</span>equippable <span style="color:#f92672">is</span> <span style="color:#f92672">not</span> <span style="color:#66d9ef">None</span>:
|
||
</span></span><span style="display:flex;"><span> bonus <span style="color:#f92672">+=</span> self<span style="color:#f92672">.</span>weapon<span style="color:#f92672">.</span>equippable<span style="color:#f92672">.</span>power_bonus
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> self<span style="color:#f92672">.</span>armor <span style="color:#f92672">is</span> <span style="color:#f92672">not</span> <span style="color:#66d9ef">None</span> <span style="color:#f92672">and</span> self<span style="color:#f92672">.</span>armor<span style="color:#f92672">.</span>equippable <span style="color:#f92672">is</span> <span style="color:#f92672">not</span> <span style="color:#66d9ef">None</span>:
|
||
</span></span><span style="display:flex;"><span> bonus <span style="color:#f92672">+=</span> self<span style="color:#f92672">.</span>armor<span style="color:#f92672">.</span>equippable<span style="color:#f92672">.</span>power_bonus
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> bonus
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">item_is_equipped</span>(self, item: Item) <span style="color:#f92672">-></span> bool:
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> self<span style="color:#f92672">.</span>weapon <span style="color:#f92672">==</span> item <span style="color:#f92672">or</span> self<span style="color:#f92672">.</span>armor <span style="color:#f92672">==</span> item
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">unequip_message</span>(self, item_name: str) <span style="color:#f92672">-></span> <span style="color:#66d9ef">None</span>:
|
||
</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>parent<span style="color:#f92672">.</span>gamemap<span style="color:#f92672">.</span>engine<span style="color:#f92672">.</span>message_log<span style="color:#f92672">.</span>add_message(
|
||
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">f</span><span style="color:#e6db74">"You remove the </span><span style="color:#e6db74">{</span>item_name<span style="color:#e6db74">}</span><span style="color:#e6db74">."</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:#66d9ef">def</span> <span style="color:#a6e22e">equip_message</span>(self, item_name: str) <span style="color:#f92672">-></span> <span style="color:#66d9ef">None</span>:
|
||
</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>parent<span style="color:#f92672">.</span>gamemap<span style="color:#f92672">.</span>engine<span style="color:#f92672">.</span>message_log<span style="color:#f92672">.</span>add_message(
|
||
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">f</span><span style="color:#e6db74">"You equip the </span><span style="color:#e6db74">{</span>item_name<span style="color:#e6db74">}</span><span style="color:#e6db74">."</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:#66d9ef">def</span> <span style="color:#a6e22e">equip_to_slot</span>(self, slot: str, item: Item, add_message: bool) <span style="color:#f92672">-></span> <span style="color:#66d9ef">None</span>:
|
||
</span></span><span style="display:flex;"><span> current_item <span style="color:#f92672">=</span> getattr(self, slot)
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> current_item <span style="color:#f92672">is</span> <span style="color:#f92672">not</span> <span style="color:#66d9ef">None</span>:
|
||
</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>unequip_from_slot(slot, add_message)
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> setattr(self, slot, item)
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> add_message:
|
||
</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>equip_message(item<span style="color:#f92672">.</span>name)
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">unequip_from_slot</span>(self, slot: str, add_message: bool) <span style="color:#f92672">-></span> <span style="color:#66d9ef">None</span>:
|
||
</span></span><span style="display:flex;"><span> current_item <span style="color:#f92672">=</span> getattr(self, slot)
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> add_message:
|
||
</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>unequip_message(current_item<span style="color:#f92672">.</span>name)
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> setattr(self, slot, <span style="color:#66d9ef">None</span>)
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">toggle_equip</span>(self, equippable_item: Item, add_message: bool <span style="color:#f92672">=</span> <span style="color:#66d9ef">True</span>) <span style="color:#f92672">-></span> <span style="color:#66d9ef">None</span>:
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (
|
||
</span></span><span style="display:flex;"><span> equippable_item<span style="color:#f92672">.</span>equippable
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">and</span> equippable_item<span style="color:#f92672">.</span>equippable<span style="color:#f92672">.</span>equipment_type <span style="color:#f92672">==</span> EquipmentType<span style="color:#f92672">.</span>WEAPON
|
||
</span></span><span style="display:flex;"><span> ):
|
||
</span></span><span style="display:flex;"><span> slot <span style="color:#f92672">=</span> <span style="color:#e6db74">"weapon"</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">else</span>:
|
||
</span></span><span style="display:flex;"><span> slot <span style="color:#f92672">=</span> <span style="color:#e6db74">"armor"</span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> getattr(self, slot) <span style="color:#f92672">==</span> equippable_item:
|
||
</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>unequip_from_slot(slot, add_message)
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">else</span>:
|
||
</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>equip_to_slot(slot, equippable_item, add_message)
|
||
</span></span></code></pre></div><p>That’s a lot to take in, so let’s go through it bit by bit.</p>
|
||
<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-py3" data-lang="py3"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Equipment</span>(BaseComponent):
|
||
</span></span><span style="display:flex;"><span> parent: Actor
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> __init__(self, weapon: Optional[Item] <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>, armor: Optional[Item] <span style="color:#f92672">=</span> <span style="color:#66d9ef">None</span>):
|
||
</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>weapon <span style="color:#f92672">=</span> weapon
|
||
</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>armor <span style="color:#f92672">=</span> armor
|
||
</span></span></code></pre></div><p>The <code>weapon</code> and <code>armor</code> attributes are what will hold the actual equippable entity. Both can be set to <code>None</code>, which represents nothing equipped in those slots. Feel free to add more slots as you see fit (perhaps you want <code>armor</code> to be head, body, legs, etc. instead, or allow for off-hand weapons/shields).</p>
|
||
<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-py3" data-lang="py3"><span style="display:flex;"><span> <span style="color:#a6e22e">@property</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">defense_bonus</span>(self) <span style="color:#f92672">-></span> int:
|
||
</span></span><span style="display:flex;"><span> bonus <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> self<span style="color:#f92672">.</span>weapon <span style="color:#f92672">is</span> <span style="color:#f92672">not</span> <span style="color:#66d9ef">None</span> <span style="color:#f92672">and</span> self<span style="color:#f92672">.</span>weapon<span style="color:#f92672">.</span>equippable <span style="color:#f92672">is</span> <span style="color:#f92672">not</span> <span style="color:#66d9ef">None</span>:
|
||
</span></span><span style="display:flex;"><span> bonus <span style="color:#f92672">+=</span> self<span style="color:#f92672">.</span>weapon<span style="color:#f92672">.</span>equippable<span style="color:#f92672">.</span>defense_bonus
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> self<span style="color:#f92672">.</span>armor <span style="color:#f92672">is</span> <span style="color:#f92672">not</span> <span style="color:#66d9ef">None</span> <span style="color:#f92672">and</span> self<span style="color:#f92672">.</span>armor<span style="color:#f92672">.</span>equippable <span style="color:#f92672">is</span> <span style="color:#f92672">not</span> <span style="color:#66d9ef">None</span>:
|
||
</span></span><span style="display:flex;"><span> bonus <span style="color:#f92672">+=</span> self<span style="color:#f92672">.</span>armor<span style="color:#f92672">.</span>equippable<span style="color:#f92672">.</span>defense_bonus
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> bonus
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">@property</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">power_bonus</span>(self) <span style="color:#f92672">-></span> int:
|
||
</span></span><span style="display:flex;"><span> bonus <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> self<span style="color:#f92672">.</span>weapon <span style="color:#f92672">is</span> <span style="color:#f92672">not</span> <span style="color:#66d9ef">None</span> <span style="color:#f92672">and</span> self<span style="color:#f92672">.</span>weapon<span style="color:#f92672">.</span>equippable <span style="color:#f92672">is</span> <span style="color:#f92672">not</span> <span style="color:#66d9ef">None</span>:
|
||
</span></span><span style="display:flex;"><span> bonus <span style="color:#f92672">+=</span> self<span style="color:#f92672">.</span>weapon<span style="color:#f92672">.</span>equippable<span style="color:#f92672">.</span>power_bonus
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> self<span style="color:#f92672">.</span>armor <span style="color:#f92672">is</span> <span style="color:#f92672">not</span> <span style="color:#66d9ef">None</span> <span style="color:#f92672">and</span> self<span style="color:#f92672">.</span>armor<span style="color:#f92672">.</span>equippable <span style="color:#f92672">is</span> <span style="color:#f92672">not</span> <span style="color:#66d9ef">None</span>:
|
||
</span></span><span style="display:flex;"><span> bonus <span style="color:#f92672">+=</span> self<span style="color:#f92672">.</span>armor<span style="color:#f92672">.</span>equippable<span style="color:#f92672">.</span>power_bonus
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> bonus
|
||
</span></span></code></pre></div><p>These properties do the same thing,
|
||
just for different things. Both calculate the “bonus” gifted by
|
||
equipment to either defense or power, based on what’s equipped. Notice
|
||
that we take the “power” bonus from both weapons and armor, and the same
|
||
applies to the “defense” bonus. This allows you to create weapons that
|
||
increase both attack and defense (maybe some sort of spiked shield) and
|
||
armor that increases attack (something magical, maybe). We won’t do that
|
||
in this tutorial (weapons will only increase power, armor will only
|
||
increase defense), but you should experiment with different equipment
|
||
types on your own.</p>
|
||
<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-py3" data-lang="py3"><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">item_is_equipped</span>(self, item: Item) <span style="color:#f92672">-></span> bool:
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> self<span style="color:#f92672">.</span>weapon <span style="color:#f92672">==</span> item <span style="color:#f92672">or</span> self<span style="color:#f92672">.</span>armor <span style="color:#f92672">==</span> item
|
||
</span></span></code></pre></div><p>This allows us to quickly check if an <code>Item</code> is equipped by the player or not. It will come in handy later on.</p>
|
||
<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-py3" data-lang="py3"><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">unequip_message</span>(self, item_name: str) <span style="color:#f92672">-></span> <span style="color:#66d9ef">None</span>:
|
||
</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>parent<span style="color:#f92672">.</span>gamemap<span style="color:#f92672">.</span>engine<span style="color:#f92672">.</span>message_log<span style="color:#f92672">.</span>add_message(
|
||
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">f</span><span style="color:#e6db74">"You remove the </span><span style="color:#e6db74">{</span>item_name<span style="color:#e6db74">}</span><span style="color:#e6db74">."</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:#66d9ef">def</span> <span style="color:#a6e22e">equip_message</span>(self, item_name: str) <span style="color:#f92672">-></span> <span style="color:#66d9ef">None</span>:
|
||
</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>parent<span style="color:#f92672">.</span>gamemap<span style="color:#f92672">.</span>engine<span style="color:#f92672">.</span>message_log<span style="color:#f92672">.</span>add_message(
|
||
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">f</span><span style="color:#e6db74">"You equip the </span><span style="color:#e6db74">{</span>item_name<span style="color:#e6db74">}</span><span style="color:#e6db74">."</span>
|
||
</span></span><span style="display:flex;"><span> )
|
||
</span></span></code></pre></div><p>Both of these methods add a message
|
||
to the message log, depending on whether the player is equipping or
|
||
removing a piece of equipment.</p>
|
||
<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-py3" data-lang="py3"><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">equip_to_slot</span>(self, slot: str, item: Item, add_message: bool) <span style="color:#f92672">-></span> <span style="color:#66d9ef">None</span>:
|
||
</span></span><span style="display:flex;"><span> current_item <span style="color:#f92672">=</span> getattr(self, slot)
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> current_item <span style="color:#f92672">is</span> <span style="color:#f92672">not</span> <span style="color:#66d9ef">None</span>:
|
||
</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>unequip_from_slot(slot, add_message)
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> setattr(self, slot, item)
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> add_message:
|
||
</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>equip_message(item<span style="color:#f92672">.</span>name)
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">unequip_from_slot</span>(self, slot: str, add_message: bool) <span style="color:#f92672">-></span> <span style="color:#66d9ef">None</span>:
|
||
</span></span><span style="display:flex;"><span> current_item <span style="color:#f92672">=</span> getattr(self, slot)
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> add_message:
|
||
</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>unequip_message(current_item<span style="color:#f92672">.</span>name)
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> setattr(self, slot, <span style="color:#66d9ef">None</span>)
|
||
</span></span></code></pre></div><p><code>equip_to_slot</code> and <code>unequip_from_slot</code> with add or remove an Item to the given “slot” (<code>weapon</code> or <code>armor</code>). We use <code>getattr</code> to get the slot, whether it’s <code>weapon</code> or <code>armor</code>. We use <code>getattr</code> because we won’t actually know which one we’re getting until the function is called. <code>getattr</code> allows us to “get an attribute” on a class (<code>self</code> in this case) and do what we want with it. We use <code>setattr</code> to “set the attribute” the same way.</p>
|
||
<p><code>unequip_from_slot</code> simply removes the item. <code>equip_to_slot</code> first checks if there’s something equipped to that slot, and calls <code>unequip_from_slot</code> if there is. This way, the player can’t equip two things to the same slot.</p>
|
||
<p>What’s with the <code>add_message</code> part? Normally, we’ll want
|
||
to add a message to the message log when we equip/remove things, but in
|
||
this section, we’ll see an exception: When we set up the player’s
|
||
initial equipment. We’ll use the same “equip” methods to set up the
|
||
initial equipment, but there’s no need to begin every game with messages
|
||
that say the player put on their starting equipment (presumably, the
|
||
player character did this before walking into the deadly dungeon). <code>add_message</code>
|
||
gives us a simple way to not add the messages if they aren’t necessary.
|
||
In your game, there might be other scenarios where you don’t want to
|
||
display these messages.</p>
|
||
<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-py3" data-lang="py3"><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">toggle_equip</span>(self, equippable_item: Item, add_message: bool <span style="color:#f92672">=</span> <span style="color:#66d9ef">True</span>) <span style="color:#f92672">-></span> <span style="color:#66d9ef">None</span>:
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (
|
||
</span></span><span style="display:flex;"><span> equippable_item<span style="color:#f92672">.</span>equippable
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">and</span> equippable_item<span style="color:#f92672">.</span>equippable<span style="color:#f92672">.</span>equipment_type <span style="color:#f92672">==</span> EquipmentType<span style="color:#f92672">.</span>WEAPON
|
||
</span></span><span style="display:flex;"><span> ):
|
||
</span></span><span style="display:flex;"><span> slot <span style="color:#f92672">=</span> <span style="color:#e6db74">"weapon"</span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">else</span>:
|
||
</span></span><span style="display:flex;"><span> slot <span style="color:#f92672">=</span> <span style="color:#e6db74">"armor"</span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> getattr(self, slot) <span style="color:#f92672">==</span> equippable_item:
|
||
</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>unequip_from_slot(slot, add_message)
|
||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">else</span>:
|
||
</span></span><span style="display:flex;"><span> self<span style="color:#f92672">.</span>equip_to_slot(slot, equippable_item, add_message)
|
||
</span></span></code></pre></div><p>Finally, we have <code>toggle_equip</code>,
|
||
which is the method that will actually get called when the player
|
||
selects an equippable item. It checks the equipment’s type (to know
|
||
which slot to put it in), and then checks to see if the same item is
|
||
already equipped to that slot. If it is, the player presumably wants to
|
||
remove it. If not, the player wants to equip it.</p>
|
||
<p>To sum up, this component holds references to equippable entities,
|
||
calculates the bonuses the player gets from them (which will get added
|
||
to the player’s power and defense values), and gives a way to equip or
|
||
remove the items.</p>
|
||
<p>Let’s add this component to the actors now. <code>entity.py</code> and add these lines:</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 components.ai import BaseAI
|
||
</span></span><span style="display:flex;"><span> from components.consumable import Consumable
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ from components.equipment import Equipment
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span> from components.equippable import Equippable
|
||
</span></span><span style="display:flex;"><span> from components.fighter import Fighter
|
||
</span></span><span style="display:flex;"><span> from components.inventory import Inventory
|
||
</span></span><span style="display:flex;"><span> from components.level import Level
|
||
</span></span><span style="display:flex;"><span> from game_map import GameMap
|
||
</span></span><span style="display:flex;"><span>...
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>class Actor(Entity):
|
||
</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> x: int = 0,
|
||
</span></span><span style="display:flex;"><span> y: int = 0,
|
||
</span></span><span style="display:flex;"><span> char: str = "?",
|
||
</span></span><span style="display:flex;"><span> color: Tuple[int, int, int] = (255, 255, 255),
|
||
</span></span><span style="display:flex;"><span> name: str = "<Unnamed>",
|
||
</span></span><span style="display:flex;"><span> ai_cls: Type[BaseAI],
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ equipment: Equipment,
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span> fighter: Fighter,
|
||
</span></span><span style="display:flex;"><span> inventory: Inventory,
|
||
</span></span><span style="display:flex;"><span> level: Level,
|
||
</span></span><span style="display:flex;"><span> ):
|
||
</span></span><span style="display:flex;"><span> super().__init__(
|
||
</span></span><span style="display:flex;"><span> x=x,
|
||
</span></span><span style="display:flex;"><span> y=y,
|
||
</span></span><span style="display:flex;"><span> char=char,
|
||
</span></span><span style="display:flex;"><span> color=color,
|
||
</span></span><span style="display:flex;"><span> name=name,
|
||
</span></span><span style="display:flex;"><span> blocks_movement=True,
|
||
</span></span><span style="display:flex;"><span> render_order=RenderOrder.ACTOR,
|
||
</span></span><span style="display:flex;"><span> )
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> self.ai: Optional[BaseAI] = ai_cls(self)
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ self.equipment: Equipment = equipment
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ self.equipment.parent = self
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span> self.fighter = fighter
|
||
</span></span><span style="display:flex;"><span> self.fighter.parent = self
|
||
</span></span><span style="display:flex;"><span>
|
||
</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 components.ai import BaseAI
|
||
from components.consumable import Consumable
|
||
<span class="new-text">from components.equipment import Equipment</span>
|
||
from components.equippable import Equippable
|
||
from components.fighter import Fighter
|
||
from components.inventory import Inventory
|
||
from components.level import Level
|
||
from game_map import GameMap
|
||
...
|
||
|
||
class Actor(Entity):
|
||
def __init__(
|
||
self,
|
||
*,
|
||
x: int = 0,
|
||
y: int = 0,
|
||
char: str = "?",
|
||
color: Tuple[int, int, int] = (255, 255, 255),
|
||
name: str = "<Unnamed>",
|
||
ai_cls: Type[BaseAI],
|
||
<span class="new-text">equipment: Equipment,</span>
|
||
fighter: Fighter,
|
||
inventory: Inventory,
|
||
level: Level,
|
||
):
|
||
super().__init__(
|
||
x=x,
|
||
y=y,
|
||
char=char,
|
||
color=color,
|
||
name=name,
|
||
blocks_movement=True,
|
||
render_order=RenderOrder.ACTOR,
|
||
)
|
||
|
||
self.ai: Optional[BaseAI] = ai_cls(self)
|
||
|
||
<span class="new-text">self.equipment: Equipment = equipment
|
||
self.equipment.parent = self</span>
|
||
|
||
self.fighter = fighter
|
||
self.fighter.parent = self
|
||
|
||
...</pre>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<p>We also need to update <code>entity_factories.py</code> once again, to create the actors with the <code>Equipment</code> component:</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 components.ai import HostileEnemy
|
||
</span></span><span style="display:flex;"><span>from components import consumable, equippable
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+from components.equipment import Equipment
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>from components.fighter import Fighter
|
||
</span></span><span style="display:flex;"><span>from components.inventory import Inventory
|
||
</span></span><span style="display:flex;"><span>from components.level import Level
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span>player = Actor(
|
||
</span></span><span style="display:flex;"><span> char="@",
|
||
</span></span><span style="display:flex;"><span> color=(255, 255, 255),
|
||
</span></span><span style="display:flex;"><span> name="Player",
|
||
</span></span><span style="display:flex;"><span> ai_cls=HostileEnemy,
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ equipment=Equipment(),
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span> fighter=Fighter(hp=30, base_defense=1, base_power=2),
|
||
</span></span><span style="display:flex;"><span> inventory=Inventory(capacity=26),
|
||
</span></span><span style="display:flex;"><span> level=Level(level_up_base=200),
|
||
</span></span><span style="display:flex;"><span>)
|
||
</span></span><span style="display:flex;"><span>orc = Actor(
|
||
</span></span><span style="display:flex;"><span> char="o",
|
||
</span></span><span style="display:flex;"><span> color=(63, 127, 63),
|
||
</span></span><span style="display:flex;"><span> name="Orc",
|
||
</span></span><span style="display:flex;"><span> ai_cls=HostileEnemy,
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ equipment=Equipment(),
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span> fighter=Fighter(hp=10, base_defense=0, base_power=3),
|
||
</span></span><span style="display:flex;"><span> inventory=Inventory(capacity=0),
|
||
</span></span><span style="display:flex;"><span> level=Level(xp_given=35),
|
||
</span></span><span style="display:flex;"><span>)
|
||
</span></span><span style="display:flex;"><span>troll = Actor(
|
||
</span></span><span style="display:flex;"><span> char="T",
|
||
</span></span><span style="display:flex;"><span> color=(0, 127, 0),
|
||
</span></span><span style="display:flex;"><span> name="Troll",
|
||
</span></span><span style="display:flex;"><span> ai_cls=HostileEnemy,
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ equipment=Equipment(),
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span> fighter=Fighter(hp=16, base_defense=1, base_power=4),
|
||
</span></span><span style="display:flex;"><span> inventory=Inventory(capacity=0),
|
||
</span></span><span style="display:flex;"><span> level=Level(xp_given=100),
|
||
</span></span><span style="display:flex;"><span>)
|
||
</span></span><span style="display:flex;"><span>...
|
||
</span></span></code></pre></div>
|
||
|
||
</div>
|
||
<div class="data-pane" data-pane="original">
|
||
|
||
<pre>from components.ai import HostileEnemy
|
||
from components import consumable, equippable
|
||
<span class="new-text">from components.equipment import Equipment</span>
|
||
from components.fighter import Fighter
|
||
from components.inventory import Inventory
|
||
from components.level import Level
|
||
|
||
|
||
player = Actor(
|
||
char="@",
|
||
color=(255, 255, 255),
|
||
name="Player",
|
||
ai_cls=HostileEnemy,
|
||
<span class="new-text">equipment=Equipment(),</span>
|
||
fighter=Fighter(hp=30, base_defense=1, base_power=2),
|
||
inventory=Inventory(capacity=26),
|
||
level=Level(level_up_base=200),
|
||
)
|
||
orc = Actor(
|
||
char="o",
|
||
color=(63, 127, 63),
|
||
name="Orc",
|
||
ai_cls=HostileEnemy,
|
||
<span class="new-text">equipment=Equipment(),</span>
|
||
fighter=Fighter(hp=10, base_defense=0, base_power=3),
|
||
inventory=Inventory(capacity=0),
|
||
level=Level(xp_given=35),
|
||
)
|
||
troll = Actor(
|
||
char="T",
|
||
color=(0, 127, 0),
|
||
name="Troll",
|
||
ai_cls=HostileEnemy,
|
||
<span class="new-text">equipment=Equipment(),</span>
|
||
fighter=Fighter(hp=16, base_defense=1, base_power=4),
|
||
inventory=Inventory(capacity=0),
|
||
level=Level(xp_given=100),
|
||
)
|
||
...</pre>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<p>One thing we need to do is change the way <code>power</code> and <code>defense</code> are calculated in the <code>Fighter</code>
|
||
component. Currently, the values are set directly in the class, but
|
||
we’ll want to calculate them based on their base values (what gets
|
||
leveled up), and the bonus values (based on the equipment).</p>
|
||
<p>We can redefine <code>power</code> and <code>defense</code> as properties, and rename what we set in the class to <code>base_power</code> and <code>base_defense</code>. <code>power</code> and <code>defense</code> will then get their values from their respective bases and equipment bonuses.</p>
|
||
<p>This will require edits to several places, but we’ll start first with the most obvious: the <code>Fighter</code> class itself.</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 Fighter(BaseComponent):
|
||
</span></span><span style="display:flex;"><span> parent: Actor
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">- def __init__(self, hp: int, defense: int, power: int):
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span><span style="color:#a6e22e">+ def __init__(self, hp: int, base_defense: int, base_power: int):
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span> self.max_hp = hp
|
||
</span></span><span style="display:flex;"><span> self._hp = hp
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">- self.defense = defense
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- self.power = power
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span><span style="color:#a6e22e">+ self.base_defense = base_defense
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ self.base_power = base_power
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span> @property
|
||
</span></span><span style="display:flex;"><span> def hp(self) -> int:
|
||
</span></span><span style="display:flex;"><span> return self._hp
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> @hp.setter
|
||
</span></span><span style="display:flex;"><span> def hp(self, value: int) -> None:
|
||
</span></span><span style="display:flex;"><span> self._hp = max(0, min(value, self.max_hp))
|
||
</span></span><span style="display:flex;"><span> if self._hp == 0 and self.parent.ai:
|
||
</span></span><span style="display:flex;"><span> self.die()
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ @property
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ def defense(self) -> int:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ return self.base_defense + self.defense_bonus
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ @property
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ def power(self) -> int:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ return self.base_power + self.power_bonus
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ @property
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ def defense_bonus(self) -> int:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ if self.parent.equipment:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ return self.parent.equipment.defense_bonus
|
||
</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">+ return 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">+ @property
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ def power_bonus(self) -> int:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ if self.parent.equipment:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ return self.parent.equipment.power_bonus
|
||
</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">+ return 0
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span> def die(self) -> None:
|
||
</span></span><span style="display:flex;"><span> ...
|
||
</span></span></code></pre></div>
|
||
|
||
</div>
|
||
<div class="data-pane" data-pane="original">
|
||
|
||
<pre>class Fighter(BaseComponent):
|
||
parent: Actor
|
||
|
||
<span class="crossed-out-text">def __init__(self, hp: int, defense: int, power: int):</span>
|
||
<span class="new-text">def __init__(self, hp: int, base_defense: int, base_power: int):</span>
|
||
self.max_hp = hp
|
||
self._hp = hp
|
||
<span class="crossed-out-text">self.defense = defense</span>
|
||
<span class="crossed-out-text">self.power = power</span>
|
||
<span class="new-text">self.base_defense = base_defense
|
||
self.base_power = base_power</span>
|
||
|
||
@property
|
||
def hp(self) -> int:
|
||
return self._hp
|
||
|
||
@hp.setter
|
||
def hp(self, value: int) -> None:
|
||
self._hp = max(0, min(value, self.max_hp))
|
||
if self._hp == 0 and self.parent.ai:
|
||
self.die()
|
||
|
||
<span class="new-text">@property
|
||
def defense(self) -> int:
|
||
return self.base_defense + self.defense_bonus
|
||
|
||
@property
|
||
def power(self) -> int:
|
||
return self.base_power + self.power_bonus
|
||
|
||
@property
|
||
def defense_bonus(self) -> int:
|
||
if self.parent.equipment:
|
||
return self.parent.equipment.defense_bonus
|
||
else:
|
||
return 0
|
||
|
||
@property
|
||
def power_bonus(self) -> int:
|
||
if self.parent.equipment:
|
||
return self.parent.equipment.power_bonus
|
||
else:
|
||
return 0</span>
|
||
|
||
def die(self) -> None:
|
||
...</pre>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<p><code>power</code> and <code>defense</code> are now computed based on the base values and the bonus values offered by the equipment (if any exists).</p>
|
||
<p>We’ll need to edit <code>level.py</code> to reflect the new attribute names as well:</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 Level(BaseComponent):
|
||
</span></span><span style="display:flex;"><span> ...
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> def increase_power(self, amount: int = 1) -> None:
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">- self.parent.fighter.power += amount
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span><span style="color:#a6e22e">+ self.parent.fighter.base_power += amount
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span> self.engine.message_log.add_message("You feel stronger!")
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> self.increase_level()
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> def increase_defense(self, amount: int = 1) -> None:
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">- self.parent.fighter.defense += amount
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span><span style="color:#a6e22e">+ self.parent.fighter.base_defense += amount
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span> self.engine.message_log.add_message("Your movements are getting swifter!")
|
||
</span></span></code></pre></div>
|
||
|
||
</div>
|
||
<div class="data-pane" data-pane="original">
|
||
|
||
<pre>class Level(BaseComponent):
|
||
...
|
||
|
||
def increase_power(self, amount: int = 1) -> None:
|
||
<span class="crossed-out-text">self.parent.fighter.power += amount</span>
|
||
<span class="new-text">self.parent.fighter.base_power += amount</span>
|
||
|
||
self.engine.message_log.add_message("You feel stronger!")
|
||
|
||
self.increase_level()
|
||
|
||
def increase_defense(self, amount: int = 1) -> None:
|
||
<span class="crossed-out-text">self.parent.fighter.defense += amount</span>
|
||
<span class="new-text">self.parent.fighter.base_defense += amount</span>
|
||
|
||
self.engine.message_log.add_message("Your movements are getting swifter!")</pre>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<p>We also have to adjust the initializations in <code>entity_factories.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>player = Actor(
|
||
</span></span><span style="display:flex;"><span> char="@",
|
||
</span></span><span style="display:flex;"><span> color=(255, 255, 255),
|
||
</span></span><span style="display:flex;"><span> name="Player",
|
||
</span></span><span style="display:flex;"><span> ai_cls=HostileEnemy,
|
||
</span></span><span style="display:flex;"><span> equipment=Equipment(),
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">- fighter=Fighter(hp=30, defense=2, power=5),
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span><span style="color:#a6e22e">+ fighter=Fighter(hp=30, base_defense=1, base_power=2),
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span> inventory=Inventory(capacity=26),
|
||
</span></span><span style="display:flex;"><span> level=Level(level_up_base=200),
|
||
</span></span><span style="display:flex;"><span>)
|
||
</span></span><span style="display:flex;"><span>orc = Actor(
|
||
</span></span><span style="display:flex;"><span> char="o",
|
||
</span></span><span style="display:flex;"><span> color=(63, 127, 63),
|
||
</span></span><span style="display:flex;"><span> name="Orc",
|
||
</span></span><span style="display:flex;"><span> ai_cls=HostileEnemy,
|
||
</span></span><span style="display:flex;"><span> equipment=Equipment(),
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">- fighter=Fighter(hp=10, defense=0, power=3),
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span><span style="color:#a6e22e">+ fighter=Fighter(hp=10, base_defense=0, base_power=3),
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span> inventory=Inventory(capacity=0),
|
||
</span></span><span style="display:flex;"><span> level=Level(xp_given=35),
|
||
</span></span><span style="display:flex;"><span>)
|
||
</span></span><span style="display:flex;"><span>troll = Actor(
|
||
</span></span><span style="display:flex;"><span> char="T",
|
||
</span></span><span style="display:flex;"><span> color=(0, 127, 0),
|
||
</span></span><span style="display:flex;"><span> name="Troll",
|
||
</span></span><span style="display:flex;"><span> ai_cls=HostileEnemy,
|
||
</span></span><span style="display:flex;"><span> equipment=Equipment(),
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">- fighter=Fighter(hp=16, defense=1, power=4),
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span><span style="color:#a6e22e">+ fighter=Fighter(hp=16, base_defense=1, base_power=4),
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span> inventory=Inventory(capacity=0),
|
||
</span></span><span style="display:flex;"><span> level=Level(xp_given=100),
|
||
</span></span><span style="display:flex;"><span>)
|
||
</span></span><span style="display:flex;"><span>...
|
||
</span></span></code></pre></div>
|
||
|
||
</div>
|
||
<div class="data-pane" data-pane="original">
|
||
|
||
<pre>player = Actor(
|
||
char="@",
|
||
color=(255, 255, 255),
|
||
name="Player",
|
||
ai_cls=HostileEnemy,
|
||
equipment=Equipment(),
|
||
<span class="crossed-out-text">fighter=Fighter(hp=30, defense=2, power=5),</span>
|
||
<span class="new-text">fighter=Fighter(hp=30, base_defense=1, base_power=2),</span>
|
||
inventory=Inventory(capacity=26),
|
||
level=Level(level_up_base=200),
|
||
)
|
||
orc = Actor(
|
||
char="o",
|
||
color=(63, 127, 63),
|
||
name="Orc",
|
||
ai_cls=HostileEnemy,
|
||
equipment=Equipment(),
|
||
<span class="crossed-out-text">fighter=Fighter(hp=10, defense=0, power=3),</span>
|
||
<span class="new-text">fighter=Fighter(hp=10, base_defense=0, base_power=3),</span>
|
||
inventory=Inventory(capacity=0),
|
||
level=Level(xp_given=35),
|
||
)
|
||
troll = Actor(
|
||
char="T",
|
||
color=(0, 127, 0),
|
||
name="Troll",
|
||
ai_cls=HostileEnemy,
|
||
equipment=Equipment(),
|
||
<span class="crossed-out-text">fighter=Fighter(hp=16, defense=1, power=4),</span>
|
||
<span class="new-text">fighter=Fighter(hp=16, base_defense=1, base_power=4),</span>
|
||
inventory=Inventory(capacity=0),
|
||
level=Level(xp_given=100),
|
||
)
|
||
...</pre>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<p>Notice that we’ve changed the player’s base values a bit. This is to
|
||
compensate for the fact that the player will be getting bonuses from the
|
||
equipment soon. Feel free to tweak these values however you see fit.</p>
|
||
<p>Now all that’s left to do is allow generate the equipment to the map,
|
||
and allow the player to interact with it. To create equipment, we can
|
||
simply edit our <code>item_chances</code> dictionary to include weapons and armor on certain floors. Edit <code>procgen.py</code> 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>item_chances: Dict[int, List[Tuple[Entity, int]]] = {
|
||
</span></span><span style="display:flex;"><span> 0: [(entity_factories.health_potion, 35)],
|
||
</span></span><span style="display:flex;"><span> 2: [(entity_factories.confusion_scroll, 10)],
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">- 4: [(entity_factories.lightning_scroll, 25)],
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- 6: [(entity_factories.fireball_scroll, 25)],
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span><span style="color:#a6e22e">+ 4: [(entity_factories.lightning_scroll, 25), (entity_factories.sword, 5)],
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ 6: [(entity_factories.fireball_scroll, 25), (entity_factories.chain_mail, 15)],
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>}
|
||
</span></span></code></pre></div>
|
||
|
||
</div>
|
||
<div class="data-pane" data-pane="original">
|
||
|
||
<pre>item_chances: Dict[int, List[Tuple[Entity, int]]] = {
|
||
0: [(entity_factories.health_potion, 35)],
|
||
2: [(entity_factories.confusion_scroll, 10)],
|
||
<span class="crossed-out-text">4: [(entity_factories.lightning_scroll, 25)],</span>
|
||
<span class="crossed-out-text">6: [(entity_factories.fireball_scroll, 25)],</span>
|
||
<span class="new-text">4: [(entity_factories.lightning_scroll, 25), (entity_factories.sword, 5)],</span>
|
||
<span class="new-text">6: [(entity_factories.fireball_scroll, 25), (entity_factories.chain_mail, 15)],</span>
|
||
}</pre>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<p>This will generate swords and chain mail at levels 4 and 6, respectively. You can change the floor or the weights if you like.</p>
|
||
<p>Now that equipment will spawn on the map, we need to allow the user
|
||
to equip and remove equippable entities. The first step is to add an
|
||
action to equip things, which we’ll call <code>EquipAction</code>. Add this class to <code>actions.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>class DropItem(ItemAction):
|
||
</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">+class EquipAction(Action):
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ def __init__(self, entity: Actor, item: Item):
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ super().__init__(entity)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ self.item = item
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ def perform(self) -> None:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ self.entity.equipment.toggle_equip(self.item)
|
||
</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 WaitAction(Action):
|
||
</span></span><span style="display:flex;"><span> ...
|
||
</span></span></code></pre></div>
|
||
|
||
</div>
|
||
<div class="data-pane" data-pane="original">
|
||
|
||
<pre>...
|
||
class DropItem(ItemAction):
|
||
...
|
||
|
||
|
||
<span class="new-text">class EquipAction(Action):
|
||
def __init__(self, entity: Actor, item: Item):
|
||
super().__init__(entity)
|
||
|
||
self.item = item
|
||
|
||
def perform(self) -> None:
|
||
self.entity.equipment.toggle_equip(self.item)</span>
|
||
|
||
|
||
class WaitAction(Action):
|
||
...</pre>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<p>The action itself is very straightforward: It holds which item is being equipped/removed, and calls the <code>toggle_equip</code> method. The <code>Equipment</code> component handles most of the work here.</p>
|
||
<p>But how do we <em>use</em> this action? The simplest way would be to
|
||
expand the functionality of our original inventory menu. If the user
|
||
selects a piece of equipment from that menu, we’ll either equip the
|
||
item, or remove it, if it’s already equipped. We should also show the
|
||
user a visual representation of which items are already equipped.</p>
|
||
<p>Modify <code>input_handlers.py</code> 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>class InventoryEventHandler(AskUserEventHandler):
|
||
</span></span><span style="display:flex;"><span> def on_render(self, console: tcod.Console) -> None:
|
||
</span></span><span style="display:flex;"><span> ...
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> if number_of_items_in_inventory > 0:
|
||
</span></span><span style="display:flex;"><span> for i, item in enumerate(self.engine.player.inventory.items):
|
||
</span></span><span style="display:flex;"><span> item_key = chr(ord("a") + i)
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">- console.print(x + 1, y + i + 1, f"({item_key}) {item.name}")
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ is_equipped = self.engine.player.equipment.item_is_equipped(item)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ item_string = f"({item_key}) {item.name}"
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ if is_equipped:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ item_string = f"{item_string} (E)"
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ console.print(x + 1, y + i + 1, item_string)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span> else:
|
||
</span></span><span style="display:flex;"><span> console.print(x + 1, y + 1, "(Empty)")
|
||
</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>class InventoryActivateHandler(InventoryEventHandler):
|
||
</span></span><span style="display:flex;"><span> """Handle using an inventory item."""
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> TITLE = "Select an item to use"
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span> def on_item_selected(self, item: Item) -> Optional[ActionOrHandler]:
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672">- """Return the action for the selected item."""
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672">- return item.consumable.get_action(self.engine.player)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#f92672"></span><span style="color:#a6e22e">+ if item.consumable:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ # Return the action for the selected item.
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ return item.consumable.get_action(self.engine.player)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ elif item.equippable:
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ return actions.EquipAction(self.engine.player, item)
|
||
</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">+ return None
|
||
</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 InventoryDropHandler(InventoryEventHandler):
|
||
</span></span><span style="display:flex;"><span> ...
|
||
</span></span></code></pre></div>
|
||
|
||
</div>
|
||
<div class="data-pane" data-pane="original">
|
||
|
||
<pre>class InventoryEventHandler(AskUserEventHandler):
|
||
def on_render(self, console: tcod.Console) -> None:
|
||
...
|
||
|
||
if number_of_items_in_inventory > 0:
|
||
for i, item in enumerate(self.engine.player.inventory.items):
|
||
item_key = chr(ord("a") + i)
|
||
<span class="crossed-out-text">console.print(x + 1, y + i + 1, f"({item_key}) {item.name}")</span>
|
||
|
||
<span class="new-text">is_equipped = self.engine.player.equipment.item_is_equipped(item)
|
||
|
||
item_string = f"({item_key}) {item.name}"
|
||
|
||
if is_equipped:
|
||
item_string = f"{item_string} (E)"
|
||
|
||
console.print(x + 1, y + i + 1, item_string)</span>
|
||
else:
|
||
console.print(x + 1, y + 1, "(Empty)")
|
||
|
||
...
|
||
|
||
class InventoryActivateHandler(InventoryEventHandler):
|
||
"""Handle using an inventory item."""
|
||
|
||
TITLE = "Select an item to use"
|
||
|
||
def on_item_selected(self, item: Item) -> Optional[ActionOrHandler]:
|
||
<span class="crossed-out-text">"""Return the action for the selected item."""</span>
|
||
<span class="crossed-out-text">return item.consumable.get_action(self.engine.player)</span>
|
||
<span class="new-text">if item.consumable:
|
||
# Return the action for the selected item.
|
||
return item.consumable.get_action(self.engine.player)
|
||
elif item.equippable:
|
||
return actions.EquipAction(self.engine.player, item)
|
||
else:
|
||
return None</span>
|
||
|
||
|
||
class InventoryDropHandler(InventoryEventHandler):
|
||
...</pre>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<p>The first change is modifying the render function to display an “(E)”
|
||
next to items that are equipped. Items that aren’t equipped are
|
||
displayed the same way as before.</p>
|
||
<p>The second change has to do with using the item. Before, we were just
|
||
assuming the item was a consumable. Now, if the item is a consumable,
|
||
we call the <code>get_action</code> method on the <code>Consumable</code> component, just like before. If it’s instead equippable, we call the <code>EquipAction</code>. If it’s neither, nothing happens.</p>
|
||
<p>Run the game now, you’ll be able to pick up and equip things. I recommend adjusting the values in <code>procgen.py</code> to make equipment spawn earlier and more often, just for testing purposes.</p>
|
||
<p>If you play around a bit, you might notice an odd bug: If the player
|
||
drops something that’s equipped… it stays equipped! That doesn’t make
|
||
sense, as dropping something should unequip it as well. Luckily, the fix
|
||
is quite simple: We can adjust our <code>DropItem</code> action to unequip an item if it’s being dropped and it’s equipped. Make the following additions to <code>actions.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 DropItem(ItemAction):
|
||
</span></span><span style="display:flex;"><span> def perform(self) -> None:
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ if self.entity.equipment.item_is_equipped(self.item):
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ self.entity.equipment.toggle_equip(self.item)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span> self.entity.inventory.drop(self.item)
|
||
</span></span></code></pre></div>
|
||
|
||
</div>
|
||
<div class="data-pane" data-pane="original">
|
||
|
||
<pre>class DropItem(ItemAction):
|
||
def perform(self) -> None:
|
||
<span class="new-text">if self.entity.equipment.item_is_equipped(self.item):
|
||
self.entity.equipment.toggle_equip(self.item)</span>
|
||
|
||
self.entity.inventory.drop(self.item)</pre>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<p>One last thing we can do is give the player a bit of equipment to
|
||
start. We’ll spawn a dagger and leather armor, and immediately add them
|
||
to the player’s inventory.</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> ...
|
||
</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>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ dagger = copy.deepcopy(entity_factories.dagger)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ leather_armor = copy.deepcopy(entity_factories.leather_armor)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ dagger.parent = player.inventory
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ leather_armor.parent = player.inventory
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ player.inventory.items.append(dagger)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ player.equipment.toggle_equip(dagger, add_message=False)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ player.inventory.items.append(leather_armor)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">+ player.equipment.toggle_equip(leather_armor, add_message=False)
|
||
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e"></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:
|
||
...
|
||
|
||
engine.message_log.add_message(
|
||
"Hello and welcome, adventurer, to yet another dungeon!", color.welcome_text
|
||
)
|
||
|
||
<span class="new-text">dagger = copy.deepcopy(entity_factories.dagger)
|
||
leather_armor = copy.deepcopy(entity_factories.leather_armor)
|
||
|
||
dagger.parent = player.inventory
|
||
leather_armor.parent = player.inventory
|
||
|
||
player.inventory.items.append(dagger)
|
||
player.equipment.toggle_equip(dagger, add_message=False)
|
||
|
||
player.inventory.items.append(leather_armor)
|
||
player.equipment.toggle_equip(leather_armor, add_message=False)</span>
|
||
|
||
return engine</pre>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<p>As mentioned earlier, we pass <code>add_message=False</code> to signify not to add a message to the message log.</p>
|
||
<p><img src="Part%2013%20-%20Gearing%20up%20%C2%B7%20Roguelike%20Tutorials_files/part-13-end.png" alt="Part 13 - End"></p>
|
||
<p>With that, we’ve reached the end of the tutorial! Thank you so much for following along, and be sure to check out the <a href="https://rogueliketutorials.com/tutorials/tcod/v2">extras section</a>. More will be added there over time. If you have a suggestion for an extra, let me know!</p>
|
||
<p>Be sure to check out the <a href="https://www.reddit.com/r/roguelikedev">Roguelike Development Subreddit</a> for help, for inspiration, or to share your progress.</p>
|
||
<p>Best of luck on your roguelike development journey!</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-13">click here</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%2013%20-%20Gearing%20up%20%C2%B7%20Roguelike%20Tutorials_files/coder.min.236049395dc3682fb2719640872958e12f1f24067bb09c327b2.js" integrity="sha256-I2BJOV3DaC+ycZZAhylY4S8fJAZ7sJwyeyM+YpDH7aw="></script>
|
||
|
||
|
||
|
||
|
||
|
||
<script src="Part%2013%20-%20Gearing%20up%20%C2%B7%20Roguelike%20Tutorials_files/codetabs.min.cc52451e7f25e50f64c1c893826f606d58410d742c214dce.js" integrity="sha256-zFJFHn8l5Q9kwciTgm9gbVhBDXQsIU3OI/tEfJlh8rA="></script>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</body></html> |