9
\$\begingroup\$

I am new to Python and am working on a small RPG game project to practice the language and OOP. I honestly don't know what I am doing though, so before I get into developing the battle system I figured I would first show my code so far in, case I'm making a big mess!

Implemented so far:

  • Save/Load game

  • Player can choose a team to play (and is able to change the team afterwards, whenever he wants)

  • Player can see his team's stats (character names, level, moves)

main.py

import jsonpickle
import os
import sys
from world import World
from player_team import Player_team
# Initialization 
world = World()
player_team = Player_team()
SAVEGAME_FILENAME = 'rpg_lqsa.json'
game_state = dict() 
def save_game():
 global game_state
 with open(SAVEGAME_FILENAME, 'w') as savegame:
 savegame.write(jsonpickle.encode(game_state))
def load_game():
 with open(SAVEGAME_FILENAME, 'r') as savegame:
 state = jsonpickle.decode(savegame.read())
 return state
def menu():
 # Display menu
 print ("***MENU***\n")
 print ("1. Fight")
 print ("2. Team")
 print ("3. Exit")
 choice = input("\Command: ")
 validate_choice(choice, 1, 3)
 # Battle
 if choice == '1':
 print()
 # Team submenu 
 elif choice == '2':
 team_menu()
 # Save and exit game
 elif choice == '3':
 save_game()
 print("\nGame saved automatically. Bye!")
 sys.exit(0)
 else:
 print ("\nUnexpected error occurred.")
 sys.exit(0)
def team_menu():
 # Display team submenu
 print ("\n***TEAM SUBMENU***\n")
 print ("1. Show team stats")
 print ("2. Switch team")
 choice = input("\Comman: ")
 validate_choice(choice, 1, 2)
 # Show team stats
 if choice == '1':
 player_team.display_team()
 input()
 # Change player's team
 elif choice == '2':
 print()
 choose_team()
 else:
 print ("\nUnexpected error occurred.")
 sys.exit(0)
def validate_choice(choice, first_num, last_num):
 error_message = '\nYou should provide a number between {} and {}.'.format(first_num, last_num)
 while True: 
 if choice.isdigit():
 if int(choice) not in range(first_num, last_num+1):
 print (error_message)
 choice = input()
 else:
 break 
 else:
 print (error_message)
 choice = input() 
def choose_team():
 # Display available teams
 print("Pick a team:\n")
 for index, team in enumerate(world.teams):
 print ('{}. {} y {}'.format(index+1, team[0].name, team[1].name))
 choice = input("\Command: ")
 validate_choice(choice, 1, len(world.teams))
 # Add team
 chosen_team = world.teams[int(choice)-1]
 player_team.set_team(chosen_team[0], chosen_team[1])
 # Save player team
 global game_state
 game_state['player_team'] = player_team
 # Team added
 print("\nDone! Your new team is ready to kick ass. Good luck!")
 input()
def new_game():
 # Introduction
 introMessage = (
 '\nVirtual Reality arrived to Montepinar '
 'and our dear neighbors decided to create '
 'a fighting tournament during the next month.\n'
 'The winner couple will earn 3 quotes as a price. Let's start!')
 print(introMessage)
 input()
 # First player team
 choose_team()
 # Show menu loop
 run_game()
def start_menu():
 global game_state
 global player_team
 while True:
 print("***RPG La Que Se Avecina***\n")
 print("1. New game")
 print("2. Load game")
 print("3. Exit")
 choice = input("\Command: ")
 validate_choice(choice, 1, 3)
 # New game
 if choice == '1':
 if not os.path.isfile(SAVEGAME_FILENAME):
 game_state["player_team"] = player_team
 new_game()
 return False
 else:
 answer = input("\nThere's a save game. Do you want to overwrite it and start again? ('yes' to confirm)\n\n")
 if answer.lower() == 'yes':
 new_game()
 return False
 print()
 # Load game
 elif choice == '2':
 if not os.path.isfile(SAVEGAME_FILENAME):
 print("\nNo saved games.")
 input()
 else:
 game_state = load_game()
 print()
 run_game()
 return False
 # Exit game
 elif choice == '3':
 print("\nBye!")
 sys.exit(0)
 return False
 else:
 print ("\nUnexpected error occurred.")
 sys.exit(0) 
 return False 
# Game loop
def run_game():
 global game_state
 global player_team
 # Load player's team
 player_team = game_state["player_team"]
 while True:
 # Menu loop
 menu()
# Start with New/Load game
if __name__ == '__main__':
 start_menu()

character.py

import random
class Character:
 def __init__(self, name, moves = None, combos = None):
 if moves == None:
 moves = []
 if combos == None:
 combos = []
 self.name = name
 self.moves = moves
 self.combos = combos
 self.partner = None
 self.health = 1000
 self.max_health = 1000
 self.exp_points = 0
 self.level = 1
 def is_alive(self):
 return self.health > 0
 def get_move_names(self):
 move_names = ""
 for move in self.moves:
 move_names += "{} ({}), ".format(move.name, move.level)
 move_names = move_names.rstrip(', ')
 return move_names
 # User interface 
 def do_damage(self, move, rival):
 move.damage = random(0, 100 * move.level)
 if move.damage == 0: 
 print ("{} avoided attack from {}!".format(rival.name, self.name))
 else: 
 print ("{} did {} damage to {}.".format(self.name, move.damage, rival.name))
 return rival.health <= 0

lqsa_character.py

from character import Character
from move import Move
class Lqsa_character:
 Antonio = Character("Antonio", moves = [Move("Centollazo"), Move("Calambrazo")])
 Enrique = Character("Enrique", moves = [Move("Dialog"), Move("Ataque2")])
 Fermin = Character("Fermín", moves = [Move("Kick"), Move("Hit")])
 Vicente = Character("Vicente", moves = [Move("Controller hit"), Move("Salto Suicida")])
 Amador = Character("Amador", moves = [Move("Pinchito"), Move("Cabezazo")])
 Teodoro = Character("Teodoro", moves = [Move("Pinchito"), Move("Cabezazo")])
 Javi = Character("Javi", moves = [Move("Golpe de Ansiolíticos"), Move("Ataque2")])
 Lola = Character("Lola", moves = [Move("Attack1"), Move("Attack2")])
 def __init__(self):
 self.assignPartners()
 def assignPartners(self):
 self.Antonio.partner = self.Enrique
 self.Enrique.partner = self.Antonio
 self.Fermin.partner = self.Vicente
 self.Vicente.partner = self.Fermin
 self.Amador.partner = self.Teodoro
 self.Teodoro.partner = self.Amador
 self.Javi.partner = self.Lola
 self.Lola.partner = self.Javi 

move.py

class Move:
 def __init__(self, name):
 self.name = name
 self.level = 1
 self.damage = 0

item.py

class Item:
 def __init__(self, name, quantity, price):
 self.name = name
 self.quantity = quantity
 self.price = price 
 self.utility = ""
 def display_info(self, item):
 return "{} - Units: {}, Utility: {}".format(item.name, item.quantity, item.utility)

player_team.py

from item import Item
class Player_team:
 def __init__(self):
 self.members = []
 self.inventory = []
 def set_team(self, character1, character2):
 self.members = [character1, character2] 
 def is_alive(self):
 for member in self.members:
 if member.health > 0:
 return True
 return False
 def add_item(self, item):
 self.inventory.append(item)
 # User Interface
 def display_team(self):
 if len(self.members) <= 0 or self.members[0] == None or self.members[1] == None:
 raise Exception("Unexpected error occurred.")
 print ("\n***Team***\n")
 for member in self.members:
 print("-> {} ({})\n Moves: [{}]\n".format(member.name, member.level, member.get_move_names()))
 def display_inventory(self):
 for item in self.inventory:
 print(Item.display_info(item), '\n')
A. Romeu
1,1236 silver badges13 bronze badges
asked Mar 3, 2018 at 21:33
\$\endgroup\$
3
  • 1
    \$\begingroup\$ Welcome to Code Review! Neat project, I wish I knew Spanish, but this is a fun way to learn for sure! \$\endgroup\$ Commented Mar 3, 2018 at 21:52
  • 1
    \$\begingroup\$ Moved most of spanish words to english, should be more understandable now Interesting project btw, about an important part of spanish folklore :) \$\endgroup\$ Commented Mar 4, 2018 at 2:03
  • \$\begingroup\$ Is translating code into English an exception to the rule of never editing the code in a question? Even though it certainly helps getting more answers, I can imagine there might be valid points about the original that get lost in translation. Also, coding in English might be one of the most important suggestions in a review. \$\endgroup\$ Commented Mar 5, 2018 at 8:53

1 Answer 1

6
\$\begingroup\$

Welcome to Code Review. This is a really awesome piece of program you have :) And thanks to A. Romeu for translations.

Because I noticed the awkward syntax highlighting towards the end of main.py here around introduction section; I would suggest multi-line strings; which is simply wrapping it all in triple-quotes.

enter image description here

 # Introduction
 introMessage = """
Virtual Reality arrived to Montepinar
and our dear neighbors decided to create
a fighting tournament during the next month.
The winner couple will earn 3 quotes as a price. Let's start!
"""
 print(introMessage)
 input()

Naming conventions should be consistent throughout the project, and with Python's PEP-8 RFCs, you have it:

  • variables and methods are named in snake_lower_case
  • class names are defined as CamelUpperFirstCase

So it will be PlayerTeam, assign_partners etc.

Following this makes your code really easy to read for fellow pythoners. If you violate these conventions, the worst you'll get is some dirty looks


Avoid using global variables references inside functions. Pass them as arguments instead (referring to global game_state; global player_team etc.).


Ideal import order (according to PEP8) is

  • standard library imports
  • related third party imports
  • local application/library specific imports

os/sys etc. are standard library, and should be imported first, followed by jsonpickle and lastly placing world and team.


Try to use docstrings for functions/class definitions. It helps in maintaining your application over a long course of time.


Your item class can be replaced by a simple namedtuple, and overriding the __str__ method is really easy.

Item = namedtuple('Item', "name, quantity, price, utility")
Item.__str__ = lambda item: f"{item.name} - Units: {item.quantity}, Utility: {item.utility}"

However, it might not be the case as there could be some methods that might need to be introduced to particular items. Still, __str__ method is to be noted.


Checking for None should be done with is and is not instead of == checks.

Something can not be equal to nothing.


You should also implement a stricter check against a character's moves. They should all be an instance of Move class.

assert all(isinstance(move, Move) for move in moves)

def get_move_names(self):
 move_names = ""
 for move in self.moves:
 move_names += "{} ({}), ".format(move.name, move.level)
 move_names = move_names.rstrip(', ')
 return move_names

can be rewritten as:

def get_move_names(self):
 return ", ".join([str(move) for move in self.moves])

and in your Move class:

def __str__(self):
 return "{} ({})".format(self.name, self.level)

Your current validate_choice takes the first input and a range of integer valid values. This can be improved

def validate_choice(prompt, valid_values):
 error_message = "Valid values are: " + ', '.join((str(x) for x in valid_values))
 while True:
 choice = input(prompt)
 if choice in valid_values:
 return choice
 else:
 print(error_message)

and usage is really simple, instead of:

choice = input("\Command: ")
validate_choice(choice, 1, 3)

you now have:

choice = validate_choice("Command: ", list(range(1, 4)))

The benefit of this style is, you can also support:

choice = validate_choice("Command: ", 'A', 'B', 'E', 'X')

I do not understand why you have a Lqsa_character class.

answered Mar 4, 2018 at 6:24
\$\endgroup\$
2
  • \$\begingroup\$ I mainly program in English but since this RPG game is based on a Spanish TV show it wouldn't really make sense. I tried to write everything logic related in English though. Sorry for the inconvenience and thanks A. Romeu for the translations! @hjpotter92, I don't know how to thank you enough for such a great answer! The class "lqsa_character" is meant just for generating the tv show characters. Where should I create them (and assign their partners) instead? In world.py maybe? or character.py? I had doubts about it so I just made a new class, but now I'm not sure what's best. :S \$\endgroup\$ Commented Mar 4, 2018 at 10:48
  • \$\begingroup\$ @Guest43242 The reason you can't accept an answer is because you have created duplicate accounts, the question was asked with one account but now you are using another. I have asked the community team to merge these accounts so that you should be able to accept the answer. \$\endgroup\$ Commented Mar 4, 2018 at 14:12

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.