diff --git a/strings.py b/strings.py
index c289fba8..4ab3b701 100644
--- a/strings.py
+++ b/strings.py
@@ -8,7 +8,9 @@ class SafeDict(dict):
return key
-def safely_format_string(string: str, words: typing.Dict[str, str], ignore_escaping=False) -> str:
+def safely_format_string(string: str, words: typing.Dict[str, str] = None, ignore_escaping=False) -> str:
+ if words is None:
+ words = {}
if ignore_escaping:
escaped = words
else:
@@ -47,6 +49,12 @@ class BRIDGE:
INACTIVE_BRIDGE = "⚠ Il collegamento tra Telegram e Discord non è attivo al momento."
+# Random spellslinging
+class CAST:
+ class ERRORS:
+ NOT_YET_AVAILABLE = "⚠ Il nuovo cast non è ancora disponibile! Per un'anteprima sulle nuove funzioni, usa /spell
."
+
+
# Ciao Ruozi!
class CIAORUOZI:
THE_LEGEND_HIMSELF = "👋 Ciao me!"
@@ -200,10 +208,22 @@ class SHIP:
RESULT = "💕 {one} + {two} = {result}"
class ERRORS:
- INVALID_SYNTAX = "⚠ Non hai specificato correttamente i due nomi!\nSintassi corretta: /ship (nome) (nome)
"
+ INVALID_SYNTAX = "⚠ Non hai specificato correttamente i due nomi!\nSintassi: /ship (nome) (nome)
"
INVALID_NAMES = "⚠ I nomi specificati non sono validi.\nRiprova con dei nomi diversi!"
+# Get information about a spell
+class SPELL:
+ HEADER = "🔍 La magia {name} ha le seguenti proprietà (v{version}):\n"
+ ACCURACY = "Precisione - {accuracy}%\n"
+ DAMAGE = "Danni - {number}d{type}{constant}\n"
+ TYPE = "Tipo - {type}\n"
+ REPEAT = "Multiattacco - ×{repeat}\n"
+
+ class ERRORS:
+ INVALID_SYNTAX = "⚠ Non hai specificato la magia di cui vuoi conoscere i dettagli!\nSintassi: /spell (nome)
"
+
+
# Secondo me, è colpa delle stringhe.
SMECDS = "🤔 Secondo me, è colpa {ds}."
diff --git a/telegrambot.py b/telegrambot.py
index 3b6429f9..9c85e180 100644
--- a/telegrambot.py
+++ b/telegrambot.py
@@ -92,7 +92,7 @@ def command(func: "function"):
# noinspection PyBroadException
try:
reply_msg(bot, main_group_id, strings.TELEGRAM.ERRORS.CRITICAL_ERROR,
- exc_info=sys.exc_info())
+ exc_info=repr(sys.exc_info()))
except Exception:
logger.error(f"Double critical error: {sys.exc_info()}")
sentry.user_context({
@@ -173,21 +173,8 @@ def cmd_cv(bot: telegram.Bot, update: telegram.Update):
@command
-@database_access
-def cmd_cast(bot: telegram.Bot, update: telegram.Update, session: db.Session):
- try:
- spell: str = update.message.text.split(" ", 1)[1]
- except IndexError:
- bot.send_message(update.message.chat.id, "⚠️ Non hai specificato nessun incantesimo!\n"
- "Sintassi corretta: `/cast `", parse_mode="Markdown")
- return
- # Find a target for the spell
- target = random.sample(session.query(db.Telegram).all(), 1)[0]
- # END
- bot.send_message(update.message.chat.id, cast(spell_name=spell,
- target_name=target.username if target.username is not None
- else target.first_name, platform="telegram"),
- parse_mode="HTML")
+def cmd_cast(bot: telegram.Bot, update: telegram.Update):
+ reply(bot, update, strings.CAST.ERRORS.NOT_YET_AVAILABLE)
@command
@@ -732,7 +719,6 @@ def cmd_dndmarkov(bot: telegram.Bot, update: telegram.Update):
reply(bot, update, sentence)
-
def exec_roll(roll) -> str:
result = int(roll.evaluate())
string = ""
@@ -772,6 +758,17 @@ def cmd_start(bot: telegram.Bot, update: telegram.Update):
reply(bot, update, strings.TELEGRAM.BOT_STARTED)
+@command
+def cmd_spell(bot: telegram.Bot, update: telegram.Update):
+ try:
+ input: str = update.message.text.split(" ", 1)[1]
+ except IndexError:
+ reply(bot, update, strings.SPELL.ERRORS.INVALID_SYNTAX)
+ return
+ spell = cast.Spell(input)
+ reply(bot, update, spell.stringify())
+
+
def process(arg_discord_connection):
if arg_discord_connection is not None:
global discord_connection
@@ -806,6 +803,7 @@ def process(arg_discord_connection):
u.dispatcher.add_handler(CommandHandler("search", cmd_search))
u.dispatcher.add_handler(CommandHandler("regex", cmd_regex))
u.dispatcher.add_handler(CommandHandler("start", cmd_start))
+ u.dispatcher.add_handler(CommandHandler("spell", cmd_spell))
u.dispatcher.add_handler(CallbackQueryHandler(on_callback_query))
logger.info("Handlers registered.")
u.start_polling()
diff --git a/utils/__init__.py b/utils/__init__.py
index 29f222d1..8b78ba4d 100644
--- a/utils/__init__.py
+++ b/utils/__init__.py
@@ -1,6 +1,6 @@
from .dirty import Dirty, DirtyDelta
from .mmstatus import MatchmakingStatus
-from .cast import cast
+from .cast import Spell, Hit, Cast
from .stagismo import smecds
-__all__ = ["Dirty", "DirtyDelta", "MatchmakingStatus", "cast", "smecds"]
+__all__ = ["Dirty", "DirtyDelta", "MatchmakingStatus", "Spell", "Hit", "Cast", "smecds"]
diff --git a/utils/cast.py b/utils/cast.py
index a8731bd1..c99c564d 100644
--- a/utils/cast.py
+++ b/utils/cast.py
@@ -1,79 +1,94 @@
import random
import math
-import db
+import typing
+import strings
+s = strings.safely_format_string
-def cast(spell_name: str, target_name: str, platform: str) -> str:
- spell = spell_name.capitalize()
- # Seed the rng with the spell name
- # so that spells always deal the same damage
- random.seed(spell)
- dmg_dice = random.randrange(1, 11)
- dmg_max = random.sample([4, 6, 8, 10, 12, 20, 100], 1)[0]
- dmg_mod = random.randrange(math.floor(-dmg_max / 5), math.ceil(dmg_max / 5) + 1)
- dmg_type = random.sample(["da fuoco", "da freddo", "elettrici", "sonici", "necrotici", "magici",
- "da acido", "divini", "nucleari", "psichici", "fisici", "puri", "da taglio",
- "da perforazione", "da impatto", "da caduta", "gelato", "onnipotenti", "oscuri",
- "di luce", "da velocità", "da cactus", "meta", "dannosi", "da radiazione",
- "tuamammici", "da maledizione", "pesanti", "leggeri", "immaginari", "da laser",
- "da neutrini", "galattici", "cerebrali", "ritardati", "ritardanti"], 1)[0]
- # Reseed the rng with a random value
- # so that the dice roll always deals a different damage
- random.seed()
- total = dmg_mod
- # Check for a critical hit
- crit = 1
- while True:
- crit_die = random.randrange(1, 21)
- if crit_die == 20:
- crit *= 2
+class Spell:
+ version = "3.0"
+
+ dice_type_distribution = ([4] * 1) +\
+ ([6] * 6) +\
+ ([8] * 24) +\
+ ([10] * 38) +\
+ ([12] * 24) +\
+ ([20] * 6) +\
+ ([100] * 1)
+
+ all_damage_types = ["da fuoco", "da freddo", "elettrici", "sonici", "necrotici", "magici",
+ "da acido", "divini", "nucleari", "psichici", "fisici", "puri", "da taglio",
+ "da perforazione", "da impatto", "da caduta", "gelato", "onnipotenti", "oscuri",
+ "di luce", "da velocità", "da cactus", "meta", "dannosi", "da radiazione",
+ "tuamammici", "da maledizione", "pesanti", "leggeri", "immaginari", "da laser",
+ "da neutrini", "galattici", "cerebrali", "ritardati", "ritardanti", "morali", "materiali",
+ "energetici", "esplosivi"]
+
+ repeat_distribution = ([1] * 8) +\
+ ([2] * 1) +\
+ ([3] * 1)
+
+ damage_types_distribution = ([1] * 6) + \
+ ([2] * 3) + \
+ ([3] * 1)
+
+ def __init__(self, name: str):
+ seed = name.capitalize()
+ random.seed(seed)
+ # Spell data
+ self.name = seed
+ self.dice_number = random.randrange(1, 21)
+ self.dice_type = random.sample(self.dice_type_distribution, 1)[0]
+ self.constant = random.randrange(math.floor(-self.dice_type / 4), math.ceil(self.dice_type / 4) + 1)
+ self.miss_chance = random.randrange(80, 101)
+ self.repeat = random.sample(self.repeat_distribution, 1)[0]
+ self.damage_types_qty = random.sample(self.damage_types_distribution, 1)[0]
+ self.damage_types = random.sample(self.all_damage_types, self.damage_types_qty)
+
+ def stringify(self) -> str:
+ string = s(strings.SPELL.HEADER, words={"name": self.name, "version": self.version})
+ string += s(strings.SPELL.ACCURACY, words={"accuracy": str(self.miss_chance)})
+ if self.constant > 0:
+ constant = "+" + str(self.constant)
+ elif self.constant == 0:
+ constant = ""
else:
- break
- for dice in range(0, dmg_dice):
- total += random.randrange(1, dmg_max + 1)
- if crit > 1:
- if platform == "telegram":
- crit_msg = f"CRITICO ×{crit}{'!' * crit}\n"
- elif platform == "discord":
- crit_msg = f"**CRITICO ×{crit}{'!' * crit}**\n"
- total *= crit
- else:
- crit_msg = ""
- if platform == "telegram":
- if dmg_dice == 10 and dmg_max == 100 and dmg_mod == 20:
- return f"❇️‼️ Ho lanciato {spell} su " \
- f"{target_name}.\n" \
- f"Una grande luce illumina il cielo, seguita poco dopo da un fungo di fumo nel luogo" \
- f" in cui si trovava {target_name}.\n" \
- f"Il fungo si espande a velocità smodata, finchè il fumo non ricopre la Terra intera e le tenebre" \
- f" cadono su di essa.\n" \
- f"Dopo qualche minuto, la temperatura ambiente raggiunge gli 0 °C, e continua a diminuire.\n" \
- f"L'Apocalisse Nucleare è giunta, e tutto per polverizzare {target_name}" \
- f" con {spell}.\n" \
- f"{target_name} subisce 10d100+20=9999 danni apocalittici!"
- return f"❇️ Ho lanciato {spell} su " \
- f"{target_name}.\n" \
- f"{crit_msg}" \
- f"{target_name} subisce {dmg_dice}d{dmg_max}" \
- f"{'+' if dmg_mod > 0 else ''}{str(dmg_mod) if dmg_mod != 0 else ''}" \
- f"{'×' + str(crit) if crit > 1 else ''}" \
- f"={total if total > 0 else 0} danni {dmg_type}!"
- elif platform == "discord":
- if dmg_dice == 10 and dmg_max == 100 and dmg_mod == 20:
- return f"❇️‼️ Ho lanciato **{spell}** su " \
- f"_{target_name}_.\n" \
- f"Una grande luce illumina il cielo, seguita poco dopo da un fungo di fumo nel luogo" \
- f" in cui si trovava _{target_name}_.\n" \
- f"Il fungo si espande a velocità smodata, finchè il fumo non ricopre la Terra intera e le tenebre" \
- f" cadono su di essa.\n" \
- f"Dopo qualche minuto, la temperatura ambiente raggiunge gli 0 °C, e continua a diminuire.\n" \
- f"L'Apocalisse Nucleare è giunta, e tutto per polverizzare _{target_name}_" \
- f" con **{spell}**.\n" \
- f"_{target_name}_ subisce 10d100+20=**9999** danni apocalittici!"
- return f"❇️ Ho lanciato **{spell}** su " \
- f"_{target_name}_.\n" \
- f"{crit_msg}" \
- f"_{target_name}_ subisce {dmg_dice}d{dmg_max}" \
- f"{'+' if dmg_mod > 0 else ''}{str(dmg_mod) if dmg_mod != 0 else ''}" \
- f"{'×' + str(crit) if crit > 1 else ''}" \
- f"=**{total if total > 0 else 0}** danni {dmg_type}!"
+ constant = str(self.constant)
+ string += s(strings.SPELL.DAMAGE,
+ words={"number": str(self.dice_number),
+ "type": str(self.dice_type),
+ "constant": constant})
+ for dmg_type in self.damage_types:
+ string += s(strings.SPELL.TYPE, words={"type": dmg_type})
+ if self.repeat > 1:
+ string += s(strings.SPELL.REPEAT, words={"repeat": str(self.repeat)})
+ return string
+
+
+class Hit:
+ def __init__(self, spell: Spell):
+ random.seed()
+ self.hit_roll = random.randrange(0, 101)
+ self.damage = 0
+ self.crit_multiplier = 1
+ self.damage_type = random.sample(spell.damage_types, 1)[0]
+ if self.hit_roll > spell.miss_chance:
+ return
+ for _ in range(spell.dice_number):
+ self.damage += random.randrange(1, spell.dice_type + 1)
+ self.damage += spell.constant
+ if self.damage < 0:
+ self.damage = 0
+ while random.randrange(1, 21) == 20:
+ self.crit_multiplier *= 2
+
+ def damage(self):
+ return self.damage * self.crit_multiplier
+
+
+class Cast:
+ def __init__(self, spell: Spell):
+ self.hits = []
+ for _ in spell.repeat:
+ self.hits.append(Hit(spell))
+