cheaprpg/cheaprpg.py

341 lines
6.5 KiB
Python
Executable File

#!/usr/bin/env python3
import time
import random
import sys
import subprocess
class Unit:
def __init__(self):
self.title = 'Default Unit'
self.hp = 100
self.hp_max = 100
self.damage_min = 10
self.damage_max = 15
self.effects = []
self.abilities = []
def add_effect(self, effect):
if self._can_effect_be_added(effect):
self.effects.append(
{'effect': effect, 'lifetime': 0}
)
def add_damage(self, damage):
self.hp -= damage
if self.hp < 0:
self.hp = 0
def remove_effect_set(self, effect):
self.effects.remove(effect)
def _can_effect_be_added(self, effect):
count = 0
for e in self.effects:
if e['effect'].id == effect.id:
count += 1
if count > effect.stags_max:
return False
return True
class Mage(Unit):
def __init__(self):
Unit.__init__(self)
self.mana = 100
self.mana_max = 100
class Player(Mage):
def __init__(self):
Mage.__init__(self)
self.target = None
self.mana = 400
self.mana_max = 400
class Ability:
def __init__(self, caster):
self.caster = caster
self.title = 'Default Ability'
self.mana_cost = 0
self.error_message = ''
def cast(self) -> bool:
raise Exception('Cast method must be implemented!')
class DaggerImpact(Ability):
def __init__(self, caster):
Ability.__init__(self, caster)
self.title = 'Klingenstoß'
self.mana_cost = 150
self.damage = 60
def cast(self) -> bool:
if self.caster.target is None:
self.error_message = 'Kein Ziel ausgewählt!'
return False
self.caster.target.hp -= self.damage
self.caster.target.add_effect(EffectBleedOut())
return True
class Ui:
def show_enemies(self, enemies):
raise Exception('Method must be implemented!')
class Cli(Ui):
def __init__(self):
Ui.__init__(self)
def clear_screen(self):
if sys.platform == 'win32':
subprocess.call(['cls'])
else:
subprocess.call(['clear'])
def show_enemies(self, player, enemies):
print()
print('Vor dir stehen:')
for e in enemies:
name = '\t%s (%d/%d)' % (e.title, e.hp, e.hp_max)
if e == player.target:
name = ' >' + name
print(name)
def show_message_board(self, *lines):
ornament = '-=W=-'
width = len(ornament) + 2
for l in lines:
length = len(l)
if length > width:
width = length
if width % 2 == 0:
width += 1
side = int((width - len(ornament) + 2) / 2)
print()
print('o' + ('-' * side) + ornament + ('-' * side) + 'o')
for l in lines:
print('| ' + l.ljust(width, ' ') + ' |')
print('o' + ('-' * (width + 2)) + 'o')
def show_player_status(self, player):
self.show_message_board(
'HP: %d/%d Mana: %d/%d' % (player.hp, player.hp_max, player.mana, player.mana_max)
)
def make_choice(self, choices = [], callbacks = []):
if len(choices) != len(callbacks):
raise Exception('Number of callbacks must equal the number of choices!')
print()
for i,c in enumerate(choices):
print('[%d] %s' % (i + 1, c))
print()
choice = 0
while choice == 0:
choice = int(input('Triff deine Wahl: '))
if choice < 1 or choice > len(choices):
choice = 0
else:
callbacks[choice - 1]()
return
class Effect:
def __init__(self):
self.id = 0
self.title = 'Effect'
self.stags_max = 1
self.lifetime = 0
def apply_to_unit(self, unit) -> str:
raise Exception('This abstract method must be implemented!')
return 'This abstract method must be implemented!'
class EffectBleedOut(Effect):
def __init__(self):
Effect.__init__(self)
self.id = 1
self.title = 'Blutung'
self.amount = 3
self.lifetime = 5
self.stags_max = 5
def apply_to_unit(self, unit):
unit.hp -= self.amount
class Fight:
def __init__(self, ui, player):
self.ui = ui
self.player = player
self.enemies = []
self.is_running = True
def add_enemy(self, enemy):
self.enemies.append(enemy)
def start(self):
while len(self.enemies) > 0:
self.ui.clear_screen()
if not self.is_running:
print('Feigling!')
return
self.ui.show_player_status(self.player)
self.ui.show_enemies(self.player, self.enemies)
choices = []
callbacks = []
choices.append('Gegner wählen')
callbacks.append(self.choose_enemy)
if self.player.target is not None:
choices.append('Angreifen')
callbacks.append(self.attack_target)
choices.append('Fähigkeit wirken')
callbacks.append(self.choose_ability)
choices.append('Weglaufen')
callbacks.append(self.quit)
ui.make_choice(choices, callbacks)
print('Du warst siegreich.')
def enemies_attack(self):
for e in self.enemies:
damage = random.randint(e.damage_min, e.damage_max)
self.player.add_damage(damage)
print('%s greift an und verursacht %d Schaden.' % (e.title, damage))
def quit(self):
self.is_running = False
def attack_target(self):
damage = random.randint(self.player.damage_min, self.player.damage_max)
self.player.target.add_damage(damage)
print('Du hast %d Schaden an %s verursacht.' % (damage, self.player.target.title))
def choose_enemy(self):
choices = []
callbacks = []
for e in self.enemies:
choices.append('%s (%d/%d)' % (e.title, e.hp, e.hp_max))
callbacks.append(lambda e=e: self.set_player_target(e))
ui.make_choice(choices, callbacks)
def set_player_target(self, target):
self.player.target = target
print('%s ist jetzt dein Ziel.' % (target.title))
def choose_ability(self):
names = []
callbacks = []
for a in self.player.abilities:
names.append('%s (%d Mana)' % (a.title, a.mana_cost))
callbacks.append(lambda a=a: self.cast_ability(a))
self.ui.make_choice(names, callbacks)
def cast_ability(self, ability):
if ability.mana_cost > self.player.mana:
print('Nicht genug Mana!')
return
ability.cast()
self.player.mana -= ability.mana_cost
print('Wirke %s.' % (ability.title))
if __name__ == '__main__':
ui = Cli()
player = Player()
player.abilities = [
DaggerImpact(player)
]
dog = Unit()
dog.title = 'Räudiger Hund'
dog.hp = 20
dog.hp_max = 20
dragon = Unit()
dragon.title = 'Feuerdrache'
dragon.hp = 800
dragon.hp_max = 800
orc = Unit()
orc.title = 'Dämlicher Ork'
orc.hp = 350
orc.hp_max = 350
fight = Fight(ui, player)
fight.add_enemy(dog)
fight.add_enemy(dragon)
fight.add_enemy(orc)
fight.start()
u = Unit()
u.add_effect(EffectBleedOut())
while u.hp > 0:
to_be_removed = []
for e in u.effects:
e['effect'].apply_to_unit(u)
e['lifetime'] += 1
if e['lifetime'] == e['effect'].lifetime:
to_be_removed.append(e)
for r in to_be_removed:
print(r['effect'].title, 'klingt ab.')
u.remove_effect_set(r)
if u.hp < 0:
u.hp = 0
print(u.hp, end='')
time.sleep(1)