You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
To have more states than ``ExampleState`` one must use an abstract type which can be used to refer to any state.
31
-
In this case a `Protocol`_ will be used, called ``State``.
32
-
33
-
Create a new module: ``game/state.py``.
34
-
In this module add the class :python:`class State(Protocol):`.
35
-
``Protocol`` is from Python's ``typing`` module.
36
-
``State`` should have the ``on_event`` and ``on_draw`` methods from ``ExampleState`` but these methods will be empty other than the docstrings describing what they are for.
37
-
These methods refer to types from ``tcod`` and those types will need to be imported.
38
-
``State`` should also have :python:`__slots__ = ()` [#slots]_ in case the class is used for a subclass.
Now there is a new ECS world but the example state does not know how to render it.
@@ -485,7 +356,6 @@ Then add the following:
485
356
486
357
Create a new :python:`class InGame:` decorated with :python:`@attrs.define(eq=False)`.
487
358
States will always use ``g.world`` to access the ECS registry.
488
-
States prefer ``console`` as a parameter over the global ``g.console`` so always use ``console`` when it exists.
489
359
490
360
.. code-block:: python
491
361
@@ -494,8 +364,8 @@ States prefer ``console`` as a parameter over the global ``g.console`` so always
494
364
"""Primary in-game state."""
495
365
...
496
366
497
-
Create an ``on_event`` method matching the ``State`` protocol.
498
-
Copying these methods from ``State`` or ``ExampleState`` should be enough.
367
+
Create an ``on_event`` and ``on_draw`` method matching the ``ExampleState`` class.
368
+
Copying ``ExampleState`` and modifying it should be enough since this wil replace ``ExampleState``.
499
369
500
370
Now to do an tcod-ecs query to fetch the player entity.
501
371
In tcod-ecs queries most often start with :python:`g.world.Q.all_of(components=[], tags=[])`.
@@ -520,9 +390,13 @@ The query to see if the player has stepped on gold is to check for whichever ent
520
390
The query for this is :python:`g.world.Q.all_of(components=[Gold], tags=[player.components[Position], IsItem]):`.
521
391
522
392
We will iterate over whatever matches this query using a :python:`for gold in ...:` loop.
523
-
Add the entities ``Gold`` component to the player.
393
+
Add the entities ``Gold`` component to the players similar component.
524
394
Keep in mind that ``Gold`` is treated like an ``int`` so its usage is predictable.
525
-
Now print the current amount of gold using :python:`print(f"Picked up {gold.components[Gold]}g, total: {player.components[Gold]}g")`.
395
+
396
+
Format the added and total of gold using a Python f-string_: :python:`text = f"Picked up {gold.components[Gold]}g, total: {player.components[Gold]}g"`.
397
+
Store ``text`` globally in the ECS registry with :python:`g.world[None].components[("Text", str)] = text`.
398
+
This is done as two lines to avoid creating a line with an excessive length.
399
+
526
400
Then use :python:`gold.clear()` at the end to remove all components and tags from the gold entity which will effectively delete it.
527
401
528
402
.. code-block:: python
@@ -539,7 +413,8 @@ Then use :python:`gold.clear()` at the end to remove all components and tags fro
539
413
# Auto pickup gold
540
414
for gold in g.world.Q.all_of(components=[Gold], tags=[player.components[Position], IsItem]):
541
415
player.components[Gold] += gold.components[Gold]
542
-
print(f"Picked up {gold.components[Gold]}g, total: {player.components[Gold]}g")
416
+
text =f"Picked up {gold.components[Gold]}g, total: {player.components[Gold]}g"
417
+
g.world[None].components[str] = text
543
418
gold.clear()
544
419
...
545
420
@@ -556,6 +431,17 @@ Draw the graphic by assigning it to the consoles Numpy array directly with :pyth
556
431
``console.rgb`` is a ``ch,fg,bg`` array and :python:`[["ch", "fg"]]` narrows it down to only ``ch,fg``.
557
432
The array is in C row-major memory order so you access it with yx (or ij) ordering.
558
433
434
+
That ends the entity rendering loop.
435
+
Next is to print the ``("Text", str)`` component if it exists.
436
+
A normal access will raise ``KeyError`` if the component is accessed before being assigned.
437
+
This case will be handled by the ``.get`` method of the ``Entity.components`` attribute.
438
+
:python:`g.world[None].components.get(("Text", str))` will return :python:`None` instead of raising ``KeyError``.
439
+
Assigning this result to ``text`` and then checking :python:`if text:` will ensure that ``text`` within the branch is not None and that the string is not empty.
440
+
We will not use ``text`` outside of the branch, so an assignment expression can be used here to check and assign the name at the same time with :python:`if text := g.world[None].components.get(("Text", str)):`.
441
+
442
+
In this branch you will print ``text`` to the bottom of the console with a white foreground and black background.
443
+
The call to do this is :python:`console.print(x=0, y=console.height - 1, string=text, fg=(255, 255, 255), bg=(0, 0, 0))`.
444
+
559
445
.. code-block:: python
560
446
561
447
...
@@ -568,6 +454,12 @@ The array is in C row-major memory order so you access it with yx (or ij) orderi
with tcod.context.new(console=g.console, tileset=tileset) as g.context:
683
-
game.state_tools.main_loop()
585
+
with tcod.context.new(console=console, tileset=tileset) as g.context:
586
+
whileTrue: # Main loop
587
+
console.clear() # Clear the console before any drawing
588
+
state.on_draw(console) # Draw the current state
589
+
g.context.present(console) # Render the console to the window and show it
590
+
for event in tcod.event.wait(): # Event loop, blocks until pending events exist
591
+
print(event)
592
+
state.on_event(event) # Dispatch events to the state
684
593
685
594
686
595
if__name__=="__main__":
@@ -692,10 +601,7 @@ You can review the part-2 source code `here <https://github.com/HexDecimal/pytho
692
601
693
602
.. rubric:: Footnotes
694
603
695
-
.. [#slots] This is done to prevent subclasses from requiring a ``__dict__`` attribute.
696
-
If you are still wondering what ``__slots__`` is then `the Python docs have a detailed explanation <https://docs.python.org/3/reference/datamodel.html#slots>`_.
697
-
698
604
.. [#g] ``global``, ``globals``, and ``glob`` were already taken by keywords, built-ins, and the standard library.
699
605
The alternatives are to either put this in the ``game`` namespace or to add an underscore such as ``globals_.py``.
0 commit comments