Note: this is a very long question.
I'm practicing python by making a game. I need to take a variable from another function (that I called spawn()) and use in a different function (that I called damage_taken()).
This is the file enemy.py. Its main job is to spawn an enemy:
import random
import player
class Enemy(object):
def types(self):
type = ["slime", "ghost", "demon"]
enemy = random.choice(type)
return enemy
class Slime(Enemy):
def types(self):
colour = ["red", "green", "blue"]
type = random.choice(colour)
return type
def health(self):
health = random.randint(1,5)
return health
class Ghost(Enemy):
def types(self):
form = ["spirit", "spectre", "phantom"]
type = random.choice(form)
return type
def health(self):
health = random.randint(10,30)
return health
class Demon(Enemy):
def types(self):
being = ["demon", "hell hound", "wendigo"]
type = random.choice(being)
return type
def health(self):
health = random.randint(15,35)
return health
This is the important code. I need to take the variable health from this function and use it in another function.
def spawn():
enemy = Enemy()
bad = enemy.types()
if bad == "slime":
slime = Slime()
target = slime.types()
health = slime.health()
print(f"A {target} {bad} has appeared. It has {health} HP")
return health
elif bad == "ghost":
ghost = Ghost()
target = ghost.types()
health = ghost.health()
print(f"A {target} has appeared. It has {health} HP")
return health
elif bad == "demon":
demon = Demon()
target = demon.types()
health = demon.health()
print(f"A {target} has appeared. It has {health} HP")
return health
This is where i am struggling. I am trying to take the variable health from the function spawn() and use it in the following function. However, it keeps on telling me health does not exist. How do i take a variable from another function and use it in this function.
def damage_taken():
spawn()
health = spawn.health - player.fight()
return health
damage_taken()
The code
spawn.health
is my failed attempt at trying to call the variable into the function.
The code:
player.fight()
is calling a function from a different file called player.py. Its main purpose is to deal with mechanics related to the player such as creating the character and deciding how much damage they deal.
If I well understood your question you just need to assign the return value from your spawn function into a variable:
def damage_taken():
spawn_health = spawn()
health = spawn_health - player.fight()
return health
I'm guessing that you would actually rather the spawn function, return the enemy object, rather than the health of the enemy, so I would recommend the following:
def spawn():
enemy = Enemy()
bad = enemy.types()
if bad == "slime":
enemy = Slime() # each of these now is assigned to the same variable name, "enemy"
target = slime.types()
health = slime.health()
print(f"A {target} {bad} has appeared. It has {health} HP")
elif bad == "ghost":
enemy = Ghost()
target = ghost.types()
health = ghost.health()
print(f"A {target} has appeared. It has {health} HP")
elif bad == "demon":
enemy = Demon()
target = demon.types()
health = demon.health()
print(f"A {target} has appeared. It has {health} HP")
return enemy
def damage_taken():
enemy = spawn()
health = enemy.health - player.fight()
return health
damage_taken()
now, there is still the problem that the enemy object will be entirely dropped after damage_taken() is called. If you want the enemy to persist, you likely will want to initialize both player and enemy outside of the scope of the damage_taken function. Something more like this:
enemy = spawn()
damage_taken(enemy)
Related
How do I set a boolean from a method to create "chance" with a poison status, or use later for dodge and other reusable.
Just now learning Simple Inheritance with Super() and not yet gotten to Complex Inheritance. Not sure if that would make a difference in how I could of coded this but using what I know, here is what I have.
What I am trying to do is create a method inside the Hero Class, and make the hero only be poisoned if a condition is met or a boolean is passed. I keep getting an issue with the logic. Still new to programming.
I've re-written the code multiple times using Idle for speed and it just keeps giving me problems, even when i place the for-loop inside the hero.PoisionedCheck(True) I only get the displayed text and not the forloop activated. Then I will get an error that "amount" parameter is not defined or called before assignment, then when I fix that I will get that scorpion.health and scorpion.energy is not defined. I'm running in circles.
Thank you for reading.
import time
class Monster:
name= 'Monster'
def __init__(self,health, energy):
self.health = health
self.energy = energy
def attack(self, amount):
print('Monster has attacked')
print(f'{amount} damage was delt!')
self.energy -= 20
def move(self,speed):
print(f'{self.name} has moved, at {speed} speed')
class Scorpion(Monster):
name='Scorpion'
def __init__(self, health, energy):
super().__init__(health, energy)
def attack(self, amount):
print('Scorpion has attacked')
print('Poison has take effect!')
# Loops Poison display repeated with time delay.
# for repeater amount of times.
repeater = 5
for poison in range(repeater):
poison = 0
amount = poison + amount
print(f'Poisoned: {amount} damage')
time.sleep(0.5)
poison = repeater
amount *= poison
print(f'Poison did a total damage of {amount} to {hero.name}')
hero.health -= amount
class Hero(Monster):
name = 'Rockwood'
def __init__(self, health, energy) -> None:
self.health = health
self.energy = energy
# How do I use this in a boolean to check Scorpion attack?
# Lets say later adding *import random* later to determine chances of poison.
# def PoisonedCheck(self, posioned):
# self.poisoned = posioned
# if posioned == True:
# print("Testing if become posioned- For loop goes after here to create damage.")
# posioned = True
# else:
# print('Became else.. ')
# posioned = False
monster = Monster(health=100, energy=100)
scorpion = Scorpion(health = 200, energy = 150)
hero = Hero(health=100, energy=100)
scorpion.attack(3)
print()
scorpion.move(110)
hero.move(50)
print(f"{hero.name} has {hero.health} health remaining. ")
print()
To be honest, I am not really sure what you are trying to achieve, hopefully this answers your question or gets you going.
Firstly I would suggest adding a target parameter to Scorpion's attack behaviour and modifying it's behaviour. That way you can define multiple heroes and have them combat the scorpion, for example:
scorpion = Scorpion(health = 200, energy = 150)
hero_one = Hero(health=100, energy=100)
hero_two = Hero(health=200, energy=100)
scorpion.attack(hero_one)
scorpion.attack(hero_two)
Secondly, Scorpion doesn't seem to lose energy after attacking and doesn't deal initial damage. You could fix that by changing Scorpion attack behaviour to first call parent attack function:
class Scorpion(Monster):
...
def attack(self, amount, target):
super().attack(amount, target)
print('Poison has take effect!')
...
But Monster attack behaviour doesn't accept second argument, so my suggestion is to modify Monster's attack function with second parameter to also deal initial damage to target (and correctly subtract target's health with damage amount):
class Monster:
...
def attack(self, amount, target):
print(f'{self.name} has attacked')
print(f'{amount} damage was delt!')
target.health -= amount
self.energy -= 20
To track poisoned status, easiest you can do is to introduce state in Monster class constructor called poisoned and set it to False (initial state is that monster and every child object is not poisoned).
Then you can change target's poisoned state in Scorpion's attack method, like so:
class Scorpion(Monster):
...
def attack(self, amount, target):
...
target.poisoned = True
...
This way you can see if one of Heroes is poisoned, like so:
print(hero_one.poisoned) --> False or True (if attacked by scorpion)
To calculate a chance (lets say 50%) if Scorpion's attack will poison the target or not, extract poison application behaviour into separate private function and modify it like this:
class Scorpion(Monster):
...
def attack(self, amount, target):
super().attack(amount, target)
self.__poison_target(amount, target)
def __poison_target(self, amount, target):
# A coin toss to decide if target will be poisoned or not.
should_apply_poison = random.randint(0, 1) == 1
if should_apply_poison:
print('Poison has take effect!')
target.poisoned = True
# Loops Poison display repeated with time delay.
# for repeater amount of times.
repeater = 5
for poison in range(repeater):
poison = 0
amount = poison + amount
print(f'Poisoned: {amount} damage')
time.sleep(0.5)
poison = repeater
amount *= poison
print(f'Poison did a total damage of {amount} to {target.name}')
target.health -= amount
...
Or you can delegate damage dealing behaviour from Scorpion to Hero by creating abstract class Poisonable which you can use to check if the target is Poisonable (in your case - Hero inherits from Poisonable). This way Scorpion class doesn't care if target is successfuly poisoned or not.
Poisonable class is contract which all classes that inherit from it must abide by (e.g. implement their own version of resistance, damage taking from poison). This way every poisoned creature can react differently.
class Poison:
def __init__(self):
self.damage_per_tick = 5
self.ticks = 3
class Scorpion(Monster):
name = 'Scorpion'
def __init__(self, health, energy):
super().__init__(health, energy)
self.poison = Poison()
def attack(self, amount, target):
super().attack(amount, target)
self.__poison_target(target)
def __poison_target(self, target):
if isinstance(target, Poisonable):
target.suffer_poison_damage(self.poison)
class Poisonable(ABC):
#abc.abstractmethod
def resist_poison(self) -> bool:
pass
#abc.abstractmethod
def suffer_poison_damage(self, poison: Poison) -> None:
pass
class Hero(Monster, Poisonable, ABC):
name = 'Rockwood'
def __init__(self, health, energy) -> None:
super().__init__(health, energy)
def resist_poison(self) -> bool:
# resist poison chance
return random.randint(0, 1) == 1
def suffer_poison_damage(self, poison: Poison) -> None:
if self.resist_poison():
pass
else:
print('Poison has take effect!')
# Loops Poison display repeated with time delay.
# for repeater amount of times.
total_amount = 0
for poison_tick in range(poison.ticks):
total_amount += poison.damage_per_tick
print(f'Poisoned: {poison.damage_per_tick} damage')
time.sleep(0.5)
print(f'Poison did a total damage of {total_amount} to {self.name}')
self.health -= total_amount
I am trying to learn Python OOP and have been struggling to understand scope, passing values and how to encapsulate. My earlier attempts have rapidly become spaghetti code (likely because my only programming experience was on 8 bit BASIC 40 years ago), and thus, I am trying classes and objects.
Here it is:
import random
class Player:
def __init__(self, weapon, health):
self.weapon = weapon
self.health = health
def reduce_health(self, amount):
self.health -= amount
def check_dead(self):
if self.health > 0:
return "Player not dead"
else:
return "Player dead"
def calculate_damage(self, weapon):
damage_inflicted = random.randint(3, 15) + weapon
return damage_inflicted
def fight(self):
player.reduce_health(self.calculate_damage(self.weapon))
print(player.health)
player.check_dead()
player = Player(1, 15)
player.fight()
The calculate_damage function is flagged as being 'static' and the check_dead function seemingly has no effect at all.
I need some hints to get my footing. Thanks.
There are a few errors in your code.
First, you are using the player variable in the fight function. This works, because you've called the instance of the Player class player, but it wouldn't if you would give it any other name. To fix your issue, use self instead ofplayer in all class functions. The self argument is automatically passed in by python and does always refer to the object on which the the function is called.
Second, the check_deadfunction does nothing because it only returns a value. There is no attribute set or value printed out in this function. In order to make it work, you need to print the value, either inside the function or outside, with the return value of the function.
This code should work:
import random
class Player:
def __init__(self, weapon, health):
self.weapon = weapon
self.health = health
def reduce_health(self, amount):
self.health -= amount
def check_dead(self):
if self.health > 0:
return "Player not dead"
else:
return "Player dead"
def calculate_damage(self, weapon):
damage_inflicted = random.randint(3, 15) + weapon
return damage_inflicted
def fight(self):
self.reduce_health(self.calculate_damage(self.weapon))
print(player.health)
print(self.check_dead())
player = Player(1, 15)
player.fight()
In this code, we print the aliveness of the player directly instead of returning it:
import random
class Player:
def __init__(self, weapon, health):
self.weapon = weapon
self.health = health
def reduce_health(self, amount):
self.health -= amount
def check_dead(self):
if self.health > 0:
print("Player not dead")
else:
print("Player dead")
def calculate_damage(self, weapon):
damage_inflicted = random.randint(3, 15) + weapon
return damage_inflicted
def fight(self):
self.reduce_health(self.calculate_damage(self.weapon))
print(player.health)
self.check_dead()
player = Player(1, 15)
player.fight()
The problem that you are having is probably due to the way you refer to the functions of check_dead(), reduce_health() and the variable health in fight(). You are calling out the player class instance, instead of the instance the function is running in, that meant that, if you were to change the name of the player class instance to something different, it would spill out an error that "player is not defined".
Why the check_dead() is not working could also be because it doesn't print out the values, it just returns them, and it you don't do anything with them when it returns them to the calling function it just scraps them.
I recommend checking the code if it is spelled right and also replace every reference in the class to self. And also make the check_dead() function wrapped in print in the fight() function. That would end up looking something like this:
import random
class Player:
def __init__(self, weapon, health):
self.weapon = weapon
self.health = health
def reduce_health(self, amount):
self.health -= amount
def check_dead(self):
if self.health > 0:
return "Player not dead"
else:
return "Player dead"
def calculate_damage(self, weapon):
damage_inflicted = random.randint(3, 15) + weapon
return damage_inflicted
def fight(self):
self.reduce_health(self.calculate_damage(self.weapon))
print(self.health)
print(self.check_dead())
player = Player(1, 15)
player.fight()
If this still doesn't work it's propablly of your running environment or your ide/editor.
Also:
Static means, that the function standalone
If a function is static it doesn't use any of the functions/variables the class gives it (which are commonly gotten by using self.[function/variable name]). By that it is independent and it could be written outside of the class and it would still work as intended.
I'm working on a simple skeleton for a game, and in an effort to try and be more "pythonic", I'm using objects/classes/dictionaries to try and capture all my actions/behaviors (as methods over functions, etc).
For some reason, every time I execute the method 'act' within the class "Player", the dictionary embedded within act runs all of its values (which are, in turn, methods from within the same instance of the class "Player"). In other words, the player chooses "attack, heal, and flee" every time, all at once, before being prompted.
I'm sure there's a simple explanation, but I've been looking for hours and can't find another example of someone's dictionary auto-running all the methods embedded within. Can you help?
Thanks!
- Jake
from random import randint
### BEGIN ALL CLASSES HERE
# To be used for all game objects (living and non-living)
class gameObject(object):
def __init__(self, name):
self.name = name
# To be used for all characters who can act in some way/be killed/change
class livingThing(gameObject):
def __init__(self, name, HP=1):
self.name = name
self.HP = HP
# The playable character(s)
class Player(livingThing):
def __init__(self,name="The Stranger", HP=4, MP=5, strength=1, intellect=1, spirit=1, luck=5, gil=6):
self.name = name
self.HP = HP
self.MP = MP
self.gil = gil
self.strength = strength
self.intellect = intellect
self.spirit = spirit
self.luck = luck
def act(player, enemy):
actions = {
"attack" : player.attack(enemy),
"heal" : player.heal(enemy),
"flee" : player.flee()
}
#Takes input from the player
decision = input("What would you like to do? ")
if decision.lower() in actions:
actions[decision.lower()]
else:
print("That didn't work! Try again.")
# Prints both player and enemy HP
def printHP(player, enemy):
print("{0}'s' HP: {1} \n{2}'s HP: {3}".format(player.name, player.HP, enemy.name, enemy.HP))
# Allows the player to attack an enemy (currently functional)
def attack(player, enemy):
enemy.HP -= player.strength
print("You strike {0} for {1} damage!".format(enemy.name, player.strength))
player.printHP(enemy)
# Allows the player to heal a certain amount of health based on its "spirit" stat (currently functional)
def heal(player, enemy):
healed = randint(0, player.spirit)
player.HP += healed
print("You've healed for {0}!".format(healed))
player.printHP(enemy)
#Allows the player to attempt to run away
def flee(player):
randluck = randint(0, player.luck)
if randluck > 3:
print("You successfully escaped!")
return player.HP
else:
print("You weren't able to escape!")
# Anything that can act with/against the player
class Actor(livingThing):
def __init__(self, name="Unknown Entity", HP=10, MP=2, gil=3):
self. name = name
self.HP = HP
self.MP = MP
self.gil = gil
### END ALL CLASSES ###
### DICTIONARIES CONTAINING ACTIONS ###
### CHARACTERS ###
fighter = Player()
monster = Actor()
fighter.act(monster)
I see the problem. When you are executing Python code, and you have a dictionary as you do, Python evaluates the dictionary fully. If you wanted your values (in your key:value) pairs to be the results of those methods, this is surely one way to do it.
In your case, what you can do is reference the function itself, and not invoke it. You can do this by getting rid of the parentheses, like this:
player.attack
instead of
player.attack()
Then, to call the function you can do something like
actions[decision.lower()](enemy)
Since one of your functions, flee, doesn't accept any parameters, you could give flee a parameter that you simply don't use in the function. If you were designing many many methods that your player can act with, then one strategy would be to give them all only named parameters, like this:
def f1(enemy=None,something=None,foo=None):
if enemy is None:
raise Exception("enemy cannot be None")
#process_enemy
If however, you also have a very high amount of parameters, then you could do this:
def attack(**kwargs):
#kwargs is a dictionary of parameters provided to the function
enemy = kwargs.get('enemy',None)
if enemy is None:
raise Exception("enemy cannot be None")
def eat(**kwargs):
food = kwargs.get('food',None)
if enemy is None:
raise Exception("food cannot be None")
attack(enemy="someenemyobject")
eat(food="somefoodobject")
attack() # raises Exception
attack(food="somefoodobject") # raises Exception
food(enemy="someenemyobject") # raises Exception
food(food="somefoodobject",enemy="someenemyobject") # does not raise Exception
I already asked something similar on the site, but I can't get over this error. The code that I'm using is:
class Enemy:
# Base class to create all future enemies
def __init__(self, name, hp, dmg, desc):
self.name = name # Enemy name
self.hp = hp # Enemy hp
self.dmg = dmg # Enemy damage
self.desc = desc # Enemy description (Will make sense later)
def is_alive(self):
return self.hp > 0
The code above is used to define all future enemies, as I stated on with #. Then, I created three enemies for testing, as my game will generate random encounters from a list. However, I'm only going to list one because the error happens with any enemy:
class Goblin(Enemy):
def __init__(self):
super().__init__(name = "Goblin",
hp = 40,
dmg = 33,
desc = "An ordinary goblin runs in your direction!")
Then, as the enemies do, the rooms of the dungeon also have a template class and a list for random purposes, but as I did before, I will only list one because it's the one that is causing errors to me:
class Room:
# Template to define all future rooms
def __init__(self, intro):
self.intro = intro
class VsRoom(Room):
# Room with an enemy
duelist = None
def __init__(self):
self.duelist = duelist = random.choice(Enemy_List)
super().__init__(intro = "There's an enemy here! \n%s" %(duelist.desc))
And then there's the "def" to run the game (currently just for testing):
def print_room():
# Tells the player the type of room they are
print("You enter the next room...")
print()
gen_room = random.choice(Rooms)() # ("Rooms" is a list)
print(gen_room.intro)
if gen_room == VsRoom:
combat() # No need to list the combat() definition, I think.
But when I run, I get this:
AttributeError: type object 'Goblin' has no attribute 'desc'
I know that AttributeError is quite easy to find here, but it seems that none of the existing questions suits me. If anybody knows why the code fails, please tell me.
I'm very new to Python, and I've been wondering where to ask this question.
Well, I'm having a small difficulty with this small if statement.
I've broken it down, as the full if statement will be used in this small text RPG game.
health = 50
if(health > 100):
health = 100
print (health)
print (health)
What I'm trying to do is make it so the Health will never go above 100.
But typing
>>> health = 150
>>> print (health)
health = 150
And the IF statement hasn't been called.
I know this is probably a really easy fix for any experienced Python developer, but I simply cannot wrap my head around it.
Thanks,
Amos.
Your if statement is not automatically run when you set health to 150. You'll need to explicitly execute it again to limit your health value.
You could make that a function:
def limit_health(health):
if health < 0:
health = 0
if health > 100:
health = 100
return health
health = limit_health(150)
or you could just use min() to prevent health from going over 100:
health = min(health, 100)
If you are not averse to using classes, make health a property of the player:
class Player(object):
MAX_HEALTH = 100
def __init__(self):
# players start out with maximum health
self.health = self.MAX_HEALTH
# other things players track and can do
#property
def health(self):
return self._health
#health.setter
def health(self, new_health):
# limit health to MAX_HEALTH at most
self._health = min(new_health, self.MAX_HEALTH)
then whenever you set player.health it'll be automatically limited for you.