| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551 |
- <?php
- /**
- * File for the rune optimization action.
- *
- * Implements an action function to be called from the {@see Controller}.
- *
- * @author Iñigo Valentin <i@inigovalentin.com>
- * @license https://www.gnu.org/licenses/gpl-3.0.en.html GNU General Public License V3
- * @package SWDB
- */
- require_once(PATH::ACTION . "Action.php");
- /**
- * Selects the runes for optimization.
- *
- * Reads the POST parameters looking for the following KEYS:
- * player: Player ID.
- * mode: Game mode ({@see GAME_MODE_ID}).
- * unit: Unit ID.
- * source: Source of the runes to select (assigned, unassigned, ...).
- * tuning: 1: All +12; 2: Even +15, odd + 12; 3: All +15.
- * set[]: Selected rune sets ({@see RUNE_SET_ID}).
- *
- * Then it selects all the runes matching the cryteria.
- *
- * @category Action
- */
- class Optimize_List_Action extends Action{
- /**
- * Executes the action.
- */
- public function execute(){
-
- // Increase max execution time.
- set_time_limit(60);
-
- $candidates = [
- 1 => [],
- 2 => [],
- 3 => [],
- 4 => [],
- 5 => [],
- 6 => []
- ];
-
- $response = null;
-
- $player = filter_input(INPUT_POST, 'player');
- if ($player == null){
- $this->code = 400;
- $this->message = "POST parameter 'player' not specified.";
- return;
- }
- $game_mode = GAME_MODE_ID::NORMAL;
- if (filter_input(INPUT_POST, 'mode') == GAME_MODE_ID::RTA){
- $game_mode = GAME_MODE_ID::RTA;
- }
-
- $unit_id = filter_input(INPUT_POST, 'unit');
- if ($unit_id == null){
- $this->code = 400;
- $this->message = "POST parameter 'unit' not specified.";
- return;
- }
-
- // Sets
- $sets = [];
- foreach ($_POST["set"] as $x){
- array_push($sets, intval($x));
- }
- if (sizeof($sets) < 2 || sizeof($sets) > 3){
- $this->code = 400;
- $this->message = "POST parameter 'set' not specified or invalid. Two or three rune sets must be specified. Broken sets are not supported.";
- return;
- }
-
- $focus = [];
- if (isset($_POST["focus_0"]) && intval($_POST["focus_0"]) > 0){
- array_push($focus, intval($_POST["focus_0"]));
- }
- if (isset($_POST["focus_1"]) && intval($_POST["focus_1"]) > 0){
- array_push($focus, intval($_POST["focus_1"]));
- }
- if (isset($_POST["focus_2"]) && intval($_POST["focus_2"]) > 0){
- array_push($focus, intval($_POST["focus_2"]));
- }
- // Remove doubles and sort. The order will be HP, ATK, DEF, SPD, CRR, CRD, RES, ACC
- $focus = array_unique($focus, SORT_NUMERIC);
-
- // 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){
- $this->code = 400;
- $this->message = "POST parameter 'source' not specified.";
- return;
- }
-
- $tuning = filter_input(INPUT_POST, 'tuning');
- if ($tuning == null){
- $this->code = 400;
- $this->message = "POST parameter 'tuning' not specified.";
- return;
- }
-
- // Instantiate the unit
- $unit = new Unit($unit_id);
-
- // Get build query:
- $base_s = "
- SELECT
- id,
- type,
- slot,
- level,
- stars,
- (SELECT stat FROM rune_stat WHERE rune = rune.id AND slot = " . RUNE_STAT_SLOT_ID::MAIN . " ) AS main_stat,
- (SELECT value FROM rune_stat WHERE rune = rune.id AND slot = " . RUNE_STAT_SLOT_ID::MAIN . " ) AS main_stat_value,
- (SELECT stat FROM rune_stat WHERE rune = rune.id AND slot = " . RUNE_STAT_SLOT_ID::INNATE . " ) AS innate_stat,
- (SELECT value FROM rune_stat WHERE rune = rune.id AND slot = " . RUNE_STAT_SLOT_ID::INNATE . " ) AS innate_stat_value,
- (SELECT stat FROM rune_stat WHERE rune = rune.id AND slot = " . RUNE_STAT_SLOT_ID::SUBSTAT_1 . " ) AS substat_1,
- (SELECT value + grind FROM rune_stat WHERE rune = rune.id AND slot = " . RUNE_STAT_SLOT_ID::SUBSTAT_1 . " ) AS substat_1_value,
- (SELECT stat FROM rune_stat WHERE rune = rune.id AND slot = " . RUNE_STAT_SLOT_ID::SUBSTAT_2 . " ) AS substat_2,
- (SELECT value + grind FROM rune_stat WHERE rune = rune.id AND slot = " . RUNE_STAT_SLOT_ID::SUBSTAT_2 . " ) AS substat_2_value,
- (SELECT stat FROM rune_stat WHERE rune = rune.id AND slot = " . RUNE_STAT_SLOT_ID::SUBSTAT_3 . " ) AS substat_3,
- (SELECT value + grind FROM rune_stat WHERE rune = rune.id AND slot = " . RUNE_STAT_SLOT_ID::SUBSTAT_3 . " ) AS substat_3_value,
- (SELECT stat FROM rune_stat WHERE rune = rune.id AND slot = " . RUNE_STAT_SLOT_ID::SUBSTAT_4 . " ) AS substat_4,
- (SELECT value + grind FROM rune_stat WHERE rune = rune.id AND slot = " . RUNE_STAT_SLOT_ID::SUBSTAT_4 . " ) AS substat_4_value
- FROM rune
- WHERE
- player = :player AND
- type IN (
- ";
- foreach ($sets as $set){
- $base_s .= ($set . ",");
- }
- $base_s .= "-1) AND ";
- switch ($source){
- case 0: // Storage only (or itself)
- $base_s .= "
- (
- rune.id IN (SELECT DISTINCT rune FROM unit_rune WHERE unit = :unit_id AND rta = " . $game_mode . ")
- OR
- rune.id NOT IN (SELECT DISTINCT rune FROM unit_rune WHERE rta = " . $game_mode . ")
- )";
- break;
- case 1: // Units in no teams (or itself)
- $base_s .= "
- (
- rune.id IN (SELECT DISTINCT rune FROM unit_rune WHERE unit = :unit_id AND rta = " . $game_mode . ")
- OR rune.id NOT IN (SELECT DISTINCT rune FROM unit_rune WHERE rta = " . $game_mode . ")
- OR rune.id NOT IN (SELECT DISTINCT rune FROM unit_rune WHERE rta = " . $game_mode . " AND unit NOT IN (SELECT DISTINCT unit FROM team_unit))
- )
- ";
- break;
- case 2: // Units in teams with 0 score (or itself)
- $base_s .= "
- (
- rune.id IN (SELECT DISTINCT rune FROM unit_rune WHERE unit = :unit_id AND rta = " . $game_mode . ")
- OR rune.id NOT IN (SELECT DISTINCT rune FROM unit_rune WHERE rta = " . $game_mode . ")
- OR rune.id NOT IN (
- SELECT DISTINCT rune
- FROM unit_rune
- WHERE
- rta = " . $game_mode . "
- AND unit 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_in = " main_stat IN (:stat_0, :stat_1, :stat_2) ";
- $s_main = [
- " main_stat = :stat_0 ",
- " main_stat = :stat_1 ",
- " main_stat = :stat_2 ",
- ];
-
- // Process focuses
- $focus_stats = [];
- foreach ($focus as $f){
- switch ($f){
- case STAT_ID::HP:
- array_push($focus_stats, RUNE_STAT_ID::HP);
- array_push($focus_stats, RUNE_STAT_ID::HP_P);
- break;
- case STAT_ID::ATK:
- array_push($focus_stats, RUNE_STAT_ID::ATK);
- array_push($focus_stats, RUNE_STAT_ID::ATK_P);
- break;
- case STAT_ID::DEF:
- array_push($focus_stats, RUNE_STAT_ID::DEF);
- array_push($focus_stats, RUNE_STAT_ID::DEF_P);
- break;
- case STAT_ID::SPD:
- array_push($focus_stats, RUNE_STAT_ID::SPD);
- break;
- case STAT_ID::CRR:
- array_push($focus_stats, RUNE_STAT_ID::CRR);
- break;
- case STAT_ID::CRD:
- array_push($focus_stats, RUNE_STAT_ID::CRD);
- break;
- case STAT_ID::ACC:
- array_push($focus_stats, RUNE_STAT_ID::ACC);
- break;
- case STAT_ID::RES:
- array_push($focus_stats, RUNE_STAT_ID::RES);
- break;
- }
- }
-
- $focus_cond = ["", "", "", "", "", "", ""];
- if (sizeof($focus) > 0){
-
- // Slot 1, no ATK flat, no DEF flat, no DEF %
- $focus_slot = $focus_stats;
- /*if (array_search(RUNE_STAT_ID::ATK, $focus_slot) !== false){
- $focus_slot = array_diff($focus_slot, [RUNE_STAT_ID::ATK])//array_splice($focus_slot, array_search(RUNE_STAT_ID::ATK, $focus_slot), 1);
- }
- if (array_search(RUNE_STAT_ID::DEF, $focus_slot) !== false){
- $focus_slot = array_splice($focus_slot, array_search(RUNE_STAT_ID::DEF, $focus_slot), 1);
- }
- if (array_search(RUNE_STAT_ID::DEF_P, $focus_slot) !== false){
- $focus_slot = array_splice($focus_slot, array_search(RUNE_STAT_ID::DEF_P, $focus_slot), 1);
- }*/
- $focus_slot = array_diff($focus_slot, [RUNE_STAT_ID::ATK, RUNE_STAT_ID::DEF, RUNE_STAT_ID::DEF_P]);
- if (sizeof($focus_slot) > 0){
- $focus_cond[1] .= "
- -- FOCUS 1
- AND (
- rune.id IN (
- SELECT DISTINCT rune FROM rune_stat WHERE slot != :slot_main AND stat IN
- (
- ";
- foreach ($focus_slot as $stat){
- $focus_cond[1] .= $stat . ", ";
- }
- $focus_cond[1] = rtrim($focus_cond[1], ", ");
- $focus_cond[1] .= "
- )
- )
- ) -- END FOCUS 1
- ";
- }
-
- // Slot 3, no ATK flat, no ATK %, no DEF flat.
- $focus_slot = $focus_stats;
- /*if (array_search(RUNE_STAT_ID::ATK, $focus_slot) !== false){
- $focus_slot = array_splice($focus_slot, array_search(RUNE_STAT_ID::ATK, $focus_slot), 1);
- }
- if (array_search(RUNE_STAT_ID::ATK_P, $focus_slot) !== false){
- $focus_slot = array_splice($focus_slot, array_search(RUNE_STAT_ID::ATK_P, $focus_slot), 1);
- }
- if (array_search(RUNE_STAT_ID::DEF, $focus_slot) !== false){
- $focus_slot = array_splice($focus_slot, array_search(RUNE_STAT_ID::DEF, $focus_slot), 1);
- }*/
- $focus_slot = array_diff($focus_slot, [RUNE_STAT_ID::ATK, RUNE_STAT_ID::ATK_P, RUNE_STAT_ID::DEF]);
- if (sizeof($focus_slot) > 0){
- $focus_cond[3] .= "
- -- FOCUS 3
- AND (
- rune.id IN (
- SELECT DISTINCT rune FROM rune_stat WHERE slot != :slot_main AND stat IN
- (
- ";
- foreach ($focus_slot as $stat){
- $focus_cond[3] .= $stat . ", ";
- }
- $focus_cond[3] = rtrim($focus_cond[3], ", ");
- $focus_cond[3] .= "
- )
- )
- ) -- END FOCUS 3
- ";
- }
-
- // Slot 5, no HP flat.
- $focus_slot = $focus_stats;
- /*if (array_search(RUNE_STAT_ID::HP, $focus_slot) !== false){
- $focus_slot = array_splice($focus_slot, array_search(RUNE_STAT_ID::HP, $focus_slot), 1);
- }*/
- $focus_slot = array_diff($focus_slot, [RUNE_STAT_ID::HP]);
- if (sizeof($focus_slot) > 0){
- $focus_cond[5] .= "
- -- FOCUS 5
- AND (
- rune.id IN (
- SELECT DISTINCT rune FROM rune_stat WHERE slot != :slot_main AND stat IN
- (
- ";
- foreach ($focus_slot as $stat){
- $focus_cond[5] .= $stat . ", ";
- }
- $focus_cond[5] = rtrim($focus_cond[5], ", ");
- $focus_cond[5] .= "
- )
- )
- ) -- END FOCUS 5
- ";
- }
-
- // TODO: Focus for even slots.
- // Slot 2, all stat, but include main
- // Slot 5, no HP flat.
- $focus_slot = $focus_stats;
- if (sizeof($focus_slot) > 0){
- $focus_cond[2] .= "
- -- FOCUS EVEN
- AND (
- rune.id IN (
- SELECT DISTINCT rune FROM rune_stat WHERE stat IN
- (
- ";
- foreach ($focus_slot as $stat){
- $focus_cond[2] .= $stat . ", ";
- }
- $focus_cond[2] = rtrim($focus_cond[2], ", ");
- $focus_cond[2] .= "
- )
- )
- ) -- END FOCUS EVEN
- ";
- }
-
- }
-
- //TODO DEBUG
- //$focus_cond[2] = "";
-
- $s = ["", "", "", "", "", "", ""];
- $s[1] = $base_s . " AND slot = 1 " . $focus_cond[1] . $s_order . " LIMIT :limit_odd;";
- $s[3] = $base_s . " AND slot = 3 " . $focus_cond[3] . $s_order . " LIMIT :limit_odd;";
- $s[5] = $base_s . " AND slot = 5 " . $focus_cond[5] . $s_order . " LIMIT :limit_odd;";
- $s[2] = $base_s . " AND slot = 2 AND " . $s_main[0] . $focus_cond[2] . $s_order . " LIMIT :limit_even;";
- $s[4] = $base_s . " AND slot = 4 AND " . $s_main[1] . $focus_cond[2] . $s_order . " LIMIT :limit_even;";
- $s[6] = $base_s . " AND slot = 6 AND " . $s_main[2] . $focus_cond[2] . $s_order . " LIMIT :limit_even;";
-
- $total_candidates = 0;
- for ($i = 1; $i <= 6; $i ++){
- //error_log($s[$i]);
- $statement = get_context()->get_db()->prepare($s[$i]);
- $statement->bindValue(':player', $player, SQLITE3_TEXT);
- $statement->bindValue(':unit_id', $unit_id, SQLITE3_TEXT);
- $statement->bindValue(':stat_0', $stats[0], SQLITE3_TEXT);
- $statement->bindValue(':stat_1', $stats[1], SQLITE3_TEXT);
- $statement->bindValue(':stat_2', $stats[2], SQLITE3_TEXT);
- $statement->bindValue(':slot_main', RUNE_STAT_SLOT_ID::MAIN, SQLITE3_INTEGER);
- $statement->bindValue(':limit_even', 150, SQLITE3_INTEGER);
- $statement->bindValue(':limit_odd', 100, SQLITE3_INTEGER);
- //error_log($statement->getSQL(true));
- $q = $statement->execute();
- while ($r = $q->fetchArray(SQLITE3_ASSOC)){
- $rune = create_rune_array($unit, $r, $tuning);
- //array_push($candidates[$i], new Rune($r["id"]));
- array_push($candidates[$i], $rune);
- $total_candidates ++;
- }
- }
- //$max_combinations = sizeof($candidates[1]) * sizeof($candidates[2]) * sizeof($candidates[3]) * sizeof($candidates[4]) * sizeof($candidates[5]) * sizeof($candidates[6]);
-
- // Build response
- $response = json_encode($candidates);
- if ($response == null){
- $this->code = 201;
- $this->message = "No valid combinations found";
- return;
- }
- else{
- $this->code = 200;
- $this->message = "OK";
- $this->output = $response;
- return;
- }
- }
-
- /**
- * Creates an array with the stats provided by a rune on a unit.
- *
- * @param Unit $unit Target unit.
- * @param int $r Rune main stat ID.
- * @param int $stars Rune stars.
- * @return int Value of the main stat at the selected level.
- */
- private function create_rune_array($unit, $r, $tuning){
- $rune = [
- "ID" => $r["id"],
- "TYPE" => $r["type"],
- "ATK" => 0,
- "DEF" => 0,
- "HP" => 0,
- "SPD" => 0,
- "CRR" => 0,
- "CRD" => 0,
- "ACC" => 0,
- "RES" => 0
- ];
- // Calculate main stat value based on requested level.
- $level = $r["level"];
- $main_value = $r["main_stat_value"];
- switch ($tuning){
- case 1: // All +12
- if ($level < 12){
- $main_value = get_main_stat_at_level(12, $r["main_stat"], $r["stars"]);
- }
- break;
- case 2: // Even +15, Odd + 12
- if ($level < 15 && $r["slot"] % 2 == 0){
- $main_value = get_main_stat_at_level(15, $r["main_stat"], $r["stars"]);
- }
- elseif ($level < 12 && $r["slot"] % 2 != 0){
- $main_value = get_main_stat_at_level(12, $r["main_stat"], $r["stars"]);
- }
- break;
- case 3: // All +15
- if ($level < 15){
- $main_value = get_main_stat_at_level(15, $r["main_stat"], $r["stars"]);
- }
- break;
- }
- // Array to loop.
- $stats = [
- [
- "STAT" => $r["main_stat"],
- "VALUE" => $main_value
- ],
- [
- "STAT" => $r["innate_stat"],
- "VALUE" => $r["innate_stat_value"]
- ],
- [
- "STAT" => $r["substat_1"],
- "VALUE" => $r["substat_1_value"]
- ],
- [
- "STAT" => $r["substat_2"],
- "VALUE" => $r["substat_2_value"]
- ],
- [
- "STAT" => $r["substat_3"],
- "VALUE" => $r["substat_3_value"]
- ],
- [
- "STAT" => $r["substat_4"],
- "VALUE" => $r["substat_4_value"]
- ]
- ];
- foreach ($stats as $stat){
- switch ($stat["STAT"]){
- case RUNE_STAT_ID::ATK:
- $rune["ATK"] += $stat["VALUE"];
- break;
- case RUNE_STAT_ID::ATK_P:
- $rune["ATK"] += ($unit->get_base_stats()["atk"] * $stat["VALUE"] / 100);
- break;
- case RUNE_STAT_ID::DEF:
- $rune["DEF"] += $stat["VALUE"];
- break;
- case RUNE_STAT_ID::DEF_P:
- $rune["DEF"] += ($unit->get_base_stats()["def"] * $stat["VALUE"] / 100);
- break;
- case RUNE_STAT_ID::HP:
- $rune["HP"] += $stat["VALUE"];
- break;
- case RUNE_STAT_ID::HP_P:
- $rune["HP"] += ($unit->get_base_stats()["hp"] * $stat["VALUE"] / 100);
- break;
- case RUNE_STAT_ID::SPD:
- $rune["SPD"] += $stat["VALUE"];
- break;
- case RUNE_STAT_ID::CRR:
- $rune["CRR"] += $stat["VALUE"];
- break;
- case RUNE_STAT_ID::CRD:
- $rune["CRD"] += $stat["VALUE"];
- break;
- case RUNE_STAT_ID::ACC:
- $rune["ACC"] += $stat["VALUE"];
- break;
- case RUNE_STAT_ID::RES:
- $rune["RES"] += $stat["VALUE"];
- break;
- }
- }
- return $rune;
- }
-
- /**
- * Gets the value of the main stat at an arbitrary level.
- *
- * @param int $level Desired level.
- * @param int $stat Rune main stat ID.
- * @param int $stars Rune stars.
- * @return int Value of the main stat at the selected level.
- */
- private function get_main_stat_at_level($level, $stat, $stars){
- $level = intval($level);
- if ($level < 0 || $level > 15){
- return 0;
- }
- switch ($stat){
- case RUNE_STAT_ID::HP:
- return RUNE_STAT_VALUES::HP[$stars][$level];
- case RUNE_STAT_ID::HP_P:
- return RUNE_STAT_VALUES::HP_P[$stars][$level];
- case RUNE_STAT_ID::ATK:
- return RUNE_STAT_VALUES::ATK[$stars][$level];
- case RUNE_STAT_ID::ATK_P:
- return RUNE_STAT_VALUES::ATK_P[$stars][$level];
- case RUNE_STAT_ID::DEF:
- return RUNE_STAT_VALUES::DEF[$stars][$level];
- case RUNE_STAT_ID::DEF_P:
- return RUNE_STAT_VALUES::DEF_P[$stars][$level];
- case RUNE_STAT_ID::SPD:
- return RUNE_STAT_VALUES::SPD[$stars][$level];
- case RUNE_STAT_ID::CRR:
- return RUNE_STAT_VALUES::CRR[$stars][$level];
- case RUNE_STAT_ID::CRD:
- return RUNE_STAT_VALUES::CRD[$stars][$level];
- case RUNE_STAT_ID::RES:
- return RUNE_STAT_VALUES::RES[$stars][$level];
- case RUNE_STAT_ID::ACC:
- return RUNE_STAT_VALUES::ACC[$stars][$level];
- default:
- return 0;
- }
- }
- }
|