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: {weapon_attack:.2f} secs\n"
blob += f"Recharge: {weapon_recharge:.2f} secs\n"
blob += f"Init Skill: {init_skill:d}\n\n"
blob += "You must set your AGG/DEF bar at %d%% to wield your weapon at 1/1.\n\n" % int(
init_result)
blob += f"Init needed for max speed at Full Agg (100%): {inits_full_agg:d}\n"
blob += f"Init needed for max speed at Neutral (87.5%): {inits_neutral:d}\n"
blob += f"Init needed for max speed at Full Def (0%): {inits_full_def:d}\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: {weapon_attack:.2f} secs\n"
blob += f"Recharge: {weapon_recharge:.2f} secs\n"
blob += f"Aimed Shot Skill: {aimed_shot_skill:d}\n\n"
blob += f"Aimed Shot Multiplier: 1 - {as_info.multiplier:d}x\n"
blob += f"Aimed Shot Recharge: {as_info.recharge:d} secs\n\n"
blob += f"You need {as_info.skill_cap:d} Aimed Shot Skill " \
f"to cap your recharge at {as_info.hard_cap:d} 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: {brawl_skill:d}\n\n"
blob += "Brawl Recharge: 15 secs (constant)\n"
blob += f"Brawl Damage: {brawl_info.min_dmg:d} - {brawl_info.max_dmg:d} " \
f"({brawl_info.crit_dmg:d})\n"
blob += f"Stun Change: {brawl_info.stun_chance:d}%\n"
blob += f"Stun Duration: {brawl_info.stun_duration:d} secs\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: {weapon_attack:.2f} secs\n"
blob += f"Recharge: {weapon_recharge:.2f} secs\n"
blob += f"Burst Recharge: {burst_recharge:d}\n"
blob += f"Burst Skill: {burst_skill:d}\n\n"
blob += f"Burst Recharge: {burst_info.recharge:d} secs\n\n"
blob += f"You need {burst_info.skill_cap:d} Burst Skill " \
f"to cap your recharge at {burst_info.hard_cap:d} secs."
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: {dimach_skill:d}\n\n"
blob += "Martial Artist\n"
blob += f"Damage: {dimach_info.ma_dmg:d}\n"
blob += f"Recharge: {self.util.time_to_readable(dimach_info.ma_recharge)}\n\n"
blob += "Keeper\n"
blob += f"Self Heal: {dimach_info.keeper_heal:d}\n"
blob += "Recharge: 5 mins (constant)\n\n"
blob += "Shade\n"
blob += f"Damage: {dimach_info.shade_dmg:d}\n"
blob += f"Self Heal: {dimach_info.shade_heal_percentage:d}% " \
f"* {dimach_info.shade_dmg:d} " \
f"= {round(dimach_info.shade_heal_percentage * dimach_info.shade_dmg / 100):d}\n"
blob += f"Recharge: {self.util.time_to_readable(dimach_info.shade_recharge)}\n\n"
blob += "All other professions\n"
blob += f"Damage: {dimach_info.general_dmg:d}\n"
blob += "Recharge: 30 mins (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: {weapon_attack:.2f} secs\n"
blob += f"Fast Attack Skill: {fast_attack_skill:d}\n\n"
blob += f"Fast Attack Recharge: {fast_attack_info.recharge:.2f} secs\n\n"
blob += f"You need {fast_attack_info.skill_cap:d} Fast Attack Skill " \
f"to cap your recharge at {fast_attack_info.hard_cap:.2f} secs."
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: {weapon_attack:.2f} secs\n"
blob += f"Fling Shot Skill: {fling_shot_skill:d}\n\n"
blob += f"Fling Shot Recharge: {fling_shot_info.recharge:.2f} secs\n\n"
blob += f"You need {fling_shot_info.skill_cap:d} Fling Shot Skill " \
f"to cap your recharge at {fling_shot_info.hard_cap:.2f} secs."
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: {weapon_attack:.2f} secs\n"
blob += f"Recharge: {weapon_recharge:.2f} secs\n"
blob += f"Full Auto Recharge: {full_auto_recharge:.2f}\n"
blob += f"Full Auto Skill: {full_auto_skill:.2f}\n\n"
blob += f"Full Auto Recharge: {full_auto_info.recharge:.2f} secs\n"
blob += f"Max Number of Bullets: {full_auto_info.max_bullets:.2f}\n\n"
blob += f"You need {full_auto_info.skill_cap:.2f} Full Auto Skill " \
f"to cap your recharge at {full_auto_info.hard_cap:.2f} secs.\n\n"
blob += "From 0 to 10K damage, the bullet damage is unchanged.\n"
blob += "From 10K to 11.5K damage, each bullet damage is halved.\n"
blob += "From 11K to 15K damage, each bullet damage is halved again.\n"
blob += "15K 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: {ma_skill:d}\n\n"
blob += "Martial Artist\n"
blob += f"Speed: {ma_info.ma_speed:.2f} / {ma_info.ma_speed:.2f} secs\n"
blob += f"Damage: {ma_info.ma_min_dmg:.2f} - {ma_info.ma_max_dmg:.2f} " \
f"({ma_info.ma_crit_dmg:.2f})\n\n"
blob += "Shade\n"
blob += f"Speed: {ma_info.shade_speed:.2f} / {ma_info.shade_speed:.2f} secs\n"
blob += f"Damage: {ma_info.shade_min_dmg:.2f} - {ma_info.shade_max_dmg:.2f} " \
f"({ma_info.shade_crit_dmg:.2f})\n\n"
blob += "All other professions\n"
blob += f"Speed: {ma_info.gen_speed:.2f} / {ma_info.gen_speed:.2f} secs\n"
blob += f"Damage: {ma_info.gen_min_dmg:.2f} - {ma_info.gen_max_dmg:.2f} " \
f"({ma_info.gen_crit_dmg:.2f})\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: {nano_attack_time:.2f} secs\n"
blob += f"Nano Cast Init: {nano_cast_init}\n\n"
blob += f"Cast Time Reduction: {nano_cast_info.cast_time_reduction:.2f}\n"
blob += f"Effective Cast Time: {nano_cast_info.effective_cast_time:.2f}\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 {nano_cast_info.bar_setting:d}% " \
f"to instacast this nano.\n\n"
blob += f"NanoC. Init needed to instacast at Full Agg (100%): " \
f"{nano_cast_info.instacast_full_agg:.2f}\n"
blob += f"NanoC. Init needed to instacast at Neutral (87.5%): " \
f"{nano_cast_info.instacast_neutral:.2f}\n"
blob += f"NanoC. Init needed to instacast at Half (50%): " \
f"{nano_cast_info.instacast_half:.2f}\n"
blob += f"NanoC. Init needed to instacast at Full Def (0%): " \
f"{nano_cast_info.instacast_full_def:.2f}\n\n"
blob += f"Cast time at Full Agg (100%): {nano_cast_info.cast_time_full_agg:.2f}\n"
blob += f"Cast time at Neutral (87.5%): {nano_cast_info.cast_time_neutral:.2f}\n"
blob += f"Cast time at Half (50%): {nano_cast_info.cast_time_half:.2f}\n"
blob += f"Cast time at Full Def (0%): {nano_cast_info.cast_time_full_def:.2f}"
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 {item_id:d}."
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 {item_id}."
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 {item_id}."
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: {weapon_attack:.2f}\n"
blob += f"Recharge: {weapon_recharge:.2f}\n\n"
blob += self.get_inits_display(weapon_attack, weapon_recharge) + "\n"
blob += "Specials\n"
if high_attributes.aimed_shot:
as_info = self.get_aimed_shot_info(weapon_attack, weapon_recharge, 1)
blob += f"Aimed Shot\n{as_info.skill_cap:.0f} skill needed to cap " \
f"Aimed Shot recharge at {as_info.hard_cap:.2f} secs\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: {burst_recharge:.2f}\n" \
f"{burst_info.skill_cap:d} skill needed " \
f"to cap Burst recharge at {burst_info.hard_cap:.2f} secs\n\n"
if high_attributes.fast_attack:
fast_attack_info = self.get_fast_attack_info(weapon_attack, 1)
blob += f"Fast Attack\n{fast_attack_info.skill_cap:.0f} skill needed " \
f"to cap Fast Attack recharge at {fast_attack_info.hard_cap:.2f} secs\n\n"
if high_attributes.fling_shot:
fling_shot_info = self.get_fling_shot_info(weapon_attack, 1)
blob += f"Fling Shot\n{fling_shot_info.skill_cap} skill needed " \
f"to cap Fling Shot recharge at {fling_shot_info.hard_cap:.2f} secs\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: {full_auto_recharge:.2f}\n" \
f"{full_auto_info.skill_cap:.0f} skill needed " \
f"to cap Full Auto recharge at {full_auto_info.hard_cap:.2f} secs\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 int(inits_recharge)
else:
return int(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) # same
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