17c776faec
-> !wants -> !orgs info -> special cmd's -> !assist -> "afk" for players without active account -> !loot add <item_ref> <count> => nolonger breaks !account Changes: -> grouped !tara, !gaunt, .. into !wb -> Display the most recent news entry on logon (default: enabled) -> improved grouping of !items -> Added the option to authentificate WS connections (Datanet module). This is used in special cases, where the Websocket Server requires the clien tto authentificate itself. (Server sends "#auth", client responds with the auth string) -> Add main name to relaying (priv <-> org) [default: disabled] -> Added logon/logoff messages back -> restricted default access to "dangerous" commands to moderator -> Added optional logging (Private Channel, Org Channel, Tells, ... disabled by default) Rewrite of the Tower Module. -> More verbosity, if enabled in config. by default, GAS and Hot timer only. -> !hot displays currently hot (and in penalty) sites, and these which go hot in < 60 minutes -> !attacks filterable by PF and Site -> display current contract QL's grouped by org: !contracts (requires managed cache)
587 lines
30 KiB
Python
587 lines
30 KiB
Python
import math
|
|
|
|
from core.chat_blob import ChatBlob
|
|
from core.command_param_types import Int, Decimal, Item
|
|
from core.decorators import instance, command
|
|
from core.dict_object import DictObject
|
|
|
|
|
|
@instance()
|
|
class SpecialsController:
|
|
def __init__(self):
|
|
pass
|
|
|
|
def inject(self, registry):
|
|
self.db = registry.get_instance("db")
|
|
self.text = registry.get_instance("text")
|
|
self.util = registry.get_instance("util")
|
|
self.items_controller = registry.get_instance("items_controller")
|
|
self.command_alias_service = registry.get_instance("command_alias_service")
|
|
|
|
def pre_start(self):
|
|
self.db.load_sql_file(self.module_dir + "/" + "weapon_attributes.sql", pre_optimized=True)
|
|
self.db.create_view("weapon_attributes")
|
|
|
|
def start(self):
|
|
self.command_alias_service.add_alias("aimshot", "aimedshot")
|
|
self.command_alias_service.add_alias("as", "aimedshot")
|
|
self.command_alias_service.add_alias("inits", "weapon")
|
|
self.command_alias_service.add_alias("init", "weapon")
|
|
self.command_alias_service.add_alias("specials", "weapon")
|
|
self.command_alias_service.add_alias("fling", "flingshot")
|
|
|
|
@command(command="aggdef", params=[Decimal("weapon_attack"), Decimal("weapon_recharge"), Int("init_skill")],
|
|
access_level="member",
|
|
description="Show agg/def information")
|
|
def aggdef_cmd(self, _, weapon_attack, weapon_recharge, init_skill):
|
|
init_result = self.get_init_result(weapon_attack, weapon_recharge, init_skill)
|
|
|
|
inits_full_agg = self.get_inits_needed(100, weapon_attack, weapon_recharge)
|
|
inits_neutral = self.get_inits_needed(87.5, weapon_attack, weapon_recharge)
|
|
inits_full_def = self.get_inits_needed(0, weapon_attack, weapon_recharge)
|
|
|
|
blob = f"Attack: <highlight>{weapon_attack:.2f} secs</highlight>\n"
|
|
blob += f"Recharge: <highlight>{weapon_recharge:.2f} secs</highlight>\n"
|
|
blob += f"Init Skill: <highlight>{init_skill:d}</highlight>\n\n"
|
|
|
|
blob += "You must set your AGG/DEF bar at <highlight>%d%%</highlight> to wield your weapon at 1/1.\n\n" % int(
|
|
init_result)
|
|
|
|
blob += f"Init needed for max speed at Full Agg (100%): <highlight>{inits_full_agg:d}</highlight>\n"
|
|
blob += f"Init needed for max speed at Neutral (87.5%): <highlight>{inits_neutral:d}</highlight>\n"
|
|
blob += f"Init needed for max speed at Full Def (0%): <highlight>{inits_full_def:d}</highlight>\n\n"
|
|
|
|
blob += self.get_inits_display(weapon_attack, weapon_recharge) + "\n\n"
|
|
|
|
blob += "Note that at the neutral position (87.5%), your attack and recharge time " \
|
|
"will match that of the weapon you are using.\n\n\n"
|
|
|
|
blob += "Based on the !aggdef command from Budabot, which was based upon a RINGBOT module made " \
|
|
"by NoGoal(RK2) and modified for Budabot by Healnjoo(RK2)"
|
|
|
|
return ChatBlob("Agg/Def Results", blob)
|
|
|
|
@command(command="aimedshot",
|
|
params=[Decimal("weapon_attack"), Decimal("weapon_recharge"), Int("aimed_shot_skill")],
|
|
access_level="member",
|
|
description="Show aimed shot information")
|
|
def aimedshot_cmd(self, _, weapon_attack, weapon_recharge, aimed_shot_skill):
|
|
as_info = self.get_aimed_shot_info(weapon_attack, weapon_recharge, aimed_shot_skill)
|
|
|
|
blob = f"Attack: <highlight>{weapon_attack:.2f} secs</highlight>\n"
|
|
blob += f"Recharge: <highlight>{weapon_recharge:.2f} secs</highlight>\n"
|
|
blob += f"Aimed Shot Skill: <highlight>{aimed_shot_skill:d}</highlight>\n\n"
|
|
|
|
blob += f"Aimed Shot Multiplier: <highlight>1 - {as_info.multiplier:d}x</highlight>\n"
|
|
blob += f"Aimed Shot Recharge: <highlight>{as_info.recharge:d} secs</highlight>\n\n"
|
|
|
|
blob += f"You need <highlight>{as_info.skill_cap:d}</highlight> Aimed Shot Skill " \
|
|
f"to cap your recharge at <highlight>{as_info.hard_cap:d}</highlight> seconds."
|
|
|
|
return ChatBlob("Aimed Shot Results", blob)
|
|
|
|
@command(command="brawl", params=[Int("brawl_skill")], access_level="member",
|
|
description="Show brawl information")
|
|
def brawl_cmd(self, _, brawl_skill):
|
|
brawl_info = self.get_brawl_info(brawl_skill)
|
|
|
|
blob = f"Brawl Skill: <highlight>{brawl_skill:d}</highlight>\n\n"
|
|
|
|
blob += "Brawl Recharge: <highlight>15 secs</highlight> (constant)\n"
|
|
blob += f"Brawl Damage: <highlight> {brawl_info.min_dmg:d} - {brawl_info.max_dmg:d} " \
|
|
f"({brawl_info.crit_dmg:d})</highlight>\n"
|
|
blob += f"Stun Change: <highlight>{brawl_info.stun_chance:d}%</highlight>\n"
|
|
blob += f"Stun Duration: <highlight>{brawl_info.stun_duration:d} secs</highlight>\n\n"
|
|
|
|
blob += "Stun chance is 10% for brawl skill less than 1000 and 20% for brawl skill 1000 or greater.\n"
|
|
blob += "Stun duration is 3 seconds for brawl skill less than 2001 and " \
|
|
"4 seconds for brawl skill 2001 or greater.\n\n\n"
|
|
|
|
blob += "Based on the !brawl command from Budabot by Imoutochan (RK1)"
|
|
|
|
return ChatBlob("Brawl Results", blob)
|
|
|
|
@command(command="burst",
|
|
params=[Decimal("weapon_attack"), Decimal("weapon_recharge"), Int("burst_recharge"), Int("burst_skill")],
|
|
access_level="member",
|
|
description="Show burst information")
|
|
def burst_cmd(self, _, weapon_attack, weapon_recharge, burst_recharge, burst_skill):
|
|
burst_info = self.get_burst_info(weapon_attack, weapon_recharge, burst_recharge, burst_skill)
|
|
|
|
blob = f"Attack: <highlight>{weapon_attack:.2f} secs</highlight>\n"
|
|
blob += f"Recharge: <highlight>{weapon_recharge:.2f} secs</highlight>\n"
|
|
blob += f"Burst Recharge: <highlight>{burst_recharge:d}</highlight>\n"
|
|
blob += f"Burst Skill: <highlight>{burst_skill:d}</highlight>\n\n"
|
|
|
|
blob += f"Burst Recharge: <highlight>{burst_info.recharge:d} secs</highlight>\n\n"
|
|
|
|
blob += f"You need <highlight>{burst_info.skill_cap:d}</highlight> Burst Skill " \
|
|
f"to cap your recharge at <highlight>{burst_info.hard_cap:d} secs</highlight>."
|
|
|
|
return ChatBlob("Burst Results", blob)
|
|
|
|
@command(command="dimach", params=[Int("dimach_skill")], access_level="member",
|
|
description="Show dimach information")
|
|
def dimach_cmd(self, _, dimach_skill):
|
|
dimach_info = self.get_dimach_info(dimach_skill)
|
|
|
|
blob = f"Dimach Skill: <highlight>{dimach_skill:d}</highlight>\n\n"
|
|
|
|
blob += "<header2>Martial Artist</header2>\n"
|
|
blob += f"Damage: <highlight>{dimach_info.ma_dmg:d}</highlight>\n"
|
|
blob += f"Recharge: <highlight>{self.util.time_to_readable(dimach_info.ma_recharge)}</highlight>\n\n"
|
|
|
|
blob += "<header2>Keeper</header2>\n"
|
|
blob += f"Self Heal: <highlight>{dimach_info.keeper_heal:d}</highlight>\n"
|
|
blob += "Recharge: <highlight>5 mins</highlight> (constant)\n\n"
|
|
|
|
blob += "<header2>Shade</header2>\n"
|
|
blob += f"Damage: <highlight>{dimach_info.shade_dmg:d}</highlight>\n"
|
|
blob += f"Self Heal: <highlight>{dimach_info.shade_heal_percentage:d}%</highlight> " \
|
|
f"* <highlight>{dimach_info.shade_dmg:d}</highlight> " \
|
|
f"= <highlight>{round(dimach_info.shade_heal_percentage * dimach_info.shade_dmg / 100):d}</highlight>\n"
|
|
blob += f"Recharge: <highlight>{self.util.time_to_readable(dimach_info.shade_recharge)}</highlight>\n\n"
|
|
|
|
blob += "<header2>All other professions</header2>\n"
|
|
blob += f"Damage: <highlight>{dimach_info.general_dmg:d}</highlight>\n"
|
|
blob += "Recharge: <highlight>30 mins</highlight> (constant)\n\n\n"
|
|
|
|
blob += "Based on the !dimach command from Budabot by Imoutochan (RK1)"
|
|
|
|
return ChatBlob("Dimach Results", blob)
|
|
|
|
@command(command="fastattack", params=[Decimal("weapon_attack"), Int("fast_attack_skill")], access_level="member",
|
|
description="Show fast attack information")
|
|
def fastattack_cmd(self, _, weapon_attack, fast_attack_skill):
|
|
fast_attack_info = self.get_fast_attack_info(weapon_attack, fast_attack_skill)
|
|
|
|
blob = f"Attack: <highlight>{weapon_attack:.2f} secs</highlight>\n"
|
|
blob += f"Fast Attack Skill: <highlight>{fast_attack_skill:d}</highlight>\n\n"
|
|
|
|
blob += f"Fast Attack Recharge: <highlight>{fast_attack_info.recharge:.2f} secs</highlight>\n\n"
|
|
|
|
blob += f"You need <highlight>{fast_attack_info.skill_cap:d}</highlight> Fast Attack Skill " \
|
|
f"to cap your recharge at <highlight>{fast_attack_info.hard_cap:.2f} secs</highlight>."
|
|
|
|
return ChatBlob("Fast Attack Results", blob)
|
|
|
|
@command(command="flingshot", params=[Decimal("weapon_attack"), Int("fling_shot_skill")], access_level="member",
|
|
description="Show fling shot information")
|
|
def flingshot_cmd(self, _, weapon_attack, fling_shot_skill):
|
|
fling_shot_info = self.get_fling_shot_info(weapon_attack, fling_shot_skill)
|
|
|
|
blob = f"Attack: <highlight>{weapon_attack:.2f} secs</highlight>\n"
|
|
blob += f"Fling Shot Skill: <highlight>{fling_shot_skill:d}</highlight>\n\n"
|
|
|
|
blob += f"Fling Shot Recharge: <highlight>{fling_shot_info.recharge:.2f} secs</highlight>\n\n"
|
|
|
|
blob += f"You need <highlight>{fling_shot_info.skill_cap:d}</highlight> Fling Shot Skill " \
|
|
f"to cap your recharge at <highlight>{fling_shot_info.hard_cap:.2f} secs</highlight>."
|
|
|
|
return ChatBlob("Fling Shot Results", blob)
|
|
|
|
@command(command="fullauto",
|
|
params=[Decimal("weapon_attack"), Decimal("weapon_recharge"), Int("full_auto_recharge"),
|
|
Int("full_auto_skill")], access_level="member",
|
|
description="Show full auto information")
|
|
def fullauto_cmd(self, _, weapon_attack, weapon_recharge, full_auto_recharge, full_auto_skill):
|
|
full_auto_info = self.get_full_auto_info(weapon_attack, weapon_recharge, full_auto_recharge, full_auto_skill)
|
|
|
|
blob = f"Attack: <highlight>{weapon_attack:.2f} secs</highlight>\n"
|
|
blob += f"Recharge: <highlight>{weapon_recharge:.2f} secs</highlight>\n"
|
|
blob += f"Full Auto Recharge: <highlight>{full_auto_recharge:.2f}</highlight>\n"
|
|
blob += f"Full Auto Skill: <highlight>{full_auto_skill:.2f}</highlight>\n\n"
|
|
|
|
blob += f"Full Auto Recharge: <highlight>{full_auto_info.recharge:.2f} secs</highlight>\n"
|
|
blob += f"Max Number of Bullets: <highlight>{full_auto_info.max_bullets:.2f}</highlight>\n\n"
|
|
|
|
blob += f"You need <highlight>{full_auto_info.skill_cap:.2f}</highlight> Full Auto Skill " \
|
|
f"to cap your recharge at <highlight>{full_auto_info.hard_cap:.2f} secs</highlight>.\n\n"
|
|
|
|
blob += "From <highlight>0 to 10K</highlight> damage, the bullet damage is unchanged.\n"
|
|
blob += "From <highlight>10K to 11.5K</highlight> damage, each bullet damage is halved.\n"
|
|
blob += "From <highlight>11K to 15K</highlight> damage, each bullet damage is halved again.\n"
|
|
blob += "<highlight>15K</highlight> is the damage cap."
|
|
|
|
return ChatBlob("Full Auto Results", blob)
|
|
|
|
@command(command="mafist", params=[Int("ma_skill")], access_level="member",
|
|
description="Show martial arts information")
|
|
def mafist_cmd(self, _, ma_skill):
|
|
ma_info = self.get_martial_arts_info(ma_skill)
|
|
|
|
blob = f"Martial Arts Skill: <highlight>{ma_skill:d}</highlight>\n\n"
|
|
|
|
blob += "<header2>Martial Artist</header2>\n"
|
|
blob += f"Speed: <highlight>{ma_info.ma_speed:.2f} / {ma_info.ma_speed:.2f} secs</highlight>\n"
|
|
blob += f"Damage: <highlight>{ma_info.ma_min_dmg:d} - {ma_info.ma_max_dmg:d} " \
|
|
f"({ma_info.ma_crit_dmg:d})</highlight>\n\n"
|
|
|
|
blob += "<header2>Shade</header2>\n"
|
|
blob += f"Speed: <highlight>{ma_info.shade_speed:.2f} / {ma_info.shade_speed:.2f} secs</highlight>\n"
|
|
blob += f"Damage: <highlight>{ma_info.shade_min_dmg:d} - {ma_info.shade_max_dmg:d} " \
|
|
f"({ma_info.shade_crit_dmg:d})</highlight>\n\n"
|
|
|
|
blob += "<header2>All other professions</header2>\n"
|
|
blob += f"Speed: <highlight>{ma_info.gen_speed:.2f} / {ma_info.gen_speed:.2f} secs</highlight>\n"
|
|
blob += f"Damage: <highlight>{ma_info.gen_min_dmg:d} - {ma_info.gen_max_dmg:d} " \
|
|
f"({ma_info.gen_crit_dmg:d})</highlight>\n\n"
|
|
|
|
return ChatBlob("Martial Arts Results", blob)
|
|
|
|
@command(command="nanoinit", params=[Decimal("nano_attack_time"), Int("nano_cast_init")], access_level="member",
|
|
description="Show nano cast init information")
|
|
def nanoinit_cmd(self, _, nano_attack_time, nano_cast_init):
|
|
nano_cast_info = self.get_nano_cast_info(nano_cast_init, nano_attack_time)
|
|
|
|
blob = f"Attack: <highlight>{nano_attack_time:.2f} secs</highlight>\n"
|
|
blob += f"Nano Cast Init: <highlight>{nano_cast_init}</highlight>\n\n"
|
|
|
|
blob += f"Cast Time Reduction: <highlight>{nano_cast_info.cast_time_reduction:.2f}</highlight>\n"
|
|
blob += f"Effective Cast Time: <highlight>{nano_cast_info.effective_cast_time:.2f}</highlight>\n\n"
|
|
|
|
if nano_cast_info.bar_setting > 100:
|
|
blob += "You cannot instacast this nano at any AGG/DEF setting.\n\n"
|
|
else:
|
|
blob += f"You must set your AGG/DEF bar to <highlight>{nano_cast_info.bar_setting:d}%</highlight> " \
|
|
f"to instacast this nano.\n\n"
|
|
|
|
blob += f"NanoC. Init needed to instacast at Full Agg (100%): " \
|
|
f"<highlight>{nano_cast_info.instacast_full_agg:.2f}</highlight>\n"
|
|
blob += f"NanoC. Init needed to instacast at Neutral (87.5%): " \
|
|
f"<highlight>{nano_cast_info.instacast_neutral:.2f}</highlight>\n"
|
|
blob += f"NanoC. Init needed to instacast at Half (50%): " \
|
|
f"<highlight>{nano_cast_info.instacast_half:.2f}</highlight>\n"
|
|
blob += f"NanoC. Init needed to instacast at Full Def (0%): " \
|
|
f"<highlight>{nano_cast_info.instacast_full_def:.2f}</highlight>\n\n"
|
|
|
|
blob += f"Cast time at Full Agg (100%): <highlight>{nano_cast_info.cast_time_full_agg:.2f}</highlight>\n"
|
|
blob += f"Cast time at Neutral (87.5%): <highlight>{nano_cast_info.cast_time_neutral:.2f}</highlight>\n"
|
|
blob += f"Cast time at Half (50%): <highlight>{nano_cast_info.cast_time_half:.2f}</highlight>\n"
|
|
blob += f"Cast time at Full Def (0%): <highlight>{nano_cast_info.cast_time_full_def:.2f}</highlight>"
|
|
|
|
return ChatBlob("Nano Cast Init Results", blob)
|
|
|
|
@command(command="weapon", params=[Int("item_id"), Int("ql", is_optional=True)], access_level="member",
|
|
description="Show weapon information")
|
|
def weapon_cmd(self, _, item_id, ql):
|
|
return self.get_weapon_info(item_id, ql)
|
|
|
|
@command(command="deflect", params=[Int("deflect_skill")], access_level="member",
|
|
description="Show deflect information")
|
|
def deflect_cmd(self, _, deflect_skill):
|
|
if deflect_skill > 3000 or deflect_skill < 1:
|
|
return "Your deflect skill can only be between 1 and 3000"
|
|
return f"With {deflect_skill} skill you will deflect {round(deflect_skill / 300, 2)}% of attacks"
|
|
|
|
@command(command="weapon", params=[Item("weapon_link")], access_level="member",
|
|
description="Show weapon information")
|
|
def weapon_cmd(self, _, item):
|
|
return self.get_weapon_info(item.high_id, item.ql)
|
|
|
|
@command(command="weapon", params=[Int("item_id"), Int("ql", is_optional=True)], access_level="member",
|
|
description="Show weapon information")
|
|
def weapon_manual_cmd(self, _, item_id, ql):
|
|
if not ql:
|
|
item = self.items_controller.get_by_item_id(item_id)
|
|
if item:
|
|
ql = item.highql
|
|
else:
|
|
return f"Could not find item with id <highlight>{item_id:d}</highlight>."
|
|
|
|
return self.get_weapon_info(item_id, ql)
|
|
|
|
def get_weapon_info(self, item_id, ql):
|
|
if ql:
|
|
item = self.db.query_single(
|
|
"SELECT * FROM aodb WHERE highid = ? AND lowql <= ? AND highql >= ? "
|
|
"UNION "
|
|
"SELECT * FROM aodb WHERE lowid = ? AND lowql <= ? AND highql >= ? "
|
|
"LIMIT 1", [item_id, ql, ql, item_id, ql, ql])
|
|
else:
|
|
item = self.db.query_single(
|
|
"SELECT * FROM aodb WHERE highid = ? UNION SELECT * FROM aodb WHERE lowid = ? LIMIT 1",
|
|
[item_id, item_id])
|
|
|
|
if not item:
|
|
return f"Could not find item with ID <highlight>{item_id}</highlight>."
|
|
|
|
ql = ql or item.highql
|
|
|
|
low_attributes = self.db.query_single("SELECT * FROM weapon_attributes WHERE id = ?", [item.lowid])
|
|
high_attributes = self.db.query_single("SELECT * FROM weapon_attributes WHERE id = ?", [item.highid])
|
|
|
|
if not low_attributes or not high_attributes:
|
|
return f"Could not find weapon information or item is not a weapon for " \
|
|
f"ID <highlight>{item_id}</highlight>."
|
|
|
|
weapon_attack = self.util.interpolate_value(ql, {item.lowql: low_attributes.attack_time,
|
|
item.highql: high_attributes.attack_time}) / 100
|
|
weapon_recharge = self.util.interpolate_value(ql, {item.lowql: low_attributes.recharge_time,
|
|
item.highql: high_attributes.recharge_time}) / 100
|
|
|
|
blob = f"{self.text.make_item(item.lowid, item.highid, ql, item.name)} (QL {ql:d})\n\n"
|
|
blob += f"Attack: <highlight>{weapon_attack:.2f}</highlight>\n"
|
|
blob += f"Recharge: <highlight>{weapon_recharge:.2f}</highlight>\n\n"
|
|
|
|
blob += self.get_inits_display(weapon_attack, weapon_recharge) + "\n"
|
|
|
|
blob += "<header2>Specials</header2>\n"
|
|
|
|
if high_attributes.aimed_shot:
|
|
as_info = self.get_aimed_shot_info(weapon_attack, weapon_recharge, 1)
|
|
blob += f"Aimed Shot\n<highlight>{as_info.skill_cap:.0f}</highlight> skill needed to cap " \
|
|
f"Aimed Shot recharge at <highlight>{as_info.hard_cap:.2f} secs</highlight>\n\n"
|
|
|
|
if high_attributes.burst:
|
|
burst_recharge = self.util.interpolate_value(ql, {item.lowql: low_attributes.burst,
|
|
item.highql: high_attributes.burst})
|
|
burst_info = self.get_burst_info(weapon_attack, weapon_recharge, burst_recharge, 1)
|
|
blob += f"Burst Recharge: <highlight>{burst_recharge:.2f}</highlight>\n" \
|
|
f"<highlight>{burst_info.skill_cap:d}</highlight> skill needed " \
|
|
f"to cap Burst recharge at <highlight>{burst_info.hard_cap:.2f} secs</highlight>\n\n"
|
|
|
|
if high_attributes.fast_attack:
|
|
fast_attack_info = self.get_fast_attack_info(weapon_attack, 1)
|
|
blob += f"Fast Attack\n<highlight>{fast_attack_info.skill_cap:.0f}</highlight> skill needed " \
|
|
f"to cap Fast Attack recharge at <highlight>{fast_attack_info.hard_cap:.2f} secs</highlight>\n\n"
|
|
|
|
if high_attributes.fling_shot:
|
|
fling_shot_info = self.get_fling_shot_info(weapon_attack, 1)
|
|
blob += f"Fling Shot\n<highlight>{fling_shot_info.skill_cap}</highlight> skill needed " \
|
|
f"to cap Fling Shot recharge at <highlight>{fling_shot_info.hard_cap:.2f} secs</highlight>\n\n"
|
|
|
|
if high_attributes.full_auto:
|
|
full_auto_recharge = self.util.interpolate_value(ql, {item.lowql: low_attributes.full_auto,
|
|
item.highql: high_attributes.full_auto})
|
|
full_auto_info = self.get_full_auto_info(weapon_attack, weapon_recharge, full_auto_recharge, 1)
|
|
blob += f"Full Auto Recharge: <highlight>{full_auto_recharge:.2f}</highlight>\n" \
|
|
f"<highlight>{full_auto_info.skill_cap:.0f}</highlight> skill needed " \
|
|
f"to cap Full Auto recharge at <highlight>{full_auto_info.hard_cap:.2f} secs</highlight>\n\n"
|
|
|
|
return ChatBlob(f"Weapon Info for {item.name} (QL {ql})", blob)
|
|
|
|
def get_init_result(self, weapon_attack, weapon_recharge, init_skill):
|
|
if init_skill < 1200:
|
|
attack_calc = (((weapon_attack - (init_skill / 600)) - 1) / 0.02) + 87.5
|
|
recharge_calc = (((weapon_recharge - (init_skill / 300)) - 1) / 0.02) + 87.5
|
|
else:
|
|
attack_calc = (((weapon_attack - (1200 / 600) - ((init_skill - 1200) / 600 / 3)) - 1) / 0.02) + 87.5
|
|
recharge_calc = (((weapon_recharge - (1200 / 300) - ((init_skill - 1200) / 300 / 3)) - 1) / 0.02) + 87.5
|
|
|
|
if attack_calc < recharge_calc:
|
|
init_result = recharge_calc
|
|
else:
|
|
init_result = attack_calc
|
|
|
|
init_result = min(init_result, 100) # max of 100
|
|
init_result = max(init_result, 0) # min of 0
|
|
|
|
return init_result
|
|
|
|
def get_inits_needed(self, init_result, weapon_attack, weapon_recharge):
|
|
inits_attack = (((init_result - 87.5) * 0.02) + 1 - weapon_attack) * -600
|
|
inits_recharge = (((init_result - 87.5) * 0.02) + 1 - weapon_recharge) * -300
|
|
|
|
if inits_attack > 1200:
|
|
inits_attack = ((((init_result - 87.5) * 0.02) + 1 - weapon_attack + 2) * -1800) + 1200
|
|
|
|
if inits_recharge > 1200:
|
|
inits_recharge = ((((init_result - 87.5) * 0.02) + 1 - weapon_attack + 4) * -900) + 1200
|
|
|
|
if inits_attack < inits_recharge:
|
|
return inits_recharge
|
|
else:
|
|
return inits_attack
|
|
|
|
def get_aimed_shot_info(self, weapon_attack, weapon_recharge, aimed_shot_skill):
|
|
result = DictObject()
|
|
result.multiplier = round(aimed_shot_skill / 95)
|
|
result.hard_cap = math.floor(weapon_attack + 10)
|
|
result.skill_cap = math.ceil((4000 * weapon_recharge - 1100) / 3)
|
|
|
|
as_recharge = math.ceil((weapon_recharge * 40) - (aimed_shot_skill * 3 / 100) + weapon_attack - 1)
|
|
if as_recharge < result.hard_cap:
|
|
as_recharge = result.hard_cap
|
|
|
|
result.recharge = as_recharge
|
|
|
|
return result
|
|
|
|
def get_brawl_info(self, brawl_skill):
|
|
min_values = {1: 1, 1000: 100, 1001: 101, 2000: 170, 2001: 171, 3000: 235}
|
|
max_values = {1: 2, 1000: 500, 1001: 501, 2000: 850, 2001: 851, 3000: 1145}
|
|
crit_values = {1: 3, 1000: 500, 1001: 501, 2000: 600, 2001: 601, 3000: 725}
|
|
|
|
brawl_info = DictObject()
|
|
brawl_info.min_dmg = self.util.interpolate_value(brawl_skill, min_values)
|
|
brawl_info.max_dmg = self.util.interpolate_value(brawl_skill, max_values)
|
|
brawl_info.crit_dmg = self.util.interpolate_value(brawl_skill, crit_values)
|
|
brawl_info.stun_chance = 10 if brawl_skill < 1000 else 20
|
|
brawl_info.stun_duration = 3 if brawl_skill < 2001 else 4
|
|
|
|
return brawl_info
|
|
|
|
def get_burst_info(self, weapon_attack, weapon_recharge, burst_recharge, burst_skill):
|
|
result = DictObject()
|
|
result.hard_cap = round(weapon_attack + 8)
|
|
result.skill_cap = math.floor(((weapon_recharge * 20) + (burst_recharge / 100) - 8) * 25)
|
|
|
|
recharge = math.floor((weapon_recharge * 20) + (burst_recharge / 100) - (burst_skill / 25) + weapon_attack)
|
|
if recharge < result.hard_cap:
|
|
recharge = result.hard_cap
|
|
|
|
result.recharge = recharge
|
|
|
|
return result
|
|
|
|
def get_dimach_info(self, dimach_skill):
|
|
# item ids: 42033, 42032, 213260, 213261, 213262, 213263
|
|
general_dmg = {1: 1, 1000: 2000, 1001: 2001, 2000: 2500, 2001: 2501, 3000: 2850}
|
|
|
|
# item ids: 42033, 42032, 213264, 213265, 213266, 213267
|
|
ma_recharge = {1: 1800, 1000: 1800, 1001: 1188, 2000: 600, 2001: 600, 3000: 300}
|
|
ma_dmg = {1: 1, 1000: 2000, 1001: 2001, 2000: 2340, 2001: 2341, 3000: 2550}
|
|
|
|
# item ids: 213269, 213270, 213271, 213272, 213273, 213274
|
|
shade_recharge = {1: 300, 1000: 300, 1001: 300, 2000: 300, 2001: 240, 3000: 200}
|
|
shade_dmg = {1: 1, 1000: 920, 1001: 921, 2000: 1872, 2001: 1873, 3000: 2750}
|
|
shade_heal_percentage = {1: 70, 1000: 70, 1001: 70, 2000: 75, 2001: 75, 3000: 80}
|
|
|
|
# item ids: 211399, 211400, 213275, 213276, 213277, 213278
|
|
keeper_heal = {1: 1, 1000: 3000, 1001: 3001, 2000: 10500, 2001: 10501, 3000: 15000}
|
|
|
|
result = DictObject()
|
|
result.general_dmg = self.util.interpolate_value(dimach_skill, general_dmg)
|
|
result.ma_recharge = self.util.interpolate_value(dimach_skill, ma_recharge)
|
|
result.ma_dmg = self.util.interpolate_value(dimach_skill, ma_dmg)
|
|
result.shade_recharge = self.util.interpolate_value(dimach_skill, shade_recharge)
|
|
result.shade_dmg = self.util.interpolate_value(dimach_skill, shade_dmg)
|
|
result.shade_heal_percentage = self.util.interpolate_value(dimach_skill, shade_heal_percentage)
|
|
result.keeper_heal = self.util.interpolate_value(dimach_skill, keeper_heal)
|
|
|
|
return result
|
|
|
|
def get_fast_attack_info(self, weapon_attack, fast_attack_skill):
|
|
result = DictObject()
|
|
result.hard_cap = weapon_attack + 5
|
|
result.skill_cap = ((weapon_attack * 16) - result.hard_cap) * 100
|
|
|
|
recharge = (weapon_attack * 16) - (fast_attack_skill / 100)
|
|
if recharge < result.hard_cap:
|
|
recharge = result.hard_cap
|
|
|
|
result.recharge = recharge
|
|
|
|
return result
|
|
|
|
def get_fling_shot_info(self, weapon_attack, fling_shot_skill):
|
|
result = DictObject()
|
|
result.hard_cap = weapon_attack + 5
|
|
result.skill_cap = ((weapon_attack * 16) - result.hard_cap) * 100
|
|
|
|
recharge = (weapon_attack * 16) - (fling_shot_skill / 100)
|
|
if recharge < result.hard_cap:
|
|
recharge = result.hard_cap
|
|
|
|
result.recharge = recharge
|
|
|
|
return result
|
|
|
|
def get_full_auto_info(self, weapon_attack, weapon_recharge, full_auto_recharge, full_auto_skill):
|
|
result = DictObject()
|
|
result.hard_cap = math.floor(weapon_attack + 10)
|
|
result.skill_cap = ((weapon_recharge * 40) + (full_auto_recharge / 100) - 11) * 25
|
|
result.max_bullets = 5 + math.floor(full_auto_skill / 100)
|
|
|
|
recharge = round(
|
|
(weapon_recharge * 40) + (full_auto_recharge / 100) - (full_auto_skill / 25) + round(weapon_attack - 1))
|
|
if recharge < result.hard_cap:
|
|
recharge = result.hard_cap
|
|
|
|
result.recharge = recharge
|
|
|
|
return result
|
|
|
|
def get_martial_arts_info(self, ma_skill):
|
|
result = DictObject()
|
|
|
|
# ma items: http://budabot.com/forum/viewtopic.php?f=7&t=1264&p=5739#p5739
|
|
# QL 1 100 500 1 500 1 500
|
|
# 211349, 211350, 211351, 211359, 211360, 211365, 211366 // Shade
|
|
# 211352, 211353, 211354, 211357, 211358, 211363, 211364 // MA
|
|
# 43712, 144745, 43713, 211355, 211356, 211361, 211362 // Gen/other
|
|
|
|
ma_min_dmg = {1: 4, 200: 45, 1000: 125, 1001: 130, 2000: 220, 2001: 225, 3000: 450}
|
|
ma_max_dmg = {1: 8, 200: 75, 1000: 400, 1001: 405, 2000: 830, 2001: 831, 3000: 1300}
|
|
ma_crit_dmg = {1: 3, 200: 50, 1000: 500, 1001: 501, 2000: 560, 2001: 561, 3000: 800}
|
|
ma_speed = {1: 1.15, 200: 1.20, 1000: 1.25, 1001: 1.30, 2000: 1.35, 2001: 1.45, 3000: 1.50}
|
|
|
|
shade_min_dmg = {1: 3, 200: 25, 1000: 55, 1001: 56, 2000: 130, 2001: 131, 3000: 280}
|
|
shade_max_dmg = {1: 5, 200: 60, 1000: 258, 1001: 259, 2000: 682, 2001: 683, 3000: 890}
|
|
shade_crit_dmg = {1: 3, 200: 50, 1000: 250, 1001: 251, 2000: 275, 2001: 276, 3000: 300}
|
|
shade_speed = {1: 1.25, 200: 1.25, 1000: 1.45, 1001: 1.45, 2000: 1.65, 2001: 1.65, 3000: 1.85}
|
|
|
|
gen_min_dmg = {1: 3, 200: 25, 1000: 65, 1001: 66, 2000: 140, 2001: 204, 3000: 300}
|
|
gen_max_dmg = {1: 5, 200: 60, 1000: 280, 1001: 281, 2000: 715, 2001: 831, 3000: 990}
|
|
gen_crit_dmg = {1: 3, 200: 50, 1000: 500, 1001: 501, 2000: 605, 2001: 605, 3000: 630}
|
|
gen_speed = {1: 1.25, 200: 1.25, 1000: 1.45, 1001: 1.45, 2000: 1.65, 2001: 1.65, 3000: 1.85}
|
|
|
|
result.ma_min_dmg = self.util.interpolate_value(ma_skill, ma_min_dmg)
|
|
result.ma_max_dmg = self.util.interpolate_value(ma_skill, ma_max_dmg)
|
|
result.ma_crit_dmg = self.util.interpolate_value(ma_skill, ma_crit_dmg)
|
|
result.ma_speed = self.util.interpolate_value(ma_skill, ma_speed, 2)
|
|
|
|
result.shade_min_dmg = self.util.interpolate_value(ma_skill, shade_min_dmg)
|
|
result.shade_max_dmg = self.util.interpolate_value(ma_skill, shade_max_dmg)
|
|
result.shade_crit_dmg = self.util.interpolate_value(ma_skill, shade_crit_dmg)
|
|
result.shade_speed = self.util.interpolate_value(ma_skill, shade_speed, 2)
|
|
|
|
result.gen_min_dmg = self.util.interpolate_value(ma_skill, gen_min_dmg)
|
|
result.gen_max_dmg = self.util.interpolate_value(ma_skill, gen_max_dmg)
|
|
result.gen_crit_dmg = self.util.interpolate_value(ma_skill, gen_crit_dmg)
|
|
result.gen_speed = self.util.interpolate_value(ma_skill, gen_speed, 2)
|
|
|
|
return result
|
|
|
|
def get_nano_cast_info(self, nano_cast_init, nano_attack_time):
|
|
if nano_cast_init > 1200:
|
|
nano_cast_reduction = (nano_cast_init - 1200) / 600 + 6
|
|
else:
|
|
nano_cast_reduction = nano_cast_init / 200
|
|
|
|
result = DictObject()
|
|
result.cast_time_reduction = nano_cast_reduction
|
|
result.effective_cast_time = nano_attack_time - nano_cast_reduction
|
|
result.instacast_full_agg = self.get_nano_init_for_instacast(nano_attack_time - 1)
|
|
result.instacast_neutral = self.get_nano_init_for_instacast(nano_attack_time - 0.75)
|
|
result.instacast_half = self.get_nano_init_for_instacast(nano_attack_time)
|
|
result.instacast_full_def = self.get_nano_init_for_instacast(nano_attack_time + 1)
|
|
result.cast_time_full_agg = result.effective_cast_time - 1
|
|
result.cast_time_neutral = result.effective_cast_time - 0.75
|
|
result.cast_time_half = result.effective_cast_time
|
|
result.cast_time_full_def = result.effective_cast_time + 1
|
|
|
|
bar_setting = round(result.effective_cast_time / 0.02 + 50)
|
|
if bar_setting < 0:
|
|
bar_setting = 0
|
|
result.bar_setting = bar_setting
|
|
|
|
return result
|
|
|
|
def get_nano_init_for_instacast(self, nano_attack_time):
|
|
if nano_attack_time < 6:
|
|
return nano_attack_time * 200
|
|
else:
|
|
return 1200 + (nano_attack_time - 6) * 600
|
|
|
|
def get_inits_display(self, weapon_attack, weapon_recharge):
|
|
num_steps = 9
|
|
step_size = 100 / (num_steps - 1)
|
|
blob = ""
|
|
for i in reversed(range(0, num_steps)):
|
|
inits_needed = self.get_inits_needed(i * step_size, weapon_attack, weapon_recharge)
|
|
blob += f"DEF [{'=' * i}||{'=' * (num_steps - 1 - i)}] AGG {i * step_size:.0f}% {inits_needed:.0f} init \n"
|
|
|
|
return blob
|