# Ты заявляешь следущее:
# "метод udar должен вызываться на экземпляре объекта (self) с двумя параметрами: hp и dmg"
def udar(self,hp,dmg):
print("Наносит урон с руки")
# при этом метод не использует переданные параметры?
rep = self.hp - self.dmg # почему self.dmg? Боец бьёт себя?
return "Осталось здоровья: " + rep
# а ниже у тебя:
# метод udar() фактически вызывается с одним параметром: другим бойцом
boec.udar(boec2)
Твоя проблема та же, что и всех новичков: ты не озаботился сформулировать обязанности метода,
что должен делать это метод с точки зрения решаемой программой задачи.
Например, сейчас у тебя путаница: метод имитирует нанесение удара бойцом self по бойцу, переданному как параметр? Или же наоборот, удар бойцом, переданным как параметр, по бойцу self? И уж точно не нужно прикручивать сюда ещё и вывод текста в консоль, это посторонняя обязанность, не относящаяся к классу бойца.
Вот моя идея (может, несколько усложнённая, но, надеюсь, понятная):
class Fighter:
def __init__(self, name: str, hp: int, damage: int): # указывай типы параметров - так проще понять, что нужно методу
"""Конструктор инициализирует экземпляр бойца заданными значениями."""
self.name: str = name
self.hp: int = hp
self.damage: int = damage
def is_defeated(self) -> bool:
"""Возвращает True, если боец не может более сражаться."""
return self.hp <= 0 # в простейшем случае, если у бойца не осталось HP
def calculate_attack_on(self, other: 'Fighter') -> int:
"""Метод определяет, сколько урона наш боец (self) нанесёт бойцу other. Возвращает число очков урона."""
# сейчас урон всегда одинаков, но потом тут можно будет прикрутить что-то посложнее
# например, рандомный урон в диапазоне, или бонусы/малусы против конкретных бойцов, или ещё что
return self.damage
def receive_damage(self, damage: int) -> int:
"""Метод определяет, сколько HP наш боец (self) потеряет, получив урон damage, и уменьшает его здоровье. Возвращает число фактически потерянных очков здоровья."""
# сейчас метод просто вычитает урон из очков здоровья.
# но потом при желании можно добавить, например, механику "последний шанс",
# когда первый "смертельный" удар оставляет бойца на 1 хп. Или сопротивление/уязвимость к урону, или ещё что.
lost = min(self.hp, damage) # если у нас 1 очко здоровья, мы не можем потерять 10
self.hp -= lost
return lost
def attack_other(self, target: 'Fighter') -> int:
"""Проводит атаку нашего бойца (self) по другому бойцу (target). Возвращает нанесённый урон."""
attack_damage = self.calculate_attack_on(target)
actual_damage = target.receive_damage(attack_damage)
return actual_damage
Как видишь, у каждого метода есть свой достаточно чётко очерченный круг обязанностей. Хотя, конечно, можно придраться и сказать, что receive_damage() делает две вещи и его стоит разбить на два метода.
Названия методов должны однозначно указывать, что они делают. Например, udar ничего не говорит о том, кто кого ударяет, тогда как attack_other намекает, что атаку производит тот, у кого вызвали метод, в адрес того, кого передали как параметр метода. То же самое касается параметров методов.
Весь вывод вынесен из класса, это не его обязанность:
boec = Fighter("вася",200,22)
boec2 = Fighter("bob",100,19)
print(f'{boec.name}: {boec.hp} HP; {boec2.name}: {boec2.hp} HP')
dmg = boec.attach_other(boec2)
print(f'{boec.name} нанёс {dmg} урона {boec2.name}')
print(f'{boec.name}: {boec.hp} HP; {boec2.name}: {boec2.hp} HP')