Making a combat system using classes - python

I've been trying to implement a combat system in my text game. I thought I could just create instances in each of my scenes that have different int values to minimize hard-coding that the exercise from the book I have I think is trying to teach me. When I access the TestRoom class by dict it says in Powershell:
TypeError: __init__() takes exactly 4 arguments (1 given)
Please help me figure out how to do this I'm new to Python and the book doesn't explain classes that well.
class Hero(object):
def __init__(self, health1, damage1, bullets1):
self.health1 = health1
self.damage1 = damage1
self.bullets1 = bullets1
class Gothon(object):
def __init__(self, health2, damage2):
self.health2 = health2
self.damage2 = damage2
class Combat(object):
def battle():
while self.health1 != 0 or self.health2 != 0 or self.bullets1 != 0:
print "You have %r health left" % self.health1
print "Gothon has %r health left" % self.health2
fight = raw_input("Attack? Y/N?\n> ")
if fight == "Y" or fight == "y":
self.health2 - self.damage1
self.health1 - self.damage2
elif fight == "N" or fight == "n":
self.health1 - self.damage2
else:
print "DOES NOT COMPUTE"
if self.health1 == 0:
return 'death'
else:
pass
class TestRoom(Combat, Hero, Gothon):
def enter():
hero = Hero(10, 2, 10)
gothon = Gothon(5, 1)
This is in Python 2.7
Side note: Is learning Pyhton 2.7 a bad thing with Python 3 out? Answer isn't really required, just wondering.

I'm not sure why you've made TestRoom inherit from those other three classes. That doesn't make sense; inheritance means an "is-a" relationship, but while a room might contain those things, it isn't actually any of those things itself.
This is the source of your problem, as TestRoom has now inherited the __init__ method from the first one of those that defines it, Hero, and so it requires the parameters that Hero does. Remove that inheritance.

Related

Menu for a turn based game

Our teacher has assigned us an assignment for doing a turned based game.
This only included name.strip() but this does not prompt player to input unique name:
def start():
print("\nNew game started...Setting up Player 1's team...\n")
for total_characters in range (1,4):
name = input('Enter a unique name for unit #'+str(total_characters)+'==> ')
if not name.strip():
print('Cant be blank name,please provide a unique name')
return start()
else:
role_selection()
def role_selection():
for total_characters in range (1):
role = input('Select unit #'+str(total_characters)+' type: (W)arrior, (T)anker, or Wi(Z)ard ==> ')
total_characters+=1
if role.upper() == 'W':
pass
elif role.upper() == 'T':
pass
elif role.upper() == 'Z':
pass
else:
print("I don't understand what are you typing.")
return role_selection()
There are things that doesn't make sense :
You have the exact same function twice :
def set_up(team_size)
def set_up(name)
You are doing :
for total_units in range(team_size):
[...]
invalid = True
[...]
while invalid: # Infinite loop
set_up() # What's this function ?
As you can see from the comments in the code above, you never set invalid to False, leading to the infinite loop.
Note: My recommendation is that you should check out some tutorial on python before moving on coding a complex project, because your homework is not that easy.
Edit :
From the new code you've posted, you could do something like this :
def new_game():
names = []
for unit_id in range(1,4):
print(f'Enter a unique name for unit #{unit_id} ->')
empty = True
while empty:
name = input('>')
if name == "":
continue
empty = False
if name in names:
print('Unit name must be unique.')
else:
print('Name looks good!')
names.append(name)
python menu
At first glance, this stood out to me:
if not name.strip():
print('Unit name could not be blank.')
invalid = True
Remember in python the indentation matters. You are setting invalid to True regardless of the if condition. Further down you have a while loop that checks it.
The other if conditions have invalid=True inside the condition. Plus you don't have invalid=False anywhere as far as I see, so you'll get an error if you don't declare it somewhere so it's always on the path before the while.
this doesn't seem like a specific problem, more an ask for general guidance for going about this kind of problem?
One thing to note is that your above script only uses functions (which store behaviour) whilst really for something like the turn based game, you need to store behaviour (attacks etc) and information (how much health is left etc).
I won't write the script for you, but here's an example of how you might define an rpg like entity, capable of attacking, being attacked by another entity etc:
class Entity:
"""Abstract class for any combat creature"""
def __init__(self, name, health):
self.name = name
self.health = health
self.is_alive = True
def attack(self, other):
dmg = random.randint(7, 12)
other.be_attacked(dmg)
def be_attacked(self, dmg):
self.health = self.health - dmg
if self.health <= 0:
self.die()
def die(self):
self.is_alive = False
def check_life(self):
return self.is_alive
You can then initialise a bunch of these to make up the 'team' you where talking about:
one_person = Entity('Lee', 34)
another_person = Entity('Someone Else', 21)
etc. Hope that helps a bit. Good luck with your project and have fun!

Accessing Classes via Variable

I am attempting to make a small game, and what I will be doing is using classes to dictate the stats of the user's preferred class. I need to make the classes so when it comes time to fight a monster (which will also be in it's own class), I will be able to have their stats callable.
This may be a "big picture" problem, but to avoid writing the code three times (for each class), I only want to call the variable "chosenClass.HP" instead of "Mage.HP" because if I did it that way, I would need to have a bunch of if statements for each class among the story. There must be a better way.
I have worked on this both ways, and I hate having to write
if userChoice is 'Mage':
HP = 150
I have looked around at the self.something, but I don't completely understand how that works. If that is the required or recommended solution, what is the best resource for learning?
print("Welcome to the game")
name = input("Choose your name\n")
print(name)
class Warrior:
HP = 100
ATK = 200
DEF = 0
class Mage:
HP = 100
ATK = 200
DEF = 0
class Rouge:
HP = 150
ATK = 250
DEF = 100
user_class = input("Choose a class between Warrior, Mage, or Rouge\n")
print(user_class)
while(user_class != 'Warrior' and user_class != 'Mage' and user_class != 'Rouge'):
print("You did not choose from the required classes, try again")
user_class = input()
theClass = user_class()
theClass.HP = 1000
My error that I get from this code is:
TypeError: 'str' object is not callable
I understand it's being given a String, but it's a variable. Why is it causing problems?
Any recommendations to make this program better?
user_class returns a string and not an actual Class that you've defined above. This is because input() returns a string and you store that in user_class. In order to create a new instance of the classes you created in your code need to instantiate the class using some sort of logic based on the player's input such as:
if user_class.lower() == 'warrior':
theClass = Warrior()
theClass.HP = 1000
Obviously this wouldn't be the best way but is just an example to show how to instantiate the class.
You need a mapping of strings to classes if you want to create an instance like this.
pc_classes = {'Mage': Mage, 'Warrior': Warrior, 'Rogue': Rogue}
while True:
try:
user_class = input("Enter Mage, Warrior, or Rogue)
if user_class in pc_classes:
break
pc = pc_classes[user_class]()

I want to create a basic attribute system with Python (classes and def statements)

I'm a fledgling coder starting out with Python, and am trying to create a simple attribute system capable of interacting with other sections of the program (e.g. def statements). Based on the meager amount of knowledge I currently have of the Python language, I'm assuming the best way to accomplish this task is to use a class coupled with def statements to create commands and actions relevant to the class in question. Whenever I try to run my code, I end up with this error:
if petname['hungry'] == True:
TypeError: 'type' object is not subscriptable
Again, I have a very limited amount of knowledge right now, so I don't know if my program is close to being usable, or if it's just useless garbage. I'll post my code here. I'd greatly appreciate some corrective criticism; or, heck, if someone could correctly rewrite it for me, that'd be great!
Here's the code I wrote. Let me know if anymore info is necessary to give a comprehensive answer:
petname = 'Dog'
class petname (object):
attributes = {'health': 20, 'attack': 4, 'size': 5, 'hunger': True}
def feed(petname):
if petname['hungry'] == True:
petname['hungry'] = False
petname['size'] = petname['size'] + 1
print("{0} happily gobbles down the treat!".format(petname))
else:
print("{0} is not hungry.".format(petname))
if petname['hungry'] == True:
print("{0} is hungry! Feed it something!".format(petname))
input()
You're reusing the name petname across your code, and expecting it to mean different things even in the same context. Your petname class doesn't make much sense, as the single pername.attributes dictionary will be shared among the instances of all petname objects.
Below, I organize the Pet object to have attributes, use inheritance to establish default values for dogs, and make feed a method of the Pet class:
class Pet(object):
def __init__(self, name, health, attack, size, hunger):
self.name = name
self.health = health
self.attack = attack
self.size = size
self.hunger = hunger
def feed(self):
if self.hunger:
self.hunger = False
self.size += 1
print("{0} happily gobbles down the treat!".format(self.name))
else:
print("{0} is not hungry.".format(petname))
class Dog(Pet):
def __init__(self, name):
super(Dog, self).__init__(name, 20, 4, 5, True)
spot = Dog("Spot")
spot.feed()
# Spot happily gobbles down the treat!

Python AttributeError: type object 'x' has no attribute 'x'

I'm currently working on a simple text-based game in Python just to practice python and object-oriented programming but I'm running into this error where it tells me that 'LargeManaPotion' has no attribute 'name' when I can see that it does, and it is declared the exact same way as 'SmallManaPotion' which works just fine. I'm assuming it's some stupid mistake that I'm just overlooking or something but would appreciate the help. Also, the program will print the potion just fine when I print the inventory of the player in the player.inventory function so I'm not sure why it doesn't work with within the trade function. Anyways, here is the relevant code. Thanks in advance.
class ManaPotion:
def __init__(self):
raise NotImplementedError("Do not create raw ManaPotion objects.")
def __str__(self):
return "{} (+{} Mana)".format(self.name, self.mana_value)
class LargeManaPotion(ManaPotion):
def __init__(self):
self.name = "Large Mana Potion"
self.mana_value = 45
self.value = 40
class SmallManaPotion(ManaPotion):
def __init__(self):
self.name = "Small Mana Potion"
self.mana_value = 15
self.value = 10
As you can see it is identical to the SmallManaPotion.
Here is the function which causes the error.
class TraderTile(MapTile):
def intro_text(self):
return "A frail not-quite-human, not-quite-creature squats in the corner " \
"\nclinking his gold coins together. \nHe looks willing to trade."
def __init__(self, x, y):
self.trader = npc.Trader()
super().__init__(x, y)
def trade(self, buyer, seller):
for i, item in enumerate(seller.inventory, 1):
#the line below here is where I'm getting the error.
print("{}. {} - {} Gold".format(i, item.name, item.value))
while True:
user_input = input("Choose an item or press Q to exit: ")
if user_input in ['q', 'Q']:
return
else:
try:
choice = int(user_input)
to_swap = seller.inventory[choice - 1]
self.swap(seller, buyer, to_swap)
except ValueError:
print("Invalid choice!")
def swap(self, seller, buyer, item):
if item.value > buyer.gold:
print("That's too expensive.")
return
seller.inventory.remove(item)
buyer.inventory.append(item)
seller.gold = seller.gold + item.value
buyer.gold = buyer.gold - item.value
print("Trade complete!")
def check_if_trade(self, player):
while True:
print("\n\nGold: {} \nWould you like to (B)uy, (S)ell, or (Q)uit?".format(player.gold))
user_input = input()
if user_input in ['Q', 'q']:
return
elif user_input in ['B', 'b']:
print("\n\nGold: {} \nHere's whats available to buy: ".format(player.gold))
self.trade(buyer=player, seller=self.trader)
elif user_input in ['S', 's']:
print("\n\nGold: {} \nHere's what's available to sell: ".format(player.gold))
self.trade(buyer=self.trader, seller=player)
else:
print("Invalid choice!")
However, this function calls LargeManaPotion but without any errors.
def print_inventory(self):
print("Inventory:")
for item in self.inventory:
print('* ' + str(item))
print("* Gold: {}".format(self.gold))
best_weapon = self.most_powerful_weapon()
print("Your best weapon is your {}".format(best_weapon))
Error and stacktrace:
Choose an action:
i: Print inventory
t: Trade
n: Go north
s: Go south
w: Go west
m: Replenish Mana
Action: t
Gold: 33
Would you like to (B)uy, (S)ell, or (Q)uit?
>>>b
Gold: 33
Here's whats available to buy:
1. Crusty Bread - 12 Gold
2. Crusty Bread - 12 Gold
3. Crusty Bread - 12 Gold
4. Healing Potion - 60 Gold
5. Healing Potion - 60 Gold
6. Small Mana Potion - 10 Gold
7. Small Mana Potion - 10 Gold
Traceback (most recent call last):
File "/Users/Cpt_Chirp/Documents/Escape/game.py", line 74, in <module>
play()
File "/Users/Cpt_Chirp/Documents/Escape/game.py", line 17, in play
choose_action(room, player)
File "/Users/Cpt_Chirp/Documents/Escape/game.py", line 30, in choose_action
action()
File "/Users/Cpt_Chirp/Documents/Escape/player.py", line 112, in trade
room.check_if_trade(self)
File "/Users/Cpt_Chirp/Documents/Escape/world.py", line 127, in check_if_trade
self.trade(buyer=player, seller=self.trader)
File "/Users/Cpt_Chirp/Documents/Escape/world.py", line 96, in trade
print("{}. {} - {} Gold".format(i, item.name, item.value))
AttributeError: type object 'LargeManaPotion' has no attribute 'name'
Process finished with exit code 1
I don't believe you have provided the correct code, but you have provided enough to determine what's going on here
a = list()
b = list
a.append(1)
b.append(1)
Which one of these will raise an error? Obviously, the append to b. While objects of type "list" have a method "append", the base class "Type List" does not.
Somewhere, you have assigned the type LargeManaPotion to a variable and attempted to access the field name from it. But the type itself does not have those fields. The reason you can do this is because in python, classes are first class objects and can be passed around like any other objects
Let's looking at something closer to your live code
class Pot(object):
def add(self):pass
pots = [Pot(), Pot(), Pot(), Pot(), Pot]
for pot in pots: pots.add()
Now where is the problem? They are all instances of Pot, are they not? Why does only the last one raise an AttributeError?
Of course, because they are not all the same. The first 4 items are instances of the class Pot. The are returned from the method __new__, defined in the class type Pot which is invoked when I use that "parentheses notation" after the variable name. At runtime, python has no idea what the variable "Pot" is. It happens to be a type variable, who's invocation generates an instance object.
The last item is an instance of the class "type Pot". It is not a Pot. It is a type. It's __class__ attribute is not Pot. It's __class__ attribute is type Types are used to generate instances. it is meaningless to "add" to a type.
Let's say you had potions in real life. You can do things with potions. You can drink them. You can check their boiling points (if they have a tag, or maybe through science).
Instead, let's say you had the recipe for a potion lying around. And you said: "drink the recipe". "What's the recipe's boiling point". The universe is responding: "that's undefined". You meant to look at a potion. Instead you looked at its recipe. Like all OO metaphors, this one is incomplete. Additional reading:
What happens when I use () notation on a class
What happens if I use () notation on a nonclass
Class attributes?
Wait what's a class
Wait what's a type
Wait what's a metaclass
Wait what's a metaphor
Wait what's meta
On the line you indicate an error with a comment, try instead calling
print(item)
I think you may see some surprising results. At a prompt, I did the following:
>>> print(LargeManaPotion)
<class '__main__.LargeManaPotion'>
>>> print(LargeManaPotion())
Large Mana Potion (+45 Mana)
You didn't seem to provide the full source for your program (thus I can't verify), but I suspect you're seeing the "<class ..." line. Since I have to guess, I'd say the place you construct the seller's inventory is referring to the class itself
LargeManaPotion
rather than instantiating (calling) it
LargeManaPotion()

Understanding Classes and __Call__ Methods Py 2.7

Okay I have been running my forehead into some code and I have determined I am just taking shots in the dark at this point. For some reason getting classes to click has been a real pain in the butt and I'm thinking if I used my own personal code it might make sense. So here is a little script game I wrote up to experiment. (Its pretty rough I'm fairly new): https://github.com/Villagesmithy/Zombies-.git
I feel that to understand what I'm doing wrong I have to have it pointed out to me in my own code. There are plenty of questions similar to mine I'm sure but they were going over my head sadly.
The error I'm getting is this:
Attribute Error: Combat instance has no __Call__ method
And here is the class that I think is the problem:
class Combat:
def battle(self):
print start.player, " has: ", hp, "."
print "The zombie has: ", zombie.zom_health()
time.sleep(1)
if haf == True:
total_damage = hero_attacks * hero_damage
zombie.zom_health() - total_damage
print "You strike the zombie for %d damage!" %(total_damage)
print "The zombie's health is %d" %zombie.zom_health()
return zombie.zom_health()
time.sleep(3)
elif haf == False:
total_damage = zombie.zom_damage()- hero.hero_armor()
if total_damage > 0:
total_damage - hp
return hp
print "A zombie shambles through the baricade and damages you!"
print "You lost %d hp! Your hp is now: %d" %(total_damage, hp)
combat_loop()
time.sleep(3)
elif total_damage <= 0:
print "A zombie lurches at you but misses!"
time.sleep(3)
combat_loop()
else:
z.zom_killed()
def initv(battle):
bat = battle()
hero_init = random.randint(1,20)
zom_init = random.randint(1,20)
if hero_init >= zom_init:
#global haf Ignoring for now
haf = True
print "You attack first!"
bat.battle()
elif hero_init < zom_init:
#global haf
haf = False
print "The zombie attacks!"
bat.battle()
def start(): #The only fucking code that works
global zombies
zombies = random.randint(20,30)
arm = random.sample(armor,1)
wea = random.sample(weapon, 1)
player = raw_input("What is your name?")
print player, ",\n"
print "Your colony is under attack by %s zombies!" %(zombies)
print "Hold them off so the others can escape."
print "You are wearing an ", arm, "and weilding a", wea
time.sleep(3)
def combat_loop():
combat_call = Combat()
while zombies > 0:
combat_call.initv()
if zombies <= 0:
print "You held off the zombies off long enough to escape!"
print "With the way clear you grab your belongings and follow suit."
print "The end!"
sys.exit()
Now if you are saying Gee this kiddo has no clue what he is doing you would be correct! I'm just hoping you guys can assist me in making this click. The whole program might need to be burned at the stake I don't know. Any help you can give would be very helpful.
I'm assuming initv is actually a method of Combat, but you forgot to have it take self as a parameter, giving it a parameter named battle instead.
When you call bat.initv(), it's passing self as battle (self is a name of convention; the first positional parameter of a method is self whatever you decide to call it). So when you do bat = battle() in initv, it's the same thing as doing self(), that is, trying to treat an instance of your class as a callable.
From what I can tell, the real goal is to call the battle method, so the definition and first line of initv should be:
def initv(self):
bat = self.battle()
which passes self under the standard name, then calls the battle method on it. It's a little unclear what the battle method returns (it seems to return None implicitly on two code paths, and whatever zombie.zom_health() returns on the third code path, which has a sleep in it that never happens thanks to the return preempting it), but this code has a number of issues, and it's quite hard to identify what "correct" behavior would be.
For the record, the error almost certainly complained about the lack of a __call__ method, not __Call__; the special method that lets instances of a class act as callables themselves is all lower case.
In combat_loop you define combat_call as Combat(). Right now combat_call is an instance of Combat. Then in your while loop, you say combat_call.initv(). Since combat_call is an instance of Combat, that is a shortcut for Combat.initv(combat_call). That is, combat_call is the only argument given to initv(). In initv(), you take one argument: battle. In this case, battle is the same thing as combat_call which is an instance of Combat. You then say bat = battle(). battle is already an instance of Combat, so you are trying to call an instance. Some instances can be called, but to do that, they need to define a __call__ method. Your class did not, so there is an error. I think that instead of taking battle as an argument, take self. Then define bat as self.battle().
One more thing, I don't think the zombie.zom_health() - total_damage line will work unless you implement the __sub__ method in your Zombie class. Even then I think it would only work if Zombie and Hero each had the same parent class say: 'Human'. But I hope not. 'Someone' would have to test that...I need some sleep first before I test more fixes for your Hero class. :) Perhaps Zombie - Hero might work. This example re-worked from your Zombie class works fine when the objects (ie Z1 and Z2) are both from the Zombie class. So...Hero.total_damage() might work just as well. I don't know for sure yet.
import random
class Zombie:
def __init__(self, zom_health=None):
self.__zom_health = None
if zom_health:
self.__zom_health = zom_health
else:
self.randomize_zom_health()
self.__zom_damage = 0
def randomize_zom_health(self):
zom_hp = random.randint(20,35)
if zom_hp <= 0:
print zom_killed
self.__zom_health = 0
else:
self.__zom_health = zom_hp
def __sub__(self, Other):
self.__zom_health -= Other.zom_damage() #or Other.total_damage()?
def zom_health(self):
return self.__zom_health
def zom_damage(self, damage=None):
if damage: self.__zom_damage = damage
return self.__zom_damage
>>> reload(Zombie)
>>> z1 = Zombie.Zombie(20)
>>> z2 = Zombie.Zombie(zom_health=30)
>>> z1.zom_health()
20
>>> z2.zom_health()
30
>>> z2.zom_damage(z2.zom_health())
30
>>> z1 - z2
>>> z1.zom_health()
-10
>>>

Categories