diff --git a/inc/chrome-fit.php b/inc/chrome-fit.php index f375744..e21727c 100644 --- a/inc/chrome-fit.php +++ b/inc/chrome-fit.php @@ -105,27 +105,8 @@ function print_formatted_mastery(&$fit, $relative, array $prereqs_per_type) { } } - $sp = function($level, $rank) { - if($level == 0) return 0; - return ceil(pow(2, 2.5 * ($level - 1.0)) * 250.0 * $rank); - }; - - foreach($prereqs_unique as $stid => $level) { - $current = isset($fit['skillset']['override'][$stid]) - ? $fit['skillset']['override'][$stid] : $fit['skillset']['default']; - - $rank = \Osmium\Fit\get_skill_rank($stid); - $needed = ceil(250.0 * $rank * pow(2, 2.5 * ($level - 1.0))); - - $totalsp += $needed; - - if($current >= $level) { - continue; - } - - $missing_unique[$stid] = $level; - $missingsp += $needed - ceil(250.0 * $rank * ($current ? pow(2, 2.5 * ($current - 1.0)) : 0)); - } + $missing_unique = \Osmium\Skills\get_missing_prerequisites_unique($prereqs_unique, $fit['skillset']); + list($totalsp, $missingsp) = \Osmium\Skills\sum_sp($prereqs_unique, $fit['skillset']); foreach($types_per_prereqs as &$arr) { $arr = array_reverse(array_keys($arr)); diff --git a/inc/chrome.php b/inc/chrome.php index 7215877..3f96a18 100644 --- a/inc/chrome.php +++ b/inc/chrome.php @@ -173,8 +173,11 @@ function format_duration($seconds) { } function format_long_duration($seconds, $precision = 6) { + $tz = date_default_timezone_get(); + date_default_timezone_set('UTC'); list($y, $m, $d, $h, $i, $s) = explode('-', date('Y-m-d-H-i-s', 0)); list($Y, $M, $D, $H, $I, $S) = explode('-', date('Y-m-d-H-i-s', $seconds)); + date_default_timezone_set($tz); $years = $Y - $y; $months = $M - $m; diff --git a/inc/fit-db.php b/inc/fit-db.php index 8e891a1..3df04b9 100644 --- a/inc/fit-db.php +++ b/inc/fit-db.php @@ -1179,6 +1179,41 @@ function fetch_fit_uri($loadoutid) { return get_fit_uri($loadoutid, $visibility, $ptoken); } +/** + * Grabs a skillset associated with the current account. + * + * @todo have some proper way to specify the default skillset. + * @see use_skillset() + */ +function use_default_skillset(&$fit, $a) { + if(!isset($a['accountid'])) { + use_skillset($fit, array(), 5, 'All V'); + return 'All V'; + } + + $row = \Osmium\Db\fetch_assoc( + \Osmium\Db\query_params( + 'SELECT importedskillset, overriddenskillset, name FROM osmium.accountcharacters + WHERE accountid = $1 LIMIT 1', + array($a['accountid']) + )); + if($row === false) { + /* No skillsets */ + use_skillset($fit, array(), 5, 'All V'); + return 'All V'; + } + + $skillset = json_decode($row['importedskillset'], true); + $overridden = json_decode($row['overriddenskillset'], true); + if(!is_array($skillset)) $skillset = array(); + if(!is_array($overridden)) $overridden = array(); + foreach($overridden as $typeid => $l) { + $skillset[$typeid] = $l; + } + use_skillset($fit, $skillset, 0, $row['name']); + return $row['name']; +} + /** * Parses a skillset title and fetches it using characters of account * $a. @@ -1242,29 +1277,6 @@ function get_available_skillset_names_for_account() { return $names; } -/** - * Takes in an array of item/module type IDs; fills the $result array with entries like: - * input_type_id => array( - * skill_type_id => required_level, - * ... - * ) - */ -function get_skill_prerequisites_for_types(array $types, array &$result) { - foreach ($types as $typeid) { - if(!isset($result[$typeid])) { - $result[$typeid] = []; - } - - foreach(get_required_skills($typeid) as $stid => $slevel) { - if(!isset($result[$stid])) { - get_skill_prerequisites_for_types([ $stid ], $result); - } - - $result[$typeid][$stid] = $slevel; - } - } -} - function get_skill_prerequisites_and_missing_prerequisites($fit) { $types = array(); @@ -1293,23 +1305,9 @@ function get_skill_prerequisites_and_missing_prerequisites($fit) { } $types = array_keys($types); - $prereqs = $missing = array(); - - get_skill_prerequisites_for_types($types, $prereqs); - - foreach($types as $tid) { - if(!isset($prereqs[$tid])) continue; - - foreach($prereqs[$tid] as $stid => $level) { - $current = isset($fit['skillset']['override'][$stid]) - ? $fit['skillset']['override'][$stid] : $fit['skillset']['default']; - - if($current < $level) { - $missing[$tid] = 1; - break; - } - } - } + $prereqs = array(); + \Osmium\Skills\get_skill_prerequisites_for_types($types, $prereqs); + $missing = \Osmium\Skills\get_missing_prerequisites($prereqs, $fit['skillset']); return [ $prereqs, $missing ]; } diff --git a/inc/fit-names.php b/inc/fit-names.php index 6e6aa95..f808a21 100644 --- a/inc/fit-names.php +++ b/inc/fit-names.php @@ -156,45 +156,6 @@ function get_groupname($groupid) { ); } -function get_required_skills($typeid) { - $typeid = (int)$typeid; - $key = 'NameCache_required_skills_'.$typeid; - $cache = \Osmium\State\get_cache_memory($key); - - if($cache !== null) { - return $cache; - } - - static $rs = [ - 182 => 277, /* RequiredSkill1 => RequiredSkill1Level */ - 183 => 278, /* etc… */ - 184 => 279, - 1285 => 1286, - 1289 => 1287, - 1290 => 1288, - ]; - - $vals = []; - - static $ctx = null; - if($ctx === null) dogma_init_context($ctx); - - /* XXX: this is hackish */ - dogma_init_context($ctx); - dogma_set_ship($ctx, $typeid); - foreach($rs as $rsattid => $rslattid) { - if(dogma_get_ship_attribute($ctx, $rsattid, $skill) === DOGMA_OK - && dogma_get_ship_attribute($ctx, $rslattid, $level) === DOGMA_OK) { - if($skill > 0 && $level > 0) { - $vals[$skill] = $level; - } - } - } - - \Osmium\State\put_cache_memory($key, $vals, 86400); - return $vals; -} - function get_implant_slot($typeid) { $typeid = (int)$typeid; $key = 'NameCache_implantness_'.$typeid; @@ -219,24 +180,3 @@ function get_implant_slot($typeid) { \Osmium\State\put_cache_memory($key, $slot, 86400); return $slot; } - -function get_skill_rank($typeid) { - $typeid = (int)$typeid; - $key = 'NameCache_skill_rank_'.$typeid; - $cache = \Osmium\State\get_cache_memory($key); - - if($cache !== null) { - return $cache; - } - - static $ctx = null; - if($ctx === null) dogma_init_context($ctx); - - /* XXX */ - dogma_set_ship($ctx, $typeid); - - dogma_get_ship_attribute($ctx, ATT_SkillTimeConstant, $rank); - - \Osmium\State\put_cache_memory($key, $rank, 86400); - return $rank; -} diff --git a/inc/root.php b/inc/root.php index 080ac93..db1c16a 100644 --- a/inc/root.php +++ b/inc/root.php @@ -98,6 +98,7 @@ require ROOT.'/inc/log.php'; require ROOT.'/inc/notification.php'; require ROOT.'/inc/reputation.php'; +require ROOT.'/inc/skills.php'; \Osmium\Forms\post_redirect_get(); diff --git a/inc/search.php b/inc/search.php index e342919..a9e0748 100644 --- a/inc/search.php +++ b/inc/search.php @@ -306,17 +306,19 @@ function print_pretty_results($relative, $query, $more = '', $paginate = false, return; } + $ordered_by_shipgroup = isset($_GET['sort']) && $_GET['sort'] == 'attshipgroup'; + if($paginate) { echo $pageinfo; echo $pageresult; - print_loadout_list($ids, $relative, $offset, $message); + print_loadout_list($ids, $relative, $offset, $message, $ordered_by_shipgroup); echo $pageresult; } else { - print_loadout_list($ids, $relative, $offset, $message); + print_loadout_list($ids, $relative, $offset, $message, $ordered_by_shipgroup); } } -function print_loadout_list(array $ids, $relative, $offset = 0, $nothing_message = 'No loadouts.') { +function print_loadout_list(array $ids, $relative, $offset = 0, $nothing_message = 'No loadouts.', $show_ship_group_sections = false) { if($ids === array()) { echo "

".$nothing_message."

\n"; return; @@ -336,11 +338,44 @@ function print_loadout_list(array $ids, $relative, $offset = 0, $nothing_message WHERE lsr.loadoutid IN ('.$in.') ORDER BY '.$orderby ); + $last_groupname = ""; + while($loadout = \Osmium\Db\fetch_assoc($lquery)) { if($first === true) { $first = false; - /* Only write the
    tag if there is at least one loadout */ - echo "
      \n"; + /* Only write the tag if there is at least one loadout */ + echo "
      \n"; + echo ""; + echo "\n"; + echo ""; + echo ""; + } + + $fit = \Osmium\Fit\get_fit($loadout['loadoutid']); + $a = \Osmium\State\get_state('a'); + if ($a) { + \Osmium\Fit\use_default_skillset($fit, $a); + } + list($prereqs, $missing_prereqs) = \Osmium\Fit\get_skill_prerequisites_and_missing_prerequisites($fit); + $prereqs_unique = \Osmium\Skills\uniquify_prerequisites($prereqs); + $missing_unique = \Osmium\Skills\uniquify_prerequisites($missing_prereqs); + + // XXX code copying + list($groupname) = \Osmium\Db\fetch_row(\Osmium\Db\query_params( + 'SELECT groupname FROM eve.invtypes + JOIN eve.invgroups ON invtypes.groupid = invgroups.groupid + WHERE typeid = $1', + array($loadout['hullid']) + )); + + /* TODO: this is a weird special case. It would be cool to take in the + * ordering here and show headers for various orderings (if dps, show + * >0dps, >100dps, >300dps, ...; if creation date, show "today", + * "yesterday", "last week", "last month", ...; etc). + */ + if ($show_ship_group_sections && $groupname != $last_groupname) { + echo ""; + $last_groupname = $groupname; } $uri = \Osmium\Fit\get_fit_uri( @@ -349,10 +384,21 @@ function print_loadout_list(array $ids, $relative, $offset = 0, $nothing_message $sn = \Osmium\Chrome\escape($loadout['typename']); - echo "
    1. \n" - ."".$sn."\n"; + $class = "undetermined"; + if ($a) { + if ($missing_prereqs) { + if (isset($missing_prereqs[$loadout['hullid']])) { + $class = "not-sittable"; + } else { + $class = "sittable"; + } + } else { + $class = "flyable"; + } + } + echo "
    2. "; + echo ""; + echo ""; + + echo ""; + + echo "\n"; + + echo ""; + + echo ""; + echo ""; + } + + if($first === false) { + echo "\n"; + echo "
      IdealShipNameTagsAuthorSocial
      $groupname
      "; $dps = $loadout['dps'] === null ? 'N/A' : \Osmium\Chrome\format($loadout['dps'], 2); $ehp = $loadout['ehp'] === null ? 'N/A' : \Osmium\Chrome\format($loadout['ehp'], 2, 'k'); $esp = $loadout['estimatedprice'] === null ? 'N/A' : \Osmium\Chrome\format($loadout['estimatedprice'], 2); @@ -366,12 +412,83 @@ function print_loadout_list(array $ids, $relative, $offset = 0, $nothing_message echo "
      " .$esp."ISK
      \n"; + echo "
      "; + echo "\n" + ."".$sn."\n"; + echo "$sn"; + echo ""; + + echo "
      \n"; + print_viewpermission_sprites($loadout); + echo "
      \n"; + echo "
      "; echo "" .\Osmium\Chrome\escape($loadout['name'])."\n"; - echo "
      \n"; + if ($a && $missing_prereqs) { + echo "
      \n"; + echo ""; + if (isset($missing_prereqs[$loadout['hullid']])) { + list($unused, $hull_needed) = \Osmium\Skills\sum_sp($missing_prereqs[$loadout['hullid']], $fit['skillset']); + $h = \Osmium\Chrome\format($hull_needed); + echo "Hull in $h SP – "; + } + list($unused, $all_needed) = \Osmium\Skills\sum_sp($missing_unique, $fit['skillset']); + $a = \Osmium\Chrome\format($all_needed); + echo "Fit in $a SP"; + echo ""; + } + echo "
      "; + $tags = array_filter(explode(' ', $loadout['taglist']), function($tag) { return trim($tag) != ''; }); + if(count($tags) == 0) { + echo "(no tags)\n"; + } else { + echo "
        \n" + .implode('', array_map(function($tag) use($relative) { + $tag = trim($tag); + return "
      • $tag
      • \n"; + }, $tags))."
      \n"; + } + echo "
      "; + echo "".\Osmium\Chrome\format_character_name($loadout, $relative); + echo " (".\Osmium\Chrome\format_reputation($loadout['reputation']).")\n"; + + echo "
      ".date('Y-m-d', $loadout['updatedate'])."
      \n"; + echo "
      "; + $votes = (abs($loadout['votes']) == 1) ? 'vote' : 'votes'; + $upvotes = \Osmium\Chrome\format($loadout['upvotes'], -1); + $downvotes = \Osmium\Chrome\format($loadout['downvotes'], -1); + echo "+{$upvotes}/-{$downvotes}
      \n"; + + $comments = ($loadout['comments'] == 1) ? 'comment' : 'comments'; + echo "" + .\Osmium\Chrome\format($loadout['comments'], -1)." {$comments}\n"; + echo "
      \n"; + } else { + echo "

      ".$nothing_message."

      \n"; + } +} + +function print_viewpermission_sprites($loadout) { $vp = $loadout['viewpermission']; $vpsize = 16; if($vp > 0) { @@ -418,44 +535,6 @@ function print_loadout_list(array $ids, $relative, $offset = 0, $nothing_message if((int)$loadout['visibility'] === \Osmium\Fit\VISIBILITY_PRIVATE) { echo \Osmium\Chrome\sprite($relative, "(hidden loadout)", 4, 13, 64, 64, $vpsize); } - - echo "\n"; - - echo "".\Osmium\Chrome\format_character_name($loadout, $relative); - echo " (".\Osmium\Chrome\format_reputation($loadout['reputation']).")\n"; - - echo " — ".date('Y-m-d', $loadout['updatedate'])."
      \n"; - - $votes = (abs($loadout['votes']) == 1) ? 'vote' : 'votes'; - $upvotes = \Osmium\Chrome\format($loadout['upvotes'], -1); - $downvotes = \Osmium\Chrome\format($loadout['downvotes'], -1); - echo "" - .\Osmium\Chrome\format($loadout['votes'], -1) - ." {$votes} (+{$upvotes}|-{$downvotes})\n"; - - $comments = ($loadout['comments'] == 1) ? 'comment' : 'comments'; - echo "" - .\Osmium\Chrome\format($loadout['comments'], -1)." {$comments}\n"; - - $tags = array_filter(explode(' ', $loadout['taglist']), function($tag) { return trim($tag) != ''; }); - if(count($tags) == 0) { - echo "(no tags)\n"; - } else { - echo "\n"; - } - echo "\n"; - } - - if($first === false) { - echo "
    \n"; - } else { - echo "

    ".$nothing_message."

    \n"; - } } function get_search_cond_from_advanced() { diff --git a/inc/skills.php b/inc/skills.php new file mode 100644 index 0000000..69f49c5 --- /dev/null +++ b/inc/skills.php @@ -0,0 +1,159 @@ + 277, /* RequiredSkill1 => RequiredSkill1Level */ + 183 => 278, /* etc… */ + 184 => 279, + 1285 => 1286, + 1289 => 1287, + 1290 => 1288, + ]; + + $vals = []; + + static $ctx = null; + if($ctx === null) dogma_init_context($ctx); + + /* XXX: this is hackish */ + dogma_init_context($ctx); + dogma_set_ship($ctx, $typeid); + foreach($rs as $rsattid => $rslattid) { + if(dogma_get_ship_attribute($ctx, $rsattid, $skill) === DOGMA_OK + && dogma_get_ship_attribute($ctx, $rslattid, $level) === DOGMA_OK) { + if($skill > 0 && $level > 0) { + $vals[$skill] = $level; + } + } + } + + \Osmium\State\put_cache_memory($key, $vals, 86400); + return $vals; +} + +function get_skill_rank($typeid) { + $typeid = (int)$typeid; + $key = 'NameCache_skill_rank_'.$typeid; + $cache = \Osmium\State\get_cache_memory($key); + + if($cache !== null) { + return $cache; + } + + static $ctx = null; + if($ctx === null) dogma_init_context($ctx); + + /* XXX */ + dogma_set_ship($ctx, $typeid); + + dogma_get_ship_attribute($ctx, \Osmium\Fit\ATT_SkillTimeConstant, $rank); + + \Osmium\State\put_cache_memory($key, $rank, 86400); + return $rank; +} + +/** + * Takes in an array of item/module type IDs; fills the $result array with entries like: + * input_type_id => array( + * skill_type_id => required_level, + * ... + * ) + */ +function get_skill_prerequisites_for_types(array $types, array &$result) { + foreach ($types as $typeid) { + if(!isset($result[$typeid])) { + $result[$typeid] = []; + } + + foreach(get_required_skills($typeid) as $stid => $slevel) { + if(!isset($result[$stid])) { + get_skill_prerequisites_for_types([ $stid ], $result); + } + + $result[$typeid][$stid] = $slevel; + } + } +} + +function uniquify_prerequisites(array $prereqs) { + $prereqs_unique = array(); + foreach($prereqs as $tid => $arr) { + foreach($arr as $stid => $level) { + if(isset($prereqs_unique[$stid])) { + $prereqs_unique[$stid] = max($prereqs_unique[$stid], $level); + } else { + $prereqs_unique[$stid] = $level; + } + } + } + return $prereqs_unique; +} + +function get_missing_prerequisites(array $prereqs, array $skillset) { + $missing = array(); + foreach($prereqs as $tid => $contents) { + foreach($prereqs[$tid] as $stid => $level) { + $current = isset($skillset['override'][$stid]) + ? $skillset['override'][$stid] : $skillset['default']; + + if($current < $level) { + if (!isset($missing[$tid])) { + $missing[$tid] = array(); + } + $missing[$tid][$stid] = $level; + break; + } + } + } + return $missing; +} + +function get_missing_prerequisites_unique(array $prereqs_unique, array $skillset) { + $missing_unique = array(); + foreach($prereqs_unique as $stid => $level) { + $current = isset($skillset['override'][$stid]) + ? $skillset['override'][$stid] : $skillset['default']; + + if($current < $level) { + $missing_unique[$stid] = $level; + } + } + return $missing_unique; +} + +function sp($level, $rank) { + if($level == 0) return 0; + return ceil(pow(2, 2.5 * ($level - 1.0)) * 250.0 * $rank); +} + +/** returns array($total_sp, $missing_sp) */ +function sum_sp(array $prereqs_unique, array $skillset) { + $total_sp = 0; + $missing_sp = 0; + foreach($prereqs_unique as $stid => $level) { + $current = isset($skillset['override'][$stid]) + ? $skillset['override'][$stid] : $skillset['default']; + + $rank = get_skill_rank($stid); + $needed = sp($level, $rank); + + $total_sp += $needed; + + if($current >= $level) { + continue; + } + + $missing_sp += $needed - sp($current, $rank); + } + return array($total_sp, $missing_sp); +} diff --git a/src/sass/search.scss b/src/sass/search.scss index a34f953..85553a1 100644 --- a/src/sass/search.scss +++ b/src/sass/search.scss @@ -61,89 +61,6 @@ div#search_full { } } -ol.loadout_sr { - margin: 0; - padding: 0; - list-style-type: none; - text-align: left; - - &> li { - display: inline-block; - position: relative; - width: 16em; - min-height: 7.25em; - vertical-align: top; - margin: 1em 3em 1em 7em; - padding: 0 0.5em; - text-align: left; - - border-left: 1px solid $subsection-border-color; - - &> a > img:first-child { - display: inline-block; - width: 4em; - height: 4em; - vertical-align: middle; - - overflow: hidden; - border-radius: 1em; - position: absolute; - left: -4.5em; - top: 0em; - } - - &> a.fitname { - display: block; - @include ellipsis; - } - - &> div.dps, &> div.ehp, &> div.esp { - position: absolute; - left: -4.75em; - width: 4.25em; - @include ellipsis; - text-align: right; - - &> span > strong:after { - content: " "; - } - - &.dps { - top: 4.25em; - } - - &.ehp { - top: 5.25em; - } - - &.esp { - top: 6.25em; - } - } - - &> div.sideicons { - position: absolute; - left: -5.25em; - top: 0.5em; - width: 16px; - } - - &> small, &> small > a { - color: $loadout-search-metadata-text-color; - text-decoration: none; - } - - &> em.notags { - display: block; - opacity: 0.5; - } - - &> ul.tags { - opacity: 0.8; - } - } -} - p.pagination { color: $pagination-meta-text-color; font-size: 0.8em; @@ -194,3 +111,73 @@ ol.pagination { } } } + +table.loadout_sr { + margin-left: auto; + margin-right: auto; + + th { + text-align: left; + padding-left: 6px; + } + + border-spacing: 0 5px; + td { + padding-left: 6px; + padding-right: 6px; + } + + tr.flyable { + background-color: $skills-flyable-bg-color; + } + + tr.sittable { + background-color: $skills-sittable-bg-color; + } + + tr.not-sittable { + background-color: $skills-not-sittable-bg-color; + } + + tr.undetermined { + background-color: $skills-undetermined-bg-color; + } + + div.dps, div.ehp, div.esp { + text-align: right; + + &> span > strong:after { + content: " "; + } + } + + td.ideal { + white-space: nowrap; + } + + td.hull a { + display: block; + position: relative; + border-radius: 1em; + overflow: hidden; + + &> img.hull-icon { + width: 4em; + height: 4em; + } + + &> .name { + position: absolute; + bottom: 0px; + left: 0px; + width: 100%; + padding: 0 0.5em; + box-sizing: border-box; + -moz-box-sizing: border-box; + text-align: center; + background: rgba(0, 0, 0, 0.4); + font-size: xx-small; + @include ellipsis; + } + } +} diff --git a/src/sass/themes/dark.scss b/src/sass/themes/dark.scss index 1ac6a8e..cc40559 100644 --- a/src/sass/themes/dark.scss +++ b/src/sass/themes/dark.scss @@ -219,6 +219,15 @@ $reputation-loss-text-color: hsl(0, 100%, 50%); $privilege-progress-bg-color: $subsection-bg-color; $privilege-progress-done-bg-color: $reputation-gain-text-color; +// +// Search +// + +$skills-flyable-bg-color: rgba(102, 255, 102, 0.2); +$skills-sittable-bg-color: rgba(255, 255, 102, 0.2); +$skills-not-sittable-bg-color: rgba(255, 102, 102, 0.2); +$skills-undetermined-bg-color: rgba(255, 255, 255, 0.1); + ///////////////////////////////////// -@import "../root.scss"; \ No newline at end of file +@import "../root.scss"; diff --git a/src/sass/themes/light.scss b/src/sass/themes/light.scss index 1ce8983..064e341 100644 --- a/src/sass/themes/light.scss +++ b/src/sass/themes/light.scss @@ -219,6 +219,15 @@ $reputation-loss-text-color: hsl(0, 100%, 50%); $privilege-progress-bg-color: $subsection-bg-color; $privilege-progress-done-bg-color: $reputation-gain-text-color; +// +// Search +// + +$skills-flyable-bg-color: rgba(102, 255, 102, 0.2); +$skills-sittable-bg-color: rgba(255, 255, 102, 0.2); +$skills-not-sittable-bg-color: rgba(255, 102, 102, 0.2); +$skills-undetermined-bg-color: rgba(0, 0, 0, 0.1); + ///////////////////////////////////// -@import "../root.scss"; \ No newline at end of file +@import "../root.scss";