|
@@ -0,0 +1,667 @@
|
|
|
|
|
+#include <stdio.h>
|
|
|
|
|
+#include <string.h>
|
|
|
|
|
+#include "../optimize.h"
|
|
|
|
|
+#include "../../db/db.h"
|
|
|
|
|
+#include "../../error/error.h"
|
|
|
|
|
+#include "output.h"
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Generates output in a human readable format.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param[in] data Optimizer data. Results, options, unit and rune data must be
|
|
|
|
|
+ * already loaded.
|
|
|
|
|
+ * @param[in,out] target File to write to. stdout can be passed, to print to the
|
|
|
|
|
+ * screen. If it's a file, it must be opened for writing, and in won't be closed
|
|
|
|
|
+ * here.
|
|
|
|
|
+ * @param[in] color {@link TRUE} to colorize the output, false to not use
|
|
|
|
|
+ * colors. It is not recommended to use colors when writing to a file.
|
|
|
|
|
+ * @param max Max number of results to print.
|
|
|
|
|
+ * @param[in] order {@link ORDER_ASC} to show the worst result first,
|
|
|
|
|
+ * {@link ORDER_DESC} to show the best first.
|
|
|
|
|
+ * @return The number of results printed.
|
|
|
|
|
+ */
|
|
|
|
|
+static int print_human(
|
|
|
|
|
+ Optimizer_Data *data, FILE *target,
|
|
|
|
|
+ unsigned char color, unsigned int max, unsigned char order
|
|
|
|
|
+);
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Generates output in JSON format.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param[in] data Optimizer data. Results, options, unit and rune data must be
|
|
|
|
|
+ * already loaded.
|
|
|
|
|
+ * @param[in,out] target File to write to. stdout can be passed, to print to the
|
|
|
|
|
+ * screen. If it's a file, it must be opened for writing, and in won't be closed
|
|
|
|
|
+ * here.
|
|
|
|
|
+ * @param max Max number of results to print.
|
|
|
|
|
+ * @return The number of results printed.
|
|
|
|
|
+ */
|
|
|
|
|
+static int print_json(Optimizer_Data *data, FILE *target, unsigned int max);
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Generates a filename for the results file.
|
|
|
|
|
+ *
|
|
|
|
|
+ * Uses the loaded data to generate a filename, with the format
|
|
|
|
|
+ * <unit_id>_<unit_name>_<timestamp>.<extension>
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param[in] data Optimizer data. Results, options, unit and rune data must be
|
|
|
|
|
+ * already loaded.
|
|
|
|
|
+ * @param[in] ext Filename extension, without the dot.
|
|
|
|
|
+ * @param[out] filename Composed filename.
|
|
|
|
|
+ * @return Number of characters in the filename.
|
|
|
|
|
+ */
|
|
|
|
|
+static int generate_filename(Optimizer_Data *data, char *ext, char *filename);
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Generates a string with the difference between two stats.
|
|
|
|
|
+ *
|
|
|
|
|
+ * If the stats are the same, the string will be filled with whitespaces.
|
|
|
|
|
+ * If colors are used, the number will have no sign symbol, and the string will
|
|
|
|
|
+ * be green if modified is larger than current, and red if it's lower.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param[in] current Base value to compare.
|
|
|
|
|
+ * @param[in] modified Modified value to compare with base.
|
|
|
|
|
+ * @param[in] padding The length for the string. The resultng number will be
|
|
|
|
|
+ * padded to the right, and the total length of the string will be this. It can
|
|
|
|
|
+ * overflow if padding is shorter than the number length + 1.
|
|
|
|
|
+ * @param[in] percent If {@link TRUE}, a percent symbol will be added to the
|
|
|
|
|
+ * number. If {@link FALSE}, a whitespace will be added.
|
|
|
|
|
+ * @param[out] string The string with the differencec number, with a length of
|
|
|
|
|
+ * {@link padding}, padded to the right. If current and modified are the same,
|
|
|
|
|
+ * all the string will be filled with whitespaces. If {@link USE_COLOR} is set
|
|
|
|
|
+ * to {@link TRUE}, the number will have no sign symbol, and the string will be
|
|
|
|
|
+ * green if modified is larger than current, and red if it's lower. If
|
|
|
|
|
+ * {@link USE_COLOR} is set to {@link FALSE}, the string will not be colorized,
|
|
|
|
|
+ * and it will have a sign symbol, even if it's positive.
|
|
|
|
|
+ * @todo: handle color.
|
|
|
|
|
+ */
|
|
|
|
|
+static int diff_string(
|
|
|
|
|
+ unsigned int current, unsigned int modified,
|
|
|
|
|
+ int padding, unsigned int percent, unsigned char color, char *string
|
|
|
|
|
+);
|
|
|
|
|
+
|
|
|
|
|
+static int generate_filename(Optimizer_Data *data, char *ext, char *filename){
|
|
|
|
|
+ char unit_name[125];
|
|
|
|
|
+ int i;
|
|
|
|
|
+ for (i = 0; i < strlen(data->unit->name); i ++){
|
|
|
|
|
+ if (
|
|
|
|
|
+ (data->unit->name[i] >= 'a' && data->unit->name[i] <= 'z')
|
|
|
|
|
+ || (data->unit->name[i] >= 'A' && data->unit->name[i] <= 'Z')
|
|
|
|
|
+ || (data->unit->name[i] >= '0' && data->unit->name[i] <= '9')
|
|
|
|
|
+ ){
|
|
|
|
|
+ unit_name[i] = data->unit->name[i];
|
|
|
|
|
+ }
|
|
|
|
|
+ else{
|
|
|
|
|
+ unit_name[i] = '-';
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ unit_name[i] = '\0';
|
|
|
|
|
+ strcpy(filename, data->unit->id);
|
|
|
|
|
+ strcat(filename, "_");
|
|
|
|
|
+ strcat(filename, unit_name);
|
|
|
|
|
+ strcat(filename, "_");
|
|
|
|
|
+ strcat(filename, data->start_time);
|
|
|
|
|
+ strcat(filename, ".");
|
|
|
|
|
+ strcat(filename, ext);
|
|
|
|
|
+ return strlen(filename);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static int diff_string(
|
|
|
|
|
+ unsigned int current, unsigned int modified,
|
|
|
|
|
+ int padding, unsigned int percent, unsigned char color, char *string
|
|
|
|
|
+){
|
|
|
|
|
+ int diff = modified - current;
|
|
|
|
|
+ int i;
|
|
|
|
|
+ if (diff == 0){
|
|
|
|
|
+ for (i = 0; i < padding + 1; i ++){
|
|
|
|
|
+ string[i] = ' ';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ string[i] = '\0';
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (diff > 0){
|
|
|
|
|
+ /*sprintf(
|
|
|
|
|
+ string, "\033[32m%*d%c\033[39m",
|
|
|
|
|
+ padding, diff, (percent == TRUE) ? '%' : ' '
|
|
|
|
|
+ );*/
|
|
|
|
|
+ sprintf(
|
|
|
|
|
+ string, "%s%*d%c%s",
|
|
|
|
|
+ (color == TRUE) ? "\033[32m" : "+",
|
|
|
|
|
+ (color == TRUE) ? padding : padding - 1,
|
|
|
|
|
+ diff, (percent == TRUE) ? '%' : ' ',
|
|
|
|
|
+ (color == TRUE) ? "\033[39m" : ""
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ else{
|
|
|
|
|
+ sprintf(
|
|
|
|
|
+ string, "\033[31m%*d%c\033[39m",
|
|
|
|
|
+ padding, diff * -1, (percent == TRUE) ? '%' : ' '
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ return diff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+extern int output(Optimizer_Data *data){
|
|
|
|
|
+ FILE *target;
|
|
|
|
|
+ unsigned char format = data->output->format;
|
|
|
|
|
+ unsigned char output = data->output->output;
|
|
|
|
|
+ int retval = ERROR_INPUT_OPTIMIZE_PARAMETER_OUTPUT;
|
|
|
|
|
+ if (format == OUTPUT_HUMAN){
|
|
|
|
|
+ if (output == OUTPUT_SCREEN){
|
|
|
|
|
+ return print_human(
|
|
|
|
|
+ data, stdout, TRUE, MAX_RESULTS_SCREEN_HUMAN, ORDER_ASC
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (output == OUTPUT_FILE){
|
|
|
|
|
+ char filename[128];
|
|
|
|
|
+ generate_filename(data, "txt", filename);
|
|
|
|
|
+ FILE * file;
|
|
|
|
|
+ file = fopen(filename, "w");
|
|
|
|
|
+ retval = print_human(
|
|
|
|
|
+ data, file, FALSE, MAX_RESULTS_FILE_HUMAN, ORDER_DESC
|
|
|
|
|
+ );
|
|
|
|
|
+ fclose(file);
|
|
|
|
|
+ return retval;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (format == OUTPUT_JSON){
|
|
|
|
|
+ if (output == OUTPUT_SCREEN){
|
|
|
|
|
+ return print_json(data, stdout, MAX_RESULTS_SCREEN_JSON);
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (output == OUTPUT_FILE){
|
|
|
|
|
+ char filename[128];
|
|
|
|
|
+ generate_filename(data, "json", filename);
|
|
|
|
|
+ FILE * file;
|
|
|
|
|
+ file = fopen(filename, "w");
|
|
|
|
|
+ retval = print_json(data, file, MAX_RESULTS_FILE_JSON);
|
|
|
|
|
+ fclose(file);
|
|
|
|
|
+ return retval;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return retval;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+extern void output_summary(Optimizer_Data *data){
|
|
|
|
|
+ // This is an output example:
|
|
|
|
|
+ ////////////////////////////////
|
|
|
|
|
+ //
|
|
|
|
|
+ // ----------------------------------- Requested rune sets:
|
|
|
|
|
+ // | Lushen | RAGE BLADE
|
|
|
|
|
+ // | ID: 7223811472 |
|
|
|
|
|
+ // ----------------------------------- Accepted stats:
|
|
|
|
|
+ // | STAT | BASE | CURR. | MIN. | ATK CRR CRD
|
|
|
|
|
+ // -----------------------------------
|
|
|
|
|
+ // | HP: | 9225 | 12262 | 10000 | Considering runes at level 15
|
|
|
|
|
+ // | ATK: | 900 | 2482 | 2482 |
|
|
|
|
|
+ // | DEF: | 461 | 703 | 703 | Runes considered by slot:
|
|
|
|
|
+ // | SPD: | 103 | 159 | 159 | 1: 27 2: 39 3: 25
|
|
|
|
|
+ // | CRR: | 15% | 79% | 79% | 4: 35 5: 24 6: 32
|
|
|
|
|
+ // | CRD: | 50% | 188% | 188% |
|
|
|
|
|
+ // | RES: | 15% | 35% | 35% | Total combinations: 707616000
|
|
|
|
|
+ // | ACC: | 0% | 11% | 10% |
|
|
|
|
|
+ // -----------------------------------
|
|
|
|
|
+ //
|
|
|
|
|
+ printf(
|
|
|
|
|
+ " ----------------------------------------- Requested rune sets:\n"
|
|
|
|
|
+ );
|
|
|
|
|
+ printf(" | %*s | ", -37, data->unit->name);
|
|
|
|
|
+ for (int i = 0; i < MAX_SETS; i ++){
|
|
|
|
|
+ if (data->filters->sets[i] != 0){
|
|
|
|
|
+ //printf(" %d ", data->filters->sets[i]);
|
|
|
|
|
+ printf(" %s ", SET_NAMES[data->filters->sets[i]]);
|
|
|
|
|
+ }
|
|
|
|
|
+ else break;
|
|
|
|
|
+ }
|
|
|
|
|
+ printf("\n");
|
|
|
|
|
+ printf(" | ID: %*s |\n", -33, data->unit->id);
|
|
|
|
|
+ printf(" ----------------------------------------- Accepted stats:\n");
|
|
|
|
|
+ printf(" | STAT | BASE | CURR. | MIN. | ");
|
|
|
|
|
+ for (int i = 0; i < DIFFERENT_STATS; i ++){
|
|
|
|
|
+ if (data->filters->stats[i] == TRUE)
|
|
|
|
|
+ printf(" %s ", STAT_NAMES[i]);
|
|
|
|
|
+ //else break;
|
|
|
|
|
+ }
|
|
|
|
|
+ printf("\n");
|
|
|
|
|
+ printf(" -----------------------------------------\n");
|
|
|
|
|
+ printf(
|
|
|
|
|
+ " | HP: | %*u | %*u | %*u | ",
|
|
|
|
|
+ 7, data->unit->base_hp, 7, data->unit->current_hp,
|
|
|
|
|
+ 7, data->filters->min_stats->hp
|
|
|
|
|
+ );
|
|
|
|
|
+ if (data->filters->level == 0)
|
|
|
|
|
+ printf("Using runes at their current level\n");
|
|
|
|
|
+ else printf("Considering runes at level %d\n", data->filters->level);
|
|
|
|
|
+ printf(
|
|
|
|
|
+ " | ATK: | %*u | %*u | %*u |\n",
|
|
|
|
|
+ 7, data->unit->base_atk, 7, data->unit->current_atk,
|
|
|
|
|
+ 7, data->filters->min_stats->atk
|
|
|
|
|
+ );
|
|
|
|
|
+ printf(
|
|
|
|
|
+ " | DEF: | %*u | %*u | %*u | Runes considered by slot:\n",
|
|
|
|
|
+ 7, data->unit->base_def, 7, data->unit->current_def,
|
|
|
|
|
+ 7, data->filters->min_stats->def
|
|
|
|
|
+ );
|
|
|
|
|
+ printf(
|
|
|
|
|
+ " | SPD: | %*u | %*u | %*u | 1:%*u 2:%*u 3:%*u\n",
|
|
|
|
|
+ 7, data->unit->base_spd, 7, data->unit->current_spd,
|
|
|
|
|
+ 7, data->filters->min_stats->spd,
|
|
|
|
|
+ 3, data->count[1], 3, data->count[2], 3, data->count[3]
|
|
|
|
|
+ );
|
|
|
|
|
+ printf(
|
|
|
|
|
+ " | CRR: | %*u%% | %*u%% | %*u%% | 4:%*u 5:%*u 6:%*u\n",
|
|
|
|
|
+ 7, data->unit->base_crr, 7, data->unit->current_crr,
|
|
|
|
|
+ 7, data->filters->min_stats->crr,
|
|
|
|
|
+ 3, data->count[4], 3, data->count[5], 3, data->count[6]
|
|
|
|
|
+ );
|
|
|
|
|
+ printf(
|
|
|
|
|
+ " | CRD: | %*u%% | %*u%% | %*u%% |\n",
|
|
|
|
|
+ 7, data->unit->base_crd, 7, data->unit->current_crd,
|
|
|
|
|
+ 7, data->filters->min_stats->crd
|
|
|
|
|
+ );
|
|
|
|
|
+ printf(
|
|
|
|
|
+ " | RES: | %*u%% | %*u%% | %*u%% | Total combinations: %llu\n",
|
|
|
|
|
+ 7, data->unit->base_res, 7, data->unit->current_res,
|
|
|
|
|
+ 7, data->filters->min_stats->res, data->max_combinations
|
|
|
|
|
+ );
|
|
|
|
|
+ printf(
|
|
|
|
|
+ " | ACC: | %*u%% | %*u%% | %*u%% |\n",
|
|
|
|
|
+ 7, data->unit->base_acc, 7, data->unit->current_acc,
|
|
|
|
|
+ 7, data->filters->min_stats->acc
|
|
|
|
|
+ );
|
|
|
|
|
+ printf(
|
|
|
|
|
+ " | EHP: | %*u | %*u | %*u |\n",
|
|
|
|
|
+ 7, data->unit->base_ehp, 7, data->unit->current_ehp,
|
|
|
|
|
+ 7, data->filters->min_stats->ehp
|
|
|
|
|
+ );
|
|
|
|
|
+ printf(
|
|
|
|
|
+ " | DMG: | %*u | %*u | %*u |\n",
|
|
|
|
|
+ 7, data->unit->base_dmg, 7, data->unit->current_dmg,
|
|
|
|
|
+ 7, data->filters->min_stats->dmg
|
|
|
|
|
+ );
|
|
|
|
|
+ printf(" -----------------------------------------\n");
|
|
|
|
|
+ return;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static int print_human(
|
|
|
|
|
+ Optimizer_Data *data, FILE *target,
|
|
|
|
|
+ unsigned char color, unsigned int max, unsigned char order
|
|
|
|
|
+){
|
|
|
|
|
+ if (data->total_results == 0){
|
|
|
|
|
+ fprintf(target, "\n\tNo results found for the selected settings.\n");
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ char diff[19];
|
|
|
|
|
+ char best_result[30] = "";
|
|
|
|
|
+ int total_printed = 0;
|
|
|
|
|
+ Unit *unit = data->unit;
|
|
|
|
|
+ int i;
|
|
|
|
|
+ if (order == ORDER_ASC){
|
|
|
|
|
+ i = data->total_results - 1;
|
|
|
|
|
+ if (i > max){
|
|
|
|
|
+ i = max;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else{
|
|
|
|
|
+ i = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ while (1){
|
|
|
|
|
+ if (i != data->total_results){
|
|
|
|
|
+ fprintf(
|
|
|
|
|
+ target,
|
|
|
|
|
+ "\n==========================================="
|
|
|
|
|
+ "==========================================\n"
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ Result result = data->results[i];
|
|
|
|
|
+ fprintf(target, "--------------------------------------\n");
|
|
|
|
|
+ diff_string(unit->current_hp, result.stats.hp, 6, FALSE, color, diff);
|
|
|
|
|
+ if (i == 0){
|
|
|
|
|
+ if (color == TRUE)
|
|
|
|
|
+ strcpy(best_result, "\033[31mBest result!\033[39m");
|
|
|
|
|
+ else strcpy(best_result, "Best result!");
|
|
|
|
|
+ }
|
|
|
|
|
+ fprintf(
|
|
|
|
|
+ target,
|
|
|
|
|
+ "| HP: | %*u | %*u | %s | Result #%d %s \n",
|
|
|
|
|
+ 6, unit->current_hp, 6, result.stats.hp, diff, i + 1, best_result
|
|
|
|
|
+ );
|
|
|
|
|
+ diff_string(unit->current_atk, result.stats.atk, 6, FALSE, color, diff);
|
|
|
|
|
+ fprintf(
|
|
|
|
|
+ target,
|
|
|
|
|
+ "| ATK: | %*u | %*u | %s |\n",
|
|
|
|
|
+ 6, unit->current_atk, 6, result.stats.atk, diff
|
|
|
|
|
+ );
|
|
|
|
|
+ diff_string(unit->current_def, result.stats.def, 6, FALSE, color, diff);
|
|
|
|
|
+ char rating[30];
|
|
|
|
|
+ if (color == TRUE){
|
|
|
|
|
+ sprintf(
|
|
|
|
|
+ rating, "%s%d%s", (result.rating > 0) ? "\033[32m" : "\033[31m",
|
|
|
|
|
+ result.rating, "\033[39m"
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ else{
|
|
|
|
|
+ sprintf(rating, "%d", result.rating);
|
|
|
|
|
+ }
|
|
|
|
|
+ fprintf(
|
|
|
|
|
+ target,
|
|
|
|
|
+ "| DEF: | %*u | %*u | %s | Rating: %s \n",
|
|
|
|
|
+ 6, unit->current_def, 6, result.stats.def, diff,
|
|
|
|
|
+ rating
|
|
|
|
|
+ );
|
|
|
|
|
+ diff_string(unit->current_spd, result.stats.spd, 6, FALSE, color, diff);
|
|
|
|
|
+ fprintf(
|
|
|
|
|
+ target,
|
|
|
|
|
+ "| SPD: | %*u | %*u | %s |\n",
|
|
|
|
|
+ 6, unit->current_spd, 6, result.stats.spd, diff
|
|
|
|
|
+ );
|
|
|
|
|
+ diff_string(unit->current_crr, result.stats.crr, 6, FALSE, color, diff);
|
|
|
|
|
+ fprintf(
|
|
|
|
|
+ target,
|
|
|
|
|
+ "| CRR: | %*u | %*u | %s |\n",
|
|
|
|
|
+ 6, unit->current_crr, 6, result.stats.crr, diff
|
|
|
|
|
+ );
|
|
|
|
|
+ diff_string(unit->current_crd, result.stats.crd, 6, TRUE, color, diff);
|
|
|
|
|
+ fprintf(
|
|
|
|
|
+ target,
|
|
|
|
|
+ "| CRD: | %*u | %*u | %s |\n",
|
|
|
|
|
+ 6, unit->current_crd, 6, result.stats.crd, diff
|
|
|
|
|
+ );
|
|
|
|
|
+ diff_string(unit->current_res, result.stats.res, 6, TRUE, color, diff);
|
|
|
|
|
+ fprintf(
|
|
|
|
|
+ target,
|
|
|
|
|
+ "| RES: | %*u | %*u | %s |\n",
|
|
|
|
|
+ 6, unit->current_res, 6, result.stats.res, diff
|
|
|
|
|
+ );
|
|
|
|
|
+ diff_string(unit->current_acc, result.stats.acc, 6, TRUE, color, diff);
|
|
|
|
|
+ fprintf(
|
|
|
|
|
+ target,
|
|
|
|
|
+ "| ACC: | %*u | %*u | %s |\n",
|
|
|
|
|
+ 6, unit->current_acc, 6, result.stats.acc, diff
|
|
|
|
|
+ );
|
|
|
|
|
+ diff_string(unit->current_ehp, result.stats.ehp, 6, TRUE, color, diff);
|
|
|
|
|
+ fprintf(
|
|
|
|
|
+ target,
|
|
|
|
|
+ "| EHP: | %*u | %*u | %s |\n",
|
|
|
|
|
+ 6, unit->current_ehp, 6, result.stats.ehp, diff
|
|
|
|
|
+ );
|
|
|
|
|
+ diff_string(unit->current_dmg, result.stats.dmg, 6, TRUE, color, diff);
|
|
|
|
|
+ fprintf(
|
|
|
|
|
+ target,
|
|
|
|
|
+ "| DMG: | %*u | %*u | %s |\n",
|
|
|
|
|
+ 6, unit->current_dmg, 6, result.stats.dmg, diff
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ fprintf(
|
|
|
|
|
+ target,
|
|
|
|
|
+ "-------------------------------------------"
|
|
|
|
|
+ "------------------------------------------\n"
|
|
|
|
|
+ );
|
|
|
|
|
+ char lines[10][190];
|
|
|
|
|
+ for (int l = 0; l < 10; l ++){
|
|
|
|
|
+ strcpy(lines[l], "");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Print all the runes in the result
|
|
|
|
|
+ int start_pos = 0;
|
|
|
|
|
+ for (int r = 0; r < 6; r ++){
|
|
|
|
|
+ char rune_id[RUNE_ID_LEN];
|
|
|
|
|
+ strcpy(rune_id, result.rune_ids[r]);
|
|
|
|
|
+ start_pos = 14 * r;
|
|
|
|
|
+ Rune rune;
|
|
|
|
|
+ char tmp[64];
|
|
|
|
|
+ char tmp2[64];
|
|
|
|
|
+ sqlite3_stmt *stmt_rune;
|
|
|
|
|
+ char *parameters[1] = {(char *) rune_id};
|
|
|
|
|
+ db_query(
|
|
|
|
|
+ &stmt_rune,
|
|
|
|
|
+ "SELECT "
|
|
|
|
|
+ " unit, type, stars, level, quality, efficiency, max_efficiency "
|
|
|
|
|
+ "FROM runes WHERE id = ?",
|
|
|
|
|
+ parameters
|
|
|
|
|
+ );
|
|
|
|
|
+ sqlite3_step(stmt_rune);
|
|
|
|
|
+
|
|
|
|
|
+ // Row 0: Set name (coloreb by quality) and level
|
|
|
|
|
+ int quality = sqlite3_column_int(stmt_rune, 4);
|
|
|
|
|
+ if (color == TRUE){
|
|
|
|
|
+ switch (quality){
|
|
|
|
|
+ case 1: strcpy(tmp, "\033[30m"); break;
|
|
|
|
|
+ case 2: strcpy(tmp, "\033[32m"); break;
|
|
|
|
|
+ case 3: strcpy(tmp, "\033[36m"); break;
|
|
|
|
|
+ case 4: strcpy(tmp, "\033[35m"); break;
|
|
|
|
|
+ case 5: strcpy(tmp, "\033[33m"); break;
|
|
|
|
|
+ default: strcpy(tmp, "\033[30m");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else strcpy(tmp, "");
|
|
|
|
|
+
|
|
|
|
|
+ sprintf(
|
|
|
|
|
+ tmp, "%s%-9s", tmp, SET_NAMES[sqlite3_column_int(stmt_rune, 2)]
|
|
|
|
|
+ );
|
|
|
|
|
+ if (color == TRUE) strcat(tmp, "\033[39m");
|
|
|
|
|
+ sprintf(tmp, "%s +%2d", tmp, sqlite3_column_int(stmt_rune, 3));
|
|
|
|
|
+ strcat(lines[0], "|");
|
|
|
|
|
+ strcat(lines[0], tmp);
|
|
|
|
|
+
|
|
|
|
|
+ // Row 1, rune ID
|
|
|
|
|
+ strcat(lines[1], "| #");
|
|
|
|
|
+ strcat(lines[1], rune_id);
|
|
|
|
|
+
|
|
|
|
|
+ // Row 2, rune location
|
|
|
|
|
+ if (strlen(sqlite3_column_text(stmt_rune, 0)) == 0){
|
|
|
|
|
+ strcat(lines[2], "| In inventory");
|
|
|
|
|
+ }
|
|
|
|
|
+ else{
|
|
|
|
|
+ if (strcmp(sqlite3_column_text(stmt_rune, 0), unit->id) == 0){
|
|
|
|
|
+ if (color == TRUE)
|
|
|
|
|
+ strcat(lines[2], "| \033[37mEquiped\033[39m ");
|
|
|
|
|
+ else strcat(lines[2], "| Equiped ");
|
|
|
|
|
+ }
|
|
|
|
|
+ else{
|
|
|
|
|
+ // Get unit name
|
|
|
|
|
+ sqlite3_stmt *stmt_unit;
|
|
|
|
|
+ char *parameters_unit[1] = {
|
|
|
|
|
+ (char *) sqlite3_column_text(stmt_rune, 0)
|
|
|
|
|
+ };
|
|
|
|
|
+ db_query(
|
|
|
|
|
+ &stmt_unit,
|
|
|
|
|
+ "SELECT name FROM units WHERE id = ?", parameters_unit
|
|
|
|
|
+ );
|
|
|
|
|
+ if (SQLITE_ROW == sqlite3_step(stmt_unit)){
|
|
|
|
|
+ strcpy(tmp, " ");
|
|
|
|
|
+ strncpy(tmp, sqlite3_column_text(stmt_unit, 0), 13);
|
|
|
|
|
+ }
|
|
|
|
|
+ else{
|
|
|
|
|
+ strcpy(tmp, " # ");
|
|
|
|
|
+ strncpy(tmp + 2, sqlite3_column_text(stmt_rune, 0), 11);
|
|
|
|
|
+ }
|
|
|
|
|
+ sqlite3_finalize(stmt_unit);
|
|
|
|
|
+ while (strlen(tmp) < 13){
|
|
|
|
|
+ strcat(tmp, " ");
|
|
|
|
|
+ }
|
|
|
|
|
+ strcat(lines[2], "|");
|
|
|
|
|
+ strcat(lines[2], tmp);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Get rune stats
|
|
|
|
|
+ sqlite3_stmt *stmt_stat;
|
|
|
|
|
+ db_query(
|
|
|
|
|
+ &stmt_stat,
|
|
|
|
|
+ "SELECT "
|
|
|
|
|
+ " slot, stat, value, grind, enchant "
|
|
|
|
|
+ "FROM rune_stats WHERE rune = ? ORDER BY slot",
|
|
|
|
|
+ parameters
|
|
|
|
|
+ );
|
|
|
|
|
+ char has_innate = FALSE;
|
|
|
|
|
+ char total_subs = 0;
|
|
|
|
|
+ char percentile = FALSE;
|
|
|
|
|
+ while (SQLITE_ROW == sqlite3_step(stmt_stat)){
|
|
|
|
|
+ int slot = sqlite3_column_int(stmt_stat, 0);
|
|
|
|
|
+ int stat = sqlite3_column_int(stmt_stat, 1);
|
|
|
|
|
+ if (
|
|
|
|
|
+ stat == 2 || stat == 4 || stat == 6 || stat == 8
|
|
|
|
|
+ || stat == 9 || stat == 10 || stat == 11
|
|
|
|
|
+ ){
|
|
|
|
|
+ percentile = TRUE;
|
|
|
|
|
+ }
|
|
|
|
|
+ else{
|
|
|
|
|
+ percentile = FALSE;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (slot == 0){
|
|
|
|
|
+ has_innate = TRUE;
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (slot > total_subs){
|
|
|
|
|
+ total_subs = slot;
|
|
|
|
|
+ }
|
|
|
|
|
+ // Stat name and value.
|
|
|
|
|
+ sprintf(
|
|
|
|
|
+ tmp, "%3s%4d%c",
|
|
|
|
|
+ STAT_NAMES_UNSIGNED[stat], sqlite3_column_int(stmt_stat, 2),
|
|
|
|
|
+ (percentile == TRUE) ? 'X': ' '
|
|
|
|
|
+ );
|
|
|
|
|
+ if (slot == 0){
|
|
|
|
|
+ strcpy(tmp2, tmp);
|
|
|
|
|
+ strcpy(tmp, (color == TRUE) ? "\033[37m" : "");
|
|
|
|
|
+ strcat(tmp, tmp2);
|
|
|
|
|
+ strcat(tmp, (color == TRUE) ? "\033[39m" : "");
|
|
|
|
|
+ }
|
|
|
|
|
+ // If enchanted, color stat name and value.
|
|
|
|
|
+ if (sqlite3_column_int(stmt_stat, 4) == 1){
|
|
|
|
|
+ strcpy(tmp2, tmp);
|
|
|
|
|
+ strcpy(tmp, (color == TRUE) ? "\033[33m" : "");
|
|
|
|
|
+ strcat(tmp, tmp2);
|
|
|
|
|
+ strcat(tmp, (color == TRUE) ? "\033[39m" : "");
|
|
|
|
|
+ }
|
|
|
|
|
+ // Grind, if any, colored
|
|
|
|
|
+ if (sqlite3_column_int(stmt_stat, 3) > 0){
|
|
|
|
|
+ strcat(tmp, (color == TRUE) ? "\033[33m+" : "+");
|
|
|
|
|
+ strcpy(tmp2, "");
|
|
|
|
|
+ sprintf(
|
|
|
|
|
+ tmp2, "%d%c",sqlite3_column_int(stmt_stat, 3),
|
|
|
|
|
+ (percentile == TRUE) ? 'X': ' '
|
|
|
|
|
+ );
|
|
|
|
|
+ while (strlen(tmp2) < 4) strcat(tmp2, " ");
|
|
|
|
|
+ strcat(tmp, tmp2);
|
|
|
|
|
+ if (color == TRUE) strcat(tmp, "\033[39m");
|
|
|
|
|
+ }
|
|
|
|
|
+ else{
|
|
|
|
|
+ strcat(tmp, " ");
|
|
|
|
|
+ }
|
|
|
|
|
+ strcat(lines[slot + 4], "|");
|
|
|
|
|
+ strcat(lines[slot + 4], tmp);
|
|
|
|
|
+ }
|
|
|
|
|
+ sqlite3_finalize(stmt_stat);
|
|
|
|
|
+ // It the rune has no innate stat, add empty space.
|
|
|
|
|
+ if (has_innate == FALSE){
|
|
|
|
|
+ strcat(lines[4], "| ");
|
|
|
|
|
+ }
|
|
|
|
|
+ // Add empty space for each subsstat the runes doeesn't have.
|
|
|
|
|
+ for (; total_subs < 4; total_subs ++){
|
|
|
|
|
+ strcat(lines[5 + total_subs], "| ");
|
|
|
|
|
+ }
|
|
|
|
|
+ // Row 9: Efficiency
|
|
|
|
|
+ sprintf(
|
|
|
|
|
+ tmp, "|E: %2.1f/%2.1fX", sqlite3_column_double(stmt_rune, 5),
|
|
|
|
|
+ sqlite3_column_double(stmt_rune, 6)
|
|
|
|
|
+ );
|
|
|
|
|
+ strcat(lines[9], tmp);
|
|
|
|
|
+
|
|
|
|
|
+ sqlite3_finalize(stmt_rune);
|
|
|
|
|
+ }
|
|
|
|
|
+ for (int l = 3; l < 10; l ++){
|
|
|
|
|
+ for (int x = 0; x < strlen(lines[l]); x ++){
|
|
|
|
|
+ if (lines[l][x] == 'X'){
|
|
|
|
|
+ lines[l][x] = '%';
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for (int l = 0; l < 10; l ++){
|
|
|
|
|
+ strcat(lines[l], "|\n");
|
|
|
|
|
+ // Use puts to keet the percent signs.
|
|
|
|
|
+ fputs(lines[l], target);
|
|
|
|
|
+ }
|
|
|
|
|
+ fprintf(
|
|
|
|
|
+ target,
|
|
|
|
|
+ "-------------------------------------------"
|
|
|
|
|
+ "------------------------------------------\n"
|
|
|
|
|
+ );
|
|
|
|
|
+ total_printed ++;
|
|
|
|
|
+ // Break conditions
|
|
|
|
|
+ if (order == ORDER_ASC){
|
|
|
|
|
+ if (0 == i){
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ i --;
|
|
|
|
|
+ }
|
|
|
|
|
+ else{
|
|
|
|
|
+ if (i == data->total_results - 1 || i == max - 1){
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ i ++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return total_printed;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static int print_json(Optimizer_Data *data, FILE *target, unsigned int max){
|
|
|
|
|
+ char tmp[300];
|
|
|
|
|
+ strcpy(tmp, "");
|
|
|
|
|
+ char json[300];
|
|
|
|
|
+ fprintf(target, "{\"result_count\":%d,", data->total_results);
|
|
|
|
|
+ fprintf(target, "\"rune_level\":%d,", data->filters->level);
|
|
|
|
|
+ fprintf(target, "\"results\":[");
|
|
|
|
|
+ int total_printed = 0;
|
|
|
|
|
+ if (data->total_results == 0){
|
|
|
|
|
+ fprintf(target, "{\"result_count\":0,\"results\":[]}\n");
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ for (int i = 0; i < data->total_results && i < max; i++){
|
|
|
|
|
+ sprintf(tmp, "{\"id\":%d,\"rating\":%d,", i, data->results[i].rating);
|
|
|
|
|
+ strcpy(json, tmp);
|
|
|
|
|
+ sprintf(tmp, "\"hp\":%u,", data->results[i].stats.hp);
|
|
|
|
|
+ strcat(json, tmp);
|
|
|
|
|
+ sprintf(tmp, "\"atk\":%u,", data->results[i].stats.atk);
|
|
|
|
|
+ strcat(json, tmp);
|
|
|
|
|
+ sprintf(tmp, "\"dfc\":%u,", data->results[i].stats.def);
|
|
|
|
|
+ strcat(json, tmp);
|
|
|
|
|
+ sprintf(tmp, "\"spd\":%u,", data->results[i].stats.spd);
|
|
|
|
|
+ strcat(json, tmp);
|
|
|
|
|
+ sprintf(tmp, "\"crr\":%u,", data->results[i].stats.crr);
|
|
|
|
|
+ strcat(json, tmp);
|
|
|
|
|
+ sprintf(tmp, "\"crd\":%u,", data->results[i].stats.crd);
|
|
|
|
|
+ strcat(json, tmp);
|
|
|
|
|
+ sprintf(tmp, "\"res\":%u,", data->results[i].stats.res);
|
|
|
|
|
+ strcat(json, tmp);
|
|
|
|
|
+ sprintf(tmp, "\"acc\":%u,", data->results[i].stats.acc);
|
|
|
|
|
+ strcat(json, tmp);
|
|
|
|
|
+ sprintf(tmp, "\"ehp\":%u,", data->results[i].stats.ehp);
|
|
|
|
|
+ strcat(json, tmp);
|
|
|
|
|
+ sprintf(tmp, "\"dmg\":%u,", data->results[i].stats.dmg);
|
|
|
|
|
+ strcat(json, tmp);
|
|
|
|
|
+ strcat(json, "\"runes\":[");
|
|
|
|
|
+ //printf(" A %s", json);
|
|
|
|
|
+ for (int j = 0; j < RUNE_SLOTS; j++){
|
|
|
|
|
+ //printf(" RUNE LOOP %d", j);
|
|
|
|
|
+ sprintf(tmp, "\"%s\",", data->results[i].rune_ids[j]);
|
|
|
|
|
+ strcat(json, tmp);
|
|
|
|
|
+ }
|
|
|
|
|
+ //printf(" B %s", json);
|
|
|
|
|
+ sprintf(tmp, "\"%s\"", data->results[i].rune_ids[5]);
|
|
|
|
|
+ strcat(json, tmp);
|
|
|
|
|
+ strcat(json, "]},");
|
|
|
|
|
+
|
|
|
|
|
+ // If last result, remove last comma
|
|
|
|
|
+ if (i == data->total_results - 1 || i == max - 1){
|
|
|
|
|
+ json[strlen(json) - 1] = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ total_printed ++;
|
|
|
|
|
+ fprintf(target, json);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // End
|
|
|
|
|
+ fprintf(target, "]}\n");
|
|
|
|
|
+ return total_printed;
|
|
|
|
|
+}
|