[], 2 => [], 3 => [], 4 => [], 5 => [], 6 => [] ]; $options = []; $response = null; $uid = filter_input(INPUT_POST, 'uid'); if ($uid == null){ return -1; } $unit_id = filter_input(INPUT_POST, 'unit'); if ($unit_id == null){ return -2; } // Sets $sets = []; foreach ($_POST["set"] as $x){ array_push($sets, intval($x)); } if (sizeof($sets) < 2 || sizeof($sets) > 3){ return -3; } // Main stats in even slots. $stats = []; foreach ($_POST["stat"] as $x){ array_push($stats, intval($x)); } $source = filter_input(INPUT_POST, 'source'); if ($source == null){ return -4; } $limit = intval(filter_input(INPUT_POST, 'limit')); if ($limit == 0){ return -5; } $tuning = filter_input(INPUT_POST, 'tuning'); if ($tuning == null){ return -6; } $min = [ "attack" => intval(filter_input(INPUT_POST, 'min_attack')), "defense" => intval(filter_input(INPUT_POST, 'min_defense')), "hp" => intval(filter_input(INPUT_POST, 'min_hp')), "speed" => intval(filter_input(INPUT_POST, 'min_speed')), "crit_rate" => intval(filter_input(INPUT_POST, 'min_crit_rate')), "crit_damage" => intval(filter_input(INPUT_POST, 'min_crit_damage')), "accuracy" => intval(filter_input(INPUT_POST, 'min_accuracy')), "resistance" => intval(filter_input(INPUT_POST, 'min_resistance')), "ehp" => intval(filter_input(INPUT_POST, 'min_ehp')), "dmg" => intval(filter_input(INPUT_POST, 'min_dmg')) ]; // Get Buld query: $base_s = " SELECT id, slot FROM rune WHERE uid = $uid AND type IN ( "; foreach ($sets as $set){ $base_s .= ($set . ","); } $base_s .= "-1) AND "; switch ($source){ case 0: // Storage only (or itself) $base_s .= " ( assigned_to = $unit_id OR assigned_to IS NULL )"; break; case 1: // Units in no teams (or itself) $base_s .= " ( assigned_to = $unit_id OR assigned_to IS NULL OR assigned_to NOT IN (SELECT DISTINCT unit FROM team_unit) ) "; break; case 2: // Units in teams with 0 score (or itself) $base_s .= " ( assigned_to = $unit_id OR assigned_to IS NULL OR assigned_to NOT IN (SELECT DISTINCT unit FROM team_unit) OR assigned_to NOT IN ( SELECT DISTINCT unit FROM team, team_unit WHERE team.id = team_unit.team AND team.score > 0 ) ) "; break; case 3: // Units with lower overall score (or itself) // TODO break; case 4: // All runes - dont filter $base_s .= " 1 = 1 "; break; default: // Invalid options - return nothing $base_s .= " 1 = 0 "; } $s_order = " ORDER BY stars DESC, original_quality DESC, max_efficiency DESC, efficiency DESC, level DESC"; $s = ["", "", "", "", "", "", ""]; $s[1] = $base_s . " AND slot = 1 " . $s_order . " LIMIT 25"; $s[3] = $base_s . " AND slot = 3 " . $s_order . " LIMIT 25"; $s[5] = $base_s . " AND slot = 5 " . $s_order . " LIMIT 25"; $s[2] = $base_s . " AND slot = 2 AND main_stat = " . $stats[0] . $s_order . " LIMIT 20"; $s[4] = $base_s . " AND slot = 4 AND main_stat = " . $stats[1] . $s_order . " LIMIT 20"; $s[6] = $base_s . " AND slot = 6 AND main_stat = " . $stats[2] . $s_order . " LIMIT 20"; $total_candidates = 0; for ($i = 1; $i <= 6; $i ++){ $q = $db->query($s[$i]); while ($r = $q->fetchArray(SQLITE3_ASSOC)){ array_push($candidates[$i], new Rune($r["id"])); $total_candidates ++; } } $max_combinations = sizeof($candidates[1]) * sizeof($candidates[2]) * sizeof($candidates[3]) * sizeof($candidates[4]) * sizeof($candidates[5]) * sizeof($candidates[6]); if ($max_combinations == 0){ $res = [ "total" => 0, "options" => [] ]; $response = json_encode($res); return $response; } elseif ($max_combinations > 125000000){ // This should never happen unles query limits are changed http_response_code(413); // Payload too large; die(); return; } // Get the unit and its base stats $unit = new Unit($unit_id, true, $uid); $unit_attack = $unit->attack + $unit->artifact_attack + $unit->building_attack; $unit_base_attack = $unit->attack; $unit_defense = $unit->defense + $unit->artifact_defense + $unit->building_defense; $unit_base_defense = $unit->defense; $unit_hp = $unit->hp + $unit->artifact_hp + $unit->building_hp; $unit_base_hp = $unit->hp; $unit_speed = $unit->speed + $unit->artifact_speed + $unit->building_speed; $unit_base_speed = $unit->speed; $unit_crit_rate = $unit->crit_rate + $unit->artifact_crit_rate + $unit->building_crit_rate; $unit_base_crit_rate = $unit->crit_rate; $unit_crit_damage = $unit->crit_damage + $unit->artifact_crit_damage + $unit->building_crit_damage; $unit_base_crit_damage = $unit->crit_damage; $unit_accuracy = $unit->accuracy + $unit->artifact_accuracy + $unit->building_accuracy; $unit_base_accuracy = $unit->accuracy; $unit_resistance = $unit->resistance + $unit->artifact_resistance + $unit->building_resistance; $unit_base_resistance = $unit->resistance; $i = [0, 0, 0, 0, 0, 0, 0]; // 7, so I can start with 1 while (true){ // Check if set combination is valid if (valid_sets($candidates, $i, $sets) == true){ // Do things $iteration = [ "attack" => $unit_attack, "defense" => $unit_defense, "hp" => $unit_hp, "speed" => $unit_speed, "crit_rate" => $unit_crit_rate, "crit_damage" => $unit_crit_damage, "accuracy" => $unit_accuracy, "resistance" => $unit_resistance, "ehp" => 0, "dmg" => 0, ]; for ($r = 1; $r <= 6; $r ++){ // Loop selected runes and calculate stats $main_value = $candidates[$r][$i[$r]]->main_stat_value; if ($tuning == 1){ // All +12 if ($candidates[$r][$i[$r]]->level < 12){ $main_value = $candidates[$r][$i[$r]]->get_main_stat_at_level(12); } } elseif ($tuning == 2){ // Even +15, Odd + 12 if ($candidates[$r][$i[$r]]->level < 15 && $candidates[$r][$i[$r]]->slot % 2 == 0){ $main_value = $candidates[$r][$i[$r]]->get_main_stat_at_level(15); } elseif ($candidates[$r][$i[$r]]->level < 12 && $candidates[$r][$i[$r]]->slot % 2 != 0){ $main_value = $candidates[$r][$i[$r]]->get_main_stat_at_level(12); } } if ($tuning == 3){ // All +15 if ($candidates[$r][$i[$r]]->level < 15){ $main_value = $candidates[$r][$i[$r]]->get_main_stat_at_level(15); } } sum_rune_stat( $candidates[$r][$i[$r]]->main_stat, $main_value, $iteration, $unit_base_attack, $unit_base_defense, $unit_base_hp, ); sum_rune_stat( $candidates[$r][$i[$r]]->innate_stat, $candidates[$r][$i[$r]]->innate_stat_value, $iteration, $unit_base_attack, $unit_base_defense, $unit_base_hp, ); sum_rune_stat( $candidates[$r][$i[$r]]->substat_1, $candidates[$r][$i[$r]]->substat_1_value, $iteration, $unit_base_attack, $unit_base_defense, $unit_base_hp, ); sum_rune_stat( $candidates[$r][$i[$r]]->substat_2, $candidates[$r][$i[$r]]->substat_2_value, $iteration, $unit_base_attack, $unit_base_defense, $unit_base_hp, ); sum_rune_stat( $candidates[$r][$i[$r]]->substat_3, $candidates[$r][$i[$r]]->substat_3_value, $iteration, $unit_base_attack, $unit_base_defense, $unit_base_hp, ); sum_rune_stat( $candidates[$r][$i[$r]]->substat_4, $candidates[$r][$i[$r]]->substat_4_value, $iteration, $unit_base_attack, $unit_base_defense, $unit_base_hp, ); } // Calculate effective_hp $hp = $iteration["hp"]; $def = $iteration["defense"]; $ehp = ceil(((($def * 3.5) + 1140) * $hp) / 1000); $iteration["ehp"] = $ehp; // Calculate effective_dmg $atk = $iteration["attack"]; $crr = $iteration["crit_rate"]; if ($crr > 100){ $crr = 100; } $crd = $iteration["crit_damage"]; $edmg = ceil(($atk * (100 - $crr) / 100) + (($atk + ($atk * $crd / 100)) * $crr / 100)); $iteration["dmg"] = $edmg; // Compare with filters: if ( $iteration["attack"] >= $min["attack"] && $iteration["defense"] >= $min["defense"] && $iteration["hp"] >= $min["hp"] && $iteration["speed"] >= $min["speed"] && $iteration["crit_rate"] >= $min["crit_rate"] && $iteration["crit_damage"] >= $min["crit_damage"] && $iteration["accuracy"] >= $min["accuracy"] && $iteration["resistance"] >= $min["resistance"] && $iteration["ehp"] >= $min["ehp"] && $iteration["dmg"] >= $min["dmg"] ){ // Calculate the variation $variation = 0; $variation += (($iteration["attack"] - $unit->total_attack) * 1); $variation += (($iteration["defense"] - $unit->total_defense) * 1); $variation += (($iteration["hp"] - $unit->total_hp) * (1 / 15)); $variation += (($iteration["speed"] - $unit->total_speed) * 1.5); $variation += (($iteration["crit_rate"] - $unit->total_crit_rate) * 0.85); $variation += (($iteration["crit_damage"] - $unit->total_crit_damage) * 0.9); $variation += (($iteration["accuracy"] - $unit->total_accuracy) * 0.85); $variation += (($iteration["resistance"] - $unit->total_resistance) * 0.85); $variation = ceil($variation); // Calculate efficiency $efficiency = 0; // Build the array $option = [ "variation" => $variation, "runes" => [ [ "id" => $candidates[1][$i[1]]->id, "html" => (HTML::rune_table($candidates[1][$i[1]])) ], [ "id" => $candidates[2][$i[2]]->id, "html" => (HTML::rune_table($candidates[2][$i[2]])) ], [ "id" => $candidates[3][$i[3]]->id, "html" => (HTML::rune_table($candidates[3][$i[3]])) ], [ "id" => $candidates[4][$i[4]]->id, "html" => (HTML::rune_table($candidates[4][$i[4]])) ], [ "id" => $candidates[5][$i[5]]->id, "html" => (HTML::rune_table($candidates[5][$i[5]])) ], [ "id" => $candidates[1][$i[1]]->id, "html" => HTML::rune_table($candidates[6][$i[6]]) ] ], "stats" => [ "attack" => ceil($iteration["attack"]), "defense" => ceil($iteration["defense"]), "hp" => ceil($iteration["hp"]), "speed" => ceil($iteration["speed"]), "crit_rate" => ceil($iteration["crit_rate"]), "crit_damage" => ceil($iteration["crit_damage"]), "accuracy" => ceil($iteration["accuracy"]), "attack" => ceil($iteration["attack"]), "resistance" => ceil($iteration["resistance"]), "ehp" => ceil($iteration["ehp"]), "dmg" => ceil($iteration["dmg"]) ] ]; array_push($options, $option); if (sizeof($options) > 5000){ http_response_code(413); // Payload too large; die(); return; } } } // Increase counters $i[6] ++; if ($i[6] == sizeof($candidates[6])){ $i[6] = 0; $i[5] ++; } if ($i[5] == sizeof($candidates[5])){ $i[5] = 0; $i[4] ++; } if ($i[4] == sizeof($candidates[4])){ $i[4] = 0; $i[3] ++; } if ($i[3] == sizeof($candidates[3])){ $i[3] = 0; $i[2] ++; } if ($i[2] == sizeof($candidates[2])){ $i[2] = 0; $i[1] ++; } // Calculate exit condition if ( $i[1] >= sizeof($candidates[1]) - 1 && $i[2] >= sizeof($candidates[2]) - 1 && $i[3] >= sizeof($candidates[3]) - 1 && $i[4] >= sizeof($candidates[4]) - 1 && $i[5] >= sizeof($candidates[5]) - 1 && $i[6] >= sizeof($candidates[6]) - 1 ){ break; } } // Sort options by variation usort($options, "sortOptions"); // Limit the array to the top options $original_options_size = sizeof($options); while (sizeof($options) > $limit || sizeof($options) > $HARD_LIMIT){ array_pop($options); } $res = [ "total" => sizeof($options), "skipped" => ($original_options_size - sizeof($options)), "options" => $options ]; $response = json_encode($res); if ($response == null){ return 0; } else{ return $response; } } /** * Validates a rune combination. * * Checks that set numbers are OK. * * @param \Rune[][] $candidates Candidate runes. * @param int[] $indexes Currently selected indexes. */ function valid_sets($candidates, $indexes, $requested){ $sets = [ RUNE_SET_ID::ENERGY => 0, RUNE_SET_ID::GUARD => 0, RUNE_SET_ID::SWIFT => 0, RUNE_SET_ID::BLADE => 0, RUNE_SET_ID::RAGE => 0, RUNE_SET_ID::FOCUS => 0, RUNE_SET_ID::ENDURE => 0, RUNE_SET_ID::FATAL => 0, RUNE_SET_ID::DESPAIR => 0, RUNE_SET_ID::VAMPIRE => 0, RUNE_SET_ID::VIOLENT => 0, RUNE_SET_ID::NEMESIS => 0, RUNE_SET_ID::WILL => 0, RUNE_SET_ID::SHIELD => 0, RUNE_SET_ID::REVENGE => 0, RUNE_SET_ID::DESTROY => 0, RUNE_SET_ID::FIGHT => 0, RUNE_SET_ID::DETERMINATION => 0, RUNE_SET_ID::ENHANCE => 0, RUNE_SET_ID::ACCURACY => 0, RUNE_SET_ID::TOLERANCE => 0 ]; for ($i = 1; $i <= 6; $i++){ $sets[$candidates[$i][$indexes[$i]]->type->id] ++; } for ($i = 0; $i < sizeof($sets); $i ++){ switch ($i){ case RUNE_SET_ID::ENERGY: case RUNE_SET_ID::GUARD: case RUNE_SET_ID::NEMESIS: case RUNE_SET_ID::BLADE: case RUNE_SET_ID::FOCUS: case RUNE_SET_ID::ENDURE: case RUNE_SET_ID::WILL: case RUNE_SET_ID::SHIELD: case RUNE_SET_ID::REVENGE: case RUNE_SET_ID::DESTROY: case RUNE_SET_ID::FIGHT: case RUNE_SET_ID::DETERMINATION: case RUNE_SET_ID::ENHANCE: case RUNE_SET_ID::ACCURACY: case RUNE_SET_ID::TOLERANCE: if ($sets[$i] % 2 != 0){ return false; } break; case RUNE_SET_ID::DESPAIR: case RUNE_SET_ID::SWIFT: case RUNE_SET_ID::RAGE: case RUNE_SET_ID::FATAL: case RUNE_SET_ID::VAMPIRE: case RUNE_SET_ID::VIOLENT: if ($sets[$i] != 4 && $sets[$i != 0]){ return false; } break; } } // Sets are valid, now check if they comply with filters $requested_sets = [ RUNE_SET_ID::ENERGY => 0, RUNE_SET_ID::GUARD => 0, RUNE_SET_ID::SWIFT => 0, RUNE_SET_ID::BLADE => 0, RUNE_SET_ID::RAGE => 0, RUNE_SET_ID::FOCUS => 0, RUNE_SET_ID::ENDURE => 0, RUNE_SET_ID::FATAL => 0, RUNE_SET_ID::DESPAIR => 0, RUNE_SET_ID::VAMPIRE => 0, RUNE_SET_ID::VIOLENT => 0, RUNE_SET_ID::NEMESIS => 0, RUNE_SET_ID::WILL => 0, RUNE_SET_ID::SHIELD => 0, RUNE_SET_ID::REVENGE => 0, RUNE_SET_ID::DESTROY => 0, RUNE_SET_ID::FIGHT => 0, RUNE_SET_ID::DETERMINATION => 0, RUNE_SET_ID::ENHANCE => 0, RUNE_SET_ID::ACCURACY => 0, RUNE_SET_ID::TOLERANCE => 0 ]; for ($i = 0; $i < sizeof($requested); $i ++){ switch ($requested[$i]){ case RUNE_SET_ID::ENERGY: case RUNE_SET_ID::GUARD: case RUNE_SET_ID::NEMESIS: case RUNE_SET_ID::BLADE: case RUNE_SET_ID::FOCUS: case RUNE_SET_ID::ENDURE: case RUNE_SET_ID::WILL: case RUNE_SET_ID::SHIELD: case RUNE_SET_ID::REVENGE: case RUNE_SET_ID::DESTROY: case RUNE_SET_ID::FIGHT: case RUNE_SET_ID::DETERMINATION: case RUNE_SET_ID::ENHANCE: case RUNE_SET_ID::ACCURACY: case RUNE_SET_ID::TOLERANCE: $requested_sets[$requested[$i]] += 2; break; case RUNE_SET_ID::DESPAIR: case RUNE_SET_ID::SWIFT: case RUNE_SET_ID::RAGE: case RUNE_SET_ID::FATAL: case RUNE_SET_ID::VAMPIRE: case RUNE_SET_ID::VIOLENT: $requested_sets[$requested[$i]] += 4; break; } } if ($sets === $requested_sets){ return true; } else{ return false; } } /** * Calculates the stat increase by a rune property. * * @param string $stat Stat type. * @param int $value Stat increase value. * @param int[] Reference to iteration stats array. */ function sum_rune_stat($stat, $value, &$iteration, $base_atk, $base_def, $base_hp){ switch ($stat){ case RUNE_STAT_ID::ATK: $iteration["attack"] += $value; break; case RUNE_STAT_ID::DEF: $iteration["defense"] += $value; break; case RUNE_STAT_ID::HP: $iteration["hp"] += $value; break; case RUNE_STAT_ID::SPD: $iteration["speed"] += $value; break; case RUNE_STAT_ID::CRR: $iteration["crit_rate"] += $value; break; case RUNE_STAT_ID::CRD: $iteration["crit_damage"] += $value; break; case RUNE_STAT_ID::ACC: $iteration["accuracy"] += $value; break; case RUNE_STAT_ID::RES: $iteration["resistance"] += $value; break; case RUNE_STAT_ID::ATK_P: $iteration["attack"] += ($base_atk * $value / 100); break; case RUNE_STAT_ID::DEF_P: $iteration["defense"] += ($base_def * $value / 100); break; case RUNE_STAT_ID::HP_P: $iteration["hp"] += ($base_hp * $value / 100); break; } } /** * Function to sort arrays based on the "increment" key. * * @param $a An array * @param $b Other array * @return int Positive if $b before $a, negative otherwise. */ function sortOptions($a, $b) { return $b["variation"] - $a["variation"]; } ?>