I have problems implementing the base power code on my Pokemon battle game on Python. Thanks if you wanna help.
Here's my code:
import time
import numpy as np
import sys
# Delay printing
def delay_print(s):
# print one character at a time
for c in s:
sys.stdout.write(c)
sys.stdout.flush()
time.sleep(0.05)
# Create the class
class Pokemon:
def __init__(self, name, types, moves, EVs, health='==================='):
# save variables as attributes
self.name = name
self.types = types
self.moves = moves
self.attack = EVs['ATTACK']
self.defense = EVs['DEFENSE']
self.health = health
self.bars = 20 # Amount of health bars
def fight(self, Pokemon2):
print("POKEMON BATTLE WOOOOOOOOOOOO!!!!!!")
print(f"\n{self.name}")
print("TYPE/", self.types)
print("ATTACK/", self.attack)
print("DEFENSE/", self.defense)
print("LVL/", 3 * (1 + np.mean([self.attack, self.defense])))
print("\nVS")
print(f"\n{Pokemon2.name}")
print("TYPE/", Pokemon2.types)
print("ATTACK/", Pokemon2.attack)
print("DEFENSE/", Pokemon2.defense)
print("LVL/", 3 * (1 + np.mean([Pokemon2.attack, Pokemon2.defense])))
time.sleep(2)
# Consider type advantages
version = ['Fire', 'Water', 'Grass']
for i, k in enumerate(version):
if self.types == k:
# Both are same type
if Pokemon2.types == k:
string_1_attack = '\nIts not very effective...'
string_2_attack = '\nIts not very effective...'
# Pokemon2 is STRONG
if Pokemon2.types == version[(i + 1) % 3]:
Pokemon2.attack *= 2
Pokemon2.defense *= 2
self.attack /= 2
self.defense /= 2
string_1_attack = '\nIts not very effective...'
string_2_attack = '\nIts super effective!'
# Pokemon2 is WEAK
if Pokemon2.types == version[(i + 2) % 3]:
self.attack *= 2
self.defense *= 2
Pokemon2.attack /= 2
Pokemon2.defense /= 2
string_1_attack = '\nIts super effective!'
string_2_attack = '\nIts not very effective...'
# Now for the actual fighting...
# Continue while pokemon still have health
while (self.bars > 0) and (Pokemon2.bars > 0):
# Print the health of each pokemon
print(f"\n{self.name}\t\tHLTH\t{self.health}")
print(f"{Pokemon2.name}\t\tHLTH\t{Pokemon2.health}\n")
print(f"{self.name}, I choose you!")
for i, x in enumerate(self.moves):
print(f"{i + 1}.", x)
index = int(input('Pick a move: '))
delay_print(f"\n{self.name} used {self.moves[index - 1]}!")
time.sleep(1)
delay_print(string_1_attack)
# Determine damage
Pokemon2.bars -= self.attack
Pokemon2.health = ""
# Add back bars plus defense boost
for j in range(int(Pokemon2.bars + .1 * Pokemon2.defense)):
Pokemon2.health += "="
time.sleep(1)
print(f"\n{self.name}\t\tHLTH\t{self.health}")
print(f"{Pokemon2.name}\t\tHLTH\t{Pokemon2.health}\n")
time.sleep(.5)
# Check to see if Pokemon fainted
if Pokemon2.bars <= 0:
delay_print("\n..." + Pokemon2.name + ' fainted. ' +
self.name + " won!")
break
# Pokemon2s turn
print(f"Let's go, {Pokemon2.name}!")
for i, x in enumerate(Pokemon2.moves):
print(f"{i + 1}.", x)
index = int(input('Pick a move: '))
delay_print(f"\n{Pokemon2.name} used {Pokemon2.moves[index - 1]}!")
time.sleep(1)
delay_print(string_2_attack)
# Determine damage
self.bars -= Pokemon2.attack
self.health = ""
# Add back bars plus defense boost
for j in range(int(self.bars + .1 * self.defense)):
self.health += "="
time.sleep(1)
print(f"{self.name}\t\tHLTH\t{self.health}")
print(f"{Pokemon2.name}\t\tHLTH\t{Pokemon2.health}\n")
time.sleep(.5)
# Check to see if Pokemon fainted;;
if self.bars <= 0:
delay_print("\n..." + self.name + ' fainted. ' +
Pokemon2.name + " won!")
break
class moves:
self.firemoves = ["Eruption", "Fire Blast", "Flamethrower", "Overheat"]
self.watermoves = ["Razor Shell", "Scald", "Hydro Pump", "Surf"]
self.grassmoves = ["Leaf Storm", "Energy Ball", "Giga Drain", "Solar Beam"]
def BasePower():
BasePower =
def Damage():
damage = ((2 * Level + 2) * Power * ATTACK / DEFENSE / 50 + 2)
money = np.random.choice(5000)
delay_print(f"\nOpponent paid you ${money}. Good luck in the future and win more batttles!\n")
if __name__ == '__main__':
# Create Pokemon
Typhlosion = Pokemon(
'Typhlosion', 'Fire',
['Eruption: 150 Base Power, power decreases as health decreases',
'Fire Blast: 110 Base Power, 10% chance to burn', 'FLamethrower: 90 Base Power, 10% chance to burn',
'Overheat, 130 Base Power, lowers attack by 2'], {
'ATTACK': 12,
'DEFENSE': 8
})
Samurott = Pokemon('Samurott', 'Water',
['Razor Shell: 75 Base Power', 'Scald: 80 Base Power', 'Hydro Pump: 120 Base Power',
'Surf: 90 Base Power'], {'ATTACK': 10, 'DEFENSE': 10
})
Serperior = Pokemon(
'Serperior', 'Grass',
['Leaf Storm: 130 Base Power, lowers attack by 2', 'Energy Ball: 90 Base Power',
'Giga Drain: 75 Base Power, yo heal 50% of damage dealt',
'Solar Beam: 120 Base Power, takes two turns to charge'], {
'ATTACK': 19,
'DEFENSE': 1
})
Typhlosion.fight(Samurott) # Get them to fight'
So as you can see here, the moves such as Typhlosion's Eruption or Serperior's Leaf Storm doesn't have their base power, as Eruption supposedly have 150 base power in full health, and Leaf Storm should have 130 base power, while dropping your attack by two.
Related
Been trying to code a small game for the past week now and whenever I calculate the damage and print the enemy's hp and the damage dealt, theres a difference of 2 every single time. Enemy's Hp is 2 less than the supposed "damage dealt". Also, is there any way to round numbers up and down?
Main
#Imports
import os
from player import Player
from items import Item
from spells import Spells
from monsters import Monster
from gameplay import Fight
#Creating spells
# Type, Name, Damage, Cost
#Below are buff spells
buff_A = Spells("buff", "Buff", 1.1, 5)
buff_B = Spells("buff", "Stronger Buff", 1.3, 15)
buff_C = Spells("buff", "Strongest Buff", 1.5, 50)
#Below Are heal spells
heal_A = Spells("heal", "Heal", 5, 10)
heal_B = Spells("heal", "Stronger Heal", 20, 30)
heal_C = Spells("heal", "Strongest Heal", 50, 100)
#Creating Items
#Type, Name, Description, Amount
potion = Item("potion", "Health Potion", "Heals you for 10 HP", 10)
elixir = Item("elixir", "Mana Potion", "Regains 15 MP", 15)
#Assiging Variables For Player
player_spells = [buff_A, heal_A]
player_items = [potion, elixir]
#Creating player
#NAME, HP, MP, ATK, DEF, SPELLS, ITEMS
player = Player("Nathan", 10, 20, 5, 5, player_spells, player_items, 1, 0)
#Creating monster
cricket = Monster("Cricket", 3, 3, 7)
enemies = [cricket]
#Fight Setup
fighting = True
#gameplay
fight1 = Fight(player, cricket)
running = True
def player_hurt(enemy):
edamage = enemy.damage_dealt()
player.take_dmg(edamage)
print("You have taken " + str(edamage) + " damage")
print(str(player.hp) + " / " + str(player.maxhp))
def enemy_hurt(es):
pdamage = player.generate_dmg()
enemies[es].damage_taken(pdamage)
print("Enemies HP is " + str(enemies[es].hp) + " / " + str(enemies[es].maxhp))
#action = input("Please select an action, Attack, Magic, Items ")
while running:
print("1 for attack etc.")
print("Please note that items are not available")
choice = input(player.actions)
list_chooser = int(choice) - 1
#ATTACK CHOOSE
if list_chooser == 0:
dmg = player.generate_dmg()
enemy_hurt(0)
print("You did " + str(dmg) + " damage")
player_hurt(enemies[0])
if cricket.hp == 0:
os.system('clear')
print("You have killed the cricket")
player.hp = player.maxhp
running = False
#Add Exp element here later
#MAGIC CHOOSE
if list_chooser == 1:
print("Choose 1 for first spell, etc. ")
magic_choice = input("Heal")
magic_choice_real = int(magic_choice) - 1
if magic_choice_real == 0:
if player.mp < heal_A.cost:
print("Insufficient mana")
if player.mp >= heal_A.cost:
player.heal(heal_A.dmg)
print("You healed for " + str(heal_A.dmg))
player.mp -= heal_A.cost
print("You lost " + str(heal_A.cost) + " mana")
player_hurt(cricket)
if list_chooser == 3:
player.get_stats()
Player
# IMPORTS
import random
#CLASS PLAYER
class Player():
def __init__(self, name, hp, mp, atk, df, magic, items, lv, exp):
self.lv = lv
self.exp = exp
self.exp_next = lv * 50
self.maxhp = hp
self.hp = hp
self.maxmp = mp
self.mp = mp
self.atklow = atk - 2
self.atkhigh = atk + self.lv
self.df = df - 100
self.dfc = df / 100
self.magic = magic
self.items = items
self.actions = ["Attack", "Magic", "Items", "Stats"]
self.name = name
#Dealing Damage
def generate_dmg(self):
return random.randrange(self.atklow, self.atkhigh)
#Taking Damage
def take_dmg(self, dmg):
self.hp -= dmg
if self.hp < 0:
self.hp = 0
return self.hp
#Healing
def heal(self, dmg):
self.hp += dmg
if self.hp > self.maxhp:
self.hp = self.maxhp
#Returning HP Values
def get_hp(self):
return self.hp
def get_maxhp(self):
return self.maxhp
#Mana Cost Logic
def reduce_mp(self, cost):
self.mp -= cost
#Actions
def get_stats(self):
print("HP")
print(self.hp)
print("--")
print(self.maxhp)
print("")
print("MP")
print(self.mp)
print("--")
print(self.maxmp)
print("")
print("ATK")
print(self.atklow + self.atkhigh)
print("LV")
print(self.lv)
print("EXP AND EXP TO NEXT")
print(str(self.exp) + " / " + str(self.exp_next))
def level_up(self):
self.lv += 1
self.exp = 0
self.maxhp += self.lv
self.atk += self.lv
Monster
import random
class Monster:
def __init__(self, name, atk, df, hp):
self.name = name
self.atk = atk
self.hp = hp
self.maxhp = hp
self.atklow = self.atk - 2
self.atkhigh = self.atk + 2
def damage_dealt(self):
return random.randrange(self.atklow, self.atkhigh)
def damage_taken(self, dmg):
self.hp -= dmg
if self.hp < 0:
self.hp = 0
return self.hp
def get_hp(self):
return self.hp
def get_maxhp(self):
return self.maxhp
items
class Item:
def __init__(self, type, name, desc, amount):
self.type = type
self.name = name
self.desc = desc
self.amount = amount
Spells
import random
class Spells:
def __init__(self, type, name, dmg, cost):
self.type = type
self.name = name
self.dmg = dmg
self.cost = cost
def generate_dmg(self):
low = self.dmg + 3
high = self.dmg - 3
return random.randrange(low, high)
gameplay
import random
import os
class Fight:
def __init__(self, enemy, player):
self.enemy = enemy
self.player = player
self.playert = True
self.enemyt = False
self.player_action_allowed = False
self.player_action_allowed = False
def turns(self):
if self.playert == True:
self.player_action_allowed = True
self.enemyt = True
self.playert = False
elif self.enemyt == True:
enemy_action_allowed = True
player_action_allowed = False
playert = True
enemyt = False
else:
playert = True
Your loop generates damage, and then calls enemy_hurt:
while running:
# ...
dmg = player.generate_dmg()
enemy_hurt(0)
print("You did " + str(dmg) + " damage")
But enemy_hurt also generates damage of its own:
def enemy_hurt(es):
pdamage = player.generate_dmg()
enemies[es].damage_taken(pdamage)
print("Enemies HP is " + str(enemies[es].hp) + " / " + str(enemies[es].maxhp))
Surely you want to only generate damage once, right? You're generating two different damage values, one you print, the other you actually subtract from the enemy's health.
When running deepcopy from the Python copy module on instances of a class, the copy process time doubles with each iteration, even though the instances are all of the same class and (at least in my mind) should therefor take an identical amount of time to copy.
The object I'm copying uses kwargs to set a bunch of attributes. It also originally called a set_dynamic_attributes method in __init__ to configure some of the attributes according to some logic, but in order to test if that was causing the slowdown, I removed the call from the __init__ and called it specifically.
Here's the offending section, and the results of it:
for a in base_wpn.base_attacks:
t0 = time.time()
a.set_dynamic_attributes()
t1 = time.time()
print('Dyn attributes set time: ' + str(t1-t0))
atk = deepcopy(a)
t2 = time.time()
print('Deepcopy time: ' + str(t2-t1))
Result:
Dyn attributes set time: 0.0
Deepcopy time: 0.01399993896484375
Dyn attributes set time: 0.0
Deepcopy time: 0.03599882125854492
Dyn attributes set time: 0.0
Deepcopy time: 0.04999995231628418
Dyn attributes set time: 0.0
Deepcopy time: 0.09999823570251465
Dyn attributes set time: 0.0
Deepcopy time: 0.011002540588378906
Dyn attributes set time: 0.0
Deepcopy time: 0.021996021270751953
Dyn attributes set time: 0.0
Deepcopy time: 0.0429990291595459
Dyn attributes set time: 0.0
Deepcopy time: 0.08499836921691895
Dyn attributes set time: 0.0
Deepcopy time: 0.17699956893920898
Dyn attributes set time: 0.0
Deepcopy time: 0.32700061798095703
Dyn attributes set time: 0.0
Deepcopy time: 0.6589939594268799
Dyn attributes set time: 0.0
Deepcopy time: 1.4200007915496826
Dyn attributes set time: 0.0
Deepcopy time: 2.466003656387329
Dyn attributes set time: 0.0
Deepcopy time: 5.228000164031982
Dyn attributes set time: 0.0
Deepcopy time: 10.528998374938965
In case it helps (I know it's not reproable without a ton more code, lots of object references), here's the object definition that the instances are built from:
class Attack():
def __init__(self, name, weapon, **kwargs):
self.name = name
self.weapon = weapon
self.skill = [self.weapon.skill]
self.attack_mod = self.weapon.attack_mod
self.parry_mod = -self.weapon.parry_mod #Modifier to OPPONENT'S parry chance
self.stamina = self.weapon.stamina
self.main_shape = None
self.striker = None
self.hands = 1
self.damage_type = 'b'
self.base_ap = 0
self.hand = True
self.added_mass = self.weapon.added_mass
self.length = 0 #Used to add or subtract from base weapon length got added/reduced reach
self.side_restrict = True #Determines if the attack can only hit one side of the enemy (i.e. hook from R hand only hitting left side)
self.restricted_locs = [] #Locations that can never be targeted with this attack (i.e. foot with uppercut)
self.allowed_angles_r = [] #Angles that are allowed as an index of angles (0 = N-> S, 7 = NW -> SE, 8 = thrust) (i.e. N->S with an uppercut)
self.allowed_angles_l = [] #Angles that are allowed as an index of angles (0 = N-> S, 7 = NW -> SE, 8 = thrust) (i.e. N->S with an uppercut)
self.main_area = 0
self.mech_adv = 0
self.force_scalar = 1 #Used to adjust force/damage for the attack
for k in kwargs:
for key in self.__dict__:
if k == key:
self.__dict__.update(kwargs)
#self.set_dynamic_attributes()
def set_dynamic_attributes(self):
for t in self.damage_type:
if t == 'b':
if self.striker == 'main':
shape = self.weapon.main_shape
else:
shape = 'round'
if shape == 'wedge':
self.main_area = self.weapon.main_length * self.weapon.avg_main_width
self.mech_adv = self.weapon.main_depth / self.weapon.main_width
elif shape == 'round':
#Using Hertz's result, but using fixed f value built off of added_mass + fist mass (.86) and v of 40 f/s2 and fixed p_ratio for target
#Equation: https://www.quora.com/Is-the-area-of-contact-between-a-cylinder-and-a-flat-surface-infinitely-small-Is-it-a-point
if self.striker == 'main':
material = self.weapon.main_material
width = self.weapon.main_width
length = self.weapon.main_length
elif self.striker == 'shaft':
material == self.weapon.shaft_material
width = 1
length = self.weapon.shaft_length
else:
material = self.weapon.accent_material
width = length = 1
e_calc = ((1-(material.p_ratio * material.p_ratio))/(material.elasticity*10))+((1-(.4*.4))/5)
self.main_area = sqrt((4*((.86 + self.added_mass)*40)*width)/(3.514*(e_calc)*min(length, 8)))
elif self.main_shape == 'flat':
self.main_area = min(self.weapon.main_length,8) * min(self.weapon.main_width,8)
elif t == 's':
if self.main_shape == 'blade':
self.main_area = min(self.weapon.main_length, 8) * self.weapon.avg_main_width
self.mech_adv = self.weapon.main_depth / self.weapon.main_width
elif self.main_shape == 'de blade':
self.main_area = min(self.weapon.main_length, 8) * self.weapon.avg_main_width
self.mech_adv = (self.weapon.main_depth/2) / self.weapon.main_width
elif t == 'p':
if self.striker == 'main':
shape = self.weapon.main_shape
length = self.weapon.main_length
depth = self.weapon.main_depth
width = self.weapon.main_width
else:
shape = 'point'
if self.striker == 'shaft':
length = min(self.weapon.shaft_length, 8)
depth = width = 1
else:
length = depth = width = 1
if shape in ['point', 'blade']:
wedge1 = length / width
wedge2 = width / depth
#Double each (since there are two wedges per side) and multiply for full MA of tip
self.mech_adv = (wedge1*2)*(wedge2*2)
self.main_area = depth * length * width
else:
if self.main_shape == 'hook':
wedge1 = self.weapon.main_length / self.weapon.main_width
wedge2 = self.weapon.main_width / self.weapon.main_depth
#Double each (since there are two wedges per side) and multiply for full MA of tip
self.mech_adv = (wedge1*2)*(wedge2*2)
self.main_area = self.weapon.main_depth * self.weapon.main_width
if self.damage_type in ['s','b']:
self.stamina += self.weapon.weight + int(self.weapon.weight*self.weapon.axis_vs_com)
self.base_ap += min(self.weapon.weight/10, (self.weapon.weight * 10)/self.weapon.axis_vs_com)
self.added_mass = self.weapon.weight / self.weapon.com_perc
self.attack_mod += (20 - ((self.weapon.weight*10) * self.weapon.com_perc))
self.parry_mod -= ((self.weapon.weight*10) * self.weapon.com_perc)
if self.weapon.main_num > 1:
self.attack_mod += self.weapon.main_num * 5
self.parry_mod -= self.weapon.main_num * 20
else:
self.stamina += self.weapon.weight/2
self.base_ap += self.weapon.weight * 5
self.added_mass = self.weapon.weight/10
if self.damage_type == 'p':
self.attack_mod -= self.weapon.weight/10
self.parry_mod -= self.weapon.weight * 5
else:
self.attack_mod += -5 + (self.weapon.main_num * 5)
self.parry_mod -= self.weapon.main_num * 20
More code by request. Here is the code for the weapon class (which the attack references) and the materials class (which weapon references):
class Weapon:
def __init__(self, **kwargs):
self.name = ''
self.shafted = False #Used to determine if wpn has a shaft
self.allowed_main_materials = [] # List of materials applicable for the main surface. Young's modulus prevents copper and bronze swords longer than 24", for example
self.main_material = m_steel #Damage component (blade, head, etc) material
self.shaft_material = m_wood
self.grip_material = m_leather
self.accent_material = m_steel
self.attack_mod = 0
self.parry_mod = 0 #Mod to weilder's ability to parry with weapon
self.b_striker = 'accent' #Striking surface for damage type. Can be main, shaft, accent, or none
self.s_striker = 'main'
self.p_striker = 'main'
self.t_striker = 'none'
self.hands = [1] #List can include 0,1,2
self.quality = 'Average'
self.base_name = 'Weapon'
self.bname_variants = [] #A list of variant names for the weapon
self.skill = None #This is the default skill used for the weapon. String
self.length = 1
self.shaft_length = 0 #Also used as tethers for flail and whip like weapons
self.shaft_diameter = 0
self.shaft_num = 0
self.pre_load = False #Used to account for weapons that can be preloaded with velocity, like flails or staves
self.avg_main_width = 1 #1.25 average longsword
self.main_width = 1 #Absolute width at widest point
self.avg_main_depth = .1 #.14 is average for a sword blade
self.main_depth = .2 #Absolute depth at deepest point
self.main_shape = 'de blade' #Acceptable values: de blade, blade, point, wedge, round, flat, hook
self.main_num = 1 #Number of main attack surfaces, mostly used for flails/flogs
self.accent_cuin = 1 #Cubic inches of accent material, such as the crossguard and pommel on a sword
self.main_com = .5 #Center of mass for the main weapon component
self.main_loc = .1 #Location along the total length for the main weapon component
self.accent_loc = .05 #Location along the total length for the accent component
self.grip_loc = .03 #location along the total length for the grip
self.main_weight = 0
self.shaft_weight = 0
self.accent_weight = 0
self.grip_weight = 0
self.weight = 0
self.main_length = 0
self.added_mass = 0
self.damage_type = 'b'
self.stamina = 0
#Maximums; used to procedurally gen weapons
self.main_len_range = (0,0) #Tuple containing min and max range for acceptable lengths
self.main_depth_range = (0,0)
self.main_avg_depth_range = (0,0)
self.main_width_range = (0,0)
self.main_avg_width_range = (0,0)
self.length_range = (0,0)
self.shaft_length_range = (0,0)
self.shaft_diameter_range = (0,0)
self.max_main_num = 1
self.max_shaft_num = 1
self.main_only_com = 0
self.shaft_only_com = 0
self.accent_only_com = 0
self.com = 0 #Center of mass for the whole weapon
self.com_perc = 0 #com as a percentage
self.axis_vs_com = 0 #Shows COM relative to grip location (axis for swings). Used to determine AP/stamina costs.
self.main_hits = 0
self.staff_hits = 0
self.accent_hits = 0
self.min_pwr_1h = 0 #1 pwr = 1 ft/lb; accelleration = 40 f/s2; weight of average hand = .86 lb
self.min_pwr_2h = 0 #1 pwr = 1.5 ft/lb; accelleration = 40 f/s2; weight of average hand = .86 lb
self.solidness = 1 #Used in damage calc
self.sharpness = 1
self.pointedness = 1
self.base_attacks = []
self.attacks = []
self.base_maneuvers = []
self.maneuvers = []
self.guards = []
self.cost = 0
self.normality = 1
for k in kwargs:
for key in self.__dict__:
if k == key:
self.__dict__.update(kwargs)
self.set_dynamic_attributes()
def set_dynamic_attributes(self):
#Determine which variant name to use
if len(self.bname_variants) > 1:
n_idx = roll_dice(1, len(self.bname_variants))
n_idx -= 1
self.base_name = self.bname_variants[n_idx]
#Name weapon using quality and material, if applicable
if self.main_material is not m_tissue:
if self.quality != 'Average':
self.name = self.quality + ' ' + self.main_material.name + ' ' + self.base_name
else:
self.name = self.main_material.name + ' ' + self.base_name
else:
self.name = self.base_name
self.attack_mod = 20 * quality_dict.get(self.quality)
self.parry_mod = 20 * quality_dict.get(self.quality)
self.main_weight = ((self.main_length * self.avg_main_depth * self.avg_main_width)*self.main_num) * (self.main_material.density * .03)
self.shaft_weight = (self.shaft_length * self.shaft_diameter * (self.shaft_material.density * .03)) * self.shaft_num
self.accent_weight = self.accent_cuin * (self.accent_material.density * .03)
self.grip_weight = self.grip_material.density * (.3 * max(self.hands))
self.weight = self.main_weight + self.shaft_weight + self.accent_weight
self.main_only_com = ((self.main_length*self.main_com)+(self.main_loc*self.length))*self.main_weight
self.shaft_only_com = (self.shaft_length*.5)*self.shaft_weight
self.accent_only_com = (self.accent_loc*self.length)*self.accent_weight
self.com = (self.main_only_com + self.shaft_only_com + self.accent_only_com)/self.weight #Center of mass for the whole weapon
self.com_perc = self.com / self.length #com as a percentage
self.axis_vs_com = self.com_perc - self.grip_loc #Shows COM relative to grip location (axis for swings). Used to determine AP/stamina costs.
self.main_hits = (self.main_material.elasticity * 1450000) * (self.main_weight/(self.main_material.density*.03)) * self.main_material.toughness
self.min_pwr_1h = ((self.added_mass + .86) * 40)/1 #1 pwr = 1 ft/lb/s; accelleration = 40 f/s2; weight of average hand = .86 lb
self.min_pwr_2h = ((self.added_mass + 1.72) * 40)/1.5 #1 pwr = 1.5 ft/lb/s; accelleration = 40 f/s2; weight of average hand = .86 lb
if self.main_material.elasticity < 1: self.solidness = self.main_material.elasticity
if self.main_material.hardness < 1:
self.sharpness = self.main_material.hardness
self.pointedness = self.main_material.hardness
else:
self.sharpness = sqrt(self.main_material.hardness)
self.pointedness = self.main_material.hardness
#Damage calc = ((((added_mass + fist mass) * velocity) / main_area) * mech_adv) * sharpness or hardness or pointedness
main_materials_cost = self.main_material.cost * self.main_weight
shaft_materials_cost = self.shaft_material.cost * self.shaft_weight
grip_materials_cost = self.grip_material.cost * self.grip_weight
accent_materials_cost = self.accent_material.cost * self.accent_weight
#Crafting costs. 1 day skilled labor = ~5 material cost
main_crafting_cost = self.main_material.craft_diff * self.main_weight
shaft_crafting_cost = self.shaft_material.craft_diff * self.shaft_weight
grip_crafting_cost = self.grip_material.craft_diff * self.grip_weight
accent_crafting_cost = self.accent_material.craft_diff * self.accent_weight
self.cost = main_crafting_cost + main_materials_cost + shaft_crafting_cost + shaft_materials_cost + grip_crafting_cost + grip_materials_cost + accent_crafting_cost + accent_materials_cost
self.normality = self.main_material.normality * self.shaft_material.normality * self.grip_material.normality * self.accent_material.normality
class Material():
_allMaterials = []
def __init__(self, **kwargs):
#Below is to create a list of instances of this class
self._allMaterials.append(self)
self.name = None
#Goal: Hardness and elasticity set the hits for the material, toughness then scales them up or down.
self.hardness = 1 #Scalar. 1 = ~7 Brinell, roughly very hard wood. Copper 85, Bronze 180, Iron 300, Steel 400-600
self.elasticity = 1 #Scalar. Basically resistance to deformation. 1 = Wood, average modulus of elasticity ~10 GPa. Bone 18, Gold 79, Copper 100, Bronze 120, Iron 210, Steel 200
self.toughness = 1 #Scalar. Resistance to breaking when deformed. 1 = Wood, very non-brittle. 10 = Gold, very malliable. .5 = Steel, somewhat brittle. 0.1 = stone
self.p_ratio = .5 #Poisson's ratio. Non-scaled, use actual values
self.normality = 1 #Scalar. How rare the material is. 1 = Iron, very common. Gold is 4 million times rarer than Iron, but probably it will be ~.01 in game
self.cost = 1 #Scalar. Raw cost of the material (taking into account refining costs). 1 = Wood, cheap $0.23/lb. 10 = Copper, $2.50/lb. 82,000 = Pure Gold
self.craft_diff = 1 #Scalar. Difficulty of crafting. 1 = 1 day of crafting per lb of finished material. Cost of 1 day of a craftsman = 5
# For metals, hardness modifies it. Hardened Steel takes 5 days to craft 1 pound of weaponry.
self.density = 1 #Scalar. Relative density per cubic inch. 1 = Wood, .03 lb/in3. Copper = .32, Silver = .38, Gold = .7, Iron = .28, Bronze = .3, Steel = .28
for key in self.__dict__:
for k in kwargs:
if k == key:
self.__dict__.update(kwargs)
self.set_dynamic_attr()
def set_dynamic_attr(self):
if self.craft_diff == 0:
self.craft_diff = .078 * self.hardness
self.craft_diff = round(self.craft_diff, 2)
Fixed it; The issue was as user10987432 suspected, it was creating copies of all objects up the tree. I fixed it by removing the weapon reference in the constructor and passing each individual attribute as a kwarg (ugh). Makes for a very ugly 4-line call, but it works and is fast. I'll see if I can shorten it once it gets on my nerves enough; for now I can just copy/paste and only change relevant fields.
Edit: I fixed the problem much more cleanly by making the individual attacks all child classes of the Attack class and just creating instances of them for each actor. Not sure why I didn't do that originally, I'm doing it for maneuvers already, but it's much easier to read and debug now. Thanks again to everyone for the help.
I'm following a Python course on Udemy.com and we are practicing Python by building an RPG game. So far the game was working fine for a single player but as soon as we added 3 players the game seems to just get stuck after executing all 3 players attacks.
The concept is that there are 3 players or so, as the game starts the player stats are shown, after all the players have attacked each other this includes the enemy as well, the player stats are printed as shown below in the picture and the game asks for input from all the three players again, but it's just running once as shown below in the picture.
I followed the code for word to word and also posted a question regarding it. So I thought I should try StackoverFlow.
Below is my code kindly see why is it not going in loop as it should.
Mainfile
# -*- coding: utf-8 -*-
from game_class.invin import Item
from game_class.game import player
from game_class.magic import Spell
import time
# Player and Enemies magic create
Fire_Shot = Spell('Fire Shot', 10, 45, "Black Magic")
Thunder_Storm = Spell("Thunder Storm",25,65,"Black Magic")
Ninja_Summon = Spell("Ninja Summon",45,75,"Ninjustu")
The_End = Spell("THE END",80,300,"Finisher")
Heal = Spell("HEAL ME",60,140,'Heal')
player_magic = [Fire_Shot,Thunder_Storm,Ninja_Summon,Heal,The_End]
enemy_magic = [
{'Name': "Big Punch", 'cost': 30, "DMG": 45},
{'Name': "Slap", 'cost': 15, "DMG": 25},
{'Name': "Rock Throw", 'cost': 20, "DMG": 30},
{'Name': "Kick", 'cost': 45, "DMG": 60}
]
boss_magic = [
{'Name': "STORM", 'cost': 10, "DMG": 45},
{'Name': "DARK BACK-BITTING", 'cost': 10, "DMG": 25},
{'Name': "D.D.T", 'cost': 10, "DMG": 30}
]
# Items create
potion = Item("Potion", 'Potion', 'Heals for 50 HP', 50)
high_potion = Item("Potion+", 'Potion', 'Heals for 120 HP', 120)
super_potion = Item("Ultra Potion", 'Potion', 'Heal for 250 HP', 250)
elixir = Item("Elixir", 'Elixir', 'Give 1 EVERYTHING BACK', 9000)
high_elixir = Item("Omega Elixir", 'Elixir', 'Give all EVERYTHING BACK', 9000)
bomb = Item("Bomb",'Attack','Deals 350 Damage',350)
player_items = [ {"item":potion,"quantity":3},
{'item':high_potion,"quantity":2}
,{"item":super_potion,"quantity":1}
,{'item':elixir,"quantity":2}
,{'item':high_elixir,"quantity":1}
,{"item": bomb, "quantity": 2} ]
# PLAYER CREATE
Player1 = player('Night Man ',1000, 100, 145, 140, player_magic, player_items)
Player2 = player('Ray Wills ', 1000, 100, 155, 135, player_magic, player_items)
Player3 = player("Randy Orton",1000, 100, 150, 120, player_magic, player_items)
Enemy1 = player("Door Keeper",1500, 200, 250, 150, enemy_magic, None)
BOSS = player("Boss Man",1200, 200, 45, 300, boss_magic, None)
players = [Player1,Player2,Player3]
# Game starts
run = True
i = 1
while run is True:
print ("=======================================")
print("\n\n")
print(" NAME HP MP\n")
for player in players:
player.get_stats()
for player in players:
print("\n")
player.chose_action()
print("=========\n")
print (player.name)
print ("=============")
choice = input("CHOSE ACTION: ")
index = int(choice) - 1
if index == 0:
dmg = player.gen_dmg()
Enemy1.get_dmg(dmg)
print(player.name+ " attacked for " + str(dmg) + " damage points")
elif index == 1:
player.chose_magic()
magic_choice = (int(input("Chose Spell: ")) - 1)
spell = player.magic[magic_choice]
magic_dmg = spell.gen_spell_dmg()
current_mp = player.get_mp()
if magic_choice == -1:
continue
if spell.cost > current_mp:
print ("\nNOT ENOUGH MANA")
continue
if spell.stype == "Heal":
player.heal(magic_dmg)
print (str(magic_dmg) +' HP restored')
print("Remaining Magic Points: " + str(player.get_mp()) +
"/" + str(player.get_max_mp()))
elif spell.stype == "Black Magic" or spell.stype == "Ninjustu" or spell.stype == "Finisher":
player.reduce_mp(spell.cost)
Enemy1.get_dmg(magic_dmg)
print (str(spell.name) + ' did damage of '+ str(magic_dmg) +" points")
print ("Remaining Magic Points: " + str(player.get_mp()) +"/" +str(player.get_max_mp()))
elif index == 2:
player.chose_item()
item_choice = (int(input("Chose Spell: ")) - 1)
if item_choice == -1:
continue
item = player.items[item_choice]['item']
if player.items[item_choice]['quantity'] == 0:
print("No Item...")
continue
player.items[item_choice]['quantity'] -= 1
if item.itype == 'Potion':
player.heal(item.prop)
print("\n"+ str(item.name) + " used and healed for "+ str(item.prop) + " HP")
elif item.itype == "Elixir":
player.hp = Player1.maxhp
player.mp = Player1.maxmp
print ("\n"+"STATS RESTORED BECASUE OF " +str(item.name))
elif item.itype == "Attack":
Enemy1.get_dmg(item.prop)
print ("You used a Bomb & that dealt damage of: " + str(item.prop))
enemy_choice = 1
enemy_dmg = Enemy1.gen_dmg()
Player1.get_dmg(enemy_dmg)
print("========================================")
print("\n")
print ("ENEMY ATTACKED YOU FOR " + str(enemy_dmg) + " POINTS")
print ("ENEMY HP: "+str(Enemy1.get_hp()) +'/'+ str(Enemy1.get_maxhp()))
if Enemy1.get_hp() == 0:
print('')
print ('ENEMY DEAD')
run = False
elif Player1.get_hp() == 0:
print('')
print('YOU DIED')
run = False
elif index == 3:
print("Arigato Gozaimasu for playing")
time.sleep(1)
print ("BYE BYE")
run = False
invin.py
class Item:
def __init__(self, name,itype,desc,prop):
self.name = name
self.itype = itype
self.desc = desc
self.prop = prop
magic.py
import random
class Spell():
def __init__(self, name, cost,dmg,stype):
self.name = name
self.cost = cost
self.dmg = dmg
self.stype = stype
def gen_spell_dmg(self):
low = self.dmg - 15
high = self.dmg + 10
return random.randrange(low,high)
game.py
# -*- coding: utf-8 -*-
from .magic import Spell
import random
class player:
def __init__(self,name, hp , mp , atk ,df ,magic,items):
self.hp = hp
self.name = name
self.items = items
self.mp = mp
self.magic = magic
self.df = df
self.maxhp = hp
self.atkH = atk + 25
self.atkL= atk - 10
self.actions=['Attack',"Magic","Items"]
self.maxmp = mp + 10
def gen_dmg(self):
return random.randrange(self.atkL,self.atkH)
def get_dmg(self,dmg):
self.hp -= dmg
if self.hp < 0:
self.hp = 0
return self.hp
def get_hp(self):
return self.hp
def get_maxhp(self):
return self.maxhp
def get_max_mp(self):
return self.maxmp
def get_mp(self):
return self.mp
def reduce_mp(self,cost):
self.mp -= cost
def spell_cost(self, i):
return self.magic[i]["cost"]
def chose_action(self):
print ("Actions")
print ("===========")
i = 1
for item in self.actions:
print(" " + str(i)+":" + str(item))
i += 1
def chose_magic(self):
print ("Spells")
print ("===========")
i = 1
for spell in self.magic:
print (" " + str(i) + ": " + str(spell.name) + str( " (Cost: " + str ( spell.cost ) +")" ) )
#the 3rd str helps to print it without the brackets
i += 1
def chose_item(self):
print ("Items")
print ("===========")
i = 1
for item in self.items:
print(" " + str(i) + ": " +
str(item['item'].name) + str(" (" + str(item['item'].desc) + ") ") + " (x"+str(item['quantity'])+")")
#the 3rd str helps to print it without the brackets
i += 1
def heal(self,dmg):
self.hp += dmg
def get_stats(self):
hp_bar = ''
bar_ticks = ( (self.hp/self.maxhp) * 100 ) / 4
mp_bar = ''
mp_bar_ticks = ( (self.mp/self.maxmp) * 100 ) / 10
while bar_ticks > 0:
hp_bar += '█'
bar_ticks -= 1
while len(hp_bar) < 25:
hp_bar = " "
while mp_bar_ticks > 0:
mp_bar += '▒'
mp_bar_ticks -= 1
while len(mp_bar) < 10:
mp_bar = " "
hp_string = str(self.hp) + "/"+str(self.maxhp)
current_hp = ''
if len(hp_string) < 9:
decreased = 9 - len(hp_string)
while decreased > 0:
current_hp += ' '
decreased -= 1
current_hp += hp_string
else:
current_hp = hp_string
mp_string = str(self.mp) +"/"+str(self.maxmp)
current_mp = ''
if len(mp_string) < 9:
mp_decreased = 9 - len(mp_string)
while mp_decreased > 0:
current_mp += ' '
mp_decreased -= 1
current_mp += mp_string
else:
current_mp = mp_string
print(" _________________________ __________")
print(str(self.name) + " " + str(hp_string) +
" |"+ hp_bar+"| " + str(mp_string) + " |"+mp_bar+"|")
The game is an RPG replica and it works by using while loop.
Each player gets a turn and then the player stats are shown after all 3 players have attacked.
This is how the loop should show player stats after all 3 players attacked
But I'm getting this
If we compare the "This is how the loop should show player stats after all 3 players attacked" and "But I'm getting this" screenshots, we can see, by looking at the code, that the issue is caused on the second run of player.get_stats(). This method is defined in the game.py file.
Inside the method we can see the following 2 lines of code:
while len(hp_bar) < 25:
hp_bar = " "
If the while-loop ever gets to run, it will be stuck forever. This is because if len(hp_bar) < 25 is True, the code does hp_bar = " ", which in turns makes len(hp_bar) to be equal to 1 now. This now gets the while loop to check if len(hp_bar) < 25 again, which returns True (as len(hp_bar) is 1) so the while-loop runs again. This creates an endless loop.
The title is terrible, but hopefully I can explain in my post. Creating a little game as my pet project for python, and I'm currently creating the inventory. Everything was... ok when developing the game until it came to making the function that will show all of the player's inventory.
elif (prompt == "examine"):
print(inventory[1].name)
gameprompt()
Ok, so I created a list that basically has a bunch of classes from Items in it. To call on the name element of these classes I have to do something like this, otherwise I just get its memory location which is largely useless to the player. I've tried
elif (prompt == "examine"):
print(inventory[].name)
gameprompt()
Thought that this above example would print only the name of all the Item objects, but there's a compilation error instead because I didn't specify which one. So I then tried~
elif (prompt == "examine"):
print(inventory[1:1000].name)
gameprompt()
Thinking that it would print all of the Item objects names up to 1000, but I obviously don't have that so I thought it would print the names up to the latest object that was there and stop but there was another compilation error from this...
If there is anyway to print out an element of a class for all class objects in a list please let me know. The full code of this game is here, although I don't think you'll need it to help me solve my problem (it is also very large.)
playername = input("What is your name?")
zone = 1
movement = 0
restcounter = 0
searchcounter = 0
class Player:
def __init__(self, name, hp, mp, atk, xp, dodgerate, atkrate):
self.name = playername
self.hp = hp
self.mp = mp
self.atk = atk
self.xp = xp
self.dodgerate = dodgerate
self.atkrate = atkrate
class Enemy(Player):
def __init__(self, name, gold, maxhp, hp, mp, atk, xp):
self.name = name
self.gold = gold
self.maxhp = maxhp
self.hp = hp
self.mp = mp
self.atk = atk
self.xp = xp
class Items:
def __init__(self, name, quantity, description, price, weight):
self.name = name
self.quantity = quantity
self.description = description
self.price = price
self.weight = weight
Player = Player(playername, 1, 1, 1, 1, 25, 3)
print(Player.name + " has been created. ")
def raceselection():
raceinput = input("Do you float towards the TEMPLE, CAVE or FOREST?")
if raceinput == "TEMPLE":
print("You are now a high elf. High elves utlize a lot of magical power at the cost of being very frail.")
Player.hp = Player.hp + 24
Player.mp = Player.mp + 100
Player.atk = Player.atk + 50
print("You awaken from your slumber. Your room's walls are gold plated, and you rested on a flat board.")
print("Out the door, you see many elves with robes praying to some goddess.")
print("You walk out of your door and into the praying area. You are immediately greeted by a tall man.")
elif raceinput == "CAVE":
print("You are now an orc.")
Player.hp = Player.hp + 1000
Player.mp = Player.mp + 15
Player.atk = Player.atk + 50
print("cave")
elif raceinput == "FOREST":
print("You are now a human.")
Player.hp = Player.hp + 50
Player.mp = Player.mp + 25
Player.atk = Player.atk + 25
else:
print("You can't float there!")
raceselection()
raceselection()
inventory = []
def gameprompt():
global inventory
global zone
global movement
global restcounter
global searchcounter
if (movement == 5):
movement = movement - movement
zone = zone + 1
print("You have advanced to zone",zone,"!!!")
gameprompt()
if (zone == 1):
print("Welcome to the first zone! Easy enemies are here with not very good loot./fix grammar, add description of zone/")
elif (zone == 2):
print("Hey, it actually travelled to the second zone, awesome!")
elif (zone == 3):
print("No way would this actually work!")
prompt = input("Would you like to walk, search or rest?: ")
if (prompt == "walk"):
encounterchance = random.randint(1, 3)
if (encounterchance == 2):
if (zone == 1):
mobspawnrate = random.randint(1,3)
if (mobspawnrate == 1):
Enemy = Enemy("Blue SlimeBall", 50, 0, 25, 15, 25, 0.500)
print("You have encountered a " + Enemy.name + "!!!")
elif (mobspawnrate == 2):
Enemy = Enemy("Blue SlimeBall", 50, 0, 25, 15, 25, 0.500)
print("You have encountered a " + Enemy.name + "!!!")
elif (mobspawnrate == 3):
Enemy = Enemy("Blue SlimeBall", 50, 0, 25, 15, 25, 0.500)
print("You have encountered a " + Enemy.name + "!!!")
else:
movement = movement + 1
print("You have walked a step. You are now at ",movement," steps")
gameprompt()
elif (prompt == "search"):
if (searchcounter == 3):
print("You cannot search this area anymore! Wait until you reach the next zone!")
gameprompt()
else:
searchchance = random.randint(1, 5)
if (searchchance == 1 or 2 or 3 or 4):
searchcounter = searchcounter + 1
print(searchcounter)
print("You have found something!")
searchchance = random.randint(1,4)
if (searchchance == 1 or 2):
inventory.append(Items("Old Boot", 1, "An old smelly boot. It's a mystery as to who it belongs to...", 5, 50))
print("You have found a Boot!")
print(inventory)
elif(searchchance == 3):
inventory.append(Items("Shiny Boot", 1, "Looks like a boot that was lightly worn. You could still wear this.", 5, 50))
print(inventory)
print("You have found a Shiny Boot!")
elif(searchchance == 4):
inventory.append(Items("Golden Boot", 1, "It's too heavy to wear, but it looks like it could sell for a fortune!", 5, 50))
print("You have found a Golden Boot?")
print(inventory)
else:
searchcounter = searchcounter + 1
print(searchcounter)
print("You did not find anything of value")
gameprompt()
elif (prompt == "rest"):
if (restcounter == 1):
print("Wait until you reach the next zone to rest again!")
gameprompt()
else:
# Add a MaxHP value to the player later, and the command rest will give 25% of that HP back.
Player.hp = Player.hp + (Player.hp / 5)
print("You have restored ",(Player.hp / 5)," hit points!")
restcounter = restcounter + 1
gameprompt()
elif (prompt == "examine"):
print(inventory[1].name)
gameprompt()
gameprompt()
A list comprehension or map would work perfectly here:
print([item.name for item in inventory])
The comprehension iterates the list, and "replaces" each element in the list with whatever the part before for evaluates to. In this case, it's item.name.
° It actually doesn't replace the element in the original list. It evaluates to a new list full of replaced items.
I have been making a text-based game. I have gotten to a point where I am trying to do a battle. I have the enemy's hp going down but when I hit it again, the enemy's hp is back to what I had originally set it as. I hope these help.
while do != 'hit rat with sword' or 'run away':
enemyhp= 50
enemyattack= [0,1,3]
OldSwordattack= [0,1,4]
print('What do you do?(attack or run away)')
do=input()
if do == 'run away':
print('You can\'t leave mom like that!')
if do == 'hit rat with sword':
hit= random.choice(OldSwordattack)
if hit == OldSwordattack[0]:
print('Your swing missed')
print('Enemy\'s HP=' + str(enemyhp))
if hit == OldSwordattack[1]:
print('Your swing hit, but very lightly.')
enemyhp= enemyhp - 1
print('Enemy\'s HP=' + str(enemyhp))
if hit == OldSwordattack[2]:
print('Your swing hit head on!')
enemyhp= enemyhp - 4
print('Enemy\'s HP=' + str(enemyhp))
>In front of mom you see a giant, yellow-teethed rat.
>What do you do?(attack or run away)
>hit rat with sword
>Your swing hit, but very lightly.
>Enemy's HP=49
>What do you do?(attack or run away)
>hit rat with sword
>Your swing hit, but very lightly.
>Enemy's HP=49
>What do you do?(attack or run away)
You see? The above is the program running. I do not see why the value is not being changed.
because you are initializing it at the beginning of the iteration. So just move the
enemyhp= 50
before the while loop, like this
enemyhp= 50
while do != 'hit rat with sword' or 'run away':
Just for fun I've extended your example program to include some more advanced language constructs. May you find it entertaining: (Python 3)
import random
import re
def dice(s, reg=re.compile('(\d+d\d+|\+|\-|\d+)')):
"""
Executes D+D-style dice rolls, ie '2d6 + 3'
"""
total = 0
sign = 1
for symbol in reg.findall(s):
if symbol == '+':
sign = 1
elif symbol == '-':
sign = -1
elif 'd' in symbol:
d,s = [int(k) for k in symbol.split('d')]
for i in range(d):
total += sign * random.randint(1, s)
else:
total += sign * int(symbol)
return total
class Character():
def __init__(self, name, attack, defense, health, weapon):
self.name = name
self.attack = attack
self.defense = defense
self.health = health
self.weapon = weapon
def hit(self, target):
if self.is_dead:
print('{} is trying to become a zombie!'.format(self.name))
return 0
attack = self.attack + self.weapon.attack_bonus + dice('2d6')
defense = target.defense + dice('2d6')
if attack > defense:
damage = int(random.random() * min(attack - defense + 1, self.weapon.damage + 1))
target.health -= damage
print('{} does {} damage to {} with {}'.format(self.name, damage, target.name, self.weapon.name))
return damage
else:
print('{} misses {}'.format(self.name, target.name))
return 0
#property
def is_dead(self):
return self.health <= 0
class Weapon():
def __init__(self, name, attack_bonus, damage):
self.name = name
self.attack_bonus = attack_bonus
self.damage = damage
def main():
name = input("So, what's your name? ")
me = Character(name, 4, 6, 60, Weapon('Old Sword', 2, 4))
mom = Character('Mom', 2, 2, 50, Weapon('Broom', 0, 1))
rat = Character('Crazed Rat', 5, 2, 40, Weapon('Teeth', 1, 2))
print("You are helping your mother clean the basement when a Crazed Rat attacks. "
"You grab your Grandfather's sword from the wall and leap to defend her!")
actors = {me, mom, rat}
coward = False
killer = None
while (me in actors or mom in actors) and rat in actors:
x = random.choice(list(actors))
if x is mom:
mom.hit(rat)
if rat.is_dead:
killer = mom
actors -= {rat}
elif x is rat:
target = random.choice(list(actors - {rat}))
rat.hit(target)
if target.is_dead:
actors -= {target}
if target is mom:
print('Your mother crumples to the floor. AARGH! This rat must die!')
me.attack += 2
else:
print('Well... this is awkward. Looks like Mom will have to finish the job alone...')
else: # your turn!
print('Me: {}, Mom: {}, Rat: {}'.format(me.health, 0 if mom.is_dead else mom.health, rat.health))
do = input('What will you do? [hit] the rat, [run] away, or [swap] weapons with Mom? ')
if do == 'hit':
me.hit(rat)
if rat.is_dead:
killer = me
actors -= {rat}
elif do == 'run':
if me.health < 20:
actors -= {me}
coward = True
else:
print("Don't be such a baby! Get that rat!")
elif do == 'swap':
me.weapon, mom.weapon = mom.weapon, me.weapon
print('You are now armed with the {}'.format(me.weapon.name))
else:
print('Stop wasting time!')
if rat.is_dead:
if mom.is_dead:
print('The rat is gone - but at a terrible cost.')
elif me.is_dead:
print("Your mother buries you with your trusty sword by your side and the rat at your feet.")
elif coward:
print("The rat is gone - but you'd better keep running!")
elif killer is me:
print("Hurrah! Your mother is very impressed, and treats you to a nice cup of tea.")
else:
print('Your mother kills the rat! Treat her to a nice cup of tea before finishing the basement.')
else:
print('I, for one, hail our new rat overlords.')
if __name__=="__main__":
main()