1
0

Compare commits

...

18 Commits

Author SHA1 Message Date
Kwarde
907581eb29
Once again, set MAX_LEVENSHTEIN_DISTANCE to 3 in game_config.py
No levenshtein installed on my local PC (except via pipenv), but I run tests via vscode without pipenv so I keep disabling this, and then forgetting to put it back when pushing it to dev :')
2024-01-19 07:48:11 +01:00
Kwarde
782d4f93b1
Merge pull request #18 from KW46/type-annotations
Type annotations
2024-01-19 07:44:33 +01:00
5735ab4cde Not making an issue for this: Print empty line before casting spells to make it look a bit clearer 2024-01-19 07:43:26 +01:00
e76d315832 Add type annotations for class variables and function return types 2024-01-19 07:41:38 +01:00
Kwarde
d19c467a7c
Merge pull request #17 from KW46/seperate-rounds-from-game
Seperate rounds from game
2024-01-19 07:29:35 +01:00
cb491f62a6 Update README.md: Run entry.py instead of game.py 2024-01-19 07:27:39 +01:00
f80da65977 Split game.py into game.py and entry.py, should make code better readable 2024-01-19 07:26:50 +01:00
Kwarde
be70fa4bf4
Merge pull request #16 from KW46/improvement-use-dicts
Switch to dictionaries, better imports, adds game_config.py and DEBUG_MODE
2024-01-18 17:05:09 +01:00
1dff4f524b FIX: KeyError when returning a mistyped spell with Levenshtein distance 2024-01-18 17:03:21 +01:00
fc8bee2f73 Move MAX_LEVENSHTEIN_DISTANCE to game_config.py 2024-01-18 16:57:32 +01:00
7cb65f5895 Better import usage, adds game_config.py, adds a debug mode (sets all spell chances to 100%) 2024-01-18 16:43:20 +01:00
1485f171be Make game.py compatible with the new dicts 2024-01-18 16:31:43 +01:00
aea461499c Make player.py compatible with wands and spells dicts 2024-01-18 15:30:08 +01:00
7d28ac961c Convert wands to dict 2024-01-18 07:05:31 +01:00
c4bb8f7d80 Remove tmp.py: Used it for a quick testrun since I had a confusion with the new dictionary 2024-01-17 16:52:00 +01:00
355eab297c Make spells.py compatible with the new dictionary. Also: only import distance from Levenshtein if MAX_LEVENSHTEIN_DISTANCE > 0 2024-01-17 16:50:38 +01:00
c138250c1c FIX: Actually make spell dictionary. ('key': value) and not ('key', value) 2024-01-17 16:14:04 +01:00
89318e7321 Convert spell objects to a dictionary 2024-01-17 16:06:56 +01:00
7 changed files with 355 additions and 327 deletions

View File

@ -3,4 +3,4 @@ Based the on Harry Potter franchise.
You can run the game via pipenv. If you don't, make sure package levenshtein is installed.<br> You can run the game via pipenv. If you don't, make sure package levenshtein is installed.<br>
Run `game.py` to run the game Run `entry.py` to run the game

53
entry.py Normal file
View File

@ -0,0 +1,53 @@
from game import intro_message_welcome, intro_get_username, intro_print_wands, intro_get_wand, intro_message_duel_start
from game import round_get_player_spells, round_set_player_spells_succes, round_get_player_spells_speed, round_cast_spells, start_round
from player import Player
current_round: int = 0
intro_message_welcome()
player1 = Player(None, None)
player2 = Player(None, None)
player1.name = intro_get_username(1)
player2.name = intro_get_username(2)
print("Welcome {p1_name} and {p2_name}! You're about to choose a wand to use in this duel! Available wands are:".format(p1_name=player1.name, p2_name=player2.name))
intro_print_wands()
player1.wand = intro_get_wand(player1)
player2.wand = intro_get_wand(player2)
print()
print("{name} will be fighting with an {wood} wand with a {core} core".format(name=player1.name, wood=player1.wand.get_wand_wood().lower(), core=player1.wand.get_wand_core().lower()))
print("{name} will be fighting with an {wood} wand with a {core} core".format(name=player2.name, wood=player2.wand.get_wand_wood().lower(), core=player2.wand.get_wand_core().lower()))
intro_message_duel_start()
try:
while True:
if player1.health == 0:
print("END! {name} has been defeated. Congratulations {name2}!".format(name=player1.name, name2=player2.name))
break
elif player2.health == 0:
print("END! {name} has been defeated. Congratulations {name2}!".format(name=player2.name, name2=player1.name))
break
current_round += 1
start_round(current_round, player1, player2)
round_get_player_spells(player1, player2)
round_set_player_spells_succes(player1, player2)
if not player1.active_spell_succes and not player2.active_spell_succes:
continue
if not player1.active_spell_succes and player2.active_spell_succes:
player2.cast_spell(player1)
continue
elif player1.active_spell_succes and not player2.active_spell_succes:
player1.cast_spell(player2)
continue
print()
fastest_caster, slowest_caster = round_get_player_spells_speed(player1, player2)
round_cast_spells(fastest_caster, slowest_caster)
except KeyboardInterrupt:
print()
print("<!> Duel ended because both {} and {} suddenly decided to test out their apparition skill!".format(player1.name, player2.name))

319
game.py
View File

@ -1,10 +1,11 @@
from player import * import random
from wands import * from player import Player
from wands import wands
from spells import Spell, spells, random_combat_spell, print_spells, find_spell_by_name
from spells import _INVALID_SPELL, SPELL_TYPE_COMMON, SPELL_TYPE_POWERFUL
from game_config import MIN_USERNAME_LEN, MAX_PLAYER_HEALTH
## INPUT_MESSAGES = (
## Definitions
##
input_messages = (
"{name}, what's your spell? ", "{name}, what's your spell? ",
"{name}, how will you obliviate your opponent? ", "{name}, how will you obliviate your opponent? ",
"{name}, go for it! Enter a spell! ", "{name}, go for it! Enter a spell! ",
@ -12,206 +13,160 @@ input_messages = (
"{name}, go time! ", "{name}, go time! ",
"{name}, it's your turn to enter a spell: " "{name}, it's your turn to enter a spell: "
) )
def print_turn_message(player: Player):
return random.choice(input_messages).format(name=player.name)
current_round = 0 ##
def round_end(): ## Intro functions
##
def intro_message_welcome() -> None:
print()
print("Welcome! You're about to perform a wizard duel!")
print("After joining in, you have to select a wand. Your wand will affect the power of your spells. Spells have three atrributes that modify the power of spells:")
print("1- DAMAGE: Damage can either deal damage to health points, or it can stun your a player for X amount of moves (DAMAGE below zero = amount of moves a player is stunned)")
print("2- SUCCES CHANCE: How much succes chance of performing a spell. Some spells are difficult to pronounce and thus could fail..")
print("3- SPEED: If both players succesfully cast a spell, the spell with the greatest speed will succeed and the other one will not")
print()
def intro_message_duel_start() -> None:
print("<!> If you need a list of available spells, enter: help (this will not take away a move)")
print("<!> If you need information of a specific spell, enter: help SPELL_NAME")
print("<!> You can press enter (without typing a spell) to cast a random basic combat spell")
print()
print("Alright! Time to duel!")
def intro_get_username(playerid: int) -> str:
while True:
user_input = input("Player {id} - What's your name? ".format(id=playerid))
if len(user_input) < MIN_USERNAME_LEN:
print("<!> Oops! Names must be at least {len} characters long!".format(len=MIN_USERNAME_LEN))
else: break
return user_input
def intro_print_wands() -> None:
for i in wands.items():
print("{wand_id}:{wand_desc}".format(wand_id=i[0], wand_desc=i[1]))
def intro_get_wand(player: Player) -> Spell:
while (True):
try:
user_input = int(input("What wand do you want {name}? (Enter one of the numbers): ".format(name=player.name)))
except ValueError:
continue
if user_input < 1 or user_input > len(wands):
continue
break
return wands[user_input]
##
## Game round functions
##
def start_round(round: int, player1: Player, player2: Player) -> None:
if (player1.stunned_rounds > 0): player1.stunned_rounds -= 1 if (player1.stunned_rounds > 0): player1.stunned_rounds -= 1
if (player2.stunned_rounds > 0): player2.stunned_rounds -= 1 if (player2.stunned_rounds > 0): player2.stunned_rounds -= 1
print("<!> Round {round} ended! Current stats:\n\ print()
print("== Round {round} ==".format(round=round))
print("-- Current stats:\n\
- {p1_name}: Health: {p1_hp} | Queued effects: {p1_effects} | Round being stunned: {p1_stunned}\n\ - {p1_name}: Health: {p1_hp} | Queued effects: {p1_effects} | Round being stunned: {p1_stunned}\n\
- {p2_name}: Health: {p2_hp} | Queued effects: {p2_effects} | Rounds being stunned: {p2_stunned}".format( - {p2_name}: Health: {p2_hp} | Queued effects: {p2_effects} | Rounds being stunned: {p2_stunned}".format(
round=current_round, round=round,
p1_name=player1.name, p1_hp=player1.health, p1_effects=player1.get_queued_effects(), p1_stunned=player1.stunned_rounds, p1_name=player1.name, p1_hp=player1.health, p1_effects=player1.get_queued_effects(), p1_stunned=player1.stunned_rounds,
p2_name=player2.name, p2_hp=player2.health, p2_effects=player2.get_queued_effects(), p2_stunned=player2.stunned_rounds p2_name=player2.name, p2_hp=player2.health, p2_effects=player2.get_queued_effects(), p2_stunned=player2.stunned_rounds
) )
) )
def get_player_spell_from_input(player: Player): def print_turn_message(player: Player) -> None:
return random.choice(INPUT_MESSAGES).format(name=player.name)
def get_player_spell_from_input(player: Player) -> tuple:
while True: while True:
player_input = input(print_turn_message(player)) player_input = input(print_turn_message(player))
if not player_input: if not player_input:
return [random_combat_spell(), 0] spell_name, spell_obj = random_combat_spell()
else: return ((spell_name, spell_obj), 0)
if player_input == "help":
print_spells() if player_input == "help":
continue print_spells()
elif player_input.find("help", 0) != -1: continue
find_what = player_input[5:] elif player_input.find("help", 0) != -1:
spell = find_spell_by_name(find_what)[0] spell_name, spell_obj = find_spell_by_name(player_input[5:])[0]
if spell_obj is spells[_INVALID_SPELL]:
if spell is spell_object_none: print("<!> Spell '{what}' does not exist!".format(what=player_input))
print("<!> Spell '{what}' does not exist!".format(what=find_what))
else:
print(spell)
continue
else: else:
return find_spell_by_name(player_input) print("'{spell_name}':{spell_desc}".format(spell_name=spell_name, spell_desc=spell_obj))
else:
## return find_spell_by_name(player_input)
## ENTRY
##
print()
print("Welcome! You're about to perform a wizard duel!")
print("After joining in, you have to select a wand. Your wand will affect the power of your spells. Spells have three atrributes that modify the power of spells:")
print("1- DAMAGE: Damage can either deal damage to health points, or it can stun your a player for X amount of moves (DAMAGE below zero = amount of moves a player is stunned)")
print("2- SUCCES CHANCE: How much succes chance of performing a spell. Some spells are difficult to pronounce and thus could fail..")
print("3- SPEED: If both players succesfully cast a spell, the spell with the greatest speed will succeed and the other one will not")
print()
#GET: USERNAMES
while True:
player1_name = input("Player 1 - What's your name? ")
player2_name = input("And now player 2 - What's your name? ")
if len(player1_name) < 2 or len(player2_name) < 2:
print("<!> Oops! Names must be at least 2 characters long! Please try again")
else: break
#GET: WANDS
print("Welcome {p1_name} and {p2_name}! You're about to choose a wand to use in this duel! Available wands are:".format(p1_name=player1_name, p2_name=player2_name))
for i in Wand.wandList:
print(i)
#Player 1
while (True):
try:
wand_input = int(input("What wand do you want {name}? (Enter one of the numbers): ".format(name=player1_name)))
except ValueError:
continue
if wand_input < 1 or wand_input > len(Wand.wandList):
continue
player1_wand = Wand.wandList[wand_input-1]
break
#Player 2
while (True):
try:
wand_input = int(input("What wand do you want {name}? (Enter one of the numbers): ".format(name=player2_name)))
except ValueError:
continue
if wand_input < 1 or wand_input > len(Wand.wandList):
continue
player2_wand = Wand.wandList[wand_input-1]
break
player1 = Player(player1_name, player1_wand)
player2 = Player(player2_name, player2_wand)
print()
print("{name} will be fighting with an {wood} wand with a {core} core".format(name=player1.name, wood=player1.wand.get_wand_wood().lower(), core=player1.wand.get_wand_core().lower()))
print("{name} will be fighting with an {wood} wand with a {core} core".format(name=player2.name, wood=player2.wand.get_wand_wood().lower(), core=player2.wand.get_wand_core().lower()))
print("<!> If you need a list of available spells, enter: help (this will not take away a move)")
print("<!> If you need information of a specific spell, enter: help SPELL_NAME")
print("<!> You can press enter (without typing a spell) to cast a random basic combat spell")
print()
print("Alright! Time to duel!")
game_running = True
try:
while (game_running):
current_round += 1
if (current_round != 1):
# Weird, right? To have round_end() at the start of a round.
# There will be multiple conditions where the current iteration will end.
# I'm lazy, hence why it's here :-)
if player1.health == 0:
print("END! {name} has been defeated. Congratulations {name2}!".format(name=player1.name, name2=player2.name))
game_running = False
break
elif player2.health == 0:
print("END! {name} has been defeated. Congratulations {name2}!".format(name=player2.name, name2=player1.name))
game_running = False
break
round_end()
print("== Round {round} ==".format(round=current_round)) def round_get_player_spells(player1: Player, player2: Player) -> None:
player1.active_spell, player1.active_spell_levenshtein_distance = get_player_spell_from_input(player1) spell, dist = get_player_spell_from_input(player1)
player2.active_spell, player2.active_spell_levenshtein_distance = get_player_spell_from_input(player2) player1.active_spell = spells.get(spell[0])
player1.active_spell_levenshtein_distance = dist
if (player1.stunned_rounds > 0 and player1.active_spell is not spell_finite_incantatem): spell, dist = get_player_spell_from_input(player2)
player1.active_spell = spell_object_stunned player2.active_spell = spells.get(spell[0])
if (player2.stunned_rounds > 0 and player2.active_spell is not spell_finite_incantatem): player2.active_spell_levenshtein_distance = dist
player2.active_spell = spell_object_stunned
# OUTCOME: SPELLS if (player1.stunned_rounds > 0 and player1.active_spell is not spells["Finite Incantatem"]):
# > Get spell succes player1.active_spell = None
player1.active_spell_succes = player1.get_spell_succes_rate(player1.active_spell) > random.random() * 100 if (player2.stunned_rounds > 0 and player2.active_spell is not spells["Finite Incantatem"]):
player2.active_spell_succes = player2.get_spell_succes_rate(player2.active_spell) > random.random() * 100 player2.active_spell = None
print(player1.cast_spell_result(player1.active_spell, player1.active_spell_succes)) def round_set_player_spells_succes(player1: Player, player2: Player) -> None:
print(player2.cast_spell_result(player2.active_spell, player2.active_spell_succes)) player1.active_spell_succes = player1.get_spell_succes_rate(player1.active_spell) > random.random() * 100
player2.active_spell_succes = player2.get_spell_succes_rate(player2.active_spell) > random.random() * 100
print(player1.cast_spell_result(player1.active_spell, player1.active_spell_succes))
print(player2.cast_spell_result(player2.active_spell, player2.active_spell_succes))
# > Add health if defensive spell was lucky (partial heal, fully heal) if player1.active_spell_succes and player1.active_spell.chance_heal_partly_succes():
if player1.active_spell_succes and player1.active_spell.chance_heal_partly_succes(): player1.give_health(MAX_PLAYER_HEALTH * 0.05)
player1.give_health(MAX_PLAYER_HEALTH * 0.05) print("<!> {name} casted {spell} above expectations, and receives {hp} health!".format(name=player1.name, spell=player1.active_spell.get_spell_name(), hp=MAX_PLAYER_HEALTH*0.05))
print("<!> {name} casted {spell} above expectations, and receives {hp} health!".format(name=player1.name, spell=player1.active_spell.name, hp=MAX_PLAYER_HEALTH*0.05)) elif player1.active_spell_succes and player1.active_spell.chance_heal_fully_succes() and player1.health < MAX_PLAYER_HEALTH:
elif player1.active_spell_succes and player1.active_spell.chance_heal_fully_succes() and player1.health < MAX_PLAYER_HEALTH: player1.give_health(MAX_PLAYER_HEALTH)
player1.give_health(MAX_PLAYER_HEALTH) print("<!> {name} casted {spell} outstanding! {name} now has full health!".format(name=player1.name, spell=player1.active_spell.get_spell_name(), hp=MAX_PLAYER_HEALTH*0.05))
print("<!> {name} casted {spell} outstanding! {name} now has full health!".format(name=player1.name, spell=player1.active_spell.name, hp=MAX_PLAYER_HEALTH*0.05)) #
# if player2.active_spell_succes and player2.active_spell.chance_heal_partly_succes():
if player2.active_spell_succes and player2.active_spell.chance_heal_partly_succes(): player2.give_health(MAX_PLAYER_HEALTH * 0.05)
player2.give_health(MAX_PLAYER_HEALTH * 0.05) print("<!> {name} casted {spell} above expectations, and receives {hp} health!".format(name=player2.name, spell=player2.active_spell.get_spell_name(), hp=MAX_PLAYER_HEALTH*0.05))
print("<!> {name} casted {spell} above expectations, and receives {hp} health!".format(name=player2.name, spell=player2.active_spell.name, hp=MAX_PLAYER_HEALTH*0.05)) elif player2.active_spell_succes and player2.active_spell.chance_heal_fully_succes() and player2.health < MAX_PLAYER_HEALTH:
elif player2.active_spell_succes and player2.active_spell.chance_heal_fully_succes() and player2.health < MAX_PLAYER_HEALTH: player2.give_health(MAX_PLAYER_HEALTH)
player2.give_health(MAX_PLAYER_HEALTH) print("<!> {name} casted {spell} outstanding! {name} now has full health!".format(name=player2.name, spell=player2.active_spell.get_spell_name(), hp=MAX_PLAYER_HEALTH*0.05))
print("<!> {name} casted {spell} outstanding! {name} now has full health!".format(name=player2.name, spell=player2.active_spell.name, hp=MAX_PLAYER_HEALTH*0.05))
# > Determine instant winner or skip to next round def round_get_player_spells_speed(player1: Player, player2: Player) -> tuple:
if not player1.active_spell_succes and not player2.active_spell_succes: player1.active_spell_speed = player1.active_spell.speed * player1.wand.speed
continue player2.active_spell_speed = player2.active_spell.speed * player2.wand.speed
if not player1.active_spell_succes and player2.active_spell_succes:
player2.cast_spell(player1)
continue
elif player1.active_spell_succes and not player2.active_spell_succes:
player1.cast_spell(player2)
continue
# > Determine fastest spell if player1.decreased_spell_speed and (player1.active_spell.type == SPELL_TYPE_COMMON or player1.active_spell.type == SPELL_TYPE_POWERFUL):
player1.active_spell_speed = player1.active_spell.speed * player1.wand.speed print("<!> {name} is slowed, spell speed decreased by 33%!".format(name=player1.name))
player2.active_spell_speed = player2.active_spell.speed * player2.wand.speed player1.active_spell_speed *= 0.67
player1.decreased_spell_speed = False
if player1.decreased_spell_speed and (player1.active_spell.type == SPELL_TYPE_COMMON or player1.active_spell.type == SPELL_TYPE_POWERFUL): if player2.decreased_spell_speed and (player2.active_spell.type == SPELL_TYPE_COMMON or player2.active_spell.type == SPELL_TYPE_POWERFUL):
print("<!> {name} is slowed, spell speed decreased by 33%!".format(name=player1.name)) print("<!> {name} is slowed, spell speed decreased by 33%!".format(name=player2.name))
player1.active_spell_speed *= 0.67 player2.active_spell_speed *= 0.67
player1.decreased_spell_speed = False player2.decreased_spell_speed = False
if player2.decreased_spell_speed and (player2.active_spell.type == SPELL_TYPE_COMMON or player2.active_spell.type == SPELL_TYPE_POWERFUL):
print("<!> {name} is slowed, spell speed decreased by 33%!".format(name=player2.name))
player2.active_spell_speed *= 0.67
player2.decreased_spell_speed = False
if player1.decreased_spell_damage and (player1.active_spell.type == SPELL_TYPE_COMMON or player1.active_spell.type == SPELL_TYPE_POWERFUL): if player1.decreased_spell_damage and (player1.active_spell.type == SPELL_TYPE_COMMON or player1.active_spell.type == SPELL_TYPE_POWERFUL):
print("<!> {name} is blinded, spell damage decreased by 33%!".format(name=player1.name)) print("<!> {name} is blinded, spell damage decreased by 33%!".format(name=player1.name))
if player2.decreased_spell_damage and (player2.active_spell.type == SPELL_TYPE_COMMON or player2.active_spell.type == SPELL_TYPE_POWERFUL): if player2.decreased_spell_damage and (player2.active_spell.type == SPELL_TYPE_COMMON or player2.active_spell.type == SPELL_TYPE_POWERFUL):
print("<!> {name} is blinded, spell damage decreased by 33%!".format(name=player2.name)) print("<!> {name} is blinded, spell damage decreased by 33%!".format(name=player2.name))
fastest_caster = player1 fastest_caster = player1
slowest_caster = player2 slowest_caster = player2
if player2.active_spell_speed > player1.active_spell_speed: if player2.active_spell_speed > player1.active_spell_speed:
fastest_caster = player2 fastest_caster = player2
slowest_caster = player1 slowest_caster = player1
print("- {p1_name}'s spell speed is {p1_spd}. {p2_name}'s spell speed is {p2_spd}. {fastest} is the fastest caster this round!".format( print("- {p1_name}'s spell speed is {p1_spd}. {p2_name}'s spell speed is {p2_spd}. {fastest} is the fastest caster this round!".format(
p1_name=player1.name, p1_spd=player1.active_spell_speed, p1_name=player1.name, p1_spd=player1.active_spell_speed,
p2_name=player2.name, p2_spd=player2.active_spell_speed, p2_name=player2.name, p2_spd=player2.active_spell_speed,
fastest=fastest_caster.name fastest=fastest_caster.name
)) ))
# CAST SPELLS
fastest_caster.cast_spell(slowest_caster)
if slowest_caster.health > 0:
slowest_caster.cast_spell(fastest_caster)
fastest_caster.active_spell = spell_object_none return (fastest_caster, slowest_caster)
fastest_caster.active_spell_levenshtein_distance = 0
slowest_caster.active_spell = spell_object_none
slowest_caster.active_spell_levenshtein_distance = 0
except KeyboardInterrupt: def round_cast_spells(player1: Player, player2: Player) -> None:
print() player1.cast_spell(player2)
print("<!> Duel ended because both {} and {} suddenly decided to test out their apparition skill!".format(player1.name, player2.name)) if player2.health > 0:
player2.cast_spell(player1)

18
game_config.py Normal file
View File

@ -0,0 +1,18 @@
##
## Player config
##
MIN_USERNAME_LEN = 2 # Minimum length of usernames
MAX_PLAYER_HEALTH = 1000 # Maximum health
MAX_STUNNED_ROUNDS = 10 # Max amount of rounds a player can be stunned
##
## Spells config
##
CHANCE_HEAL_PARTLY = 25 # Percentage chance a player is healed with 5% of max health when using a defensive spell
CHANCE_HEAL_FULLY = 5 # Percentage chance a player is fully healed when using a defensive spell
MAX_LEVENSHTEIN_DISTANCE = 3 # Max Levenshtein distance (max amount of typos a user can make) before considering a spell fails. Setting this to 0 disables it (and in that case also doesn't require the levenshtein package)
##
## Misc
##
DEBUG_MODE = False # Enable or disable debug mode. Sets all spell chances to 100% when enabled

View File

@ -1,46 +1,48 @@
import random
from wands import Wand from wands import Wand
from spells import * from spells import SPELL_TYPE_NONE, SPELL_TYPE_USELESS, SPELL_TYPE_DEFENSE, SPELL_TYPE_UNFORGIVABLE#, SPELL_TYPE_COMMON, SPELL_TYPE_POWERFUL
from spells import Spell, spells, _INVALID_SPELL
MAX_PLAYER_HEALTH = 1000 from game_config import MAX_PLAYER_HEALTH, MAX_STUNNED_ROUNDS
MAX_STUNNED_ROUNDS = 10
class Player: class Player:
def __init__(self, name: str, wand: Wand): def __init__(self, name: str, wand: Wand) -> None:
self.name = name self.name: str = name
self.health = MAX_PLAYER_HEALTH self.health: float = MAX_PLAYER_HEALTH
self.wand = wand self.wand: Wand = wand
self.active_spell = spell_object_none self.active_spell: Spell = spells[_INVALID_SPELL]
self.active_spell_succes = False self.active_spell_succes: bool = False
self.active_spell_levenshtein_distance = 0 # Penalty => If >0 then damage reduction, 15 per distance self.active_spell_levenshtein_distance: int = 0 # Penalty => If >0 then damage reduction, 15 per distance
self.stunned_rounds = 0 self.stunned_rounds: int = 0
self.decreased_spell_speed = False self.decreased_spell_speed: bool = False
self.decreased_spell_damage = False self.decreased_spell_damage: bool = False
self.lumos = False self.lumos: bool = False
def give_health(self, health: int): def give_health(self, health: int) -> float:
self.health += health self.health += health
if self.health > MAX_PLAYER_HEALTH: if self.health > MAX_PLAYER_HEALTH:
self.health = MAX_PLAYER_HEALTH self.health = MAX_PLAYER_HEALTH
return self.health return self.health
def take_health(self, health: int): def take_health(self, health: int) -> float:
self.health -= health self.health -= health
if self.health < 0: if self.health < 0:
self.health = 0 self.health = 0
return self.health return self.health
def add_stunned_rounds(self, rounds: int): def add_stunned_rounds(self, rounds: int) -> None:
self.stunned_rounds += rounds + 1 self.stunned_rounds += rounds + 1
if (self.stunned_rounds > MAX_STUNNED_ROUNDS + 1): if (self.stunned_rounds > MAX_STUNNED_ROUNDS + 1):
self.stunned_rounds = MAX_STUNNED_ROUNDS self.stunned_rounds = MAX_STUNNED_ROUNDS
def get_spell_succes_rate(self, spell: Spell): def get_spell_succes_rate(self, spell: Spell) -> float:
if spell == None:
return 0
return 1 * self.wand.succes_rate * spell.succes_rate return 1 * self.wand.succes_rate * spell.succes_rate
def get_queued_effects(self): def get_queued_effects(self) -> str:
output = "" output = ""
effect_slowed = "Slowed" effect_slowed = "Slowed"
effect_blinded = "Blinded" effect_blinded = "Blinded"
@ -55,58 +57,59 @@ class Player:
if not output: output = "None" if not output: output = "None"
return output return output
def cast_spell_result(self, spell: Spell, succes: bool): def cast_spell_result(self, spell: Spell, succes: bool) -> str:
if spell == None:
return "<!> {name} can't cast anything but Finite Incantatem since they are stunned".format(name=self.name)
if succes: if succes:
message = "{name} casted '{spell}' with succes" message = "{name} casted '{spell}' with succes"
else: else:
if spell.type == SPELL_TYPE_UNFORGIVABLE: if spell.type == SPELL_TYPE_UNFORGIVABLE:
message = "{name} tried to cast '{spell}' but didn't truly mean it. Cast FAILED!" message = "{name} tried to cast '{spell}' but didn't truly mean it. Cast FAILED!"
elif spell.type == SPELL_TYPE_NONE: elif spell.type == SPELL_TYPE_NONE:
if spell == spell_object_none: if spell == spells[_INVALID_SPELL]:
return "{name} must not be feeling well, since they casted a non existing spell. Cast FAILED!".format(name=self.name) return "{name} must not be feeling well, since they casted a non existing spell. Cast FAILED!".format(name=self.name)
elif spell == spell_object_stunned:
return "<!> {name} can't cast anything but {spell} since they are stunned".format(name=self.name, spell=spell_finite_incantatem.name)
else: message = "{name} tried to cast '{spell}' but mispronounced it. Cast FAILED!" else: message = "{name} tried to cast '{spell}' but mispronounced it. Cast FAILED!"
return message.format(name=self.name, spell=spell.name) return message.format(name=self.name, spell=spell.get_spell_name())
def cast_spell(self, opponent): #: Player ? def cast_spell(self, opponent) -> None:
spell_name = self.active_spell.name.lower() spell_name = self.active_spell.get_spell_name()
if self.active_spell is spell_object_none: if self.active_spell is None:
print("- {name} does nothing".format(name=self.name)) print("- {name} does nothing".format(name=self.name))
return return
if self.stunned_rounds > 0 and self.active_spell is not spell_finite_incantatem: if self.stunned_rounds > 0 and self.active_spell is not spells["Finite Incantatem"]:
print("<!> {name} tries to cast a spell but fails because they are stunned!".format(name=self.name)) print("<!> {name} tries to cast a spell but fails because they are stunned!".format(name=self.name))
return return
if self.active_spell.type == SPELL_TYPE_USELESS: if self.active_spell.type == SPELL_TYPE_USELESS:
if self.active_spell is spell_lumos: if self.active_spell is spells["Lumos"]:
if self.lumos: if self.lumos:
print("- {name} casts {spell} with no effect: {name} already has a brutal shining light at the tip of their wand!".format(name=self.name, spell=spell_name)) print("- {name} casts {spell} with no effect: {name} already has a brutal shining light at the tip of their wand!".format(name=self.name, spell=spell_name))
else: else:
print("- {name} casts {spell}! Look at that brutal shining light at the tip of their wand. Absolutely gorgeous!".format(name=self.name, spell=spell_name)) print("- {name} casts {spell}! Look at that brutal shining light at the tip of their wand. Absolutely gorgeous!".format(name=self.name, spell=spell_name))
self.lumos = True self.lumos = True
elif self.active_spell is spell_nox: elif self.active_spell is spells["Nox"]:
if not self.lumos: if not self.lumos:
print("- {name} shouts: {spell}! Nothing happened".format(name=self.name, spell=spell_name.upper())) print("- {name} shouts: {spell}! Nothing happened".format(name=self.name, spell=spell_name.upper()))
else: else:
print("- {name} shouts {spell}! Their brutal shining light dissapears".format(name=self.name, spell=spell_name.upper())) print("- {name} shouts {spell}! Their brutal shining light dissapears".format(name=self.name, spell=spell_name.upper()))
self.lumos = False self.lumos = False
elif self.active_spell is spell_rennervate: elif self.active_spell is spells["Rennervate"]:
if opponent.stunned_rounds == 0: if opponent.stunned_rounds == 0:
print("- {name} casts {spell} with no effect, because {name_o} is not currently stunned!".format(name=self.name, spell=spell_name, name_o=opponent.name)) print("- {name} casts {spell} with no effect, because {name_o} is not currently stunned!".format(name=self.name, spell=spell_name, name_o=opponent.name))
else: else:
print("- {name} casts {spell}! {name_o} is no longer stunned!".format(name=self.name, spell=spell_name, name_o=opponent.name)) print("- {name} casts {spell}! {name_o} is no longer stunned!".format(name=self.name, spell=spell_name, name_o=opponent.name))
opponent.stunned_rounds = 0 opponent.stunned_rounds = 0
elif self.active_spell is spell_igni: elif self.active_spell is spells["Igni"]:
print("- Geralt casts Igni. Wait. Geralt? White Wolf? The butcher of Blaviken? What is he doing here? Is he even here? What is going on?") print("- Geralt casts Igni. Wait. Geralt? White Wolf? The butcher of Blaviken? What is he doing here? Is he even here? What is going on?")
else: else:
print("<!> [debug]{name} casted SPELL_TYPE_USELESS {spell}. Behaviour unscripted!".format(name=self.name, spell=spell_name)) print("<!> [debug]{name} casted SPELL_TYPE_USELESS {spell}. Behaviour unscripted!".format(name=self.name, spell=spell_name))
elif self.active_spell.type == SPELL_TYPE_DEFENSE: elif self.active_spell.type == SPELL_TYPE_DEFENSE:
spell_succes = True spell_succes = True
if self.active_spell is spell_finite_incantatem: if self.active_spell is spells["Finite Incantatem"]:
if self.stunned_rounds > 0: if self.stunned_rounds > 0:
if not 10 > random.random() * 100: if not 10 > random.random() * 100:
spell_succes = False spell_succes = False
@ -125,42 +128,42 @@ class Player:
print("- {name} casts {spell}. {name_o} is looking confused. What spell did {name} try to cancel?".format(name=self.name, spell=spell_name, name_o=opponent.name)) print("- {name} casts {spell}. {name_o} is looking confused. What spell did {name} try to cancel?".format(name=self.name, spell=spell_name, name_o=opponent.name))
elif not spell_succes: elif not spell_succes:
print("- {name}: (nothing, still stunned)".format(name=self.name)) print("- {name}: (nothing, still stunned)".format(name=self.name))
elif self.active_spell is spell_impendimenta: elif self.active_spell is spells["Impendimenta"]:
if opponent.active_spell is spell_protego and opponent.active_spell_succes: if opponent.active_spell is spells["Protego"] and opponent.active_spell_succes:
print("- {name} casts {spell} on {name_o}. FAILURE! {name_o} blocks the attack!".format(name=self.name, name_o=opponent.name, spell=spell_name)) print("- {name} casts {spell} on {name_o}. FAILURE! {name_o} blocks the attack!".format(name=self.name, name_o=opponent.name, spell=spell_name))
else: else:
print("- {name} casts {spell} on {name_o}. {name_o} is slowed and their next offensive move will have a 33% slower spell speed!".format(name=self.name, name_o=opponent.name, spell=spell_name)) print("- {name} casts {spell} on {name_o}. {name_o} is slowed and their next offensive move will have a 33% slower spell speed!".format(name=self.name, name_o=opponent.name, spell=spell_name))
opponent.decreased_spell_speed = True opponent.decreased_spell_speed = True
elif self.active_spell is spell_lumos_solem: elif self.active_spell is spells["Lumos Solem"]:
# Light still goes through the shield, therefor always succeed blinding attempts # Light still goes through the shield, therefor always succeed blinding attempts
print("- {name} casts {spell} on {name_o}. {name_o} is blinded and their next offensive move will have 33% less damage!".format(name=self.name, name_o=opponent.name, spell=spell_name)) print("- {name} casts {spell} on {name_o}. {name_o} is blinded and their next offensive move will have 33% less damage!".format(name=self.name, name_o=opponent.name, spell=spell_name))
opponent.decreased_spell_damage = True opponent.decreased_spell_damage = True
elif self.active_spell is spell_protego and self.active_spell_succes: elif self.active_spell is spells["Protego"] and self.active_spell_succes:
print("- {name} casts {spell}".format(name=self.name, spell=spell_name)) print("- {name} casts {spell}".format(name=self.name, spell=spell_name))
else: else:
print("<!> [debug]{name} casted SPELL_TYPE_DEFENSE {spell}. Behaviour unscripted!".format(name=self.name, spell=spell_name)) print("<!> [debug]{name} casted SPELL_TYPE_DEFENSE {spell}. Behaviour unscripted!".format(name=self.name, spell=spell_name))
elif self.active_spell.type == SPELL_TYPE_UNFORGIVABLE: elif self.active_spell.type == SPELL_TYPE_UNFORGIVABLE:
if self.active_spell is spell_avada_kedavra: if self.active_spell is spells["Avada Kedavra"]:
print("- THE NERVE! {name} casts the killing curse! See you in the after life {name_o}!".format(name=self.name, name_o=opponent.name)) print("- THE NERVE! {name} casts the killing curse! See you in the after life {name_o}!".format(name=self.name, name_o=opponent.name))
opponent.health = 0 opponent.health = 0
elif self.active_spell is spell_crucio: elif self.active_spell is spells["Crucio"]:
print("- THE NERVE! {name} casts the torture curse. {name_o} is greatly hurt and falls to the ground. They are stunned for 5 (more) moves".format(name=self.name, name_o=opponent.name)) print("- THE NERVE! {name} casts the torture curse. {name_o} is greatly hurt and falls to the ground. They are stunned for 5 (more) moves".format(name=self.name, name_o=opponent.name))
opponent.add_stunned_rounds(5) opponent.add_stunned_rounds(5)
opponent.take_health(self.active_spell.damage) opponent.take_health(self.active_spell.damage)
elif self.active_spell is spell_imperio: elif self.active_spell is spells["Imperio"]:
print("- THE NERVE! {name} casts the Imperius curse. \"Why don't you take a nice nap for {max_stunned_rounds} moves, {name_o}?\". {name_o} submits with pleasure".format(name=self.name, name_o=opponent.name, max_stunned_rounds=MAX_STUNNED_ROUNDS)) print("- THE NERVE! {name} casts the Imperius curse. \"Why don't you take a nice nap for {max_stunned_rounds} moves, {name_o}?\". {name_o} submits with pleasure".format(name=self.name, name_o=opponent.name, max_stunned_rounds=MAX_STUNNED_ROUNDS))
opponent.add_stunned_rounds(MAX_STUNNED_ROUNDS) opponent.add_stunned_rounds(MAX_STUNNED_ROUNDS)
else: else:
if self.active_spell is spell_mimblewimble: if self.active_spell is spells["Mimblewimble"]:
if opponent.active_spell is spell_protego and opponent.active_spell_succes: if opponent.active_spell is spells["Protego"] and opponent.active_spell_succes:
print("- {name} casts {spell} on {name_o}. FAILURE! {name_o} blocks the attack!".format(name=self.name, spell=spell_name, name_o=opponent.name)) print("- {name} casts {spell} on {name_o}. FAILURE! {name_o} blocks the attack!".format(name=self.name, spell=spell_name, name_o=opponent.name))
else: else:
print("- {name} casts {spell} on {name_o}. {name_o}'s tongue is tied in a knot. That's annoying! {name_o} is silenced for 1 (more) move".format(name=self.name, spell=spell_name, name_o=opponent.name)) print("- {name} casts {spell} on {name_o}. {name_o}'s tongue is tied in a knot. That's annoying! {name_o} is silenced for 1 (more) move".format(name=self.name, spell=spell_name, name_o=opponent.name))
opponent.add_stunned_rounds(-self.active_spell.damage) opponent.add_stunned_rounds(-self.active_spell.damage)
elif self.active_spell is spell_silencio: elif self.active_spell is spells["Silencio"]:
if opponent.active_spell is spell_protego and opponent.active_spell_succes: if opponent.active_spell is spells["Protego"] and opponent.active_spell_succes:
print("- {name} casts {spell} on {name_o}. FAILURE! {name_o} blocks the attack!".format(name=self.name, spell=spell_name, name_o=opponent.name)) print("- {name} casts {spell} on {name_o}. FAILURE! {name_o} blocks the attack!".format(name=self.name, spell=spell_name, name_o=opponent.name))
else: else:
if opponent.stunned_rounds == 0: if opponent.stunned_rounds == 0:
@ -181,8 +184,8 @@ class Player:
total_damage = self.active_spell.damage * self.wand.damage * damage_modifier - damage_penalty total_damage = self.active_spell.damage * self.wand.damage * damage_modifier - damage_penalty
if total_damage < 0: total_damage = 0 if total_damage < 0: total_damage = 0
if opponent.active_spell is spell_protego and opponent.active_spell_succes: if opponent.active_spell is spells["Protego"] and opponent.active_spell_succes:
print("- {name} casts {spell} on {name_o}. FAILURE! {name_o} blocks the attack!".format(name=self.name, spell=spell_name, name_o=opponent.name)) print("- {name} casts {spell} on {name_o}. FAILURE! {name_o} blocks the attack!".format(name=self.name, spell=spell_name, name_o=opponent.name))
else: else:
print("- {name} casts {spell} on {name_o} causing {dmg} damage!".format(name=self.name, spell=spell_name, name_o=opponent.name, dmg=total_damage)) print("- {name} casts {spell} on {name_o} causing {dmg} damage!".format(name=self.name, spell=spell_name, name_o=opponent.name, dmg=total_damage))
opponent.take_health(total_damage) opponent.take_health(total_damage)

143
spells.py
View File

@ -1,5 +1,8 @@
import random import random
from Levenshtein import distance from game_config import CHANCE_HEAL_PARTLY, CHANCE_HEAL_FULLY, DEBUG_MODE, MAX_LEVENSHTEIN_DISTANCE
if MAX_LEVENSHTEIN_DISTANCE > 0:
from Levenshtein import distance
SPELL_TYPE_NONE = -1 SPELL_TYPE_NONE = -1
SPELL_TYPE_USELESS = 0 SPELL_TYPE_USELESS = 0
@ -8,25 +11,14 @@ SPELL_TYPE_COMMON = 2
SPELL_TYPE_POWERFUL = 3 SPELL_TYPE_POWERFUL = 3
SPELL_TYPE_UNFORGIVABLE = 4 SPELL_TYPE_UNFORGIVABLE = 4
# If SPELL_TYPE_DEFENSE is casted, always these chances to heal partly (restore 5% of max hp) or completely _INVALID_SPELL = ".@wizardduel@__spell_invalid__" #Internal usage only
CHANCE_HEAL_PARTLY = 25
CHANCE_HEAL_FULLY = 5
#Maximum Levenshtein distance. Eg if a user casts 'Pritrgo' instead of 'Protego', distance would be 2 and Protego would still be cast if MAX_LEVENSHTEIN_DISTANCE is at least 2
#Set to 0 to disable
MAX_LEVENSHTEIN_DISTANCE = 3
__INVALID_SPELL = ".@wizardduel@__spell_invalid__" #Internal usage only
## ##
## Spell class ## Spell class
## ##
class Spell: class Spell:
spellList = []
#Class special methods #Class special methods
""" """
name (str) Name of the spell as used in combat
speed (int) Speed of the spell. This will determine what spell is casted first. speed (int) Speed of the spell. This will determine what spell is casted first.
damage (int) Damage that the spell does. damage (int) Damage that the spell does.
<!> Negative damage takes away moves from opponent (eg -2 = cancel 2 moves of opponent) <!> Negative damage takes away moves from opponent (eg -2 = cancel 2 moves of opponent)
@ -39,103 +31,116 @@ class Spell:
- SPELL_TYPE_POWER: Powerful combat spell - deals alot of damage or takes away a few moves from opponent - SPELL_TYPE_POWER: Powerful combat spell - deals alot of damage or takes away a few moves from opponent
- SPELL_TYPE_UNFORGIVABLE: deals alot of damage or takes away alot of moves from opponent - SPELL_TYPE_UNFORGIVABLE: deals alot of damage or takes away alot of moves from opponent
""" """
def __init__(self, name: str, speed: int, damage: int, succes_rate: int, description: str, type: int = SPELL_TYPE_COMMON): def __init__(self, speed: int, damage: int, succes_rate: int, description: str, type: int = SPELL_TYPE_COMMON) -> None:
self.name = name self.speed: int = speed
self.speed = speed self.damage: int = damage
self.damage = damage self.succes_rate: int = succes_rate
self.succes_rate = succes_rate self.description: str = description
self.description = description self.type: int = type
self.type = type
Spell.spellList.append(self) def __repr__(self) -> str:
return "\n\t{desc}\n\tSUCCES RATE: {srate}%\tSPEED: {speed}\tDAMAGE: {dmg}".format(type=type, desc=self.description, srate=self.succes_rate, speed=self.speed, dmg=self.damage)
def __repr__(self):
return " ['{spell}']\n\t{desc}\n\tSUCCES RATE: {srate}%\tSPEED: {speed}\tDAMAGE: {dmg}".format(spell=self.name, type=type, desc=self.description, srate=self.succes_rate, speed=self.speed, dmg=self.damage)
def chance_heal_partly_succes(self): def chance_heal_partly_succes(self) -> bool:
return self.type == SPELL_TYPE_DEFENSE and CHANCE_HEAL_PARTLY > random.random() * 100 return self.type == SPELL_TYPE_DEFENSE and CHANCE_HEAL_PARTLY > random.random() * 100
def chance_heal_fully_succes(self): def chance_heal_fully_succes(self) -> bool:
return self.type == SPELL_TYPE_DEFENSE and CHANCE_HEAL_FULLY > random.random() * 100 return self.type == SPELL_TYPE_DEFENSE and CHANCE_HEAL_FULLY > random.random() * 100
def get_spell_name(self) -> str:
return str(list(i for i in spells if spells[i] == self)).strip("[]'")
## ##
## Spells ## Spells
## ##
# Useless spells - These don't do anything useful in combat spells = {
spell_lumos = Spell("Lumos", 100, 000, 100, "Creates a small light at the tip of your wand", SPELL_TYPE_USELESS) # Useless spells - These don't do anything useful in combat
spell_nox = Spell("Nox", 100, 000, 100, "Counter spell of Lumos", SPELL_TYPE_USELESS) "Lumos": Spell(100, 000, 100, "Creates a small light at the tip of your wand", SPELL_TYPE_USELESS),
spell_rennervate = Spell("Rennervate", 100, 000, 100, "Revives your opponent if they are stunned", SPELL_TYPE_USELESS) "Nox": Spell(100, 000, 100, "Counter spell of Lumos", SPELL_TYPE_USELESS),
spell_igni = Spell("Igno", 100, 000, 100, "Damages an enemy using fire. Except, this is a Witcher sign. It thus has no effect at all", SPELL_TYPE_USELESS) "Rennervate": Spell(100, 000, 100, "Revives your opponent if they are stunned", SPELL_TYPE_USELESS),
"Igni": Spell(100, 000, 100, "Damages an enemy using fire. Except, this is a Witcher sign. It thus has no effect at all", SPELL_TYPE_USELESS),
# Defensive spell. Each cast from this category has a 5% chance of completely restoring health or 25% chance to heal 5% of maximum health # Defensive spell. Each cast from this category has a 5% chance of completely restoring health or 25% chance to heal 5% of maximum health
spell_finite_incantatem = Spell("Finite Incantatem", 100, 000, 45, "Cancel all effects casted upon you. If you are stunned/silenced, there's a 10% chance this spell might work", SPELL_TYPE_DEFENSE) "Finite Incantatem": Spell(100, 000, 45, "Cancel all effects casted upon you. If you are stunned/silenced, there's a 10% chance this spell might work", SPELL_TYPE_DEFENSE),
spell_impendimenta = Spell("Impendimenta", 94, 000, 60, "Slows your opponent. EFFECT: Decrease opponent's spell speed by 33% in their next offensive move", SPELL_TYPE_DEFENSE) "Impendimenta": Spell(94, 000, 60, "Slows your opponent. EFFECT: Decrease opponent's spell speed by 33% in their next offensive move", SPELL_TYPE_DEFENSE),
spell_lumos_solem = Spell("Lumos Solem", 94, 000, 60, "Blinds your opponent. EFFECT: Decrease opponent's spell damage by 33% in their next offensive move", SPELL_TYPE_DEFENSE) "Lumos Solem": Spell(94, 000, 60, "Blinds your opponent. EFFECT: Decrease opponent's spell damage by 33% in their next offensive move", SPELL_TYPE_DEFENSE),
spell_protego = Spell("Protego", 100, 000, 80, "Create a shield that blocks most attacks", SPELL_TYPE_DEFENSE) "Protego": Spell(100, 000, 80, "Create a shield that blocks most attacks", SPELL_TYPE_DEFENSE),
# Common combat spells. High chance of succes, deals some damage # Common combat spells. High chance of succes, deals some damage
spell_reducto = Spell("Reducto", 75, 150, 85, "Blast an object near your opponent") "Reducto": Spell(75, 150, 85, "Blast an object near your opponent"),
spell_rictusempra = Spell("Rictusempra", 85, 90, 90, "Causes your opponent to curl up in laughter, tiring them out") "Rictusempra": Spell(85, 90, 90, "Causes your opponent to curl up in laughter, tiring them out"),
spell_stupefy = Spell("Stupefy", 95, 75, 95, "Knock over your opponent") "Stupefy": Spell(95, 75, 95, "Knock over your opponent"),
# Powerful combat spells. Medium chance of succes, deals more damage or stuns opponents # Powerful combat spells. Medium chance of succes, deals more damage or stuns opponents
spell_bombarda = Spell("Bombarda", 50, 180, 75, "Creates an explosion near your opponent", SPELL_TYPE_POWERFUL) "Bombarda": Spell(50, 180, 75, "Creates an explosion near your opponent", SPELL_TYPE_POWERFUL),
spell_confringo = Spell("Confringo", 50, 200, 70, "Creates an explosion directly at your opponent", SPELL_TYPE_POWERFUL) "Confringo": Spell(50, 200, 70, "Creates an explosion directly at your opponent", SPELL_TYPE_POWERFUL),
spell_mimblewimble = Spell("Mimblewimble", 50, -1, 70, "Ties a knot in your opponents tongue, causing them to be unable to cast a spell for 1 (more) move", SPELL_TYPE_POWERFUL) "Mimblewimble": Spell(50, -1, 70, "Ties a knot in your opponents tongue, causing them to be unable to cast a spell for 1 (more) move", SPELL_TYPE_POWERFUL),
spell_sectumsempra = Spell("Sectumsempra", 90, 400, 35, "Slices your opponent", SPELL_TYPE_POWERFUL) "Sectumsempra": Spell(90, 400, 35, "Slices your opponent", SPELL_TYPE_POWERFUL),
spell_silencio = Spell("Silencio", 35, -3, 55, "Silences your opponent, causing them unable to cast spells for 3 moves. <!>Only works if opponent is not stunned yet", SPELL_TYPE_POWERFUL) "Silencio": Spell(35, -3, 55, "Silences your opponent, causing them unable to cast spells for 3 moves. <!>Only works if opponent is not stunned yet", SPELL_TYPE_POWERFUL),
# Unforgivable spells. Very low chance of success, instantly kills or deals alot of damage/stun amount # Unforgivable spells. Very low chance of success, instantly kills or deals alot of damage/stun amount
spell_avada_kedavra = Spell("Avada Kedavra", 999, 999, 2, "Instantly end your opponent", SPELL_TYPE_UNFORGIVABLE) "Avada Kedavra": Spell(999, 999, 2, "Instantly end your opponent", SPELL_TYPE_UNFORGIVABLE),
spell_crucio = Spell("Crucio", 999, 500, 5, "Cause excruciating pain to your opponent, causing alot of damage and making them unable to cast spells for 5 moves", SPELL_TYPE_UNFORGIVABLE) "Crucio": Spell(999, 500, 5, "Cause excruciating pain to your opponent, causing alot of damage and making them unable to cast spells for 5 moves", SPELL_TYPE_UNFORGIVABLE),
spell_imperio = Spell("Imperio", 999, -1, 3, "Muddle with your opponent's mind, convincing them to stop casting spells for 10 moves", SPELL_TYPE_UNFORGIVABLE) "Imperio": Spell(999, -1, 3, "Muddle with your opponent's mind, convincing them to stop casting spells for 10 moves", SPELL_TYPE_UNFORGIVABLE),
# Internal usage # Internal usage
spell_object_none = Spell(__INVALID_SPELL, 0, 0, 0, "(internal) invalid spell", SPELL_TYPE_NONE) _INVALID_SPELL: Spell(0, 0, 0, "(internal) invalid spell", SPELL_TYPE_NONE)
spell_object_stunned = Spell(__INVALID_SPELL, 0, 0, 0, "(internal) object when stunned", SPELL_TYPE_NONE) }
# Set succes rates to 100% if debug mode is enabled
if DEBUG_MODE:
for i in spells.items():
i[1].succes_rate = 100
## ##
## Standalone spell functions ## Standalone spell functions
## ##
def random_combat_spell(): def random_combat_spell() -> tuple:
return random.choice([i for i in Spell.spellList if i.type == SPELL_TYPE_COMMON]) return random.choice([i for i in spells.items() if i[1].type == SPELL_TYPE_COMMON]) # note: returns tuple ('spell_name', spell_obj)
def find_spell_by_name(input: str): # Returns a list with: [spell_object, levenshtein_distance]. If distance is greater than 0 (typos were made), damage goes down def find_spell_by_name(input: str) -> tuple: # Returns a multidimensional tuple: ( ('spell_name', spell_object), levenshtein_distance )
for i in Spell.spellList: ret = (input, spells.get(input.title(), spells[_INVALID_SPELL]))
if input.title() == i.name.title(): dist = 0
return [i, 0] if ret[1] == spells[_INVALID_SPELL]:
else: ret = (_INVALID_SPELL, ret[1])
if MAX_LEVENSHTEIN_DISTANCE > 0: if MAX_LEVENSHTEIN_DISTANCE > 0:
dist = distance(i.name.title(), input.title()) for i in spells.items():
if dist < MAX_LEVENSHTEIN_DISTANCE: dist = distance(i[0].title(), input.title())
return [i, dist] if dist <= MAX_LEVENSHTEIN_DISTANCE:
return [spell_object_none, 0] ret = (i[0], i[1])
break
return (ret, dist)
def print_spells(): def print_spells() -> None:
header_spells_useless = "== USELESS SPELLS ==" header_spells_useless = "== USELESS SPELLS =="
header_spells_defensive = "== DEFENSIVE SPELLS ==" header_spells_defensive = "== DEFENSIVE SPELLS =="
header_spells_common = "== COMMON COMBAT SPELLS ==" header_spells_common = "== COMMON COMBAT SPELLS =="
header_spells_powerful = "== POWERFUL COMBAT SPELLS ==" header_spells_powerful = "== POWERFUL COMBAT SPELLS =="
header_spells_unforgivable = "== UNFORGIVABLE CURSES ==" header_spells_unforgivable = "== UNFORGIVABLE CURSES =="
for i in Spell.spellList: for i in spells.items():
if i.type == SPELL_TYPE_UNFORGIVABLE or i.type == SPELL_TYPE_USELESS or i.type == SPELL_TYPE_NONE: if i[1].type == SPELL_TYPE_UNFORGIVABLE or i[1].type == SPELL_TYPE_USELESS or i[1].type == SPELL_TYPE_NONE:
continue continue
if i.type == SPELL_TYPE_USELESS and header_spells_useless: if i[1].type == SPELL_TYPE_USELESS and header_spells_useless:
print("\n"+header_spells_useless) print("\n"+header_spells_useless)
header_spells_useless = "" header_spells_useless = ""
elif i.type == SPELL_TYPE_DEFENSE and header_spells_defensive: elif i[1].type == SPELL_TYPE_DEFENSE and header_spells_defensive:
print("\n"+header_spells_defensive) print("\n"+header_spells_defensive)
header_spells_defensive = "" header_spells_defensive = ""
elif i.type == SPELL_TYPE_COMMON and header_spells_common: elif i[1].type == SPELL_TYPE_COMMON and header_spells_common:
print("\n"+header_spells_common) print("\n"+header_spells_common)
header_spells_common = "" header_spells_common = ""
elif i.type == SPELL_TYPE_POWERFUL and header_spells_powerful: elif i[1].type == SPELL_TYPE_POWERFUL and header_spells_powerful:
print("\n"+header_spells_powerful) print("\n"+header_spells_powerful)
header_spells_powerful = "" header_spells_powerful = ""
elif i.type == SPELL_TYPE_UNFORGIVABLE and header_spells_unforgivable: elif i[1].type == SPELL_TYPE_UNFORGIVABLE and header_spells_unforgivable:
print("\n"+header_spells_unforgivable) print("\n"+header_spells_unforgivable)
header_spells_unforgivable = "" header_spells_unforgivable = ""
print(i) print(
str(i)
.strip('(')
.strip(')')
.replace(',', ':', 1)
)

View File

@ -7,18 +7,13 @@ WAND_WOOD_ELDER = 1 #Speed -4%
WAND_WOOD_APPLE = 2 #Damage -5% WAND_WOOD_APPLE = 2 #Damage -5%
class Wand: class Wand:
_next_wand_id = 1 def __init__(self, wand_core: int, wand_wood: int) -> None:
wandList = [] self.core: int = wand_core
self.wood: int = wand_wood
def __init__(self, wand_core: int, wand_wood: int): self.succes_rate: float = 1
self.id = Wand._next_wand_id self.speed: float = 1
self.damage: float = 1
self.core = wand_core
self.wood = wand_wood
self.succes_rate = 1
self.speed = 1
self.damage = 1
if self.core == WAND_CORE_UNICORN: if self.core == WAND_CORE_UNICORN:
self.succes_rate *= 1.03 self.succes_rate *= 1.03
@ -34,21 +29,18 @@ class Wand:
elif self.wood == WAND_WOOD_APPLE: elif self.wood == WAND_WOOD_APPLE:
self.damage *= 0.95 self.damage *= 0.95
Wand.wandList.append(self) def __repr__(self) -> str:
Wand._next_wand_id += 1 return"\t{wood} wand with core: {core}\n\t\t-- SPEED: {info_speed}\tDAMAGE: {info_dmg}\tSUCCES RATE: {info_srate}".format(
wood=self.get_wand_wood(), core=self.get_wand_core(), info_srate=round(self.succes_rate, 2), info_speed=self.speed, info_dmg=round(self.damage, 2)
def __repr__(self):
return"\t{id}: {wood} wand with core: {core}\n\t\t-- SPEED: {info_speed}\tDAMAGE: {info_dmg}\tSUCCES RATE: {info_srate}".format(
id=self.id, wood=self.get_wand_wood(), core=self.get_wand_core(), info_srate=round(self.succes_rate, 2), info_speed=self.speed, info_dmg=round(self.damage, 2)
) )
def get_wand_core(self): def get_wand_core(self) -> str:
if self.core == WAND_CORE_UNICORN: return "Unicorn hair" if self.core == WAND_CORE_UNICORN: return "Unicorn hair"
elif self.core == WAND_CORE_PHOENIX: return "Phoenix feather" elif self.core == WAND_CORE_PHOENIX: return "Phoenix feather"
elif self.core == WAND_CORE_DRAGON: return "Dragon heartstring" elif self.core == WAND_CORE_DRAGON: return "Dragon heartstring"
else: return "Muggle's electric wire" else: return "Muggle's electric wire"
def get_wand_wood(self): def get_wand_wood(self) -> str:
if self.wood == WAND_WOOD_ASH: return "Ash" if self.wood == WAND_WOOD_ASH: return "Ash"
elif self.wood == WAND_WOOD_ELDER: return "Elder" elif self.wood == WAND_WOOD_ELDER: return "Elder"
elif self.wood == WAND_WOOD_APPLE: return "Apple" elif self.wood == WAND_WOOD_APPLE: return "Apple"
@ -57,14 +49,16 @@ class Wand:
## ##
## Wands ## Wands
## ##
wand_1 = Wand(WAND_CORE_UNICORN, WAND_WOOD_ASH) wands = {
wand_2 = Wand(WAND_CORE_UNICORN, WAND_WOOD_ELDER) 1: Wand(WAND_CORE_UNICORN, WAND_WOOD_ASH),
wand_3 = Wand(WAND_CORE_UNICORN, WAND_WOOD_APPLE) 2: Wand(WAND_CORE_UNICORN, WAND_WOOD_ELDER),
3: Wand(WAND_CORE_UNICORN, WAND_WOOD_APPLE),
wand_4 = Wand(WAND_CORE_PHOENIX, WAND_WOOD_ASH) 4: Wand(WAND_CORE_PHOENIX, WAND_WOOD_ASH),
wand_5 = Wand(WAND_CORE_PHOENIX, WAND_WOOD_ELDER) 5: Wand(WAND_CORE_PHOENIX, WAND_WOOD_ELDER),
wand_6 = Wand(WAND_CORE_PHOENIX, WAND_WOOD_APPLE) 6: Wand(WAND_CORE_PHOENIX, WAND_WOOD_APPLE),
wand_7 = Wand(WAND_CORE_DRAGON, WAND_WOOD_ASH) 7: Wand(WAND_CORE_DRAGON, WAND_WOOD_ASH),
wand_8 = Wand(WAND_CORE_DRAGON, WAND_WOOD_ELDER) 8: Wand(WAND_CORE_DRAGON, WAND_WOOD_ELDER),
wand_9 = Wand(WAND_CORE_DRAGON, WAND_WOOD_APPLE) 9: Wand(WAND_CORE_DRAGON, WAND_WOOD_APPLE)
}