I am in the process of creating a text-based RPG for the purpose of learning more about OOP in Python. Right now things are going well, but I'm facing a problem that I don't exactly can find a good answer: Skill implementation. I also think that I'm going to face similar problem with the Items.
At this moment I have 3 classes(professions): Fighter, Rogue, and Mage. Each of these classes should have their own skills, but, skills have similar things(name, description, MP usage), so I created a BasicSkill class, and 3 subclasses: PassiveSkill, ActiveSkill and ChanelledSkill.
Explaining: PassiveSkill are skills that doesn't require MP to use, are always active, and in most of cases, give the player buffs, like dual-wielding, more resistance, etc. ActiveSkill are skills that require MP to be used, and, in majority, cause damage or a instant buff. Finally, ChanelledSkill are skills that require more than one turn to be used, cost more MP than the others, and can be interrupted
But, when comes to the skill creation, I can only think in something like this:
def hack_and_slash(self):
"""This is a Fighter skill, it's melee and has no cool down."""
from skills import ActiveSkill
hack_and_slash = ActiveSkill("Hack n' Slash", "A powerful double attack that has a chance of causing bleeding.",
8, 3)
self.mp -= hack_and_slash.
# more about the skill goes here, like what it does in terms of damaging, buffing, etc.
Which I think is not exactly what I'm aiming for, since I have to instantiate it every time I use, or something in the same lines.
I want to create the skills in a way that would require less instantiation, and also make more possible to cast. Also, all characters(player, npc, and enemies) can cast skills.
There's a bunch of files in this project, so I will post the ones I think are more relevant here, and, if you want, you can check out the project here.
This is my Player class:
from character import Character
# TODO implementation of player basic attacks and abilities
# TODO implementation of a D&D style skill system(diplomacy, stealth, streetwise, etc)
class Player(Character):
LEVEL_UP = 100 # initial XP to lvl up
COMMANDS = {'attack': 'a', 'character': 'c', 'inventory': 'i'} # commands available for the player
# in future this skills will be used inside and outside combat situations, for like take out an enemy in stealth,
# or convince a guard to let you in a closed door
SKILLS = {'persuasion': 0, 'intimidation': 0, 'stealth': 0, 'perception': 0}
DAILY_SELF_HEALS = 4 # each ingame day player can use 4 self heals, it resets after getting the rested status
ATTRIBUTES = {'strenght': 0, 'constitution': 0, 'dexterity': 0, 'intelligence': 0, 'wisdom': 0, 'charisma': 0}
def __init__(self, armor_class):
super().__init__(input("Tell us your name, hero:\n>"), 20, 10, {'gold': 10, 'torch': 1}, armor_class, 1)
self.exp = 0
self.max_hp = self.hp # max HP of the player
self.max_mp = self.mp # max MP of the player
self._strength = 0 # should I change it to dictionary with the attributes instead?
self._constitution = 0
self._dexterity = 0
self._intelligence = 0
self._wisdom = 0
self._charisma = 0
def level_up(self):
if self.exp >= self.LEVEL_UP:
self.lvl += 1
self.LEVEL_UP *= 1.25
self.LEVEL_UP = int(self.LEVEL_UP)
self.exp = 0
stat_choice = input(
"You have 2 points to spend in your attributes!\nType which attributes you want to raise up: ")
stat_choice = stat_choice.split(',')
self.ATTRIBUTES[stat_choice[0]] += 1
self.ATTRIBUTES[stat_choice[1]] += 1
self.max_hp = int(self.max_hp * 1.1)
self.max_mp = int(self.max_mp * 1.1)
return True
else:
return False
def gain_exp(self, exp):
self.exp += exp
print("You gained %d XP" % exp)
if self.level_up():
print("Congratulations, you gained a level!\nYour current level is %d\n" % self.lvl)
else:
print("Your current XP is %d/%d\n" % (self.exp, self.LEVEL_UP))
def get_loot(self, enemy):
# buggy right now, problem with remove inventory from dead enemy
for item in enemy.inventory:
while True:
get_item = input("You found %s! do you want to get it?(Y=yes/N=no/A=all items)" % item)
if get_item.lower() == "a":
self.inventory.update(enemy.inventory)
enemy.inventory = {}
return
elif get_item.lower() == "y":
self.inventory[item] = enemy.inventory[item]
enemy.inventory.pop(item, None)
break
elif get_item.lower() == "n":
break
else:
print("Unfortunately, you don't have this choice hero, take a look again...")
def combat(self, enemy):
"""
the combat between two entities(player and enemy)
:param enemy: the enemy of current character
:return:
"""
if super().combat(enemy):
self.gain_exp(10)
self.get_loot(enemy)
else:
print("\t\t[GAME OVER...]\t\t")
def self_heal(self):
if self.DAILY_SELF_HEALS > 0:
self.DAILY_SELF_HEALS -= 1
self.hp += int(self.max_hp / 4)
def rest(self):
rest_time = input("\tHow much time would you like to rest? ")
if rest_time >= 6:
self.STATUS['rested'] = True
def __str__(self):
str_info = "\tName: [%s]\tLEVEL: %d\n\tHP: %2d\t\tMP: %2d" % (self.name, self.lvl, self.hp, self.mp)
str_stats = "\t\tSTR: %2d\n\t\tCON: %2d\n\t\tDEX: %2d\n\t\tINT: %2d\n\t\tWIS: %2d\n\t\tCHA: %2d\n" % (
self._strength, self._constitution, self._dexterity, self._intelligence, self._wisdom,
self._charisma)
return "|%s|\n%s" % (str_info, str_stats)
This, my Character class:
# all character, being Player, NPC, or Enemy content should be implemented here
from utils import Dice
class Character:
"""
Main character class, all living entities derivate from it
"""
# a dictionary of possible status of player, the idea is that this status change gameplay/combat in the future.
# By default all characters are rested.
STATUS = {'rested': True, 'hunger': False, 'poisoned': False, 'bleeding': False, 'blind': False, 'frozen': False,
'paralyzed': False, 'dead': False}
# Right now there are only four equipment slot, which one should receive an armor object, not yet implemented
EQUIPMENT_SLOTS = {'head': None, 'chest': None, 'legs': None, 'boots': None}
def __init__(self, name, hp, mp, inventory, armor_class, lvl):
"""
Constructor for a character
:param name: name for the character
:type name: str
:param hp: hit points(health) for the character
:type hp: int
:param mp: magic points(mana) for the character
:type mp: int
:param inventory: inventory of character(gold, items, equips)
:type inventory: dict
:param armor_class: main defense
:type armor_class: int
:param lvl: level of character
:type lvl: int
:return: Character object
"""
self.name = name
self.hp = hp
self.mp = mp
self.inventory = inventory
self.armor_class = armor_class
self.lvl = lvl
self.atk_bonus = 1
self.atk_dmg = 4
self.movement_speed = 6
def take_dmg(self, dmg):
self.hp -= dmg
if self.hp <= 0:
print("\n[%s has died!]" % self.name)
self.STATUS['dead'] = True
else:
print("\n[%s has %d health left" % (self.name, self.hp))
def attack(self, enemy):
"""
All characters can attack, so this is the main attack(physical) method
:param enemy: Enemy that is in combat with character
:return:
"""
d20 = Dice(20)
dmg = self.atk_dmg
if d20.roll() + self.atk_bonus >= enemy.armor_class:
if d20 == 20:
print("CRITICAL HIT!")
dmg *= 2
else:
dice_dmg = Dice(self.atk_dmg)
dmg = dice_dmg.roll()
print("\n[%s hit %s for %d damage]" % (self.name, enemy.name, dmg))
enemy.take_dmg(dmg)
if enemy.STATUS['dead']:
return
else:
print("\n[%s missed]" % self.name)
def combat(self, enemy):
while True:
self.attack(enemy)
if enemy.STATUS['dead']:
return True
enemy.attack(self)
if self.STATUS['dead']:
break
return False
def equip_armor(self, armor_piece):
"""
Equip a piece of armor in the correspondent slot
:param armor_piece: armor piece to equip
:type armor_piece: object
:return:
"""
replace_equip = input(
"Would you like to replace %s with %s?(Y/N)" % (self.EQUIPMENT_SLOTS['head'], armor_piece.name))
if replace_equip.lower() == "y":
self.EQUIPMENT_SLOTS['head'] = armor_piece
return
else:
return
def list_inventory(self):
"""
list a character's inventory
:return:
"""
print("\t\t[%s] INVENTORY:\t\t\n" % self.name)
for item in self.inventory:
print("%s:\t%d" % (item, self.inventory[item]))
The Skill class:
# All basic skill implementation should be here
class BasicSkill:
SKILL_TYPE = {'passive': False, 'active': False, 'channeled': False}
def __init__(self, name, description, skill_type, skill_dmg=0, skill_range=0):
"""
Basic Skill class
:param name: skill name
:type name: string
:param description: description
:type description: string
:param skill_type: passive, active or channeled
:type skill_type: string
:param skill_dmg: default=0, meaning it's passive
:type skill_dmg: int
:param skill_range: default=0, meaning it's personal
:type skill_range: int
:return:
"""
self._name = name
self._description = description
self.SKILL_TYPE[skill_type] = True
self.skill_dmg = skill_dmg
self.skill_range = skill_range
class PassiveSkill(BasicSkill):
def __init__(self, name, description, skill_type='passive'):
super().__init__(name, description, skill_type)
class ActiveSkill(BasicSkill):
def __init__(self, name, description, skill_dmg, mp_use, cooldown=1, skill_type='active', skill_range=1):
super().__init__(name, description, skill_type, skill_dmg, skill_range)
self.mp_use = mp_use
self.cooldown = cooldown
class ChanneledSkill(BasicSkill):
def __init__(self, name, description, skill_dmg, channeling_time, cooldown=2, skill_type='chanelled',
skill_range=1):
super().__init__(name, description, skill_type, skill_dmg, skill_range)
self.channeling_time = channeling_time
self.cooldown = cooldown
and, as example, the Fighter class, all the classes/professions are implemented in similar way:
from player import Player
# TODO implement fighter stats and attacks
class Fighter(Player):
"""
Fighter class, a strong warrior that uses meele attacks to face his opponents
"""
def __init__(self):
super().__init__(armor_class=10)
print("\nYou have chosen the path of strength!")
self._strength += 2
self._constitution += 2
self.atk_bonus += 1
self.atk_dmg += 3
def hack_and_slash(self):
from skills import ActiveSkill
hack_and_slash = ActiveSkill("Hack n' Slash", "A powerful double attack that has a chance of causing bleeding.",
8, 3)
self.mp -= hack_and_slash.mp_use
def __str__(self):
super().__str__()
Any ideas are welcome, as well as code improvement tips. I tried to figure out a good way to make this skill implementation system, but I couldn't exactly find one. The only alternative I tough was a dictionary of skills, but I don't know how would it work. Anyway, thanks for the help.
Related
i feel like this is a very simple concept but i just cant seem to wrap my head around classes and returning values from them... this what i've come up with to try and practice, very much WIP but i'm stuck here.
thanks in advance
import random
global d6
print("welcome to fight night")
#dice roll
class die():
def __init__(self, sides):
self.sides = sides
def roll(self):
chance = random.randint(1,self.sides)
if chance == 3:
return True
if chance != 3:
return False
d6 = die(6)
class Player():
def __init__(self, name, weight, spec):
self.name = name
self.weight = weight
self.spec = spec
fighter1 = Player("BRUCE LEE", 150, "SUPER SPEED")
fighter2 = Player(GEORGE FORMAN", 225, "REACH")
## how do i return true or false here to check hit?
print(fighter1(name) + "attacks"+ fighter2(name))
d6.roll()
if d6 == True:
print(fighter(name) + "hit" + fighter2(name))
if d6 == False:
print("attack missed")
A few things about class conventions:
The name of a class should typically start with an uppercase letter.
The () after the name of the class is not necessary unless you're
using inheritance.
Code:
class Die:
def __init__(self, number_of_sides):
self.number_of_sides = number_of_sides
def roll(self):
"""
Returns True if the roll succeeds, returns False otherwise.
There is a (1 / self.number_of_sides) chance the roll will succeed.
"""
from random import choice
return choice([True] + [False] * (self.number_of_sides-1))
die = Die(6)
if die.roll():
print("Nice!")
else:
print("Sorry, try again.")
EDIT I think you need a more fundamental example of how the return keyword works:
def give_me_a_number():
return 5 + 4
x = give_me_a_number()
print(x)
Output:
9
d6.roll() returns a value (which is True or False) -- d6 does not become that value, it returns it to you. When you roll a 3 on a real d6, the die does not itself entirely transform into the number 3 in a puff of smoke and mathemagicks, it just shows it to you on one of its faces. :)
So you could do:
if d6.roll():
print(fighter1.name + " hit " fighter2.name)
else:
print("attack missed")
Or if you wanted to be more verbose:
die_result = d6.roll()
if die_result is True:
print(fighter1.name + " hit " fighter2.name)
if die_result is False:
print("attack missed")
I would restructure your die class such that it only returns a random value as the result of roll. You generally want objects (instances of classes) to be independent - the user of the die object should determine what is done with the random value the object generates, not the die itself.
For example, let's say we have many players, of varying strengths. A strong player attacking a weak player should have odds different from a weak player attacking a strong player. This data (strong vs weak) should be inside the player object, not the die object. So I'd make two more methods for the Player, and an attribute like 'strength' which gets an int value...
import random
class Die:
def __init__(self, sides):
self.sides = sides
def roll(self):
chance = random.randint(1,self.sides)
return chance
class Player:
def __init__(self, name, weight, spec):
self.name = name
self.weight = weight
self.spec = spec
def get_stats(self):
return self.spec
def attack(self, target):
odds = self.spec / (self.spec + target.get_stats())
#print(odds)
d6 = Die(6);
temp = d6.roll()
#print(temp)
if (d6.roll() / 6 < odds):
return ("hit")
else:
return ("miss")
if __name__ == "__main__":
print("welcome to fight night")
fighter1 = Player("BRUCE LEE", 150, 100)
fighter2 = Player("GEORGE FORMAN", 225, 100)
for x in range(20):
result = fighter1.attack(fighter2)
print(result)
So, now the Player objects control the odds themselves, not the die objects, making everything more flexible. (Making the Die a local object inside the function is a bit high on overhead, incidentally, but global objects/variables have many issues and are probably best avoided)
I have starting working on this Text-Based Pokemon game and I am stuck on trying to figure an evolution system that can change one class object into another class object, if that makes sense.
I've attempted to do this just by simply making the class object equal to the one I want to change:
# Trainer
new_trainer = Trainer('Trainer', 'Male')
# Test pokemon (the parameter is just a level value)
bulbasaur = Bulbasaur(10)
ivysaur = Ivysaur(20)
# Adding pokemon to party
new_trainer.add_pokemon(bulbasaur)
new_trainer.add_pokemon(ivysaur)
# Display pokemon lists
print(new_trainer.pokemon_list)
new_trainer.display_party()
# Attempting to 'evolve' bulbasuar
bulbasaur = Ivysaur(bulbasaur.lvl)
# Displaying results again to see if it worked
print(new_trainer.pokemon_list)
new_trainer.display_party()
But by looking at the output, it shows that nothing has happened:
Bulbasaur was added to your party!
Ivysaur was added to your party!
[<__main__.Bulbasaur object at 0x0000023587EDA080>, <__main__.Ivysaur object at 0x0000023587EDA0F0>]
1. Bulbasaur
2. Ivysaur
[<__main__.Bulbasaur object at 0x0000023587EDA080>, <__main__.Ivysaur object at 0x0000023587EDA0F0>]
1. Bulbasaur
2. Ivysaur
EDIT:
It turns that that the variable value DOES indeed change, but I simply need to update the Trainer's pokemon list. However, I am still unsure on how to do this.
The display_party function in the Trainer class looks like this:
def display_party(self): # Displays pokemon party list
if not self.pokemon_list:
return print("There are no pokemon in your party.")
else:
for pokemon in range(len(self.pokemon_list)):
print("{0}. {1}".format(pokemon + 1, self.pokemon_list[pokemon].name))
I was just thinking of adding an update_party function, which goes into the list, deletes the old value and inserts the new value, but how will it know what value to delete?
Perhaps there is a better way to do this? And sorry if my question doesn't include that much information, I will add more of my code if it doesn't make much sense already. This is my first time asking a question here :D
EDIT 2:
For those who don't understand how the code works, this is basically how it works so far:
# Formula used to calculate pokemon health
def health_stat_calculation(base_stat, lvl):
return int(((base_stat * 3 * lvl) / 100) + lvl + 10)
# Formula used to calculate pokemon stats
def stat_calculation(base_stat, lvl):
return int(((base_stat * 3 * lvl) / 100) + 5)
# Trainer class
class Trainer:
def __init__(self, name, gender):
self.name = name
self.gender = gender
self.pokemon_list = []
self.item_list = []
def add_badge(self, badge): # Gives player a badge
self.player_badges.append(badge)
def add_pokemon(self, pokemon): # Adds a pokemon to party
for poke in self.pokemon_list:
if pokemon is poke:
return print("You already have this pokemon in your party!")
if len(self.pokemon_list) > 5:
return print("You have too much pokemon in your party!")
else:
self.pokemon_list.append(pokemon)
return print("{} was added to your party!".format(pokemon.name))
def display_party(self): # Displays pokemon party list
if not self.pokemon_list:
return print("There are no pokemon in your party.")
else:
for pokemon in range(len(self.pokemon_list)):
print("{0}. {1}".format(pokemon + 1, self.pokemon_list[pokemon].name))
def add_item(self, item): # Adds an item to player's inventory
if len(self.item_list) > 50:
print("You have too much items in your inventory!")
else:
self.item_list.append(item)
print("{} was added to your inventory!")
# Generic pokemon class
class Pokemon:
def __init__(self, name, lvl, exp, id_number, health, attack, defence, sp_attack, sp_defense, speed):
self.name = name
self.lvl = lvl
self.exp = exp
self.id_number = id_number
self.health = health
self.attack = attack
self.defence = defence
self.sp_attack = sp_attack
self.sp_defense = sp_defense
self.speed = speed
def get_pokemon_name(self):
return self.name
# User friendly stat display
def display_stats(self):
print("\nStats for {}\n---------------".format(self.name))
print("Level: {}".format(self.lvl))
print("Index No.{}".format(self.id_number))
print("Health: {}".format(self.health))
print("Attack: {}".format(self.attack))
print("Defense: {}".format(self.defense))
print("Sp. Attack: {}".format(self.sp_attack))
print("Sp. Defense: {}".format(self.sp_defense))
print("Speed: {}\n".format(self.speed))
# Level up pokemon and show change in stats
def level_up(self):
self.display_stats()
self.lvl += 1
print("\n{} has leveled up!\n".format(self.name))
self.calculate_stats()
self.display_stats()
# Calculate generic pokemon stats
def calculate_stats(self):
self.health = health_stat_calculation(self.base_health, self.lvl)
self.attack = stat_calculation(self.base_attack, self.lvl)
self.defense = stat_calculation(self.base_defense, self.lvl)
self.sp_attack = stat_calculation(self.base_sp_attack, self.lvl)
self.sp_defense = stat_calculation(self.base_sp_defense, self.lvl)
self.speed = stat_calculation(self.base_speed, self.lvl)
# Bulbasaur class
class Bulbasaur(Pokemon):
def __init__(self, lvl, name="Bulbasaur"):
self.name = name
self.lvl = 16
self.exp = 0
self.id_number = 1
# Bulbasaur Base stats
self.base_health = 45
self.base_attack = 49
self.base_defense = 49
self.base_sp_attack = 65
self.base_sp_defense = 65
self.base_speed = 45
self.calculate_stats()
# Ivysaur class
class Ivysaur(Pokemon):
def __init__(self, lvl, name="Ivysaur"):
self.name = name
self.lvl = 16
self.exp = 0
self.id_number = 2
# Bulbasaur Base stats
self.base_health = 60
self.base_attack = 62
self.base_defense = 63
self.base_sp_attack = 80
self.base_sp_defense = 80
self.base_speed = 60
self.calculate_stats()
Casting from one class to another makes no sense in your case.
Just go with
class Pokemon():
def __init__(self, name, level=0):
self.level = level
self.name = name
def evolve(self):
self.level += 1
def __str__(self):
return 'Pokemon: {}, Level: {}'.format(self.name, self.level)
p1 = Pokemon('Bulbasaur')
p2 = Pokemon('Ivysaur')
print(p1)
print(p2)
p1.evolve()
p2.evolve()
print(p1)
print(p2)
You could turn the kind, Bulbasaur, Ivysaur, etc., into a component of the Pokemon class. The Bulbasaur has its next evolution stage as an attribute and in the evolve method of the Pokemon you can just set the kind to the next evolution stage.
class Pokemon:
def __init__(self, kind, level):
self.level = level
self.kind = kind # The kind is just a component now.
def evolve(self):
# Create a new instance of the next evolution stage
# and use it to replace the previous kind.
self.kind = self.kind.evolution_stage()
class Bulbasaur:
def __init__(self):
self.base_health = 45
# A reference to the next evolution stage class.
self.evolution_stage = Ivysaur
class Ivysaur:
def __init__(self):
self.base_health = 60
# self.evolution_stage = Venusaur
# Pass a Bulbasaur instance as the `kind` argument.
pokemons = [Pokemon(Bulbasaur(), 3)]
print(pokemons[0].kind, pokemons[0].kind.base_health)
pokemons[0].evolve() # Changes the .kind attribute.
print(pokemons[0].kind, pokemons[0].kind.base_health)
I don't know if this is the cleanest way, but somehow it worked when I added this function into the Pokemon class and used the dict dunder to update the contents of the old object to the new one. The temp_pos was used as a temporary placeholder for the current object's name so it didn't get changed when updating it to the new_pokemon object.
# Evolve function
def evolve(self, evln, trainer):
temp_name = self.name
new_pokemon = evln(self.lvl)
self.__dict__.update(new_pokemon.__dict__)
self.name = temp_name
I'm not even sure you can do this. I am trying to get the integer values from attack_moves: fighter: sword punch kick in the hope that I can use these values later in an attack on a monster.
from random import randint
class Human(object):
# Question is
# Is there a way of getting sword, punch and kick interger values?
attack_moves = {
'fighter': {'sword': randint(1,8),'punch': randint(1,4),
'kick': randint(1,5)},
'Wizard': {'fire ball': randint (1,7),'staff': randint(1,6)},
}
def __init__(self, name,
present_human_hit_points, max_human_hit_points):
self.name = name
self.present_human_hit_points = present_human_hit_points
self.max_human_hit_points = max_human_hit_points
def decrease_health(self, amount):
self.amount = amount
death = False
while not death:
if self.present_human_hit_points > 0:
self.present_human_hit_points -= amount
print self.present_human_hit_points
else:
death = True
class Fighter(Human):
def __init__(self, name,
present_human_hit_points, max_human_hit_points):
Human.__init__(self, name,
present_human_hit_points, max_human_hit_points)
Human.attack_moves = Human.attack_moves.get('fighter')
#def attack_type(self, attack_moves):
# self.attack_moves = Human.attack_moves
# print attack_moves
human = Fighter("bob", 50, 100)
print human.name, human.present_human_hit_points,
print human.max_human_hit_points
print human.attack_moves
I'm trying to build a simple RPG game to learn more about classes in Python, but I'm struggling with the fight method. The idea is that after both player and monster are created, the method fight would have the parameter enemy, that is the monster the player is fighting.
Here is what I did in the fight (it's inside Player's class) method:
def fight(self, enemy):
self.enemy = enemy
d20 = Dice(20)
if d20.roll() >= enemy.armor_class:
print("You hitted %s" % enemy.name)
enemy.hp -= self._strength
print("\n%s has %d hp left" % (enemy.name, enemy.hp))
else:
print("you missed")
Here is the call I'm doing right now (in the main file):
player = Fighter()
goblin = Enemy("Goblin", 10, 10, 1, 5)
player.fight(goblin)
but, whenever I run it, I get the following error:
TypeError: fight() takes 1 positional argument but 2 were given
Both classes(Player and Enemy) inherit from the Character's class, so what's the deal with this error? Do I need an import from enemy to solve this?
EDIT1 : Above the complete Player class:
class Player(Character):
def __init__(self, armor_class):
super().__init__(input("Tell us your name, hero:\n>"), 20, 10, {}, armor_class, 1)
self.exp = 0
self._strength = 0
self._constitution = 0
self._dexterity = 0
self._intelligence = 0
self._wisdom = 0
self._charisma = 0
def fight(self, enemy):
self.enemy = enemy
d20 = Dice(20)
if d20.roll() >= enemy.armor_class:
print("You hitted %s" % enemy.name)
enemy.hp -= self._strength
print("\n%s has %d hp left" % (enemy.name, enemy.hp))
else:
print("you missed")
def __str__(self):
str_info = "\tName: [%s]\tLEVEL: %d\n\tHP: %2d\t\tMP: %2d\n" % (self.name, self.lvl, self.hp, self.mp)
str_stats = "\t\tSTR: %2d\n\t\tCON: %2d\n\t\tDEX: %2d\n\t\tINT: %2d\n\t\tWIS: %2d\n\t\tCHA: %2d\n" % (
self._strength, self._constitution, self._dexterity, self._intelligence, self._wisdom,
self._charisma)
return str_info + str_stats
The Fighter class:
class Fighter(Player):
"""
Fighter class, a strong warrior that uses meele attacks to face his opponents
"""
def __init__(self):
super().__init__(armor_class=10)
self._strength += 2
def fight(self):
super().fight()
def __str__(self):
return super().__str__()
And my actual Enemy class:
class Enemy(Character):
def __init__(self, name, hp, mp, lvl, atk):
super().__init__(name, hp, mp, {}, 10, lvl)
self.atk = atk
My main.py is as follows
from fighter import Fighter
from enemy import Enemy
player = Fighter()
goblin = Enemy("Goblin", 10, 10, 1, 5)
player.fight(goblin)
In Fighter.fight you are using super().fight() which instantiates a new Player object and calls fight() on this new instance. Use self.fight() to call the method on the instance of the Fighter.
Never use super() to access methods of the super class. You are inheriting from that class so all methods from the super class are available in the scope of the subclass.
super()'s purpose is to call the constructor of the super class (__init__()).
Edit: Methods from the superclass can in fact be accessed by using super().method(). The problem here is the wrong number of parameters to the fight() method. (See comments below)
So i'm trying to make a very simple text-based RPG as my semester project for my programming class. I just recently learned (which is probably pretty apparent by my code) how to define classes and felt they would work much better than a function.
However, i'm having trouble with the 'character' class. Instead of having the player name predefined, I want the user to be able input their own name, which i've done in the 'Intro' function. Now my problem is taking the variable 'pName' and setting as the player's name, which I havent been able to do.
My questions are:
1. Can I do this? (use a function variable as a class attribute?)
2. Is there a better, more efficient way of doing this? And
3. Is there any additional information you guys could give me about classes or about how I can go about finishing this program?
Anything is well-appreciated and thanks in advance for the help!
import random, time
#I'm not at all hellbent on keeping this same format, it's just
#the way i've been taught and i'm most comfortable with.
def Intro():
print('Welcome puny warrior, to my Dungeon of DOOM, and stuff.')
pName = input('Tell me your name, puny warrior: ')
playResponse = input('Do you want to begin puny warrior who calls himself ' + pName + '? Y/N: ')
playResponse = playResponse.upper()
if playResponse[0:1] == 'Y':
pass
else:
print('You die now', pName)
class character(object):
def __init__(self, name, health, attack):
self.name = name
self.health = health
self.attack = attack
#this part obviously doesn't work, but I decided to leave it as a visual aid
player = character(pName, 25, 5)
#should I just make this class a child of the 'character' class?
class foes(object):
def __init__(self, name, health, attack):
self.name = name
self.health = health
self.attack = attack
zombie = foes('Zombie', 10, 3)
dragon = foes('Dragon',20, 5)
skeleton = foes('Skeleton', 8, 4)
You are trying to call internal Intro() variable - fast fix could be like this:
def Intro():
print('Welcome puny warrior, to my Dungeon of DOOM, and stuff.')
pName = input('Tell me your name, puny warrior: ')
playResponse = input('Do you want to begin puny warrior who calls himself ' + pName + '? Y/N: ')
playResponse = playResponse.upper()
if playResponse[0:1] == 'Y':
pass
else:
print('You die now', pName)
return pName
class character(object):
def __init__(self, name, health, attack):
self.name = name
self.health = health
self.attack = attack
#this part obviously doesn't work, but I decided to leave it as a visual aid
player = character(Intro(), 25, 5)
#should I just make this class a child of the 'character' class?
class foes(object):
def __init__(self, name, health, attack):
self.name = name
self.health = health
self.attack = attack
zombie = foes('Zombie', 10, 3)
dragon = foes('Dragon',20, 5)
skeleton = foes('Skeleton', 8, 4)
Some Pointers
Take a look at PEP8, the Style Guide for Python.
Use the Character Class to define both the player, and foes.
Implement a main() method and use it appropriately. For more information about this topic, visit this discussion.
Make use of Python dictionaries, as they are very powerful.
#!/usr/bin/env python
"""Simple, text-based RPG."""
import random # Generate pseudo-random numbers.
import time # Time access and conversions.
class Character(object):
"""Define the character."""
def __init__(self, name, health, attack):
self.name = str(name) # Expecting a string.
self.health = int(health) # Expecting an integer.
self.attack = int(attack) # Expecting an integer.
def properties(self):
"""Returns dictionary containing the character properties."""
# Because using dictionaries is awesome.
characteristics = {
'name': self.name,
'health': self.health,
'attack': self.attack
}
return characteristics # Returns a dictionary
def get_player():
"""Acquire the name of the player, and begin the game."""
# Newline characters should improve readability on the command line.
print('\nWelcome puny warrior, to my Dungeon of DOOM, and stuff.\n')
# Variable names should be easy to read and understand.
player_name = input(
"""
Welcome puny warrior, to my Dungeon of DOOM... and stuff.
Tell me your name: """
)
# Get and store the player's response.
player_response = input(
"""
Do you want to begin puny warrior who calls himself %s? (Y/N): \
""" % player_name
)
if player_response.upper() == "Y":
pass
else:
print('\nYou die now\n', player_name)
return player_name
def score():
"""Assign a score between 1 and 100."""
return random.randint(1, 100)
def main():
"""Where the automagic happens."""
# Assuming you want random integers.
player = Character(get_player(), score(), score()).properties()
zombie = Character('Zombie', score(), score()).properties()
dragon = Character('Dragon', score(), score()).properties()
skeleton = Character('Skeleton', score(), score()).properties()
# Since you're working with a dictictionary, you can now do things like:
print(player['name'])
print(zombie['name'], zombie['health'])
print("%s: %s, %s" % (dragon['name'], dragon['health'], dragon['attack']))
# The correct methodology.
if __name__ == "__main__":
main()
I'm not really sure what your question is. There's no difference to Python between using a variable like pName and a string like "Zombie" in a class instantiation. The only thing wrong with your code
is that you're doing that instantiation outside Intro(), so pName is not defined.