This is a small text-based Role-Playing Game that I wrote while learning Python. The game is the simplest RPG adventure I could think of, that maintains the essential traits of the classics.
The more I dive into Python the more I understand the work done to create a language that is easy by design. I tried to apply some non-trivial functionalities such as:
- Class inheritance (Enemy and Player inherit from Character)
- Function pointers (Commands contains a dictionary of functions)
- String manipulation (creating output and parsing input)
I think that this game can be a clean example to understand how to apply these aspects in a Python program.
To play the game
- Install Python (installed by default in many Linux distributions, see http://www.python.org/download/ for other platforms)
- Copy the following code into a file called (for example)
rpg.py - In Linux, run “
python rpg.py” from the command line in the directory of therpg.pyfile. In Windows, open the file with Python. - Enjoy!
- Quit and get a life.
from random import randint
class Character:
def __init__(self):
self.name = ""
self.health = 1
self.health_max = 1
def do_damage(self, enemy):
damage = min(
max(randint(0, self.health) - randint(0, enemy.health), 0),
enemy.health)
enemy.health = enemy.health - damage
if damage == 0: print "%s evades %s's attack." % (enemy.name, self.name)
else: print "%s hurts %s!" % (self.name, enemy.name)
return enemy.health <= 0
class Enemy(Character):
def __init__(self, player):
Character.__init__(self)
self.name = 'a goblin'
self.health = randint(1, player.health)
class Player(Character):
def __init__(self):
Character.__init__(self)
self.state = 'normal'
self.health = 10
self.health_max = 10
def quit(self):
print "%s can't find the way back home, and dies of starvation.\nR.I.P." % self.name
self.health = 0
def help(self): print Commands.keys()
def status(self): print "%s's health: %d/%d" % (self.name, self.health, self.health_max)
def tired(self):
print "%s feels tired." % self.name
self.health = max(1, self.health - 1)
def rest(self):
if self.state != 'normal': print "%s can't rest now!" % self.name; self.enemy_attacks()
else:
print "%s rests." % self.name
if randint(0, 1):
self.enemy = Enemy(self)
print "%s is rudely awakened by %s!" % (self.name, self.enemy.name)
self.state = 'fight'
self.enemy_attacks()
else:
if self.health < self.health_max:
self.health = self.health + 1
else: print "%s slept too much." % self.name; self.health = self.health - 1
def explore(self):
if self.state != 'normal':
print "%s is too busy right now!" % self.name
self.enemy_attacks()
else:
print "%s explores a twisty passage." % self.name
if randint(0, 1):
self.enemy = Enemy(self)
print "%s encounters %s!" % (self.name, self.enemy.name)
self.state = 'fight'
else:
if randint(0, 1): self.tired()
def flee(self):
if self.state != 'fight': print "%s runs in circles for a while." % self.name; self.tired()
else:
if randint(1, self.health + 5) > randint(1, self.enemy.health):
print "%s flees from %s." % (self.name, self.enemy.name)
self.enemy = None
self.state = 'normal'
else: print "%s couldn't escape from %s!" % (self.name, self.enemy.name); self.enemy_attacks()
def attack(self):
if self.state != 'fight': print "%s swats the air, without notable results." % self.name; self.tired()
else:
if self.do_damage(self.enemy):
print "%s executes %s!" % (self.name, self.enemy.name)
self.enemy = None
self.state = 'normal'
if randint(0, self.health) < 10:
self.health = self.health + 1
self.health_max = self.health_max + 1
print "%s feels stronger!" % self.name
else: self.enemy_attacks()
def enemy_attacks(self):
if self.enemy.do_damage(self): print "%s was slaughtered by %s!!!\nR.I.P." %(self.name, self.enemy.name)
Commands = {
'quit': Player.quit,
'help': Player.help,
'status': Player.status,
'rest': Player.rest,
'explore': Player.explore,
'flee': Player.flee,
'attack': Player.attack,
}
p = Player()
p.name = raw_input("What is your character's name? ")
print "(type help to get a list of actions)\n"
print "%s enters a dark cave, searching for adventure." % p.name
while(p.health > 0):
line = raw_input("> ")
args = line.split()
if len(args) > 0:
commandFound = False
for c in Commands.keys():
if args[0] == c[:len(args[0])]:
Commands[c](p)
commandFound = True
break
if not commandFound:
print "%s doesn't understand the suggestion." % p.name
"""
Copyright 2010 Francesco Balducci
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
See <http://www.gnu.org/licenses/> for a copy of the GNU General Public License.
"""
Entries
Marcin
2010/08/25
Cool simple and easy and powerful too i try add some new features.
Jon
2011/01/08
Nice work! I like the simplistic “adventure”.
antonymityymity
2012/01/09
thanks! i am working on an rpg myself while learning new coding techniques like classes and inheritance. after about an hour of hacking around i have one file that has a base character class and a couple other classes that inherit the base and modify its stats, and another file that imports these classes and creates instances of thm and displays their relevant stats.
i also was happy to see how you have put specific game-related methods into your classes. it appears that say for a thief character i would want to write the steal method into the thief class itself, ya? and other such class-specific commands and behaviors into each appropriate class? it does seem to make sense from an abstraction standpoint – if i want to make another game down the line with a thief character, i won’t have to hunt around for all the specific thief methods in my code and copy/paste them into the new code if they’re already all tied into that 1 class definition. fantastic!
learned1
2012/02/01
What Version is it in?
Balau
2012/02/01
I tried it both on python2.6 and python2.7
Wian Rifqi Dwi
2012/07/06
Please could you make a Tutorial too, from scratch please I beg…
I just don’t know where to start..
Balau
2012/07/06
As I said in the beginning of the post I am learning Python, which means I am not an expert that can write tutorials.
You can find many tutorials in this “Beginner’s Guide” page. They should be better than anything I could write right now.
In particular you could try Invent with Python, which is quite relevant to the content of my post.
Wian Rifqi Dwi
2012/08/01
Hello Sir, sorry for my rudenesess word…
I’m having problem with the program so it always appear the error & said “Traceback (most recent call last):
File “C:\Users\Wiqidi\Documents\Python\rpg.py”, line 23, in
class Player(Character):
File “C:\Users\Wiqidi\Documents\Python\rpg.py”, line 86, in Player
‘quit’: Player.quit,
NameError: name ‘Player’ is not defined” why is that?
Please show me the way how to fix this?
Balau
2012/08/01
Most likely you have a problem in indentation (number of spaces) of some lines.
Your error says that the “quit” line is inside the “class Player(Character)”, but it should not be in that class, it should be at “top” level
You should not have spaces before “Commands” in line 85, try to see if you have some, and if so remove them.
steve
2012/08/10
Balau line 100 is giving me some issues. I input my name and it then says enters the cave then says line 100 player instance has no attribute ‘health’. Any suggestions? Thanks
Balau
2012/08/11
I don’t know what it might be. You should try to open the file with an editor that shows the whitespaces (spaces, tabs and newlines) and check that the indentation is correct.
See also this chapter of Python manual: 2.1.8 Indentation.
Rufus!
2012/11/01
Thanks. This is awesome. I’m just barely getting into programming. I couldn’t understand how to really use classes and methods until messing around with your game. So, thanks for putting this up and making it open for us to use.
Nick
2013/01/04
Balau, the program says ‘AttributeError: Player instance has no attribute ‘health”. What should I do? I am really trying to get into programming but I can’t seem to make this work. I have tried everything I know, so if you could answer that would be nice
Balau
2013/01/04
As I said in a comment just above yours, to someone with your exact same problem, check whitespace and indentation.
Bill101
2013/01/29
Love the game, its really is well done. After messing arround with it for about 3 hours I got some basic things like weapons and armor in! But thanks for the great base system without it I would be still writing all my RPGs with millions of if statements
Maxwell Phelps
2013/04/09
I added some extra features if anyone is interested.
Please email me with questions and or suggestions, Im very new to writing code and even newer to python. There are now merchants that you can buy stimpacks(health) or E.Cells(ammo) or sell stimpacks. There are randomized enemies and a currency system.
I’m still working on it so comments are appreciated.
import random
from random import randint
class Character:
def __init__(self):
self.name = ""
self.health = 1
self.health_max = 1
def do_damage(self, enemy):
damage = min(
max(randint(0, self.health) - randint(0, enemy.health), 0),
enemy.health)
enemy.health = enemy.health - damage
if damage == 0: print "%s evades the %s's attack.\n -%dhp" % (enemy.name, self.name, damage)
else: print "%s hurts the%s!\n -%dhp" % (self.name, enemy.name, damage)
return enemy.health <= 0
class Enemy(Character):
def __init__(self, player):
enemy_name1 = randint(0, 7)
if enemy_name1 == 0:
en = " super mutant"
elif enemy_name1 == 1:
en = " raider"
elif enemy_name1 == 2:
en = " mole rat"
elif enemy_name1 == 3:
en = " Brotherhood paladin"
elif enemy_name1 == 4:
en = " Enclave solider"
elif enemy_name1 == 5:
en = " feral goul"
elif enemy_name1 == 6:
en = " deathclaw"
elif enemy_name1 == 7:
en = " mirelurk"
Character.__init__(self)
self.name = "%s"%(en)
self.health = randint(3 ,player.health_max)
class Player(Character):
def __init__(self):
Character.__init__(self)
self.state = 'normal'
self.merchstate = 'normal'
self.health = 10
self.health_max = 10
self.loot = 10
self.stim = 2
self.ammo = 10
def quit(self):
print "%s can't find the way back home, and dies of starvation.\nR.I.P." % self.name
self.health = 0
def help(self): print Commands.keys()
def status(self): print "%s's health: %d/%d\n caps:%d stim:%d e.cells:%d" % (self.name, self.health, self.health_max, self.loot, self.stim, self.ammo)
def oofammo(self):
if self.ammo 0:
if self.loot >=20:
self.stim = self.stim +1
self.loot = self.loot - 20
self.merchstim = self.merchstim -1
print"%s bought 1 Stimpack." % self.name
else: print "Not enough caps."
else: print "Merchant has no Stimpacks."
else:
print "No merchant."
self.merchstate = 'normal'
def item2(self):
if self.merchstate == 'buy' :
if self.merchammo > 0:
if self.loot >= 10:
self.ammo = self.ammo + 5
self.loot = self.loot - 10
self.merchammo = self.merchammo - 5
print"%s bought 5 E.Cells." % self.name
else: print"Not enough caps."
else: print"Merchant has no E.Cells."
else:
print "No merchant."
self.merchstate = 'normal'
def sell(self):
if self.merchstate == 'buy' :
if self.stim > 0:
self.stim = self.stim -1
self.loot = self.loot + 10
self.merchstim = self.merchstim +1
print"%s sold 1 Stimpack." % self.name
else: print "%s has no Stimpacks." % self.name
else:
print "No merchant."
self.merchstate = 'normal'
def stim(self):
if self.stim > 0:
if self.health self.health_max:
self.health = self.health_max
else: print "full health."
else: print "no stimpacks."
def merch(self):
self.merchstate = 'buy'
self.merchammo = 0
self.merchstim = 0
self.merchstim = self.merchstim + randint(0,10)
self.merchammo = self.merchammo + randint(0, 50)
print "%s encounters a merchant!" % self.name
print "Merchants items:\n Stimpacks:%d\n E.Cells:%d" % (self.merchstim, self.merchammo)
def tired(self):
print "%s feels tired." % self.name
self.health = max(1, self.health - 1)
def rest(self):
if self.state != 'normal': print "%s can't rest now!" % self.name; self.enemy_attacks()
else:
print "%s rests." % self.name
if randint(0, 1):
self.enemy = Enemy(self)
print "%s is rudely awakened by a %s!" % (self.name, self.enemy.name)
self.state = 'fight'
self.enemy_attacks()
else:
if self.health 0:
print "%s explores%s" % (self.name, Player.item)
if randint(0, 1):
self.enemy = Enemy(self)
print "%s encounters a%s!\n Enemy hp: %d." % (self.name, self.enemy.name, self.enemy.health)
self.state = 'fight'
else:
if randint(0, 1): self.tired()
if randint(0, 1): self.merch()
def flee(self):
if self.state != 'fight': print "%s runs in circles for a while." % self.name; self.tired()
else:
if randint(1, self.health + 5) > randint(1, self.enemy.health):
print "%s flees from the%s." % (self.name, self.enemy.name)
self.enemy = None
self.state = 'normal'
else: print "%s couldn't escape from the%s!" % (self.name, self.enemy.name); self.enemy_attacks()
def attack(self):
self.oofammo()
self.ammo = self.ammo - 1
if self.state != 'fight':
if self.ammo > 0: print "%s shoots into the air, without notable results." % self.name; self.tired()
else:
if self.do_damage(self.enemy):
print "%s executes %s!" % (self.name, self.enemy.name)
self.enemy = None
self.state = 'normal'
eloot = randint(0, 25)
self.loot = eloot + self.loot
print "you recovered %d caps from the corpse." %(eloot)
eammo = randint (0, 5)
self.ammo = eammo + self.ammo
print "you recovered %d energy cells from the corpse." %(eammo)
stimp = randint(0, 4)
if stimp == 0:
self.stim = self.stim +1
print "you recoverd a stimpack!"
if randint(0, self.health_max) 0):
line = raw_input("> ")
args = line.split()
if len(args) > 0:
commandFound = False
for c in Commands.keys():
if args[0] == c[:len(args[0])]:
Commands[c](p)
commandFound = True
break
if not commandFound:
print "%s doesn't understand the suggestion." % p.name
"""
Code by Maxwell Phelps 2013
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
See for a copy of the GNU General Public License.
"""
Maxwell Phelps
2013/04/09
Sorry about that last post I’m not sure how to properly paste the code. and theres a typo around line 80: self.merchammo = self.merchammo – 10, should be: self.merchammo = self.merchammo – 5
Balau
2013/04/09
I corrected your comment but unfortunately WordPress comments screw up the indentation. Maybe it’s better to show the code somewhere else such as a Github repo.
Thanks for the contribution.
robert phelps
2013/04/09
Will do thanks
Maxwell Phelps
2013/04/10
Heres: http://rmaxwellnichols.wordpress.com a link to my wordpress. I have it properly indented.
Scary Mark
2013/04/10
Bill101
Do you still have your code for armour and weapons, I’ve been playing with the code for adding magic and extra foes, and would like to have a look at your code…
Thanks Balau your code has help me to understand so much…