Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Text Adventure / RPG in Pure Python
#1
Greetings everybody, I've been motivated lately to work on a long time project of mine. A text adventure /RPG, in pure Python. I've refactored this thing over and over again. This time I thought I would write out the methods to my madness(pun intended). This is still a rough draft. So, there could be some errors. Any comments, tips or ways to improve this code is much appreciated.

Originally posted here:
http://mipython.magwebdesigns.net/WP/202...id-part-1/


CREATING THE GAME AND GRID

Repl for this section

https://pythontextadventuretutorial1.man...5.repl.run

MI Python Text Adventure RPG Tutorial 1 |Create The Game, Objects, and Grid

Hey every one, Thought I would share my methodology for making a text adventure / RPG in Python. I wanted to make this in pure Python with no added dependencies, other then built in Python dependencies. I wanted to create a simple and logical text adventure frame work. Each Python class is its own “module”. I wanted each “module” to handle its own functions to keep everything clear and concise.

Today we will be implementing our Game Grid in the Game class. This is the “world ” we will be putting our game objects in. It is really just a one dimensional list of room objects. Each room object has a position x and a position y attribute. The grid method will parse the rooms list and if the players position x and position y match the rooms. That rooms status will be printed.

Below we create the Game class that will be our game world. Initialize it with the name “Game”. Next we create a status function. I tend to create a status function for every object.


class Game():
  def __init__(self):
    self.name = "GAME"

  def status(self):
    print(self.name + " IS INSTANTIATED")
Creating the Game Grid:

We create the grid function for our game. This will be the method that holds the player and room objects in the game class. The game is going to be broken down into areas named “Rooms”. Each room will have it’s own position x and position y attribute. Along with a list of enemies and items. We will get to more of this in a bit though. For now we are passing the player in as an argument along with a list of rooms to our game grid method. The grid method loops through a list of rooms with a for statement. If the player is on the same x / y coordinates on the grid as a room that rooms status will be displayed.


def grid(self,player):

    print("INSTANTIATE GRID")
    rooms =  [start_room,empty_room]

    for room in rooms:
      if player.pos_x == room.pos_x and player.pos_y == room.pos_y:

        room.status()
Create Game Objects:

Lets quickly create a player and room class so we can instantiate a player and a room in our game. We will create a name and position x / y attributes for now. In our player status we will print the current x/y coords.


class Player():
  def __init__(self):
    self.name = "PLAYER"
    self.pos_x = 0
    self.pos_y = 0
  def status():
     print(self.name)
     print( self.name + " POS X: " + str(self.pos_x))
     print( self.name + " POS Y: " + str(self.pos_y))
Now lets create our rooms that will serve as the x,y areas on the grid. Below we initialize the Room class with : name, desc, pos_x,pos_y attributes. We are adding arguments to each instance of a room by passing the variables in when we instantiate our room objects. Then we make a status method. That is what the player will see upon interaction with a room.


class Room():
  def __init__(self,name,desc,pos_x,pos_y):
    self.name = name
    self.desc = desc
    self.pos_x = pos_x
    self.pos_y = pos_y

   def status():
     print(self.name)
     print(self.desc)
     print("ROOM: X: " + str(self.pos_x))
     print("ROOM Y: " + str(self.pos_y))
Instance Our Objects:

Lets instantiate our objects now. Below we instance the: game , player and room objects. For the rooms we pass in individual arguments. Don’t forget to add the room instances to the game.grid rooms list!!! Then we execute the game grid method with the player passed in as an argument.


### INSTANCE GAME
game = Game()

### INSTANCE PLAYER
player = Player()

### ROOMS name,desc,pos_x,pos_y
start_room = Room("START ROOM","THIS IS THE STARTING ROOM",0,0)
room_2 = Room("ROOM 2", "THIS IS ROOM 2",0,1)

### RUN GAME GRID METHOD
game.grid(player)


The Python RPG text adventure code so far should look like below.


class Game():
  def __init__(self):
    self.name = "GAME"

  def status(self):
    print(self.name + " IS INSTANTIATED")

  def grid(self,player):


    print("INSTANTIATE GRID")
    rooms = [start_room,room_2]

    for room in rooms:
      if player.pos_x == room.pos_x and player.pos_y == room.pos_y:

        room.status()

class Player():
  def __init__(self):
    self.name = "PLAYER"
    self.pos_x = 0
    self.pos_y = 0
  def status(self):
     print(self.name)
     print( self.name + " POS X: " + str(self.pos_x))
     print( self.name + " POS Y: " + str(self.pos_y))

class Room():
  def __init__(self,name,desc,pos_x,pos_y):
    self.name = name
    self.desc = desc
    self.pos_x = pos_x
    self.pos_y = pos_y

  def status(self):
    print(self.name)
    print(self.desc)
    print("ROOM: X: " + str(self.pos_x))
    print("ROOM Y: " + str(self.pos_y))

###   INSTANCE GAME
game = Game()

###    INSTANCE PLAYER
player = Player()

###   ROOMS        name,desc,pos_x,pos_y
start_room = Room("START ROOM","THIS IS THE STARTING ROOM",0,0)
empty_room = Room("EMPTY ROOM", "THIS IS AN EMPTY ROOM",0,1)

###   RUN GAME GRID METHOD
game.grid(player)
Execute this code and then change the players pos_y to 1. Execute the code again. You should see different room statuses displayed. One for the starting room and one for the empty room in our Python RPG text adventure .
User Interaction:

Of course this is no fun with out user interaction from the player. So lets add an input method for the player. I’ve debated whether to put the user input in the game class or the player class. My rational being that a player should always have self control over their actions within a game, specially a RPG. The game should dictate the options yet the user should be self aware and with at least illusory control. Just a little philosophical rant.

Lets add an input method to our Player class. we are adding the .upper() method to the input so everything imputed is in upper case.

def user_input(self):
    user_input = input(">>> ").upper()
    return user_input
Now lets add an options method for our Game class. “N”, “E”, “S”, and W” are our movement options for now. we will expand on this later. If the Player imputes “N” the players.pos_y is increased by one, etc..


def options(self,player):
    print("MOVE: (N), (E), (S), (W)")
    ###   PLAYER USER INPUT
    user_input = player.user_input()

    if user_input == "N":
      player.pos_y += 1
    elif user_input == "E":
      player.pos_x += 1
    elif user_input == "S":
      player.pos_y -= 1
    elif user_input == "W":
      player.pos_x -= 1
    else:
      print("INVALID CHOICE")
    return


Lets also write a main loop method for our Game class. With the Player passed in as an argument.

	
  def main_loop(self,player):
    game.status()
    while True:
      game.grid(player)
      player.status()
Final Code For This Section:

The updated code is below. Add the Game option method to the Game grid method then we start the Game main loop. The Game options method prints a string of game options and imputes the Players user_input and Player status methods. When the Player imputes “N”, “E”,” S” or “W” the Players pos x / y is updated accordingly. If the players pos x/y equals a rooms pos x/y in the grid room list. That rooms status is printed.


class Game():
  def __init__(self):
    self.name = "GAME"

  def status(self):
    print(self.name + " IS INSTANTIATED")

  def grid(self,player):
    rooms = [start_room,room_2,room_3]

    for room in rooms:
     
      if player.pos_x == room.pos_x and player.pos_y == room.pos_y:
        room.status()


  def options(self,player):
     #player.status()
     print("MOVE: (N), (E), (S), (W)")
     ###   PLAYER USER INPUT
     user_input = player.user_input()

     if user_input == "N":
       player.pos_y += 1
     elif user_input == "E":
       player.pos_x += 1
     elif user_input == "S":
       player.pos_y -= 1
     elif user_input == "W":
       player.pos_x -= 1
     else:
       print("INVALID CHOICE")
     return

  def main_loop(self,player):
      self.status()
      while True:
        self.grid(player)
        player.status()
        self.options(player)
       


class Player():
  def __init__(self):
    self.name = "PLAYER"
    self.pos_x = 0
    self.pos_y = 0
  def status(self):
     print(self.name)
     print(self.name + " POS X: " + str(self.pos_x))
     print(self.name + " POS Y: " + str(self.pos_y))
     
  def user_input(self):
     user_input = input(">>> ").upper()
     return user_input

class Room():
  def __init__(self,name,desc,pos_x,pos_y):
    self.name = name
    self.desc = desc
    self.pos_x = pos_x
    self.pos_y = pos_y

  def status(self):
    print(self.name)
    print(self.desc)
    print("ROOM: X: " + str(self.pos_x))
    print("ROOM Y: " + str(self.pos_y))

###   INSTANCE GAME
game = Game()

###    INSTANCE PLAYER
player = Player()

###   ROOMS        name,desc,pos_x,pos_y
start_room = Room("START ROOM","THIS IS THE STARTING ROOM",0,0)
room_2 = Room("ROOM 2", "THIS IS ROOM 2",0,1)
room_3 = Room("ROOM 3", "THIS IS ROOM 3",0,2)

###   RUN GAME MAIN LOOP
game.main_loop(player)
Run the main.py file and impute a direction. The player is able to interact with the grid at this point.

Next time we will be adding enemies and item objects to our rooms. We will also populate our game grid and organize our program structure more logically.
Reply
#2
MI Python Text Adventure RPG Tutorial 2 |Add Character and Item Objects

REPL HERE:

https://pythontextadventuretutorial2.man...5.repl.run

Lets refactor our code a bit here. Before our main.py Python file becomes too cumbersome and convoluted. We are going to split all our classes into separate files or “Modules”. We will have one file for the Player, Character, Room and Item. Then import them into our Game class in the main.py Python file.

While we are at it we will add some more attributes to our objects. Player and Character objects will have hit points, strength, dexterity, damage, items list, armors list, and weapons list attributes. For our Room objects we will add an enemies list, npc list, items list, weapons list and armors list. More about these attributes later on. Now on to our Python RPG text adventure.
Player Module:

Lets create our Player.py Python file

First we import random and the items module for later on. Then we define our Player attributes. For the items, weapons and armor we initialize empty lists. Update the Player status method with the new attribute variables to be printed.

For the Player object we add a create method to name and roll our Player. Then a random die roll method.

The create method in our Player class takes the Player user_input method. If the user input is an “N”. A second Player user input call is prompted. The second user input prompt will be the Players name.

If the create methods input is an “R”. We call the Players roll_die method and pass in 100 as the argument for the max integer for randint().

If the Players user input is a “P” the player may move on to the game. If the player name and hp are not None.

The roll_die method takes an integer for the max value of the “die”. The roll variable uses the Python random.randint() method with the “die” passed in as the max value. Then the result is printed concatenated on to the players name as a string with the Python str() method and returned.


import random
from items import *

class Player():
  def __init__(self):
    self.name = None
    self.pos_x = 0
    self.pos_y = 0
    self.max_hp = None
    self.hp = self.max_hp
    self.st = None
    self.dex = None
    self.dmg = None
    self.items = []
    self.weapons = []
    self.armors = []

  def status(self):
    print(self.name)
    print(str(self.name) + " POS X: " + str(self.pos_x))
    print(str(self.name) + " POS Y: " + str(self.pos_y))
    print("HP: " + str(self.hp))
    print("STR: " + str(self.st))
    print("DEX: " + str(self.dex))
    print("DMG: " + str(self.dmg))

    for item in self.items:
      print(item.name)
    for weapon in self.weapons:
      print(weapon.name)
    for armor in self.armors:
      print(armor.name)
     
  def user_input(self):
    user_input = input(">>> ").upper()
    return user_input

  def create(self):
    player.status()
    print("CREATE YOUR CHARACTER")
    print("(N)AME YOUR CHARACTER")
    print("(R)OLL YOUR STATS")
    print("(P)LAY GAME")

    user_input = self.user_input()

    if user_input == "N":
      print("WHAT IS YOUR NAME")
      user_input = self.user_input()
      self.name = user_input
      self.create()

    elif user_input == "R":
      print("ROLLING STATS")

      print("ROLLING HP")
      roll = self.roll_die(20)
      self.hp = roll

      print("ROLLING STRENGTH")
      roll = self.roll_die(100)
      self.st = roll

      print("ROLLING DEXTERITY")
      roll = self.roll_die(100)
      self.dex = roll

      self.create()
      return

    elif user_input == "P":
      print("PLAY THE GAME")
      if self.name == None or self.hp == None:
        print("FINISH MAKING YOUR CHARACTER")
        self.create()
    else:
      print(" INVALID CHOOSE AN OPTION N, R, P")

  def roll_die(self,die):
    roll = random.randint(0,die)
    print(self.name + " ROLLS: " + str(roll))
    return roll

player = Player()
player.create()
Run the above Player.py file. Our Player creation method lets the user name and roll the players stats. You should see the Player status method being updated.
Item Module:

Here we are initializing our Item class. We give each item a: name, damage value, armor value, gold piece value and weight. We will then instance a few items below. A short_sword, wizards_staff, leather_armor and a gem. For the “weapon” objects damage. We are passing in the randint() method. Note that if we don’t want an item to do damage or have an armor bonus. We just add a 0 integer to the arguments passed in to the instance. Then update the status method to print the new arguments.

import random
from Item import *

class Item():
  def __init__(self,name,dmg_value,ar_value,gp_value,weight):
    self.name = name
    self.dmg_value = dmg_value
    self.ar_value = ar_value
    self.gp_value = gp_value
    self.weight = weight
   
  def status(self):
    print(self.name)

    print("DMG VALUE: " + str(self.dmg_value))
    print("AR VALUE: " + str(self.ar_value))
    print("GP VALUE: " + str(self.gp_value))
    print("WEIGHT " + str(self.weight))


###   INSTANCE ITEM name,dmg_value,ar_value,gp_value,weight
###   WEAPONS
short_sword = Item("SHORT SWORD",random.randint(1,6),0,50,3)
wizards_staff = Item("WIZARDS STAFF",random.randint(1,40),0,400,4)
###   ARMORS
leather_armor = Item("LEATHER ARMOR",0,2,80,12)
cloth_robe = Item("CLOTH ROBE",0,1,5,2)
###   ITEMS
gem = Item("GEM",0,0,100,.1)
Character Module:

Lets Create our Character.py Python file. The Character class is similar to the Player class except we are not giving any input. Also instead of a create method for the class we are passing in the attributes upon instantiation. Lets also add a dialog argument, so the characters print a string out to the player. Update the Character status() while we are at it to print the new attributes. Then we make an orc_peon instance of our Character class. Lets also pass in the Item objects we created above to the orc peon’s: items, weapons and armors list. Don’t forget to import our Item.py file. We will make the Characters dmg equal to the random dmg_value of the first item in the weapons list. In this case the orc_peon with the short_sword.


import random
from Item import *

class Character():
  def __init__(self,name,pos_x,pos_y,max_hp,st,dex,gp,xp,items,weapons,armors,dialog):
    self.name = name
    self.pos_x = pos_x
    self.pos_y = pos_y
    self.max_hp = max_hp
    self.hp = self.max_hp
    self.st = st
    self.dex = dex
    self.gp = gp
    self.xp = xp
    self.items = items
    self.weapons = weapons
    self.armors = armors
    self.dmg = self.weapons[0].dmg_value
    self.ar = self.armors[0].ar_value
    self.dialog = dialog

  def status(self):
    print(self.name)
    print(str(self.name) + " POS X: " + str(self.pos_x))
    print(str(self.name) + " POS Y: " + str(self.pos_y))
    print("HP: " + str(self.hp))
    print("STR: " + str(self.st))
    print("DEX: " + str(self.dex))
    print("DMG: " + str(self.dmg))
    print("AR: " + str(self.ar))
    print("GP: " + str(self.gp))
    print("XP: " + str(self.xp))

    for item in self.items:
      print("ITEMS: ")
      print(item.name)
    for weapon in self.weapons:
      print("WEAPONS: ")
      print(weapon.name)
    for armor in self.armors:
      print("ARMORS: ")
      print(armor.name)

  def roll_die(self,die):
    roll = random.randint(0,die)
    print(self.name + " ROLLS: " + str(roll))
    return roll


#name,pos_x,pos_y,max_hp,st,dex,gp,xp,items,weapons,armors

orc_peon = Character("ORC PEON",0,0,6,30,60,50,20,[gem],[short_sword],[leather_armor],"DIE DIE DIE")

friendly_wizard = Character("FRIENDLY WIZARD",0,0,6,30,60,50,20,[],[wizards_staff],[cloth_robe],"  HELLO ADVENTURER I AM A FRIENDLY WIZARD.  I HAVE BROUGHT YOU TO THIS REALITY")
Room Module:

For our Room class import the Character and Item files. Lets add: enemies, items, weapons and armors arguments to the class. When we instance each room we will pass in a list for each. Then add our Character objects to the correct list. Update the status method. Then we instance our rooms.

	
from Character import *
from Item import *

class Room():
  def __init__(self,name,desc,pos_x,pos_y,npcs,enemies,items,weapons,armors):
    self.name = name
    self.desc = desc
    self.pos_x = pos_x
    self.pos_y = pos_y
    self.npcs = npcs
    self.enemies = enemies
    self.items = items
    self.weapons = weapons
    self.armors = armors

  def status(self):
    print(self.name)
    print(self.desc)
    print("ROOM: X: " + str(self.pos_x))
    print("ROOM Y: " + str(self.pos_y))

    print("YOU SEE:")
    for npc in self.npcs:
      print(npc.name)
    for enemy in self.enemies:
      print(enemy.name)
    for item in self.items:
      print(item.name)
    for weapon in self.weapons:
      print(weapon)
    for armor in self.armors:
      print(armor)

###       name,desc,pos_x,pos_y,npcs,enemies,items,weapons,armors

start_room = Room("START ROOM","THIS IS THE STARTING ROOM",0,0,[friendly_wizard],[],[],[],[])

peon_room = Room("ORC PEON ROOM","THIS IS THE ORC PEON ROOM",0,1,[],[orc_peon,orc_peon,orc_peon],[],[],[])

gem_room = Room("GEM ROOM","THIS IS THE GEM ROOM",0,-1,[],[],[gem,gem,gem],[],[])
Game Module:

Now lets import and pass in all our objects to our Game class in the main.py Python file. Don’t forget to add the rooms we created to the Game grid rooms list!!!

from Character import *
from Item import *
from Room import *
from Player import *


class Game():
  def __init__(self):
    self.name = "GAME"

  def status(self):
    print(self.name + " IS INSTANTIATED")

  def grid(self,player):
    rooms = [start_room,peon_room,gem_room]

    for room in rooms:
     
      if player.pos_x == room.pos_x and player.pos_y == room.pos_y:
        room.status()


  def options(self,player):
     #player.status()
     print("MOVE: (N), (E), (S), (W)")
     ###   PLAYER USER INPUT
     user_input = player.user_input()

     if user_input == "N":
       player.pos_y += 1
     elif user_input == "E":
       player.pos_x += 1
     elif user_input == "S":
       player.pos_y -= 1
     elif user_input == "W":
       player.pos_x -= 1
     else:
       print("INVALID CHOICE")
     return

  def main_loop(self,player):
      self.status()
      player.create()
      while True:
        self.grid(player)
        player.status()
        self.options(player)

game = Game()
game.main_loop(player)
Run the game main loop. We now have player creation with a grid based movement system. Characters have inventories and rooms have characters. The player is able to see the status of each room.

Of course this is no fun if we can’t kill some orc peons and take their gems or talk to the friendly wizard. So, next time we will implement a battle system along with inventory management and a dialog system in our Python text adventure RPG.
Reply
#3
Hey everybody, got combat and a basic inventory done. Posted the 3rd part of the Python Text Adventure RPG here:

MI Python Text Adventure RPG Tutorial 3 |Battle System and Inventory Management

Repl's here so you can play it.

https://pythontextadventuretutorial3.man....repl.run/
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020