def test_sample_sch_rotation_correct_casts_237gcd(self): """ Ensure the time variable sim allows weaving after instant casts on a cursed gcd """ my_stat_spread = CharacterStatSpread(wd=180, mainstat=5577, det=2272, crit=3802, dh=1100, speed=1674, ten=380, pie=340) test_char = Character(Jobs.SCH, my_stat_spread) mypps = SchPps() # in 2 minutes on a 4th gcd chain opener, youd have: 2 AF, 9 ED, 1 CS, 1 SC, 1 Diss, 4 bio, 3 R2, 42 B3 sim_result = mypps.get_total_potency_variable_time( 120, test_char, SampleSchRotation(), 0.1) # assert this is the expected gcd self.assertAlmostEqual(2.37, test_char.get_gcd(), places=2) self.assertEqual(len(sim_result.timeline[SchAction.AETHERFLOW]), 2) self.assertEqual(len(sim_result.timeline[SchAction.ENERGYDRAIN]), 9) self.assertEqual(len(sim_result.timeline[SchAction.CHAINSTRATAGEM]), 1) self.assertEqual(len(sim_result.timeline[SchAction.SWIFTCAST]), 2) self.assertEqual(len(sim_result.timeline[SchAction.DISSIPATION]), 1) self.assertEqual(len(sim_result.timeline[SchAction.BIOLYSIS]), 4) self.assertEqual(len(sim_result.timeline[SchAction.RUIN2]), 3) self.assertEqual(len(sim_result.timeline[SchAction.BROIL3]), 41) # assert expected bio timings self.assertListEqual( [round(x, 2) for x in sim_result.timeline[SchAction.BIOLYSIS]], [0, 29.14, 61.15, 90.69])
def test_total_potency(self): """ Test the spreadsheet port for total potency """ my_stat_spread = CharacterStatSpread( wd=180, mainstat=5577, det=2272, crit=3802, dh=1100, speed=2139, ten=380, pie=340) test_char = Character(Jobs.SCH, my_stat_spread) mypps = SchPps() self.assertEqual(24326.1264, mypps.total_potency_spreadsheet_port(test_char, 0.12, 4, 0))
def test_cycle(self): """ Test the spreadsheet port for cycle length """ my_stat_spread = CharacterStatSpread( wd=180, mainstat=5577, det=2272, crit=3802, dh=1100, speed=2139, ten=380, pie=340) test_char = Character(Jobs.SCH, my_stat_spread) mypps = SchPps() self.assertEqual(174.12, mypps.get_cycle(test_char, 0.12))
def test_mp(self): """ Test the spreadsheet port for mp generation """ my_stat_spread = CharacterStatSpread( wd=180, mainstat=5577, det=2272, crit=3802, dh=1100, speed=2139, ten=380, pie=340) test_char = Character(Jobs.SCH, my_stat_spread) mypps = SchPps() self.assertEqual(508.0952931652587, mypps.get_mp_per_min(test_char, caster_tax=0.1, succ=0, adlo=0, energy_drain=4, rez=0), )
def update_stats(): """Calculates damage, mp consumption, and gcd based on input. Accepts and returns JSON. JSON format is as follows: input: {'player': Object {'weaponDamage': int 'mainStat': int 'det': int 'crit': int 'dh': int 'speed': int 'ten': int 'pie': int} 'job': string 'comp': Array} output: {'dps': float, 'gcd': float, 'mp': float} """ data = request.get_json() if not data: return "abort", abort(400) player_data = data['player'] try: my_job = Jobs.create_job(data['job'])[0] except KeyError: return f"Currently {data['job']} is not supported." player = Character( my_job, CharacterStatSpread(wd=player_data['weaponDamage'], mainstat=player_data['mainStat'], det=player_data['det'], crit=player_data['crit'], dh=player_data['dh'], speed=player_data['speed'], ten=player_data['ten'], pie=player_data['pie'])) my_sch_pps = SchPps() potency = my_sch_pps.get_pps(player) try: comp_jobs = [Jobs.create_job(comp_job)[0] for comp_job in data['comp']] except KeyError: return "A job was not supported in that team comp." my_comp = Comp(comp_jobs) dps = round(player.calc_damage(potency, my_comp), 2) gcd = player.get_gcd() mp = round(my_sch_pps.get_mp_per_min(player), 2) return jsonify({"dps": dps, "gcd": gcd, "mp": mp})
def test_variable_time_sim_notes_af_overspending(self): """ Ensure the time variable sim allows weaving after instant casts """ my_stat_spread = CharacterStatSpread(wd=180, mainstat=5577, det=2272, crit=3802, dh=1100, speed=2139, ten=380, pie=340) test_char = Character(Jobs.SCH, my_stat_spread) mypps = SchPps() # Burn an aetherflow we don't have fixed_rotation = [SchAction.ENERGYDRAIN] sim_result = mypps.get_total_potency_variable_time( 1, test_char, FixedSchRotation(fixed_rotation, SchAction.BROIL3), 0.1) # 3 ED, 2 B3, 1 R2 self.assertIn(SchSimNotice.SCH_SIM_AETHERFLOW_OVERSPENDING, sim_result.notices)
def test_variable_time_sim_allows_weaves(self): """ Ensure the time variable sim allows weaving after instant casts """ my_stat_spread = CharacterStatSpread(wd=180, mainstat=5577, det=2272, crit=3802, dh=1100, speed=2139, ten=380, pie=340) test_char = Character(Jobs.SCH, my_stat_spread) mypps = SchPps() # If the sim properly supports weaving, this should be 4 full gcds fixed_rotation = [ SchAction.BIOLYSIS, SchAction.AETHERFLOW, SchAction.ENERGYDRAIN, SchAction.BROIL3, SchAction.RUIN2, SchAction.ENERGYDRAIN, SchAction.SWIFTCAST, SchAction.BROIL3, SchAction.ENERGYDRAIN ] sim_result = mypps.get_total_potency_variable_time( test_char.get_gcd() * 4, test_char, FixedSchRotation(fixed_rotation, SchAction.BROIL3), 0.1) # 3 ED, 2 B3, 1 R2 self.assertEqual(1080, sim_result.get_non_dot_potency())
def etro_main(): """Calculates damage, given a gearsetID from Etro. Should check database to see if gearset exist first. JSON format: 'job': String, 'comp': Array, 'etro_id': String, """ data = request.get_json() # Check comp first before making request if not data: return "abort", abort(400) try: comp_jobs = [Jobs.create_job(comp_job)[0] for comp_job in data['comp']] except KeyError: return "An unsupported job was found in the team composition." my_comp = Comp(comp_jobs) player_job_data = Jobs.create_job(data['job']) # TODO: Check internal database to see if cached before request try: etro_data = requests.get("/".join( ["https://etro.gg/api/gearsets", data["etro_id"]])).json() except requests.exceptions.RequestException: return "There was an error making the etro request" etro_stats = etro_data["totalParams"] eq_stats = {stat["name"]: stat["value"] for stat in etro_stats} # For Etro sets that are non-tanks if "TEN" not in eq_stats: eq_stats["TEN"] = 0 # Making speed job-agnostic if "SPS" in eq_stats: eq_stats["SPEED"] = eq_stats["SPS"] else: eq_stats["SPEED"] = eq_stats["SKS"] player = Character( player_job_data[0], CharacterStatSpread(wd=eq_stats["Weapon Damage"], mainstat=eq_stats[player_job_data[1]], det=eq_stats["DET"], crit=eq_stats["CRT"], dh=eq_stats["DH"], speed=eq_stats["SPEED"], ten=eq_stats["TEN"], pie=eq_stats["PIE"])) my_sch_pps = SchPps() potency = my_sch_pps.get_pps(player) dps = round(player.calc_damage(potency, my_comp), 2) gcd = player.get_gcd() mp = round(my_sch_pps.get_mp_per_min(player), 2) return jsonify({"dps": dps, "gcd": gcd, "mp": mp})
def update_stats(): """Calculates damage, mp consumption, and gcd based on input. Accepts and returns JSON. JSON format is as follows: input: {'player': Object {'weaponDamage': int 'mainStat': int 'det': int 'crit': int 'dh': int 'speed': int 'ten': int 'pie': int} 'job': string 'comp': Array 'rotation': Object {'adloquium': int 'energyDrain': int 'raise': int 'succor': int}} output: {'dps': float, 'gcd': float, 'mp': float} """ data = request.get_json() if not data: return "abort", abort(400) player_data = data['player'] try: my_job = Jobs.create_job(data['job'])[0] except KeyError: return f"Currently {data['job']} is not supported." player = Character( my_job, CharacterStatSpread(wd=player_data['weaponDamage'], mainstat=player_data['mainStat'], det=player_data['det'], crit=player_data['crit'], dh=player_data['dh'], speed=player_data['speed'], ten=player_data['ten'], pie=player_data['pie'])) try: rotation_data = data['rotation'] energy_drain_per_min = rotation_data.get('energyDrain', 4) adloquium_per_min = rotation_data.get('adloquium', 0) raise_per_min = rotation_data.get('raise', 0) succor_per_min = rotation_data.get('succor', 0) except KeyError: # Rotation data was not present in request, use defaults energy_drain_per_min, adloquium_per_min, raise_per_min, succor_per_min = 4, 0, 0, 0 my_sch_pps = SchPps() potency = my_sch_pps.get_pps(player, num_ed_per_min=energy_drain_per_min, num_filler_casts=adloquium_per_min + raise_per_min + succor_per_min) try: comp_jobs = [Jobs.create_job(comp_job)[0] for comp_job in data['comp']] except KeyError: return "A job was not supported in that team comp." dps = round(player.calc_damage(potency, Comp(comp_jobs)), 2) gcd = player.get_gcd() mp = round( my_sch_pps.get_mp_per_min(player, succ=succor_per_min, adlo=adloquium_per_min, energy_drain=energy_drain_per_min, rez=raise_per_min), 2) return jsonify({"dps": dps, "gcd": gcd, "mp": mp})