Zonr and I have been working on a fun text game containing different rooms, monsters (with a D&D-esque battle system), and save/load functions.
Here are the files. You need to have all of them for it to work correctly:
www.glenwinters.com/RPG/RPG_jan20.py
www.glenwinters.com/RPG/data.txt
www.glenwinters.com/RPG/Glen.txt
www.glenwinters.com/RPG/George.txt
Here's the main code:
import random
import cPickle
# Load file_list
try:
f = open("data.txt","r")
file_list = cPickle.load(f)
f.close()
except EOFError: # If data.txt is empty and cannot load
file_list = []
# Room class
class Room(object):
def __init__(self,name,description):
self.name = name
self.description = description
self.north = 0
self.east = 0
self.south = 0
self.west = 0
# possible list of monsters in room,monster in room
self.current_monster = None
self.monster_probability = 0
self.possible_monsters_names = []
# Create rooms
bedroom = Room("bedroom","You are in a bedroom. You can go NORTH, EAST, or SOUTH.")
garden = Room("garden","You are in a garden. You can go NORTH or EAST.")
pathway = Room("pathway","You are on a pathway. You can go WEST or EAST.")
open_area = Room("open area","You are in an open area. You can go WEST or NORTH. The open area expands to the SOUTH.")
study = Room("study","You are in your private study. You can go WEST.")
cliff = Room("cliff", "You are in an open area that ends in a steep cliff on the south side. There seems to be a small rocky path to the WEST.")
rocky_path = Room("rocky path","You are on a narrow cliff winding down the side of the cliff to the south. Be careful not to fall! The top of the cliff is to the EAST. There is a small cave to the WEST.")
hallway = Room("hallway","You are in a long fancy hallway outside your bedroom. There are doors to the west, north, and east (but they're locked). Your room is to the SOUTH.")
cave = Room("cave","You are in a dark cave. You can go EAST back to the rocky path.")
# These room variables point to the same instance!
# After a room changes, these don't have to be updatesd.
bedroom.north = hallway
bedroom.east = study
bedroom.south = garden
bedroom.west = 0
garden.north = bedroom
garden.east = pathway
garden.south = 0
garden.west = 0
pathway.north = 0
pathway.east = open_area
pathway.south = 0
pathway.west = garden
open_area.north = 0
open_area.east = 0
open_area.south = cliff
open_area.west = pathway
study.north = 0
study.east = 0
study.south = 0
study.west = bedroom
hallway.north = 0
hallway.east = 0
hallway.south = bedroom
hallway.west = 0
cliff.north = open_area
cliff.east = 0
cliff.south = 0
cliff.west = rocky_path
rocky_path.north = 0
rocky_path.east = cliff
rocky_path.south = 0
rocky_path.west = cave
cave.north = 0
cave.east = rocky_path
cave.south = 0
cave.west = 0
# Room monster control
# bedroom
bedroom.possible_monsters_names = None
bedroom.current_monster = None
bedroom.monster_probability = 0
garden.possible_monsters_names = ["rat"]
garden.current_monster = None
garden.monster_probability = 10
pathway.possible_monsters_names = ["rat"]
pathway.current_monster = None
pathway.monster_probability = 10
open_area.possible_monsters_names = ["rat"]
open_area.current_monster = None
open_area.monster_probability = 10
study.possible_monsters_names = None
study.current_monster = None
study.monster_probability = 0
hallway.possible_monsters_names = None
hallway.current_monster = None
hallway.monster_probability = 0
cliff.possible_monsters_names = ["rat","goblin"]
cliff.current_monster = None
cliff.monster_probability = 10
rocky_path.possible_monsters_names = ["goblin"]
rocky_path.current_monster = None
rocky_path.monster_probability = 20
cave.possible_monsters_names = ["troll"]
cave.current_monster = None
cave.monster_probability = 1
# Monster class
class Monster(object):
def __init__(self,name,level,strength,ac,hp,dex,give_exp,run_probability):
self.name = name
self.level = level
self.str = strength
self.ac = ac
self.hp = hp
self.dex = dex
self.give_exp = give_exp
self.run_probability = 10 # 90% of running is default
# Monster models (name,level strength, ac, hitpoints in form (mix,max), dex, give_exp,run_probability)
rat = ( "rat", 1, 1, 7 ,(5 ,10), 0, 5 , 10)
goblin = ("goblin", 2, 2, 12,(16,16), 0, 10, 5 )
troll = ( "troll", 5, 4, 16,(60,60),-1, 30, 2 )
# Character class
class Character(object):
def __init__(self,name):
self.name = name
self.level = 1
self.exp = 0
self.location = bedroom
self.str = 0
self.ac = 0
self.max_hp = 0
self.hp = 0
self.dex = 0
self.update_stats()
self.current_weapon = fists
def update_stats(self):
self.str = eval("level" + str(self.level))[0]
self.ac = eval("level" + str(self.level))[1]
self.hp = eval("level" + str(self.level))[2]
self.max_hp = eval("level" + str(self.level))[2]
self.dex = eval("level" + str(self.level))[3]
def level_up(self):
if self.level < 10:
while self.exp >= 25:
self.level += 1
self.exp -= 25
self.update_stats()
print "LEVEL UP!"
print \
'''
NEW STATS
Level: %s
Strength: %s
Armor Class: %s
Hit Points: %s
Dexterity: %s
''' % (self.level,self.str,self.ac,self.hp,self.dex)
else:
print "You can't level any higher. 10 is the max."
# Char level models [str,ac,hp,dex]
level1 = [1,10,12,0]
level2 = [1,10,19,0]
level3 = [2,11,26,0]
level4 = [2,11,33,0]
level5 = [3,12,40,0]
level6 = [3,12,47,0]
level7 = [4,13,54,0]
level8 = [4,13,61,0]
level9 = [5,14,68,0]
level10 = [6,15,75,0]
#level11 = [500,20,2000,0] for fun... *shifty eyes*
class Weapon(object):
def __init__(self,name,roll):
self.name = name
self.roll = roll
def attack(self):
dice_numbers = self.roll.split("d")
dice_count = int(dice_numbers[0])
dice_sides = int(dice_numbers[1])
total_damage = 0
for i in range(dice_count):
total_damage += random.randrange(1,dice_sides+1)
return total_damage + currentchar.str
# Default weapon
fists = Weapon("Fists","1d6")
# Prototypes
dagger = ("Dagger","1d6")
def create_monster():
#print "CHECK: ", currentchar.location.current_monster
if not currentchar.location.current_monster and currentchar.location.possible_monsters_names and currentchar.location.monster_probability != 0:
ifmonster = random.randrange(1,currentchar.location.monster_probability+1) # Pick a number between 1 and 10
#print "CHECK: ", ifmonster
if ifmonster == 1:
new_monster_name = random.choice(currentchar.location.possible_monsters_names)
# evalutes new_monster_name (string) to the variable that contains the monster's attributes
new_monster = Monster(eval(new_monster_name)[0],eval(new_monster_name)[1],eval(new_monster_name)[2],eval(new_monster_name)[3],random.randrange(eval(new_monster_name)[4][0],eval(new_monster_name)[4][1]+1),eval(new_monster_name)[5],eval(new_monster_name)[6],eval(new_monster_name)[7])
currentchar.location.current_monster = new_monster
print "There is a %s in the room." % (new_monster.name)
#print "CHECK: It has %d strength, %d AC, %d hitpoints." % (new_monster.str,new_monster.ac,new_monster.hp)
def see_if_fight():
fight_yn = "none"
while fight_yn not in ("y","n"):
fight_yn = raw_input("Do you want to fight it? (y/n) ").lower()
if fight_yn == "y":
return battle()
def enter_room(direction):
results = "none"
if direction == "n":
if currentchar.location.north:
currentchar.location = currentchar.location.north
print currentchar.location.description
if currentchar.location.current_monster:
print "There is a %s in the room." % (currentchar.location.current_monster.name)
results = see_if_fight()
if results == "won": # If just battled, print description of room
print currentchar.location.description
else:
create_monster()
if currentchar.location.current_monster:
results = see_if_fight()
if results == "won":
print currentchar.location.description
else:
print "You cannot move north."
print currentchar.location.description
elif direction == "e":
if currentchar.location.east:
currentchar.location = currentchar.location.east
print currentchar.location.description
if currentchar.location.current_monster:
print "There is a %s in the room." % (currentchar.location.current_monster.name)
results = see_if_fight()
if results == "won":
print currentchar.location.description
else:
create_monster()
if currentchar.location.current_monster:
results = see_if_fight()
if results == "won":
print currentchar.location.description
else:
print "You cannot move east."
print currentchar.location.description
elif direction == "s":
if currentchar.location.south:
currentchar.location = currentchar.location.south
print currentchar.location.description
if currentchar.location.current_monster:
print "There is a %s in the room." % (currentchar.location.current_monster.name)
results = see_if_fight()
if results == "won":
print currentchar.location.description
else:
create_monster()
if currentchar.location.current_monster:
results = see_if_fight()
if results == "won":
print currentchar.location.description
else:
print "You cannot move south."
print currentchar.location.description
elif direction == "w":
if currentchar.location.west:
currentchar.location = currentchar.location.west
print currentchar.location.description
if currentchar.location.current_monster:
print "There is a %s in the room." % (currentchar.location.current_monster.name)
results = see_if_fight()
if results == "won":
print currentchar.location.description
else:
create_monster()
if currentchar.location.current_monster:
results = see_if_fight()
if results == "won":
print currentchar.location.description
else:
print "You cannot move west."
print currentchar.location.description
if currentchar.location == bedroom:
print "There is a bed in the corner of the room."
heal = "none"
while heal not in ("y","n"):
heal = raw_input("Would you like to rest and heal? (y/n) ").lower()
if heal == "y":
print "You take a nap.\nYour health is replenished!"
currentchar.hp = currentchar.max_hp
if results == "none":
heal_monsters()
return results
def roll_1d20():
return random.randrange(1,20+1)
def roll_1d6():
return random.randrange(1,6+1)
def who_first():
monster_roll = roll_1d20() + currentchar.location.current_monster.dex
char_roll = roll_1d20() + currentchar.dex
if char_roll >= monster_roll:
return "char","monster" # (who goes first, who goes second)
else:
return "monster","char" # (Who goes first, who goes second)
def swap_turn(attacker,defender):
if attacker == "char":
new_attacker = "monster"
new_defender = "char"
else:
new_attacker = "char"
new_defender = "monster"
return new_attacker,new_defender
# You can fight once; never replenishes health
def battle():
the_monster = currentchar.location.current_monster
attacker,defender = who_first()
if attacker != "char":
print "The %s attacks you!" % (the_monster.name)
menu = \
'''
What do you want to do?
(1) Attack
(2) Run'''
print "-" * 12
print " You: %s hp" % (currentchar.hp)
print " %s: %s hp" % (the_monster.name.title(),the_monster.hp)
print "-" * 12
battle = "going"
while currentchar.hp > 0 and the_monster.hp > 0:
if attacker == "char":
print menu
choice = "none"
while choice not in ("1","2"):
choice = raw_input("Enter choice: ")
# ATTACK
if choice == "1":
ac_roll = roll_1d20() + currentchar.str
if ac_roll >= the_monster.ac:
damage = currentchar.current_weapon.attack()
the_monster.hp = the_monster.hp - damage
if the_monster.hp < 0:
the_monster.hp = 0
print "You hit for %d damage.\n" % (damage)
print "-" * 12
print " %s: %s hp" % (currentchar.name,currentchar.hp)
print " %s: %s hp" % (the_monster.name.title(),the_monster.hp)
print "-" * 12
raw_input("Enter to continue...")
else:
print "You didn't hit him.\n"
raw_input("Enter to continue...")
# RUN
elif choice == "2":
# Can't run if you get a 1
ifrun = random.randrange(1,the_monster.run_probability+1)
if ifrun != 1:
battle = "character ran"
break
else:
ac_roll = roll_1d20() + the_monster.str
if ac_roll >= currentchar.ac:
damage = roll_1d6()
currentchar.hp = currentchar.hp - damage
if currentchar.hp < 0:
currentchar.hp = 0
print "The %s hit for %d damage.\n" % (the_monster.name,damage)
print "-" * 12
print " You: %s hp" % (currentchar.hp)
print " %s: %s hp" % (the_monster.name.title(),the_monster.hp)
print "-" * 12
raw_input("Enter to continue...")
else:
print "The %s missed.\n" % (the_monster.name)
raw_input("Enter to continue...")
# Switch who is attacker and defender
attacker,defender = swap_turn(attacker,defender)
if currentchar.hp <= 0:
print "You died."
return "dead"
elif the_monster.hp <= 0:
print "You killed the %s." % (the_monster.name)
currentchar.exp += the_monster.give_exp
print "You gained %d experience points.\nYou have %d experience points.\n" % (the_monster.give_exp,currentchar.exp)
currentchar.location.current_monster = None
currentchar.level_up()
return "won"
elif battle == "character ran":
print "You successfully ran from the battle."
def stats():
print \
'''
Name: %s
Level: %d
Experience points: %d
Strength: %d
Armor Class: %d
Hit Points: %d
Dexterity: %d
Current Weapon: %s
''' % (currentchar.name,currentchar.level,currentchar.exp,currentchar.str,currentchar.ac,currentchar.hp,currentchar.dex,currentchar.current_weapon.name)
def heal_monsters():
rooms = ["bedroom","garden","pathway","open_area","study","cliff","rocky_path","hallway","cave"]
for room_str in rooms:
room = eval(room_str)
if room != currentchar.location and room.current_monster:
room.current_monster.hp += 2
def commands():
print "The commands are: 'n','e','s','w' for moving; 'exit' to exit; 'stats' to see your stats; 'help' to see these commands again."
def save():
print "Saving..."
save_file = str(currentchar.name) + ".txt"
game_d = {}
# Save the rooms' monsters
rooms = ["bedroom","garden","pathway","open_area","study","cliff","rocky_path","hallway","cave"]
info_names = ["currentchar"]
info_values = [currentchar]
for room in rooms:
info_names.append(room+"_monster")
info_values.append(eval(room+".current_monster"))
info_names.append("files")
info_values.append(file_list)
for index in range(len(info_names)):
game_d[info_names[index]] = info_values[index]
f = open(save_file,"w")
cPickle.dump(game_d,f)
f.close()
if save_file not in file_list:
file_list.append(save_file)
f = open("data.txt","w")
cPickle.dump(file_list,f)
f.close()
print "Game Saved."
def delete_file():
print "Files:"
for f in file_list:
print f,
print
f_del = raw_input("What file would you like to delete? ")
if f_del in file_list:
print "%s has been deleted from the game." % (f_del)
print "Don't forget to remove the actual file."
file_list.remove(f_del)
f = open("data.txt","w")
cPickle.dump(file_list,f)
f.close()
else:
print "There is no such file."
def load(data_file="not given"):
if data_file == "not given" and file_list:
for num,f in enumerate(file_list):
print "%s %s" % ("(" + str(num+1) + ")",f)
choose_file = "none"
while choose_file not in range(len(file_list)+1):
choose_file = raw_input("Choose a file number: ")
choose_file = int(choose_file)
data_d = cPickle.load(file_list[choose_file-1])
else:
f = open(data_file,"r")
data_d = cPickle.load(f)
new_char = data_d["currentchar"]
bedroom.current_monster = data_d["bedroom_monster"]
garden.current_monster = data_d["garden_monster"]
pathway.current_monster = data_d["pathway_monster"]
open_area.current_monster = data_d["open_area_monster"]
study.current_monster = data_d["study_monster"]
cliff.current_monster = data_d["cliff_monster"]
rocky_path.current_monster = data_d["rocky_path_monster"]
hallway.current_monster = data_d["hallway_monster"]
cave.current_monster = data_d["cave_monster"]
f.close()
return new_char
# Load new character
if file_list:
for num,f in enumerate(file_list):
print "%-4s %s" % ("(" + str(num+1) + ")",f)
choose_file = "none"
print
while choose_file != "new" and choose_file not in range(len(file_list)+1):
try:
choose_file = raw_input("Choose a file number, or 'new' for new character:\n")
if choose_file != "new":
choose_file = int(choose_file)
except:
pass
if choose_file == "new":
print "New character!"
name = raw_input("Name: ")
currentchar = Character(name)
else:
currentchar = load(file_list[int(choose_file)-1])
else:
print "New character!"
name = raw_input("Name: ")
currentchar = Character(name)
battle_results = "alive"
print "\nWelcome to Glen's RPG. Enjoy..."
commands()
print currentchar.location.description
while battle_results != "dead":
command = raw_input("Enter a Command: ").lower()
if command == "n":
battle_results = enter_room("n")
elif command == "e":
battle_results = enter_room("e")
elif command == "s":
battle_results = enter_room("s")
elif command == "w":
battle_results = enter_room("w")
elif command == "stats":
stats()
elif command == "save":
save()
elif command == "load":
currentchar = load()
elif command == "delete":
delete_file()
elif command == "help":
commands()
# Levels up the current character
elif command == "xp":
currentchar.exp += 25
currentchar.level_up()
print currentchar.exp
elif command == "exit":
break
else:
print "That's not a command."
print "Thanks for playing."
So, you can walk around nine rooms and there is a probability of encountering monsters as you walk along in certain rooms. The cave always contains a troll, but it's too tough for the beginner. As the user fights rats along the path of the garden, pathway, and open area for awhile, he/she will level up every 25 exp points (rats give 5). As the user gains levels, he/she will get better stats.
I understand that it's not so interesting at this point since we haven't implemented keys to open doors, weapons/items dropped by monsters, and a more complex fighting system (something other than attack and run). This is just a fun project we're doing independently, so it's not destined to be some huge project, just something to do to better our python skills.
Feedback would be great, both code-wise and conceptually.
Thanks.
note: the save/load functions are currently being debugged, so please just test the game once you load a file.