From 5e59f3dea8d74bc9567d2841ab56afa9aa544f69 Mon Sep 17 00:00:00 2001 From: Donald Burr Date: Wed, 21 Oct 2015 04:01:13 -0700 Subject: [PATCH 01/17] Remove the "not yet complete" note on web app --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c31b7e4..7ba5f46 100644 --- a/README.md +++ b/README.md @@ -183,7 +183,7 @@ You will have 50 love gems on 01/16/2016. Good things come to those who wait! ## The Web App -You asked for a web app, and here it is! The [web_app](web_app) directory contains a (mostly) fully featured web app version of SIF Tools. The best part is, you don't need a web server to run it. You should just be able to open the [sif_tools.html](web_app/sif_tools.html) file in your local web browser and run it right on your own computer. You will need to have JavaScript enabled in your browser however. (Of course you can host these files on a web server just like any other website, if you happen to have access to a web server.) +You asked for a web app, and here it is! The [web_app](web_app) directory contains a fully featured web app version of SIF Tools. In fact, the web app actually does some things that the original Python scripts don't! (yet.) The best part is, you don't need a web server to run it. You should just be able to open the [sif_tools.html](web_app/sif_tools.html) file in your local web browser and run it right on your own computer. You will need to have JavaScript enabled in your browser however. (Of course you can host these files on a web server just like any other website, if you happen to have access to a web server.) Or you can run the copy hosted on my own web server [here](https://beta.DonaldBurr.com/sif_tools/sif_tools.html). ## Credits From 654fa9ffce9af2774fbccb8d44220d7061975a19 Mon Sep 17 00:00:00 2001 From: Donald Burr Date: Wed, 21 Oct 2015 15:47:36 -0700 Subject: [PATCH 02/17] Run all html/js through prettifiers --- web_app/index.html | 19 +- web_app/js/sif_tools.js | 672 +++++++++++++++++++--------------------- web_app/sif_tools.html | 391 +++++++++++++---------- 3 files changed, 555 insertions(+), 527 deletions(-) diff --git a/web_app/index.html b/web_app/index.html index 7c11c2c..9c94c0e 100644 --- a/web_app/index.html +++ b/web_app/index.html @@ -1,10 +1,13 @@ + - - SIFTools - - - -
This is not the web page you're looking for... move along, move along -
- + + + SIFTools + + + +
+ This is not the web page you're looking for... move along, move along +
+ diff --git a/web_app/js/sif_tools.js b/web_app/js/sif_tools.js index a0d11aa..fcd09d1 100644 --- a/web_app/js/sif_tools.js +++ b/web_app/js/sif_tools.js @@ -22,20 +22,29 @@ // TODO: // ===== -// * account for 1.2x bonus when feeding cards of the same attribute -// (maybe also the super (1.5) and ultra (2.0) success bonuses too?) -// * @sifen_trackbot update code gets "stuck" sometimes - not sure if the problem -// is in the twitter fetcher or elsewhere -// * needs a lot more error/bounds checking +// * General improvements: +// - needs a lot more error/bounds checking in general +// * Card Level Calc: +// - account for 1.2x bonus when feeding cards of the same attribute +// - maybe also the super (1.5) and ultra (2.0) success bonuses too? +// - detect "overkill" (i.e. feeding too much EXP to a card) +// - add "max level" button to automatically fill in the max level of the given type of card +// * Event Tracker: +// - if time remaining < 24 don't put parenthesis around the hours/minutes/seconds display +// - @sifen_trackbot update code gets "stuck" sometimes - not sure if the problem +// is in the twitter fetcher or elsewhere // Set to 0 to disable debugging, 1+ to enable debugging (higher = more verbose) var DEBUG_LEVEL = 0; // EXP tables -var exp_table_n = [-1, 0, 6, 18, 28, 40, 51, 61, 72, 82, 93, 104, 114, 124, 135, 145, 156, 165, 176, 187, 196, 207, 217, 226, 238, 247, 257, 268, 277, 288, 297, 308, 317, 328, 337, 348, 358, 367, 377, 388, 397]; -var exp_table_r = [-1, 0, 14, 31, 45, 55, 67, 76, 85, 94, 103, 110, 119, 125, 134, 140, 148, 155, 161, 168, 174, 181, 187, 193, 199, 206, 211, 217, 223, 228, 235, 240, 245, 251, 256, 262, 267, 272, 277, 283, 288, 292, 298, 303, 308, 313, 317, 323, 327, 332, 337, 342, 346, 351, 356, 360, 365, 370, 374, 378, 383]; -var exp_table_sr = [-1, 0, 54, 98, 127, 150, 169, 187, 203, 218, 232, 245, 257, 269, 281, 291, 302, 311, 322, 331, 340, 349, 358, 366, 374, 383, 391, 398, 406, 413, 421, 428, 435, 442, 449, 456, 462, 469, 475, 482, 488, 494, 500, 507, 512, 518, 524, 530, 536, 541, 547, 552, 558, 563, 568, 574, 579, 584, 590, 594, 600, 605, 609, 615, 619, 625, 629, 634, 639, 643, 648, 653, 657, 662, 667, 670, 676, 680, 684, 689, 693]; -var exp_table_ur = [-1, 0, 201, 294, 345, 382, 411, 438, 460, 481, 499, 517, 532, 547, 561, 574, 587, 598, 611, 621, 631, 642, 651, 661, 670, 679, 687, 696, 704, 712, 720, 727, 734, 742, 749, 755, 763, 769, 775, 782, 788, 794, 800, 806, 812, 818, 823, 829, 834, 840, 845, 850, 856, 860, 866, 870, 875, 880, 885, 890, 894, 899, 903, 908, 912, 917, 921, 925, 930, 933, 938, 942, 946, 950, 954, 959, 961, 966, 970, 974, 977, 981, 985, 988, 992, 996, 999, 1003, 1006, 1010, 1013, 1017, 1020, 1024, 1027, 1030, 1034, 1037, 1040, 1043, 1047]; +var exp_table_n = [ -1, 0, 6, 18, 28, 40, 51, 61, 72, 82, 93, 104, 114, 124, 135, 145, 156, 165, 176, 187, 196, 207, 217, 226, 238, 247, 257, 268, 277, 288, 297, 308, 317, 328, 337, 348, 358, 367, 377, 388, 397 ]; + +var exp_table_r = [ -1, 0, 14, 31, 45, 55, 67, 76, 85, 94, 103, 110, 119, 125, 134, 140, 148, 155, 161, 168, 174, 181, 187, 193, 199, 206, 211, 217, 223, 228, 235, 240, 245, 251, 256, 262, 267, 272, 277, 283, 288, 292, 298, 303, 308, 313, 317, 323, 327, 332, 337, 342, 346, 351, 356, 360, 365, 370, 374, 378, 383 ]; + +var exp_table_sr = [ -1, 0, 54, 98, 127, 150, 169, 187, 203, 218, 232, 245, 257, 269, 281, 291, 302, 311, 322, 331, 340, 349, 358, 366, 374, 383, 391, 398, 406, 413, 421, 428, 435, 442, 449, 456, 462, 469, 475, 482, 488, 494, 500, 507, 512, 518, 524, 530, 536, 541, 547, 552, 558, 563, 568, 574, 579, 584, 590, 594, 600, 605, 609, 615, 619, 625, 629, 634, 639, 643, 648, 653, 657, 662, 667, 670, 676, 680, 684, 689, 693 ]; + +var exp_table_ur = [ -1, 0, 201, 294, 345, 382, 411, 438, 460, 481, 499, 517, 532, 547, 561, 574, 587, 598, 611, 621, 631, 642, 651, 661, 670, 679, 687, 696, 704, 712, 720, 727, 734, 742, 749, 755, 763, 769, 775, 782, 788, 794, 800, 806, 812, 818, 823, 829, 834, 840, 845, 850, 856, 860, 866, 870, 875, 880, 885, 890, 894, 899, 903, 908, 912, 917, 921, 925, 930, 933, 938, 942, 946, 950, 954, 959, 961, 966, 970, 974, 977, 981, 985, 988, 992, 996, 999, 1003, 1006, 1010, 1013, 1017, 1020, 1024, 1027, 1030, 1034, 1037, 1040, 1043, 1047 ]; // global variable to keep event state, because we need it to live between function calls. // YES I KNOW THIS IS BAD. SO SUE ME. IT WORKS THOUGH. :P @@ -43,140 +52,172 @@ var exp_table_ur = [-1, 0, 201, 294, 345, 382, 411, 438, 460, 481, 499, 517, 532 var current_type_of_event = 1; // debug logging -function LOG(level, msg) -{ - if (DEBUG_LEVEL > 0 && DEBUG_LEVEL >= level) { - console.log(msg); - } +function LOG(level, msg) { + if (DEBUG_LEVEL > 0 && DEBUG_LEVEL >= level) { + console.log(msg); + } } // Main function, runs automatically at document-ready (i.e. when the page is finished loading) -$(document).ready(function(){ +$(document).ready(function() { // Hide the address bar on mobile browsers - setTimeout(function(){ + setTimeout(function() { // some sites suggest 0,0 and others 0,1 - not sure which is correct window.scrollTo(0, 0); }, 0); - // set up UI (buttons, etc.) - setup_ui_elements(); - // set up button handlers - setup_button_handlers(); - // set up slider handlers + // set up UI (buttons, etc.) + setup_ui_elements(); + // set up button handlers + setup_button_handlers(); + // set up slider handlers window.timerInterval = 0; }); // Set up UI elements (tabs, buttons, etc.) -function setup_ui_elements() -{ - LOG(1, "setup_ui_elements()"); - var default_tab = $.cookie("default_tab"); - if (isNaN(default_tab)) { - default_tab = 0; +function setup_ui_elements() { + LOG(1, "setup_ui_elements()"); + var QueryString = function() { + // http://stackoverflow.com/a/979995 + // This function is anonymous, is executed immediately and + // the return value is assigned to QueryString! + var query_string = {}; + var query = window.location.search.substring(1); + var vars = query.split("&"); + for (var i = 0; i < vars.length; i++) { + var pair = vars[i].split("="); + // If first entry with this name + if (typeof query_string[pair[0]] === "undefined") { + query_string[pair[0]] = decodeURIComponent(pair[1]); + } else if (typeof query_string[pair[0]] === "string") { + var arr = [ query_string[pair[0]], decodeURIComponent(pair[1]) ]; + query_string[pair[0]] = arr; + } else { + query_string[pair[0]].push(decodeURIComponent(pair[1])); + } + } + return query_string; + }(); + var default_tab = 0; + if (!isNaN(QueryString.tab)) { + if (QueryString.tab >= 1 && QueryString.tab <= 4) { + default_tab = QueryString.tab - 1; + } + } else { + var default_tab_from_cookie = $.cookie("default_tab"); + if (isNaN(default_tab_from_cookie)) { + default_tab = 0; + } else { + default_tab = default_tab_from_cookie; + } } - - $( "#tabs" ).tabs({ - active: default_tab, - create: function(event, ui) { - var theTab = ui.tab.index(); - LOG(3, "INIT tab created " + theTab); - set_up_tab(theTab); - }, - activate: function(event, ui) { - var theTab = ui.newTab.index(); - LOG(3, "INIT tab selected " + theTab); - set_up_tab(theTab); - } - }); - + $("#tabs").tabs({ + active: default_tab, + create: function(event, ui) { + var theTab = ui.tab.index(); + LOG(3, "INIT tab created " + theTab); + set_up_tab(theTab); + }, + activate: function(event, ui) { + var theTab = ui.newTab.index(); + LOG(3, "INIT tab selected " + theTab); + set_up_tab(theTab); + } + }); // only set up keypads on mobile browsers // not sure what the best way of doing this is var pageWidth = $(window).width(); if (pageWidth < 1024) { - $( "#current_rank" ).prop('readonly', true); - $( "#current_rank" ).keypad(); // {prompt: 'Enter here'} - $( "#current_exp" ).prop('readonly', true); - $( "#current_exp" ).keypad(); // {prompt: 'Enter here'} - $( "#desired_rank" ).prop('readonly', true); - $( "#desired_rank" ).keypad(); // {prompt: 'Enter here'} - $( "#current_gems" ).prop('readonly', true); - $( "#current_gems" ).keypad(); // {prompt: 'Enter here'} - $( "#gem_desired_gems" ).prop('readonly', true); - $( "#gem_desired_gems" ).keypad(); // {prompt: 'Enter here'} - $( "#card_current_level" ).prop('readonly', true); - $( "#card_current_level" ).keypad(); // {prompt: 'Enter here'} - $( "#card_current_exp" ).prop('readonly', true); - $( "#card_current_exp" ).keypad(); // {prompt: 'Enter here'} - $( "#card_desired_level" ).prop('readonly', true); - $( "#card_desired_level" ).keypad(); // {prompt: 'Enter here'} - $( "#card_feed_exp" ).prop('readonly', true); - $( "#card_feed_exp" ).keypad(); // {prompt: 'Enter here'} + $("#current_rank").prop("readonly", true); + $("#current_rank").keypad(); + // {prompt: 'Enter here'} + $("#current_exp").prop("readonly", true); + $("#current_exp").keypad(); + // {prompt: 'Enter here'} + $("#desired_rank").prop("readonly", true); + $("#desired_rank").keypad(); + // {prompt: 'Enter here'} + $("#current_gems").prop("readonly", true); + $("#current_gems").keypad(); + // {prompt: 'Enter here'} + $("#gem_desired_gems").prop("readonly", true); + $("#gem_desired_gems").keypad(); + // {prompt: 'Enter here'} + $("#card_current_level").prop("readonly", true); + $("#card_current_level").keypad(); + // {prompt: 'Enter here'} + $("#card_current_exp").prop("readonly", true); + $("#card_current_exp").keypad(); + // {prompt: 'Enter here'} + $("#card_desired_level").prop("readonly", true); + $("#card_desired_level").keypad(); + // {prompt: 'Enter here'} + $("#card_feed_exp").prop("readonly", true); + $("#card_feed_exp").keypad(); } else { - $( "#current_rank" ).prop('readonly', false); - $( "#current_exp" ).prop('readonly', false); - $( "#desired_rank" ).prop('readonly', false); - $( "#current_gems" ).prop('readonly', false); - $( "#gem_desired_gems" ).prop('readonly', false); - $( "#card_current_level" ).prop('readonly', false); - $( "#card_current_exp" ).prop('readonly', false); - $( "#card_desired_level" ).prop('readonly', false); - $( "#card_feed_exp" ).prop('readonly', false); + $("#current_rank").prop("readonly", false); + $("#current_exp").prop("readonly", false); + $("#desired_rank").prop("readonly", false); + $("#current_gems").prop("readonly", false); + $("#gem_desired_gems").prop("readonly", false); + $("#card_current_level").prop("readonly", false); + $("#card_current_exp").prop("readonly", false); + $("#card_desired_level").prop("readonly", false); + $("#card_feed_exp").prop("readonly", false); } - // set up date/time pickers - $( "#gem_desired_date" ).datepicker(); - $( "#event_end_date" ).datepicker(); + $("#gem_desired_date").datepicker(); + $("#event_end_date").datepicker(); // $( "#event_end_time" ).timepicker(); - $('#event_end_time').timepicker({ 'timeFormat': 'H:i', 'disableTextInput': true }); - - // set up buttons - ["calculate-rank", "reset-rank", "calculate-gems", "reset-gems", "calculate-card", "reset-card", "start-stop-timer", "clear-timer"].forEach(function(entry) { - var selector = "#button-" + entry; - LOG(1, "setting up " + selector); - $(selector).button(); - }); - + $("#event_end_time").timepicker({ + timeFormat: "H:i", + disableTextInput: true + }); + // set up buttons + [ "calculate-rank", "reset-rank", "calculate-gems", "reset-gems", "calculate-card", "reset-card", "start-stop-timer", "clear-timer" ].forEach(function(entry) { + var selector = "#button-" + entry; + LOG(1, "setting up " + selector); + $(selector).button(); + }); // hide result divs - ["rank-calc-result-area", "gem-calc-result-area", "card-calc-result-area"].forEach(function(entry) { + [ "rank-calc-result-area", "gem-calc-result-area", "card-calc-result-area" ].forEach(function(entry) { var selector = "#" + entry; LOG(1, "setting up " + selector); $(selector).hide(); }); - // set up radio button listeners $("input[name=gem-mode]").change(handle_gem_mode_select); $("input[name=card-mode]").change(handle_card_mode_select); - // hide non-selected option divs - ["gem-event-options-area", "gem-desired-gems-area", "card-exp-area", "gem_jp_daily_gems"].forEach(function(entry) { + [ "gem-event-options-area", "gem-desired-gems-area", "card-exp-area", "gem_jp_daily_gems" ].forEach(function(entry) { var selector = "#" + entry; LOG(1, "setting up " + selector); $(selector).hide(); }); - // set up checkbox change event handler $("#gems_include_events").change(function() { handle_gem_event_box(this.checked); }); - // set up gem event calc note dialog // $("#dialog").dialog({ autoOpen: false }); $("a#gem_event_readme").click(function(e) { - e.preventDefault(); - $("#gem_event_readme_dialog").dialog({height:300}); + e.preventDefault(); + $("#gem_event_readme_dialog").dialog({ + height: 300 + }); }); $("a#gem_quest_readme").click(function(e) { - e.preventDefault(); - $("#gem_quest_readme_dialog").dialog({height:300}); + e.preventDefault(); + $("#gem_quest_readme_dialog").dialog({ + height: 300 + }); }); - // set up show/hide JP-only options - $('#gem_game_version').on('change', function (e) { + $("#gem_game_version").on("change", function(e) { handle_gem_game_version_change(); }); } -function handle_gem_game_version_change() -{ +function handle_gem_game_version_change() { var game_version = $("#gem_game_version").val(); if (game_version === "JP") { $("#gem_jp_daily_gems").show(); @@ -185,8 +226,7 @@ function handle_gem_game_version_change() } } -function handle_gem_event_box(show_it) -{ +function handle_gem_event_box(show_it) { if (show_it) { $("#gem-event-options-area").show(); } else { @@ -194,8 +234,7 @@ function handle_gem_event_box(show_it) } } -function setup_button_handlers() -{ +function setup_button_handlers() { $("#button-calculate-rank").click(function(evt) { calculate_rank(); }); @@ -223,43 +262,47 @@ function setup_button_handlers() } // tab functions +function set_up_tab(tab) { + switch (tab) { + case 0: + rank_calc_tab_selected(); + break; -function set_up_tab(tab) -{ - switch(tab) { - case 0: rank_calc_tab_selected(); break; - case 1: love_gem_calc_tab_selected(); break; - case 2: card_level_calc_tab_selected(); break; - case 3: event_end_calc_tab_selected(); break; + case 1: + love_gem_calc_tab_selected(); + break; + + case 2: + card_level_calc_tab_selected(); + break; + + case 3: + event_end_calc_tab_selected(); + break; } } -function rank_calc_tab_selected() -{ +function rank_calc_tab_selected() { LOG(1, "rank_calc_tab_selected"); $.cookie("default_tab", 0); } -function love_gem_calc_tab_selected() -{ - LOG(1, "love_gem_calc_tab_selected"); +function love_gem_calc_tab_selected() { + LOG(1, "love_gem_calc_tab_selected"); $.cookie("default_tab", 1); } -function card_level_calc_tab_selected() -{ - LOG(1, "card_level_calc_tab_selected"); +function card_level_calc_tab_selected() { + LOG(1, "card_level_calc_tab_selected"); $.cookie("default_tab", 2); } -function event_end_calc_tab_selected() -{ - LOG(1, "event_end_calc_tab_selected"); +function event_end_calc_tab_selected() { + LOG(1, "event_end_calc_tab_selected"); $.cookie("default_tab", 3); } -function calculate_rank() -{ +function calculate_rank() { // validate data var current_rank = parseInt($("#current_rank").val()); var current_exp_input = $("#current_exp").val(); @@ -278,17 +321,17 @@ function calculate_rank() } else if (desired_rank <= current_rank) { window.alert("Error: desired rank must be greater than current rank."); } else { - var required_exp = 0 - for (rank = current_rank ; rank < desired_rank ; rank++) { - var required_exp_for_next_rank = Math.round(34.45 * rank - 551) + var required_exp = 0; + for (rank = current_rank; rank < desired_rank; rank++) { + var required_exp_for_next_rank = Math.round(34.45 * rank - 551); // account for half EXP on JP (only if rank < 100) if (game_version === "JP" && rank < 100) { required_exp_for_next_rank /= 2; } - required_exp = required_exp + required_exp_for_next_rank + required_exp = required_exp + required_exp_for_next_rank; } // account for exp we already have - required_exp -= current_exp + required_exp -= current_exp; // convert to integer required_exp = Math.round(required_exp); // now calc the # of songs needed @@ -298,10 +341,9 @@ function calculate_rank() var hard_count = Math.round(required_exp / 46); var expert_count = Math.round(required_exp / 83); // calc LP and FP - var LP = 25 + Math.floor(Math.min(desired_rank, 300) / 2) + Math.floor(Math.max(desired_rank - 300, 0) / 3) + var LP = 25 + Math.floor(Math.min(desired_rank, 300) / 2) + Math.floor(Math.max(desired_rank - 300, 0) / 3); // calc friend slots - var friend_slots = 10 + Math.floor(Math.min(desired_rank, 50) / 5) + Math.floor(Math.max(desired_rank - 50, 0) / 10) - + var friend_slots = 10 + Math.floor(Math.min(desired_rank, 50) / 5) + Math.floor(Math.max(desired_rank - 50, 0) / 10); // display the results $("#rank-result-exp").text(required_exp); $("#rank-result-songs-easy").text(easy_count); @@ -315,8 +357,7 @@ function calculate_rank() } } -function reset_rank() -{ +function reset_rank() { $("#rank-calc-result-area").hide(); $("#current_rank").val(""); $("#current_exp").val(""); @@ -330,8 +371,7 @@ function reset_rank() $("#rank-results-fp").text("-"); } -function handle_gem_mode_select() -{ +function handle_gem_mode_select() { var mode = $(this).val(); if (mode === "DATE") { $("#gem-date-area").show(); @@ -342,8 +382,7 @@ function handle_gem_mode_select() } } -function handle_card_mode_select() -{ +function handle_card_mode_select() { var mode = $(this).val(); if (mode === "LEVEL") { $("#card-level-area").show(); @@ -354,11 +393,9 @@ function handle_card_mode_select() } } -function is_muse_members_birthday(moment) -{ +function is_muse_members_birthday(moment) { var the_month = month(moment); var the_day = day(moment); - var is_bday = false; var bday_name = ""; if (the_month == 1 && the_day == 17) { @@ -389,11 +426,10 @@ function is_muse_members_birthday(moment) is_bday = true; bday_name = "Rin"; } - return [is_bday, bday_name]; + return [ is_bday, bday_name ]; } -function is_gem_day(moment) -{ +function is_gem_day(moment) { var the_day = day(moment); // according the login bonus chart, gems are given out on days numbered 1,6,11,16,21,26,30 if (the_day == 1 || the_day == 6 || the_day == 11 || the_day == 16 || the_day == 21 || the_day == 26 || the_day == 30) { @@ -403,163 +439,154 @@ function is_gem_day(moment) } // moment.js starts months/days/years at 0... weird... -function month(moment) -{ +function month(moment) { return moment.month() + 1; } // moment.js uses date() not day() -function day(moment) -{ +function day(moment) { return moment.date(); } -function year(moment) -{ +function year(moment) { return moment.year(); } -function hour(moment) -{ +function hour(moment) { return moment.hour(); } -function minute(moment) -{ +function minute(moment) { return moment.minute(); } -function second(moment) -{ +function second(moment) { return moment.second(); } -function is_same_day(m1, m2) -{ +function is_same_day(m1, m2) { if (month(m1) == month(m2) && day(m1) == day(m2) && year(m1) == year(m2)) { return true; } return false; } -function handle_event(day_of_month, game_version, typical_tier) -{ +function handle_event(day_of_month, game_version, typical_tier) { LOG(1, "start event type is " + current_type_of_event); - // format of returned tuple: // tuple[0] - was this an event day? (boolean, duh) // tuple[1] - name of event, or "" if none (string) // tuple[2] - amount of gems spent (int) // tuple[3] - amount of gems gained (int) var return_tuple = []; - return_tuple[0] = false; return_tuple[1] = ""; return_tuple[2] = 0; return_tuple[3] = 0; - // ok, if it's not the 1st or 15th, then no event is going on if (day_of_month == 1 || day_of_month == 15) { return_tuple[0] = true; - // now we calculate! - // according to /u/eryncerise, current "typical" tiering gem requirements are: // Tier 1 = 10-15 gems, Tier 2 = 15-20 gems, no tier = (usually) no gems (assuming you play efficiently) switch (typical_tier) { - case 1: return_tuple[2] = 20; break; - case 2: return_tuple[2] = 15; break; - case 3: return_tuple[2] = 0; break; + case 1: + return_tuple[2] = 20; + break; + + case 2: + return_tuple[2] = 15; + break; + + case 3: + return_tuple[2] = 0; + break; } - // now handle different event types since they have different reward progressions // TODO: need to use different tier cutoff point values depending on EN or JP (which means I need to go on tomodachi and average both EN and JP data // for the past X events of each type. Fun for the whole family! :P) switch (current_type_of_event) { - case 1: - LOG(1, "TOKEN"); - // Token Event - return_tuple[1] = "Token Event"; - // completion rewards: gems at 200, 1000, 4000, 15000, 18000, 21000, 25000 (2), 30000 (2), 35000 (3) - // event ranking rewards: tiers 1-4, no love gems for YOU! tier 5 on the other hand gets 3 of em! - if (typical_tier == 1) { - // completion rewards: - // quick check of the last 5 token events on Tomodachi gives me a ballpark 30k average for t1 cutoff - // (no one has gotten 35k so I am going to ignore that final gem reward in the completion rewards progression) - // makes for a total of 10 gems - // event rewards: tier 1, NO GEMS FOR YOU! - return_tuple[3] = 10; - } else if (typical_tier == 2) { - // completion rewards: - // quick check of the last 5 token events on Tomodachi gives me a ballpark 23k average for t2 cutoff - // makes for a total of 10 gems - // event rewards: tier 2, NO GEMS FOR YOU! - return_tuple[3] = 6; - } else { - // completion reward: duh, Sr is at 11000, assume they stop playing there. +3 - // event rewards: they're only after the event SR, we optimistically assume that they manage to snag tier 5 if they got that far. +3 gems. - return_tuple[3] = 6; - } - break; - case 2: - LOG(1, "SM"); - // Score Match - return_tuple[1] = "Score Match"; - // NOTE: as I write this (10/18/15) EN is running a Token Event, which means unfortunately that the previous - // event (Score Match) data is no longer accessible. But JP just finished the Maki Score Match, so I can still - // get at the event rewards info. I am assuming that the event rewards are the same as EN (and that I am reading - // them correctly, a big assumption since I don't read Japanese :P ) - // completion rewards: gems at 500, 2000, 8000, 35000, 42500, 50000, 60000 (2), 70000 (2), 80000 (3) - // event ranking rewards: tiers 1-2, no love gems for YOU! tier 3 gets 2 gems, tier 4 gets 2, tier 5 gets 4 + case 1: + LOG(1, "TOKEN"); + // Token Event + return_tuple[1] = "Token Event"; + // completion rewards: gems at 200, 1000, 4000, 15000, 18000, 21000, 25000 (2), 30000 (2), 35000 (3) + // event ranking rewards: tiers 1-4, no love gems for YOU! tier 5 on the other hand gets 3 of em! + if (typical_tier == 1) { + // completion rewards: + // quick check of the last 5 token events on Tomodachi gives me a ballpark 30k average for t1 cutoff + // (no one has gotten 35k so I am going to ignore that final gem reward in the completion rewards progression) + // makes for a total of 10 gems + // event rewards: tier 1, NO GEMS FOR YOU! + return_tuple[3] = 10; + } else if (typical_tier == 2) { + // completion rewards: + // quick check of the last 5 token events on Tomodachi gives me a ballpark 23k average for t2 cutoff + // makes for a total of 10 gems + // event rewards: tier 2, NO GEMS FOR YOU! + return_tuple[3] = 6; + } else { + // completion reward: duh, Sr is at 11000, assume they stop playing there. +3 + // event rewards: they're only after the event SR, we optimistically assume that they manage to snag tier 5 if they got that far. +3 gems. + return_tuple[3] = 6; + } + break; - if (typical_tier == 1) { - // completion rewards: - // quick check of the last 5 token events on Tomodachi gives me a ballpark 30k average for t1 cutoff - // (no one has gotten 35k so I am going to ignore that final gem reward in the completion rewards progression) - // makes for a total of 10 gems - // event rewards: tier 1, NO GEMS FOR YOU! - return_tuple[3] = 10; - } else if (typical_tier == 2) { - // completion rewards: - // quick check of the last 5 token events on Tomodachi gives me a ballpark 23k average for t2 cutoff - // makes for a total of 10 gems - // event rewards: tier 2, NO GEMS FOR YOU! - return_tuple[3] = 6; - } else { - // completion reward: duh, Sr is at 11000, assume they stop playing there. +3 - // event rewards: they're only after the event SR, we optimistically assume that they manage to snag tier 5 if they got that far. +3 gems. - return_tuple[3] = 6; - } - break; - case 3: - LOG(1, "MF"); - // Medley Festival - return_tuple[1] = "Medley Festival (INCOMPLETE)"; - // TODO: I know NOTHING about medfes, and there is currently none going on on JP so I can't access reward data. - // So return bogus values for now. - return_tuple[3] = 0; - break; + case 2: + LOG(1, "SM"); + // Score Match + return_tuple[1] = "Score Match"; + // NOTE: as I write this (10/18/15) EN is running a Token Event, which means unfortunately that the previous + // event (Score Match) data is no longer accessible. But JP just finished the Maki Score Match, so I can still + // get at the event rewards info. I am assuming that the event rewards are the same as EN (and that I am reading + // them correctly, a big assumption since I don't read Japanese :P ) + // completion rewards: gems at 500, 2000, 8000, 35000, 42500, 50000, 60000 (2), 70000 (2), 80000 (3) + // event ranking rewards: tiers 1-2, no love gems for YOU! tier 3 gets 2 gems, tier 4 gets 2, tier 5 gets 4 + if (typical_tier == 1) { + // completion rewards: + // quick check of the last 5 token events on Tomodachi gives me a ballpark 30k average for t1 cutoff + // (no one has gotten 35k so I am going to ignore that final gem reward in the completion rewards progression) + // makes for a total of 10 gems + // event rewards: tier 1, NO GEMS FOR YOU! + return_tuple[3] = 10; + } else if (typical_tier == 2) { + // completion rewards: + // quick check of the last 5 token events on Tomodachi gives me a ballpark 23k average for t2 cutoff + // makes for a total of 10 gems + // event rewards: tier 2, NO GEMS FOR YOU! + return_tuple[3] = 6; + } else { + // completion reward: duh, Sr is at 11000, assume they stop playing there. +3 + // event rewards: they're only after the event SR, we optimistically assume that they manage to snag tier 5 if they got that far. +3 gems. + return_tuple[3] = 6; + } + break; + + case 3: + LOG(1, "MF"); + // Medley Festival + return_tuple[1] = "Medley Festival (INCOMPLETE)"; + // TODO: I know NOTHING about medfes, and there is currently none going on on JP so I can't access reward data. + // So return bogus values for now. + return_tuple[3] = 0; + break; } - // lastly bump the event type current_type_of_event++; - if ((game_version === "EN" && current_type_of_event > 2) || (game_version === "JP" && current_type_of_event > 3)) { + if (game_version === "EN" && current_type_of_event > 2 || game_version === "JP" && current_type_of_event > 3) { current_type_of_event = 1; } } - LOG(1, "end event type is " + current_type_of_event); LOG(1, return_tuple); // now return what we got return return_tuple; } -function calculate_gems() -{ +function calculate_gems() { // reset current event indicator (start out with a token event) current_type_of_event = 1; - - var verbose = $("#gems_verbose").is(':checked'); + var verbose = $("#gems_verbose").is(":checked"); var current_gems_text = $("#current_gems").val(); var current_gems = 0; if (current_gems_text != "") { @@ -569,47 +596,43 @@ function calculate_gems() alert("Error: invalid number of current gems. Please check your input and try again."); return; } - var tier = parseInt($("#gems_tier_level").val()); var game_version = $("#gem_game_version").val(); var calc_daily_quest_gems = false; - var calc_event_gems = $("#gems_include_events").is(':checked'); + var calc_event_gems = $("#gems_include_events").is(":checked"); if (game_version === "JP") { - calc_daily_quest_gems = $("#gems_include_daily_gems").is(':checked'); + calc_daily_quest_gems = $("#gems_include_daily_gems").is(":checked"); } var mode = $("input[name=gem-mode]:checked").val(); - if (mode === "DATE") { + if (mode === "DATE") { var target_date = $("#gem_desired_date").val(); if (target_date === "") { alert("Error: invalid date. Please check and try again."); return; } var target_date_object = moment(new Date(target_date)); - if (!target_date_object.isValid()) { + if (!target_date_object.isValid()) { alert("Error: invalid date. Please check and try again."); return; } - var now = moment(new Date()); if (target_date_object.isBefore(now) || is_same_day(now, target_date_object)) { window.alert("Error: the date must be in the future."); return; } - // ready to rock var resultsString = sprintf("Today is %02d/%02d/%04d and you currently have %d love gems.
(Assuming you collected any gems you got today and already counted those.)", month(now), day(now), year(now), current_gems); var verboseText = ""; if (calc_daily_quest_gems) { verboseText = "(Including daily 'quest' gems in the calculation. There will not be a separate daily entry for each one.)

"; } - var gems = current_gems - now = now.add(1, 'days') + var gems = current_gems; + now = now.add(1, "days"); while (now.isBefore(target_date_object) || is_same_day(now, target_date_object)) { // is it a login bonus? if (is_gem_day(now)) { gems += 1; } - // is it a birthday? var birthday_tuple = is_muse_members_birthday(now); var is_bday = birthday_tuple[0]; @@ -617,12 +640,10 @@ function calculate_gems() if (is_bday) { gems += 5; } - // account for daily login quest gem if (calc_daily_quest_gems) { gems++; } - // account for event // format of returned tuple: // tuple[0] - was this an event day? (boolean, duh) @@ -654,24 +675,20 @@ function calculate_gems() gems += won_gems; } } - // record verbose output if desired if (verbose) { - if (is_gem_day(now) || is_bday || is_event) { + if (is_gem_day(now) || is_bday || is_event) { verboseText += sprintf("%02d/%02d/%04d
", month(now), day(now), year(now)); - if (is_gem_day(now)) { verboseText += "Free gem as login bonus!
"; } - if (is_bday) { verboseText += sprintf("It's %s's birthday! You get 5 gems!
", name); } - // account for events if (is_event) { verboseText += sprintf("A " + event_name + " just ended!
", month(now), day(now), year(now), event_name); - if (spent_gems == -1) { + if (spent_gems == -1) { verboseText += "You didn't have enough gems to participate.
"; } else { if (spent_gems == 0) { @@ -681,14 +698,12 @@ function calculate_gems() } } } - // add a newline verboseText += sprintf("That brings you to %d gems!

", gems); } } - now = now.add(1, 'days') + now = now.add(1, "days"); } - resultsString = resultsString + sprintf("
You will have %d love gems on %02d/%02d/%04d. Good things come to those who wait!", gems, month(target_date_object), day(target_date_object), year(target_date_object)); $("#gem-result-summary").html(resultsString); if (verbose) { @@ -698,35 +713,29 @@ function calculate_gems() $("#gem-result-verbose-area").html(verboseText); $("#gem-result-textarea").hide(); } - } else if (mode === "GEMS") { + } else if (mode === "GEMS") { var target_gems = parseInt($("#gem_desired_gems").val()); if (isNaN(target_gems)) { window.alert("Error: you have entered an invalid (non-numeric) value. Please check your input and try again."); return; } - var now = moment(new Date()); // arbitrary limit to make sure we don't go wander off into infinity - var cutoff = moment(now).add(5, 'years'); + var cutoff = moment(now).add(5, "years"); var resultsString = sprintf("Today is %02d/%02d/%04d and you currently have %d love gems.
(Assuming you collected any gems you got today and already counted those.)", month(now), day(now), year(now), current_gems); var verboseText = ""; if (calc_daily_quest_gems) { verboseText = "(Including daily 'quest' gems in the calculation. There will not be a separate daily entry for each one.)

"; } - // make sure we don't go off into infinity (i.e. user gives input that is impossible to calculate) var abort = false; - - var gems = current_gems - + var gems = current_gems; while (gems < target_gems && !abort) { - now = now.add(1, 'days') - + now = now.add(1, "days"); // is it a login bonus? if (is_gem_day(now)) { gems += 1; } - // is it a birthday? var birthday_tuple = is_muse_members_birthday(now); var is_bday = birthday_tuple[0]; @@ -734,12 +743,10 @@ function calculate_gems() if (is_bday) { gems += 5; } - // account for daily login quest gem if (calc_daily_quest_gems) { gems++; } - // account for event // format of returned tuple: // tuple[0] - was this an event day? (boolean, duh) @@ -771,24 +778,20 @@ function calculate_gems() gems += won_gems; } } - // record verbose output if desired if (verbose) { - if (is_gem_day(now) || is_bday || is_event) { + if (is_gem_day(now) || is_bday || is_event) { verboseText += sprintf("%02d/%02d/%04d
", month(now), day(now), year(now)); - if (is_gem_day(now)) { verboseText += "Free gem as login bonus!
"; } - if (is_bday) { verboseText += sprintf("It's %s's birthday! You get 5 gems!
", name); } - // account for events if (is_event) { verboseText += sprintf("A " + event_name + " just ended!
", month(now), day(now), year(now), event_name); - if (spent_gems == -1) { + if (spent_gems == -1) { verboseText += "You didn't have enough gems to participate.
"; } else { if (spent_gems == 0) { @@ -798,19 +801,16 @@ function calculate_gems() } } } - // add a newline verboseText += sprintf("That brings you to %d gems!

", gems); } } - // do we abort? - if (now.isAfter(cutoff)) { + if (now.isAfter(cutoff)) { verboseText += sprintf("(Cannot proceed, this appears to be an unattainable amount of gems given your constraints.)"); abort = true; } } - resultsString = resultsString + sprintf("
You will have %d love gems on %02d/%02d/%04d. Good things come to those who wait!", gems, month(now), day(now), year(now)); if (abort) { resultsString += "
(Note: the calculation was stopped because it appears that you will be unable to attain the desired amount of gems given your constraints.)"; @@ -824,27 +824,24 @@ function calculate_gems() $("#gem-result-textarea").hide(); } } - $("#gem-calc-result-area").show(); } -function reset_gems() -{ +function reset_gems() { $("#gem-calc-result-area").hide(); $("#gem-result-summary").text("-"); $("#gem-desired-gems-area").hide(); $("#gem-date-area").hide(); $("#current_gems").val(0); - var $radios = $('input:radio[name=gem-mode]'); - $radios.filter('[value=DATE]').prop('checked', true); + var $radios = $("input:radio[name=gem-mode]"); + $radios.filter("[value=DATE]").prop("checked", true); $("#gem_desired_date").val(""); $("#gem_desired_gems").val(""); - $('#gems_verbose').prop('checked', false); + $("#gems_verbose").prop("checked", false); $("#gem-date-area").show(); } -function get_level_cap(rarity) -{ +function get_level_cap(rarity) { if (rarity === "N") { return 40; } else if (rarity === "R") { @@ -858,32 +855,29 @@ function get_level_cap(rarity) return -1; } -function is_valid_level(rarity, level) -{ +function is_valid_level(rarity, level) { var return_value = false; if (level >= 1) { - return_value = (level <= get_level_cap(rarity)); + return_value = level <= get_level_cap(rarity); } return return_value; } -function is_valid_exp(rarity, level, exp) -{ +function is_valid_exp(rarity, level, exp) { var return_value = false; if (rarity === "N" && is_valid_level(rarity, level)) { - return_value = (exp >= 0 && exp < exp_table_n[level+1]); + return_value = exp >= 0 && exp < exp_table_n[level + 1]; } else if (rarity === "R" && is_valid_level(rarity, level)) { - return_value = (exp >= 0 && exp < exp_table_r[level+1]); - } else if (rarity === "SR" && is_valid_level(rarity, level+1)) { - return_value = (exp >= 0 && exp < exp_table_sr[level+1]); + return_value = exp >= 0 && exp < exp_table_r[level + 1]; + } else if (rarity === "SR" && is_valid_level(rarity, level + 1)) { + return_value = exp >= 0 && exp < exp_table_sr[level + 1]; } else if (rarity === "UR" && is_valid_level(rarity, level)) { - return_value = (exp >= 0 && exp < exp_table_ur[level+1]); + return_value = exp >= 0 && exp < exp_table_ur[level + 1]; } - return return_value + return return_value; } -function calculate_card() -{ +function calculate_card() { var current_level = parseInt($("#card_current_level").val()); var current_exp_input = $("#card_current_exp").val(); var current_exp = 0; @@ -891,7 +885,6 @@ function calculate_card() current_exp = parseInt(current_exp_input); } var rarity = $("#card_rarity").val(); - if (isNaN(current_level) || !is_valid_level(rarity, current_level)) { alert("Error: invalid level. Please check your input and try again."); return; @@ -900,19 +893,17 @@ function calculate_card() alert("Error: invalid EXP. Please check your input and try again."); return; } - var mode = $("input[name=card-mode]:checked").val(); - if (mode === "LEVEL") { + if (mode === "LEVEL") { var target_level = parseInt($("#card_desired_level").val()); if (isNaN(target_level) || !is_valid_level(rarity, target_level)) { window.alert("Error: the desired level is invalid."); return; } - // ready to rock var required_exp = 0; var resultsString = ""; - for (level = current_level+1; level <= target_level; level++) { + for (level = current_level + 1; level <= target_level; level++) { if (rarity === "N") { required_exp += exp_table_n[level]; } else if (rarity === "R") { @@ -923,32 +914,26 @@ function calculate_card() required_exp += exp_table_ur[level]; } } - // subtract what we have required_exp -= current_exp; - var resultString = sprintf("To get a %s card from level %d (with %d EXP) to %d requires %d EXP.
", rarity, current_level, current_exp, target_level, required_exp); - // calculate equiv N cards var number_of_n_cards = Math.round(required_exp / 100) + 1; resultString += sprintf("(the equivalent of about %d level-1 N cards fed to it)", number_of_n_cards); - // output the result $("#card-result-summary").html(resultString); - } else if (mode === "EXP") { + } else if (mode === "EXP") { var exp_to_feed = parseInt($("#card_feed_exp").val()); if (isNaN(exp_to_feed)) { window.alert("Error: you have entered an invalid (non-numeric) value. Please check your input and try again."); return; } - // ready to rock var resultsString = "FOO"; - // XXX do some calculating var exp_tally = current_exp; var level = 0; - for (level = current_level+1; level <= get_level_cap(rarity); level++) { + for (level = current_level + 1; level <= get_level_cap(rarity); level++) { if (rarity === "N") { exp_tally += exp_table_n[level]; } else if (rarity === "R") { @@ -962,34 +947,29 @@ function calculate_card() break; } } - level--; - var resultString = sprintf("If you feed a %s card at level %d (with %d EXP) a total of %d EXP,
it will end up at level %d.%s", rarity, current_level, current_exp, exp_to_feed, level, (level == get_level_cap(rarity) ? " (MAX LEVEL!)" : "")); - + var resultString = sprintf("If you feed a %s card at level %d (with %d EXP) a total of %d EXP,
it will end up at level %d.%s", rarity, current_level, current_exp, exp_to_feed, level, level == get_level_cap(rarity) ? " (MAX LEVEL!)" : ""); // output the result $("#card-result-summary").html(resultString); } - $("#card-calc-result-area").show(); } -function reset_card() -{ +function reset_card() { $("#card-calc-result-area").hide(); $("#card-result-summary").text("-"); $("#card-level-area").hide(); $("#card-exp-area").hide(); $("#card_current_level").val(""); $("#card_current_exp").val(""); - var $radios = $('input:radio[name=card-mode]'); - $radios.filter('[value=LEVEL]').prop('checked', true); + var $radios = $("input:radio[name=card-mode]"); + $radios.filter("[value=LEVEL]").prop("checked", true); $("#card_desired_level").val(""); $("#card_feed_exp").val(""); $("#card-level-area").show(); } -function start_stop_timer() -{ +function start_stop_timer() { if (window.timerInterval != 0) { // stop it clearInterval(window.timerInterval); @@ -999,7 +979,7 @@ function start_stop_timer() // 2013-02-08 09:30 # An hour and minute time part var dateString = $("#event_end_date").val() + " " + $("#event_end_time").val() + "Z"; window.the_end = moment(dateString, "MM/DD/YYYY HH:mmZ"); - window.timerInterval = setInterval(run_timer, 1000); + window.timerInterval = setInterval(run_timer, 1e3); $("#button-start-stop-timer span").text("Stop Timer"); // run the first update ourselves, so that it doesn't stay blank until the timer kicks in window.immediately_refresh_tier_cutoffs = true; @@ -1007,57 +987,52 @@ function start_stop_timer() } } -function run_timer() -{ +function run_timer() { var now = moment(new Date()); var end = window.the_end; var string = "

CURRENT TIME

" + now.utc().format("MM/DD/YYYY HH:mm:ss") + " UTC

EVENT ENDS:

" + end.utc().format("MM/DD/YYYY HH:mm:ss") + " UTC

"; - if (now.isBefore(end)) { + if (now.isBefore(end)) { var ms = end.diff(now.utc()); var t = moment.duration(ms).asMilliseconds(); - var seconds = Math.floor( (t/1000) % 60 ); - var minutes = Math.floor( (t/1000/60) % 60 ); - var hours = Math.floor( (t/(1000*60*60)) % 24 ); - var days = Math.floor( t/(1000*60*60*24) ); - var total_hours = (days * 24) + hours; - + var seconds = Math.floor(t / 1e3 % 60); + var minutes = Math.floor(t / 1e3 / 60 % 60); + var hours = Math.floor(t / (1e3 * 60 * 60) % 24); + var days = Math.floor(t / (1e3 * 60 * 60 * 24)); + var total_hours = days * 24 + hours; var time_till = "

"; - if (total_hours >= 24) { time_till += sprintf("%d hours
", total_hours); } time_till += "("; if (days >= 1) { - time_till += sprintf("%d day%s ", days, (days > 1 ? "s" : "")); + time_till += sprintf("%d day%s ", days, days > 1 ? "s" : ""); } if (hours >= 1) { - time_till += sprintf("%d hour%s ", hours, (hours > 1 ? "s" : "")); + time_till += sprintf("%d hour%s ", hours, hours > 1 ? "s" : ""); } - time_till += sprintf("%d minute%s %d second%s)

", minutes, (minutes > 1? "s" : ""), seconds, (seconds > 1 ? "s" : "")); + time_till += sprintf("%d minute%s %d second%s)", minutes, minutes > 1 ? "s" : "", seconds, seconds > 1 ? "s" : ""); // d.asDays() + " days " + d.asHours() + moment.utc(ms).format(":mm:ss"); - // var time_till = moment.utc(end.diff(now)).format("HH:mm:ss");//.format("HH:mm:ss"); string += "

TIME REMAINING:

" + time_till + "

"; } else { string += "

EVENT IS OVER

"; } $("#timer_output_area").html(string); - // @sifen_trackbot updates come out at 36 minutes past the hour, fetch them at 37 minutes to allow for some slop - if ((minute(now) == 37 && second(now) == 0) || window.immediately_refresh_tier_cutoffs) { + if (minute(now) == 37 && second(now) == 0 || window.immediately_refresh_tier_cutoffs) { $("#tier_info_output_area").html("

Updating tier cutoff data, please wait...

"); // dumb ass way to fetch and display @sifen_trackbot tier cutoff tweets hourly // using this twitter fetcher: http://jasonmayes.com/projects/twitterApi/#sthash.budgYosd.dpbs twitterFetcher.fetch({ - "id": '654587648904794112', - "domId": '', - "maxTweets": 1, - "enableLinks": false, - "showUser": true, - "showTime": true, - "dateFunction": '', - "showRetweet": false, - "customCallback": function(tweets) { + id: "654587648904794112", + domId: "", + maxTweets: 1, + enableLinks: false, + showUser: true, + showTime: true, + dateFunction: "", + showRetweet: false, + customCallback: function(tweets) { if (tweets.length > 0) { // we only care about the first one var tweet = tweets[0]; @@ -1077,14 +1052,13 @@ function run_timer() $("#tier_info_output_area").html("

Latest Tier Cutoffs as of UTC " + updateTime + ":
" + tier1 + "
" + tier2 + "

"); } }, - "showInteraction": false + showInteraction: false }); window.immediately_refresh_tier_cutoffs = false; } } -function clear_timer() -{ +function clear_timer() { $("#timer_output_area").html("

Timer Not Running

"); $("#tier_info_output_area").html(""); } \ No newline at end of file diff --git a/web_app/sif_tools.html b/web_app/sif_tools.html index f44546e..2ceca61 100644 --- a/web_app/sif_tools.html +++ b/web_app/sif_tools.html @@ -1,9 +1,9 @@ - + - - - + + + SIF Tools @@ -11,175 +11,226 @@ - - - - - - - - - - - + + + + + + + + + + + - - - -
- - -
-
- Current Rank:    -
- Current EXP:    -
- Desired Rank:    -
- Game Version:    - -

-
Calculate
-

-
-

Results

- EXP required:   -

- You will need to play the following number of songs in order to get this amount of EXP:

- EASY:   -
- NORMAL:   -
- HARD:   -
- EXPERT:   -

- At this rank you will have - LP and - friend slots.

-
Reset
-
-
-
-
-
- Current Gems:    -
- Game Version:    - -

- Include daily "quest" gems? - (What is this?)
- -
- Include gems from events? (Read this first)
-
- - Average tier:    - -
-
- Mode:
- Number of gems you'll have on a date?
- Date you will have this many gems?

-
- Date:    -
-
- Desired gems:    -
-

- Verbose Mode
-

-
Calculate
-

-
-

Results

- -

-
-
-
-
-
Reset
-
-
+ - -
-
- Card Rarity:    - -
- Current Level:    -
- Current EXP:    -

- Mode:
- EXP needed to get card to a level?
- Final level after feeding an amount of EXP?

-
- Desired level:    -
-
- EXP:    -
-

-
Calculate
-

-
-

Results

- -

-
Reset
-
-
-
- -
-
-

Enter Event End Date/Time (in UTC):


- - -

-
Start Timer
-
-

Timer Not Running

+
+ +
+
+ Current Rank:   
+ Current EXP:   
+ Desired Rank:   
+ Game Version:   
+
+
+ Calculate +

+
+
+

Results

EXP required:   -
+
+ You will need to play the following number of songs in order to get this amount of EXP:
+
+ EASY:   -
+ NORMAL:   -
+ HARD:   -
+ EXPERT:   -
+
+ At this rank you will have - LP and - friend slots.
+
+
+ Reset +
+
+
-
+
+
+ Current Gems:   
+ Game Version:   
+
+ Include daily "quest" gems? + (What is this?)
+ Include gems from events? (Read this first)
+
+ Average tier:    +

+ Mode:
+ Number of gems you'll have on a date?
+ Date you will have this many gems?
+
+
+ Date:    +
+
+ Desired gems:    +

+
+ Verbose Mode
+
+
+
+ Calculate +

+
+
+

Results

-
+
+
+
+ - +
+
+
+ Reset +
+
+
-
Clear Timer
-
- -
-

-
-
-
- +
+
+ Card Rarity:   
+ Current Level:   
+ Current EXP:   
+
+ Mode:
+ EXP needed to get card to a level?
+ Final level after feeding an amount of EXP?
+
+
+ Desired level:    +
+
+ EXP:    +

+
+
+ Calculate +

+
+
+

Results

-
+
+
+ Reset +
+
+
+
+
+
+

Enter Event End Date/Time (in UTC):


+
+
+
+ Start Timer +
+
+

Timer Not Running

+
+
+
+ Clear Timer +
+
+
+

+
+
+
- \ No newline at end of file + From 3ab33e86244829d2015fd9fab606f5913ac8ad12 Mon Sep 17 00:00:00 2001 From: Donald Burr Date: Wed, 21 Oct 2015 16:04:47 -0700 Subject: [PATCH 03/17] Events were being calculated even if not selected --- web_app/js/sif_tools.js | 112 +++++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 54 deletions(-) diff --git a/web_app/js/sif_tools.js b/web_app/js/sif_tools.js index fcd09d1..1d086a1 100644 --- a/web_app/js/sif_tools.js +++ b/web_app/js/sif_tools.js @@ -644,35 +644,37 @@ function calculate_gems() { if (calc_daily_quest_gems) { gems++; } - // account for event - // format of returned tuple: - // tuple[0] - was this an event day? (boolean, duh) - // tuple[1] - name of event, or "" if none (string) - // tuple[2] - amount of gems spent (int) - // tuple[3] - amount of gems gained (int) - var event_results = handle_event(day(now), game_version, tier); - var is_event = event_results[0]; - var event_name = ""; - var spent_gems = 0; - var won_gems = 0; - if (is_event) { - event_name = event_results[1]; - spent_gems = event_results[2]; - won_gems = event_results[3]; - // did any gems get spent? - if (spent_gems > 0) { - // do we have enough to cover it? - if (gems >= spent_gems) { - // spend the gems - gems -= spent_gems; - // now reap the winnings - gems += won_gems; + if (calc_event_gems) { + // account for event + // format of returned tuple: + // tuple[0] - was this an event day? (boolean, duh) + // tuple[1] - name of event, or "" if none (string) + // tuple[2] - amount of gems spent (int) + // tuple[3] - amount of gems gained (int) + var event_results = handle_event(day(now), game_version, tier); + var is_event = event_results[0]; + var event_name = ""; + var spent_gems = 0; + var won_gems = 0; + if (is_event) { + event_name = event_results[1]; + spent_gems = event_results[2]; + won_gems = event_results[3]; + // did any gems get spent? + if (spent_gems > 0) { + // do we have enough to cover it? + if (gems >= spent_gems) { + // spend the gems + gems -= spent_gems; + // now reap the winnings + gems += won_gems; + } else { + // flag to indicate that we didn't have the gems + spent_gems = -1; + } } else { - // flag to indicate that we didn't have the gems - spent_gems = -1; + gems += won_gems; } - } else { - gems += won_gems; } } // record verbose output if desired @@ -747,35 +749,37 @@ function calculate_gems() { if (calc_daily_quest_gems) { gems++; } - // account for event - // format of returned tuple: - // tuple[0] - was this an event day? (boolean, duh) - // tuple[1] - name of event, or "" if none (string) - // tuple[2] - amount of gems spent (int) - // tuple[3] - amount of gems gained (int) - var event_results = handle_event(day(now), game_version, tier); - var is_event = event_results[0]; - var event_name = ""; - var spent_gems = 0; - var won_gems = 0; - if (is_event) { - event_name = event_results[1]; - spent_gems = event_results[2]; - won_gems = event_results[3]; - // did any gems get spent? - if (spent_gems > 0) { - // do we have enough to cover it? - if (gems >= spent_gems) { - // spend the gems - gems -= spent_gems; - // now reap the winnings - gems += won_gems; + if (calc_event_gems) { + // account for event + // format of returned tuple: + // tuple[0] - was this an event day? (boolean, duh) + // tuple[1] - name of event, or "" if none (string) + // tuple[2] - amount of gems spent (int) + // tuple[3] - amount of gems gained (int) + var event_results = handle_event(day(now), game_version, tier); + var is_event = event_results[0]; + var event_name = ""; + var spent_gems = 0; + var won_gems = 0; + if (is_event) { + event_name = event_results[1]; + spent_gems = event_results[2]; + won_gems = event_results[3]; + // did any gems get spent? + if (spent_gems > 0) { + // do we have enough to cover it? + if (gems >= spent_gems) { + // spend the gems + gems -= spent_gems; + // now reap the winnings + gems += won_gems; + } else { + // flag to indicate that we didn't have the gems + spent_gems = -1; + } } else { - // flag to indicate that we didn't have the gems - spent_gems = -1; + gems += won_gems; } - } else { - gems += won_gems; } } // record verbose output if desired From 0f07e6e475448c9a0f517fe8a2f7d7b67ce2a504 Mon Sep 17 00:00:00 2001 From: Donald Burr Date: Thu, 22 Oct 2015 15:33:25 -0700 Subject: [PATCH 04/17] Add "max level" button and overkill detection * "max level" button to quickly fill in a card's max level * "overkill" detection detects when you are feeding way more EXP than is necessary to max level a card. --- web_app/js/sif_tools.js | 30 ++++++++++++++++++++++++------ web_app/sif_tools.html | 3 ++- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/web_app/js/sif_tools.js b/web_app/js/sif_tools.js index 1d086a1..d28cdb5 100644 --- a/web_app/js/sif_tools.js +++ b/web_app/js/sif_tools.js @@ -27,8 +27,6 @@ // * Card Level Calc: // - account for 1.2x bonus when feeding cards of the same attribute // - maybe also the super (1.5) and ultra (2.0) success bonuses too? -// - detect "overkill" (i.e. feeding too much EXP to a card) -// - add "max level" button to automatically fill in the max level of the given type of card // * Event Tracker: // - if time remaining < 24 don't put parenthesis around the hours/minutes/seconds display // - @sifen_trackbot update code gets "stuck" sometimes - not sure if the problem @@ -173,7 +171,7 @@ function setup_ui_elements() { disableTextInput: true }); // set up buttons - [ "calculate-rank", "reset-rank", "calculate-gems", "reset-gems", "calculate-card", "reset-card", "start-stop-timer", "clear-timer" ].forEach(function(entry) { + [ "calculate-rank", "reset-rank", "calculate-gems", "reset-gems", "card-max-level", "calculate-card", "reset-card", "start-stop-timer", "clear-timer" ].forEach(function(entry) { var selector = "#button-" + entry; LOG(1, "setting up " + selector); $(selector).button(); @@ -247,6 +245,9 @@ function setup_button_handlers() { $("#button-reset-gems").click(function(evt) { reset_gems(); }); + $("#button-card-max-level").click(function(evt) { + card_set_max_level(); + }); $("#button-calculate-card").click(function(evt) { calculate_card(); }); @@ -382,6 +383,18 @@ function handle_gem_mode_select() { } } +function card_set_max_level() { + var card_type = $("#card_rarity").val(); + var level = 0; + switch (card_type) { + case "N": level = 40; break; + case "R": level = 60; break; + case "SR": level = 80; break; + case "UR": level = 100; break; + } + $("#card_desired_level").val(level); +} + function handle_card_mode_select() { var mode = $(this).val(); if (mode === "LEVEL") { @@ -952,7 +965,12 @@ function calculate_card() { } } level--; - var resultString = sprintf("If you feed a %s card at level %d (with %d EXP) a total of %d EXP,
it will end up at level %d.%s", rarity, current_level, current_exp, exp_to_feed, level, level == get_level_cap(rarity) ? " (MAX LEVEL!)" : ""); + var resultString = ""; + if (exp_to_feed > exp_tally) { + resultString = sprintf("If you feed a %s card at level %d (with %d EXP) a total of %d EXP,
it will end up at level %d.%s
This is way overkill, you fed %d more EXP than was necessary.", rarity, current_level, current_exp, exp_to_feed, level, (level == get_level_cap(rarity) ? " (MAX LEVEL!)" : ""), exp_to_feed - exp_tally); + } else { + resultString = sprintf("If you feed a %s card at level %d (with %d EXP) a total of %d EXP,
it will end up at level %d.%s", rarity, current_level, current_exp, exp_to_feed, level, level == get_level_cap(rarity) ? " (MAX LEVEL!)" : ""); + } // output the result $("#card-result-summary").html(resultString); } @@ -1022,8 +1040,8 @@ function run_timer() { string += "

EVENT IS OVER

"; } $("#timer_output_area").html(string); - // @sifen_trackbot updates come out at 36 minutes past the hour, fetch them at 37 minutes to allow for some slop - if (minute(now) == 37 && second(now) == 0 || window.immediately_refresh_tier_cutoffs) { + // @sifen_trackbot updates come out at 36 minutes past the hour, fetch them at 38 minutes to allow for some slop + if (minute(now) == 38 && second(now) == 0 || window.immediately_refresh_tier_cutoffs) { $("#tier_info_output_area").html("

Updating tier cutoff data, please wait...

"); // dumb ass way to fetch and display @sifen_trackbot tier cutoff tweets hourly // using this twitter fetcher: http://jasonmayes.com/projects/twitterApi/#sthash.budgYosd.dpbs diff --git a/web_app/sif_tools.html b/web_app/sif_tools.html index 2ceca61..ed0c3b9 100644 --- a/web_app/sif_tools.html +++ b/web_app/sif_tools.html @@ -192,6 +192,7 @@
Desired level:    +
Max
EXP:    @@ -233,4 +234,4 @@
- + \ No newline at end of file From 5a4ad872732444c5493de930d3ea97957599dc11 Mon Sep 17 00:00:00 2001 From: Donald Burr Date: Thu, 22 Oct 2015 15:35:20 -0700 Subject: [PATCH 05/17] Duh, card_set_max_level() should use get_level_cap() --- web_app/js/sif_tools.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/web_app/js/sif_tools.js b/web_app/js/sif_tools.js index d28cdb5..35349b3 100644 --- a/web_app/js/sif_tools.js +++ b/web_app/js/sif_tools.js @@ -384,14 +384,8 @@ function handle_gem_mode_select() { } function card_set_max_level() { - var card_type = $("#card_rarity").val(); - var level = 0; - switch (card_type) { - case "N": level = 40; break; - case "R": level = 60; break; - case "SR": level = 80; break; - case "UR": level = 100; break; - } + var rarity = $("#card_rarity").val(); + var level = get_level_cap(rarity); $("#card_desired_level").val(level); } From 31b0d36abc8fc026965ae480deb0269539b6f8b9 Mon Sep 17 00:00:00 2001 From: Donald Burr Date: Fri, 23 Oct 2015 13:43:21 -0700 Subject: [PATCH 06/17] Move UI update (show/hide) code into a new update_ui() func --- web_app/js/sif_tools.js | 51 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/web_app/js/sif_tools.js b/web_app/js/sif_tools.js index 35349b3..c1a1bf6 100644 --- a/web_app/js/sif_tools.js +++ b/web_app/js/sif_tools.js @@ -185,12 +185,6 @@ function setup_ui_elements() { // set up radio button listeners $("input[name=gem-mode]").change(handle_gem_mode_select); $("input[name=card-mode]").change(handle_card_mode_select); - // hide non-selected option divs - [ "gem-event-options-area", "gem-desired-gems-area", "card-exp-area", "gem_jp_daily_gems" ].forEach(function(entry) { - var selector = "#" + entry; - LOG(1, "setting up " + selector); - $(selector).hide(); - }); // set up checkbox change event handler $("#gems_include_events").change(function() { handle_gem_event_box(this.checked); @@ -213,6 +207,51 @@ function setup_ui_elements() { $("#gem_game_version").on("change", function(e) { handle_gem_game_version_change(); }); + // update the UI based on what is selected + update_ui(); +} + +// show/hide option div's based on which modes are selected +function update_ui() +{ + // gem screen - show/hide the options based on game version + var gem_game_version = $("#gem_game_version").val(); + if (gem_game_version === "JP") { + $("#gem_jp_daily_gems").show(); + } else { + $("#gem_jp_daily_gems").hide(); + } + // gem screen - hide event tier selector + var gem_events_selected = $("#gems_include_events").is(":checked"); + if (gem_events_selected) { + $("#gem-event-options-area").show(); + } else { + $("#gem-event-options-area").hide(); + } + // gem screen - mode + var gem_mode = $("#gem-mode").val(); + switch(gem_mode) { + case "DATE": + $("#gem-date-area").show(); + $("#gem-desired-gems-area").hide(); + break; + case "GEMS": + $("#gem-date-area").hide(); + $("#gem-desired-gems-area").show(); + break; + } + // card screen - mode + var card_mode = $("#card-mode").val(); + switch(card_mode) { + case "LEVEL": + $("#card-level-area").show(); + $("#card-exp-area").hide(); + break; + case "EXP": + $("#card-level-area").hide(); + $("#card-exp-area").show(); + break; + } } function handle_gem_game_version_change() { From 21d184d671a963384bbd06cf1d5681dc56ebefa1 Mon Sep 17 00:00:00 2001 From: Donald Burr Date: Fri, 23 Oct 2015 18:12:07 -0700 Subject: [PATCH 07/17] Need to go back to the old way of triggering radio button updates --- web_app/js/sif_tools.js | 71 ++++++++++++++++------------------------- web_app/sif_tools.html | 8 ++--- 2 files changed, 31 insertions(+), 48 deletions(-) diff --git a/web_app/js/sif_tools.js b/web_app/js/sif_tools.js index c1a1bf6..ae2aa46 100644 --- a/web_app/js/sif_tools.js +++ b/web_app/js/sif_tools.js @@ -187,7 +187,7 @@ function setup_ui_elements() { $("input[name=card-mode]").change(handle_card_mode_select); // set up checkbox change event handler $("#gems_include_events").change(function() { - handle_gem_event_box(this.checked); + update_ui(); }); // set up gem event calc note dialog // $("#dialog").dialog({ autoOpen: false }); @@ -205,7 +205,7 @@ function setup_ui_elements() { }); // set up show/hide JP-only options $("#gem_game_version").on("change", function(e) { - handle_gem_game_version_change(); + update_ui(); }); // update the UI based on what is selected update_ui(); @@ -254,23 +254,6 @@ function update_ui() } } -function handle_gem_game_version_change() { - var game_version = $("#gem_game_version").val(); - if (game_version === "JP") { - $("#gem_jp_daily_gems").show(); - } else { - $("#gem_jp_daily_gems").hide(); - } -} - -function handle_gem_event_box(show_it) { - if (show_it) { - $("#gem-event-options-area").show(); - } else { - $("#gem-event-options-area").hide(); - } -} - function setup_button_handlers() { $("#button-calculate-rank").click(function(evt) { calculate_rank(); @@ -397,6 +380,28 @@ function calculate_rank() { } } +function handle_gem_mode_select() { + var mode = $(this).val(); + if (mode === "DATE") { + $("#gem-date-area").show(); + $("#gem-desired-gems-area").hide(); + } else if (mode === "GEMS") { + $("#gem-date-area").hide(); + $("#gem-desired-gems-area").show(); + } +} + +function handle_card_mode_select() { + var mode = $(this).val(); + if (mode === "LEVEL") { + $("#card-level-area").show(); + $("#card-exp-area").hide(); + } else if (mode === "EXP") { + $("#card-level-area").hide(); + $("#card-exp-area").show(); + } +} + function reset_rank() { $("#rank-calc-result-area").hide(); $("#current_rank").val(""); @@ -411,34 +416,12 @@ function reset_rank() { $("#rank-results-fp").text("-"); } -function handle_gem_mode_select() { - var mode = $(this).val(); - if (mode === "DATE") { - $("#gem-date-area").show(); - $("#gem-desired-gems-area").hide(); - } else if (mode === "GEMS") { - $("#gem-date-area").hide(); - $("#gem-desired-gems-area").show(); - } -} - function card_set_max_level() { var rarity = $("#card_rarity").val(); var level = get_level_cap(rarity); $("#card_desired_level").val(level); } -function handle_card_mode_select() { - var mode = $(this).val(); - if (mode === "LEVEL") { - $("#card-level-area").show(); - $("#card-exp-area").hide(); - } else if (mode === "EXP") { - $("#card-level-area").hide(); - $("#card-exp-area").show(); - } -} - function is_muse_members_birthday(moment) { var the_month = month(moment); var the_day = day(moment); @@ -517,7 +500,7 @@ function is_same_day(m1, m2) { return false; } -function handle_event(day_of_month, game_version, typical_tier) { +function calculate_event(day_of_month, game_version, typical_tier) { LOG(1, "start event type is " + current_type_of_event); // format of returned tuple: // tuple[0] - was this an event day? (boolean, duh) @@ -697,7 +680,7 @@ function calculate_gems() { // tuple[1] - name of event, or "" if none (string) // tuple[2] - amount of gems spent (int) // tuple[3] - amount of gems gained (int) - var event_results = handle_event(day(now), game_version, tier); + var event_results = calculate_event(day(now), game_version, tier); var is_event = event_results[0]; var event_name = ""; var spent_gems = 0; @@ -802,7 +785,7 @@ function calculate_gems() { // tuple[1] - name of event, or "" if none (string) // tuple[2] - amount of gems spent (int) // tuple[3] - amount of gems gained (int) - var event_results = handle_event(day(now), game_version, tier); + var event_results = calculate_event(day(now), game_version, tier); var is_event = event_results[0]; var event_name = ""; var spent_gems = 0; diff --git a/web_app/sif_tools.html b/web_app/sif_tools.html index ed0c3b9..d20658a 100644 --- a/web_app/sif_tools.html +++ b/web_app/sif_tools.html @@ -133,8 +133,8 @@

Mode:
- Number of gems you'll have on a date?
- Date you will have this many gems?
+ Number of gems you'll have on a date?
+ Date you will have this many gems?

Date:    @@ -186,8 +186,8 @@ "">

Mode:
- EXP needed to get card to a level?
- Final level after feeding an amount of EXP?
+ EXP needed to get card to a level?
+ Final level after feeding an amount of EXP?

Desired level:    Date: Sun, 25 Oct 2015 02:49:27 -0700 Subject: [PATCH 08/17] Add option to account for 1.2x bonus when feeding cards of same attribute --- web_app/js/sif_tools.js | 24 ++++++++++++++++++------ web_app/sif_tools.html | 1 + 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/web_app/js/sif_tools.js b/web_app/js/sif_tools.js index ae2aa46..156c515 100644 --- a/web_app/js/sif_tools.js +++ b/web_app/js/sif_tools.js @@ -25,7 +25,6 @@ // * General improvements: // - needs a lot more error/bounds checking in general // * Card Level Calc: -// - account for 1.2x bonus when feeding cards of the same attribute // - maybe also the super (1.5) and ultra (2.0) success bonuses too? // * Event Tracker: // - if time remaining < 24 don't put parenthesis around the hours/minutes/seconds display @@ -913,6 +912,7 @@ function is_valid_exp(rarity, level, exp) { function calculate_card() { var current_level = parseInt($("#card_current_level").val()); var current_exp_input = $("#card_current_exp").val(); + var same_attribute = $("#card_same_attribute").is(":checked"); // 1.2x bonus for feeding same attribute cards var current_exp = 0; if (current_exp_input != "") { current_exp = parseInt(current_exp_input); @@ -937,15 +937,21 @@ function calculate_card() { var required_exp = 0; var resultsString = ""; for (level = current_level + 1; level <= target_level; level++) { + var exp = 0; if (rarity === "N") { - required_exp += exp_table_n[level]; + exp = exp_table_n[level]; } else if (rarity === "R") { - required_exp += exp_table_r[level]; + exp = exp_table_r[level]; } else if (rarity === "SR") { - required_exp += exp_table_sr[level]; + exp = exp_table_sr[level]; } else if (rarity === "UR") { - required_exp += exp_table_ur[level]; + exp = exp_table_ur[level]; } + required_exp += exp; + } + if (same_attribute) { + // feeding cards of same attribute get a 1.2x bonus, so reduce the needed exp by that + required_exp = Math.floor(required_exp / 1.2); } // subtract what we have required_exp -= current_exp; @@ -961,12 +967,18 @@ function calculate_card() { window.alert("Error: you have entered an invalid (non-numeric) value. Please check your input and try again."); return; } + var real_exp_to_feed = exp_to_feed; + if (same_attribute) { + // feeding cards of same attribute get a 1.2x bonus, so reduce the needed exp by that + real_exp_to_feed = Math.round(real_exp_to_feed * 1.2); + } // ready to rock var resultsString = "FOO"; // XXX do some calculating var exp_tally = current_exp; var level = 0; for (level = current_level + 1; level <= get_level_cap(rarity); level++) { + var exp_temp = 0; if (rarity === "N") { exp_tally += exp_table_n[level]; } else if (rarity === "R") { @@ -976,7 +988,7 @@ function calculate_card() { } else if (rarity === "UR") { exp_tally += exp_table_ur[level]; } - if (exp_tally > exp_to_feed) { + if (exp_tally > real_exp_to_feed) { break; } } diff --git a/web_app/sif_tools.html b/web_app/sif_tools.html index d20658a..8e5fb89 100644 --- a/web_app/sif_tools.html +++ b/web_app/sif_tools.html @@ -197,6 +197,7 @@
EXP:   

+ Assume cards are same attribute

Calculate From bc53505c1652aaae2c1011a17efe6915d4aa799a Mon Sep 17 00:00:00 2001 From: Donald Burr Date: Mon, 26 Oct 2015 10:30:33 -0700 Subject: [PATCH 09/17] Refactor EXP table lookups --- web_app/js/sif_tools.js | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/web_app/js/sif_tools.js b/web_app/js/sif_tools.js index 156c515..504c825 100644 --- a/web_app/js/sif_tools.js +++ b/web_app/js/sif_tools.js @@ -35,6 +35,7 @@ var DEBUG_LEVEL = 0; // EXP tables +// from: http://www59.atwiki.jp/lovelive-sif/pages/32.html var exp_table_n = [ -1, 0, 6, 18, 28, 40, 51, 61, 72, 82, 93, 104, 114, 124, 135, 145, 156, 165, 176, 187, 196, 207, 217, 226, 238, 247, 257, 268, 277, 288, 297, 308, 317, 328, 337, 348, 358, 367, 377, 388, 397 ]; var exp_table_r = [ -1, 0, 14, 31, 45, 55, 67, 76, 85, 94, 103, 110, 119, 125, 134, 140, 148, 155, 161, 168, 174, 181, 187, 193, 199, 206, 211, 217, 223, 228, 235, 240, 245, 251, 256, 262, 267, 272, 277, 283, 288, 292, 298, 303, 308, 313, 317, 323, 327, 332, 337, 342, 346, 351, 356, 360, 365, 370, 374, 378, 383 ]; @@ -909,6 +910,20 @@ function is_valid_exp(rarity, level, exp) { return return_value; } +function get_exp_table_entry(rarity, level) { + var exp = 0; + if (rarity === "N") { + exp = exp_table_n[level]; + } else if (rarity === "R") { + exp = exp_table_r[level]; + } else if (rarity === "SR") { + exp = exp_table_sr[level]; + } else if (rarity === "UR") { + exp = exp_table_ur[level]; + } + return exp; +} + function calculate_card() { var current_level = parseInt($("#card_current_level").val()); var current_exp_input = $("#card_current_exp").val(); @@ -937,17 +952,7 @@ function calculate_card() { var required_exp = 0; var resultsString = ""; for (level = current_level + 1; level <= target_level; level++) { - var exp = 0; - if (rarity === "N") { - exp = exp_table_n[level]; - } else if (rarity === "R") { - exp = exp_table_r[level]; - } else if (rarity === "SR") { - exp = exp_table_sr[level]; - } else if (rarity === "UR") { - exp = exp_table_ur[level]; - } - required_exp += exp; + required_exp += get_exp_table_entry(rarity, level); } if (same_attribute) { // feeding cards of same attribute get a 1.2x bonus, so reduce the needed exp by that @@ -969,7 +974,7 @@ function calculate_card() { } var real_exp_to_feed = exp_to_feed; if (same_attribute) { - // feeding cards of same attribute get a 1.2x bonus, so reduce the needed exp by that + // feeding cards of same attribute get a 1.2x bonus, so account for that real_exp_to_feed = Math.round(real_exp_to_feed * 1.2); } // ready to rock @@ -978,16 +983,7 @@ function calculate_card() { var exp_tally = current_exp; var level = 0; for (level = current_level + 1; level <= get_level_cap(rarity); level++) { - var exp_temp = 0; - if (rarity === "N") { - exp_tally += exp_table_n[level]; - } else if (rarity === "R") { - exp_tally += exp_table_r[level]; - } else if (rarity === "SR") { - exp_tally += exp_table_sr[level]; - } else if (rarity === "UR") { - exp_tally += exp_table_ur[level]; - } + exp_tally += get_exp_table_entry(rarity, level); if (exp_tally > real_exp_to_feed) { break; } From a92b72f485b0e3bf2d1c1158e30a1263ead24ef3 Mon Sep 17 00:00:00 2001 From: Donald Burr Date: Mon, 26 Oct 2015 19:14:10 -0700 Subject: [PATCH 10/17] Add work-in-progress event updater bot --- event_bot/event_bot.py | 248 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100755 event_bot/event_bot.py diff --git a/event_bot/event_bot.py b/event_bot/event_bot.py new file mode 100755 index 0000000..b01277a --- /dev/null +++ b/event_bot/event_bot.py @@ -0,0 +1,248 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import datetime +import time +import signal +import sys +import feedparser +import re +import htmlentitydefs +import subprocess +import curses +import logging +from HTMLParser import HTMLParser +from apscheduler.schedulers.blocking import BlockingScheduler + +# http://www.leancrew.com/all-this/2013/03/combining-python-and-applescript/ + +############################################################################################## +# Interface with Applescript +############################################################################################## + +def asrun(ascript): + "Run the given AppleScript and return the standard output and error." + + osa = subprocess.Popen(['osascript', '-'], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + return osa.communicate(ascript)[0] + +def asquote(astr): + "Return the AppleScript equivalent of the given string." + + astr = astr.replace('"', '" & quote & "') + return '"{}"'.format(astr) + +############################################################################################## +# Extract text from HTML +############################################################################################## + +class HTMLTextExtractor(HTMLParser): + def __init__(self): + HTMLParser.__init__(self) + self.result = [ ] + + def handle_data(self, d): + self.result.append(d) + + def handle_charref(self, number): + codepoint = int(number[1:], 16) if number[0] in (u'x', u'X') else int(number) + self.result.append(unichr(codepoint)) + + def handle_entityref(self, name): + codepoint = htmlentitydefs.name2codepoint[name] + self.result.append(unichr(codepoint)) + + def get_text(self): + return u''.join(self.result) + +def html_to_text(html): + s = HTMLTextExtractor() + s.feed(html) + return s.get_text() + +def get_first(iterable, default=None): + if iterable: + for item in iterable: + return item + return default + +############################################################################################## +# Cleanup +############################################################################################## + +def cleanup(signal, frame): + global clock_job + global poll_job + global screen + print "Exiting" + clock_job.remove() + poll_job.remove() + curses.nocbreak() + screen.keypad(False) + curses.echo() + curses.endwin() + sys.exit(0) + +############################################################################################## +# Utility +############################################################################################## + +def send_to_line(string): + split_string = string.splitlines() + astring = """ + activate application "LINE" + tell application "System Events" + """ + for s in split_string: + astring += """ + keystroke "%s" + keystroke return using control down + """ % s + astring += """ + keystroke return + delay 0.5 + end tell + activate application "iTerm" + """ + asrun(astring) + +############################################################################################## +# Clock +############################################################################################## + +def clock(): + global screen + global status + global width + global height + global event_pending + global in_event + global event_name + global event_duration + global update_time + global pct_done + global t1 + global t1_diff + global t2 + global t2_diff + screen.addstr(0, 0, time.strftime("%d/%m/%Y %H:%M:%S")) + if not event_pending and not in_event: + screen.addstr(2, 0, "status: idle") + screen.addstr(4, 0, " " * width) + screen.addstr(5, 0, " " * width) + screen.addstr(6, 0, " " * width) + elif event_pending: + screen.addstr(2, 0, "status: event pending") + screen.addstr(4, 0, "\"%s\"" % event_name) + screen.addstr(5, 0, "time period: %s" % event_duration) + screen.addstr(6, 0, " " * width) + elif in_event: + screen.addstr(2, 0, "status: event active") + screen.addstr(4, 0, "\"%s\" (%d%% complete)" % (event_name, pct_done)) + screen.addstr(5, 0, "Tier 1: %d (%s)" % (t1, t1_diff)) + screen.addstr(6, 0, "Tier 2: %d (%s)" % (t2, t2_diff)) + screen.refresh() + +############################################################################################## +# Poll +############################################################################################## + +def poll(): + global screen + global width + global height + global event_pending + global in_event + global event_name + global event_duration + global update_time + global pct_done + global t1 + global t1_diff + global t2 + global t2_diff + + screen.addstr(0, width-11, "updating...", curses.A_REVERSE) + screen.refresh() + d = feedparser.parse('https://script.google.com/macros/s/AKfycbwLQxnGfWEOq-aJwpbB68bCAHAu8_vChD8KIk-5h3glFMIZn-g/exec?654587648904794112') + + latest_entry = get_first(d.entries) + desc_stripped = html_to_text(latest_entry["description"]) + + string_to_send = "" + + # simplistic parsing + if u"New Event Announced" in desc_stripped and not event_pending: + # new event - split it + split_string = desc_stripped.splitlines() + # name is in item1, time is item2 + event_name = split_string[1] + event_duration = split_string[2] + #print "New event detected:" + #print "Name: %s" % event_name + #print "Time period: %s" % event_time + string_to_send = """New Event Announcement!\n"%s"\n%s""" % (event_name, event_duration) + # don't keep yammering on until the event actually starts + event_pending = True + in_event = False + elif u"FINAL" in desc_stripped and in_event: + in_event = False + string_to_send = """Event Has Ended! "%s" has ended. Final numbers: blah""" % event_name + if string_to_send: + # sanitise it + string_to_send = string_to_send.replace('"', '\\"') + send_to_line(string_to_send) + screen.addstr(0, width-11, " ") + screen.refresh() + +############################################################################################## +# Start of main script +############################################################################################## + +clock_job = None +poll_job = None +in_event = False +width = 0 +height = 0 +screen = curses.initscr() +(height, width) = screen.getmaxyx() +screen.clear() +screen.refresh() +event_pending = False +in_event = False +event_name = "" +event_duration = "" +update_time = "" +pct_done = 0 +t1 = 0 +t1_diff = "" +t2 = 0 +t2_diff = "" + +# disable logging +# my_filter = NoRunningFilter() +# logging.getLogger("apscheduler.scheduler").addFilter(my_filter) +logging.basicConfig() + +# setup signal handler +signal.signal(signal.SIGINT, cleanup) + +# Start the scheduler +sched = BlockingScheduler() +sched.daemonic = False + +# add some jobs +clock_job = sched.add_job(clock, 'interval', seconds=1) +poll_job = sched.add_job(poll, 'interval', seconds=10) +#poll_job = sched.add_job(poll, 'cron', minute=40) + +#print "job scheduled" + +# Kick it off +#print "START" + +sched.start() + +#print "END" From b95ead015c3e93523390989a11a5614f276c48f6 Mon Sep 17 00:00:00 2001 From: Donald Burr Date: Tue, 27 Oct 2015 00:34:26 -0700 Subject: [PATCH 11/17] Mostly finished app logic *should* work, we'll see when the score match starts --- event_bot/event_bot.py | 82 +++++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 32 deletions(-) diff --git a/event_bot/event_bot.py b/event_bot/event_bot.py index b01277a..01a182f 100755 --- a/event_bot/event_bot.py +++ b/event_bot/event_bot.py @@ -90,23 +90,25 @@ def cleanup(signal, frame): ############################################################################################## def send_to_line(string): - split_string = string.splitlines() - astring = """ + # first sanitise it + sanitised_string = string.replace('"', '\\"') + split_string = sanitised_string.splitlines() + script = """ activate application "LINE" tell application "System Events" """ for s in split_string: - astring += """ + script += """ keystroke "%s" keystroke return using control down """ % s - astring += """ + script += """ keystroke return delay 0.5 end tell activate application "iTerm" """ - asrun(astring) + asrun(script) ############################################################################################## # Clock @@ -129,19 +131,27 @@ def clock(): global t2_diff screen.addstr(0, 0, time.strftime("%d/%m/%Y %H:%M:%S")) if not event_pending and not in_event: + screen.addstr(2, 0, " " * width) screen.addstr(2, 0, "status: idle") screen.addstr(4, 0, " " * width) screen.addstr(5, 0, " " * width) screen.addstr(6, 0, " " * width) elif event_pending: + screen.addstr(2, 0, " " * width) screen.addstr(2, 0, "status: event pending") + screen.addstr(4, 0, " " * width) screen.addstr(4, 0, "\"%s\"" % event_name) + screen.addstr(5, 0, " " * width) screen.addstr(5, 0, "time period: %s" % event_duration) screen.addstr(6, 0, " " * width) elif in_event: + screen.addstr(2, 0, " " * width) screen.addstr(2, 0, "status: event active") - screen.addstr(4, 0, "\"%s\" (%d%% complete)" % (event_name, pct_done)) + screen.addstr(4, 0, " " * width) + screen.addstr(4, 0, "\"%s\" (%s complete)" % (event_name, pct_done)) + screen.addstr(5, 0, " " * width) screen.addstr(5, 0, "Tier 1: %d (%s)" % (t1, t1_diff)) + screen.addstr(6, 0, " " * width) screen.addstr(6, 0, "Tier 2: %d (%s)" % (t2, t2_diff)) screen.refresh() @@ -150,6 +160,7 @@ def clock(): ############################################################################################## def poll(): + global pattern global screen global width global height @@ -174,25 +185,36 @@ def poll(): string_to_send = "" # simplistic parsing - if u"New Event Announced" in desc_stripped and not event_pending: - # new event - split it - split_string = desc_stripped.splitlines() - # name is in item1, time is item2 - event_name = split_string[1] - event_duration = split_string[2] - #print "New event detected:" - #print "Name: %s" % event_name - #print "Time period: %s" % event_time - string_to_send = """New Event Announcement!\n"%s"\n%s""" % (event_name, event_duration) - # don't keep yammering on until the event actually starts - event_pending = True - in_event = False + # also a kinda dorky (and possibly broken) state machine + # should really rewrite this eventually, but want to get it working first :P + if u"New Event Announced": + if not event_pending: + # new event - split it + split_string = desc_stripped.splitlines() + # name is in item1, time is item2 + event_name = split_string[1] + event_duration = split_string[2] + #print "New event detected:" + #print "Name: %s" % event_name + #print "Time period: %s" % event_time + string_to_send = """New Event Announcement!\n\n"%s"\n\n%s""" % (event_name, event_duration) + # don't keep yammering on until the event actually starts + event_pending = True + in_event = False elif u"FINAL" in desc_stripped and in_event: in_event = False - string_to_send = """Event Has Ended! "%s" has ended. Final numbers: blah""" % event_name + string_to_send = """Event Has Ended!\n\n"%s" has ended.\n\nFinal numbers:\n\nTier 1: %d (diff: %s)\nTier 2: %d (diff: %s)\n\nAs of: %s\n\n""" % (event_name, t1, t1_diff, t2, t2_diff, update_time) + else: + # if it's not final and not announce, then assume it's a bona fide event update + event_pending = False + in_event = True + results = re.findall(pattern, desc_stripped) + if results: + (event_name, tier1_s, t1_diff, tier2_s, t2_diff, update_time, pct_done) = results[0] + t1 = int(tier1_s) + t2 = int(tier2_s) + string_to_send = """\n"%s" (%s complete)\n\nTier 1: %d (diff: %s)\nTier 2: %d (diff: %s)\n\nAs of: %s\n\n""" % (event_name, pct_done, t1, t1_diff, t2, t2_diff, update_time) if string_to_send: - # sanitise it - string_to_send = string_to_send.replace('"', '\\"') send_to_line(string_to_send) screen.addstr(0, width-11, " ") screen.refresh() @@ -220,10 +242,9 @@ t1 = 0 t1_diff = "" t2 = 0 t2_diff = "" +pattern = re.compile("^(.*) Tier 1: (\d+)pts(([+-]\d+)).*Tier 2: (\d+)pts(([+-]\d+)).*Time: (.*)(([\d\.%]+))") # disable logging -# my_filter = NoRunningFilter() -# logging.getLogger("apscheduler.scheduler").addFilter(my_filter) logging.basicConfig() # setup signal handler @@ -235,14 +256,11 @@ sched.daemonic = False # add some jobs clock_job = sched.add_job(clock, 'interval', seconds=1) -poll_job = sched.add_job(poll, 'interval', seconds=10) -#poll_job = sched.add_job(poll, 'cron', minute=40) - -#print "job scheduled" - -# Kick it off -#print "START" +# poll_job = sched.add_job(poll, 'interval', seconds=1) # use for testing only! REALLY resource expensive +poll_job = sched.add_job(poll, 'interval', minutes=1) # use for testing only! +# poll_job = sched.add_job(poll, 'cron', minute=40) # use for production +# start the scheduler sched.start() -#print "END" +# never exits (quit the script using control-c) \ No newline at end of file From 78274390854a740e110efbb836c7ce1da72a2ab0 Mon Sep 17 00:00:00 2001 From: Donald Burr Date: Tue, 27 Oct 2015 00:35:55 -0700 Subject: [PATCH 12/17] nothing to see here, move along... *waves hands* --- event_bot/event_bot.py | 266 ----------------------------------------- 1 file changed, 266 deletions(-) delete mode 100755 event_bot/event_bot.py diff --git a/event_bot/event_bot.py b/event_bot/event_bot.py deleted file mode 100755 index 01a182f..0000000 --- a/event_bot/event_bot.py +++ /dev/null @@ -1,266 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import datetime -import time -import signal -import sys -import feedparser -import re -import htmlentitydefs -import subprocess -import curses -import logging -from HTMLParser import HTMLParser -from apscheduler.schedulers.blocking import BlockingScheduler - -# http://www.leancrew.com/all-this/2013/03/combining-python-and-applescript/ - -############################################################################################## -# Interface with Applescript -############################################################################################## - -def asrun(ascript): - "Run the given AppleScript and return the standard output and error." - - osa = subprocess.Popen(['osascript', '-'], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE) - return osa.communicate(ascript)[0] - -def asquote(astr): - "Return the AppleScript equivalent of the given string." - - astr = astr.replace('"', '" & quote & "') - return '"{}"'.format(astr) - -############################################################################################## -# Extract text from HTML -############################################################################################## - -class HTMLTextExtractor(HTMLParser): - def __init__(self): - HTMLParser.__init__(self) - self.result = [ ] - - def handle_data(self, d): - self.result.append(d) - - def handle_charref(self, number): - codepoint = int(number[1:], 16) if number[0] in (u'x', u'X') else int(number) - self.result.append(unichr(codepoint)) - - def handle_entityref(self, name): - codepoint = htmlentitydefs.name2codepoint[name] - self.result.append(unichr(codepoint)) - - def get_text(self): - return u''.join(self.result) - -def html_to_text(html): - s = HTMLTextExtractor() - s.feed(html) - return s.get_text() - -def get_first(iterable, default=None): - if iterable: - for item in iterable: - return item - return default - -############################################################################################## -# Cleanup -############################################################################################## - -def cleanup(signal, frame): - global clock_job - global poll_job - global screen - print "Exiting" - clock_job.remove() - poll_job.remove() - curses.nocbreak() - screen.keypad(False) - curses.echo() - curses.endwin() - sys.exit(0) - -############################################################################################## -# Utility -############################################################################################## - -def send_to_line(string): - # first sanitise it - sanitised_string = string.replace('"', '\\"') - split_string = sanitised_string.splitlines() - script = """ - activate application "LINE" - tell application "System Events" - """ - for s in split_string: - script += """ - keystroke "%s" - keystroke return using control down - """ % s - script += """ - keystroke return - delay 0.5 - end tell - activate application "iTerm" - """ - asrun(script) - -############################################################################################## -# Clock -############################################################################################## - -def clock(): - global screen - global status - global width - global height - global event_pending - global in_event - global event_name - global event_duration - global update_time - global pct_done - global t1 - global t1_diff - global t2 - global t2_diff - screen.addstr(0, 0, time.strftime("%d/%m/%Y %H:%M:%S")) - if not event_pending and not in_event: - screen.addstr(2, 0, " " * width) - screen.addstr(2, 0, "status: idle") - screen.addstr(4, 0, " " * width) - screen.addstr(5, 0, " " * width) - screen.addstr(6, 0, " " * width) - elif event_pending: - screen.addstr(2, 0, " " * width) - screen.addstr(2, 0, "status: event pending") - screen.addstr(4, 0, " " * width) - screen.addstr(4, 0, "\"%s\"" % event_name) - screen.addstr(5, 0, " " * width) - screen.addstr(5, 0, "time period: %s" % event_duration) - screen.addstr(6, 0, " " * width) - elif in_event: - screen.addstr(2, 0, " " * width) - screen.addstr(2, 0, "status: event active") - screen.addstr(4, 0, " " * width) - screen.addstr(4, 0, "\"%s\" (%s complete)" % (event_name, pct_done)) - screen.addstr(5, 0, " " * width) - screen.addstr(5, 0, "Tier 1: %d (%s)" % (t1, t1_diff)) - screen.addstr(6, 0, " " * width) - screen.addstr(6, 0, "Tier 2: %d (%s)" % (t2, t2_diff)) - screen.refresh() - -############################################################################################## -# Poll -############################################################################################## - -def poll(): - global pattern - global screen - global width - global height - global event_pending - global in_event - global event_name - global event_duration - global update_time - global pct_done - global t1 - global t1_diff - global t2 - global t2_diff - - screen.addstr(0, width-11, "updating...", curses.A_REVERSE) - screen.refresh() - d = feedparser.parse('https://script.google.com/macros/s/AKfycbwLQxnGfWEOq-aJwpbB68bCAHAu8_vChD8KIk-5h3glFMIZn-g/exec?654587648904794112') - - latest_entry = get_first(d.entries) - desc_stripped = html_to_text(latest_entry["description"]) - - string_to_send = "" - - # simplistic parsing - # also a kinda dorky (and possibly broken) state machine - # should really rewrite this eventually, but want to get it working first :P - if u"New Event Announced": - if not event_pending: - # new event - split it - split_string = desc_stripped.splitlines() - # name is in item1, time is item2 - event_name = split_string[1] - event_duration = split_string[2] - #print "New event detected:" - #print "Name: %s" % event_name - #print "Time period: %s" % event_time - string_to_send = """New Event Announcement!\n\n"%s"\n\n%s""" % (event_name, event_duration) - # don't keep yammering on until the event actually starts - event_pending = True - in_event = False - elif u"FINAL" in desc_stripped and in_event: - in_event = False - string_to_send = """Event Has Ended!\n\n"%s" has ended.\n\nFinal numbers:\n\nTier 1: %d (diff: %s)\nTier 2: %d (diff: %s)\n\nAs of: %s\n\n""" % (event_name, t1, t1_diff, t2, t2_diff, update_time) - else: - # if it's not final and not announce, then assume it's a bona fide event update - event_pending = False - in_event = True - results = re.findall(pattern, desc_stripped) - if results: - (event_name, tier1_s, t1_diff, tier2_s, t2_diff, update_time, pct_done) = results[0] - t1 = int(tier1_s) - t2 = int(tier2_s) - string_to_send = """\n"%s" (%s complete)\n\nTier 1: %d (diff: %s)\nTier 2: %d (diff: %s)\n\nAs of: %s\n\n""" % (event_name, pct_done, t1, t1_diff, t2, t2_diff, update_time) - if string_to_send: - send_to_line(string_to_send) - screen.addstr(0, width-11, " ") - screen.refresh() - -############################################################################################## -# Start of main script -############################################################################################## - -clock_job = None -poll_job = None -in_event = False -width = 0 -height = 0 -screen = curses.initscr() -(height, width) = screen.getmaxyx() -screen.clear() -screen.refresh() -event_pending = False -in_event = False -event_name = "" -event_duration = "" -update_time = "" -pct_done = 0 -t1 = 0 -t1_diff = "" -t2 = 0 -t2_diff = "" -pattern = re.compile("^(.*) Tier 1: (\d+)pts(([+-]\d+)).*Tier 2: (\d+)pts(([+-]\d+)).*Time: (.*)(([\d\.%]+))") - -# disable logging -logging.basicConfig() - -# setup signal handler -signal.signal(signal.SIGINT, cleanup) - -# Start the scheduler -sched = BlockingScheduler() -sched.daemonic = False - -# add some jobs -clock_job = sched.add_job(clock, 'interval', seconds=1) -# poll_job = sched.add_job(poll, 'interval', seconds=1) # use for testing only! REALLY resource expensive -poll_job = sched.add_job(poll, 'interval', minutes=1) # use for testing only! -# poll_job = sched.add_job(poll, 'cron', minute=40) # use for production - -# start the scheduler -sched.start() - -# never exits (quit the script using control-c) \ No newline at end of file From 53137ab8a2d5dcd6fd597c664b74b559c39ddd68 Mon Sep 17 00:00:00 2001 From: Donald Burr Date: Sat, 31 Oct 2015 23:25:04 -0700 Subject: [PATCH 13/17] Don't put parenthesis if time remaining < 24 hrs --- web_app/js/sif_tools.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/web_app/js/sif_tools.js b/web_app/js/sif_tools.js index 504c825..88d9961 100644 --- a/web_app/js/sif_tools.js +++ b/web_app/js/sif_tools.js @@ -27,7 +27,6 @@ // * Card Level Calc: // - maybe also the super (1.5) and ultra (2.0) success bonuses too? // * Event Tracker: -// - if time remaining < 24 don't put parenthesis around the hours/minutes/seconds display // - @sifen_trackbot update code gets "stuck" sometimes - not sure if the problem // is in the twitter fetcher or elsewhere @@ -1049,14 +1048,16 @@ function run_timer() { if (total_hours >= 24) { time_till += sprintf("%d hours
", total_hours); } - time_till += "("; + if (total_hours >= 24) { + time_till += "("; + } if (days >= 1) { time_till += sprintf("%d day%s ", days, days > 1 ? "s" : ""); } if (hours >= 1) { time_till += sprintf("%d hour%s ", hours, hours > 1 ? "s" : ""); } - time_till += sprintf("%d minute%s %d second%s)", minutes, minutes > 1 ? "s" : "", seconds, seconds > 1 ? "s" : ""); + time_till += sprintf("%d minute%s %d second%s%s", minutes, minutes > 1 ? "s" : "", seconds, (seconds > 1 ? "s" : ""), (total_hours >= 24) ? ")" : ""); // d.asDays() + " days " + d.asHours() + moment.utc(ms).format(":mm:ss"); // var time_till = moment.utc(end.diff(now)).format("HH:mm:ss");//.format("HH:mm:ss"); string += "

TIME REMAINING:

" + time_till + "

"; From b5bfa2dc5a32ef2d876533e8775946789e8dfc89 Mon Sep 17 00:00:00 2001 From: Donald Burr Date: Tue, 8 Dec 2015 21:35:23 -0800 Subject: [PATCH 14/17] Add MedFes calculations for # of songs needed to play --- web_app/js/sif_tools.js | 17 +++++++++++++++++ web_app/sif_tools.html | 10 +++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/web_app/js/sif_tools.js b/web_app/js/sif_tools.js index 88d9961..5b22f41 100644 --- a/web_app/js/sif_tools.js +++ b/web_app/js/sif_tools.js @@ -362,6 +362,15 @@ function calculate_rank() { var normal_count = Math.round(required_exp / 26); var hard_count = Math.round(required_exp / 46); var expert_count = Math.round(required_exp / 83); + var m3_easy_count = Math.round(required_exp / (12*3)); + var m3_normal_count = Math.round(required_exp / (26*3)); + var m3_hard_count = Math.round(required_exp / (46*3)); + var m3_expert_count = Math.round(required_exp / (83*3)); + // Increases the amount of EXP gained by 10%. + var m3b_easy_count = Math.round(required_exp / (12*1.1*3)); + var m3b_normal_count = Math.round(required_exp / (26*1.1*3)); + var m3b_hard_count = Math.round(required_exp / (46*1.1*3)); + var m3b_expert_count = Math.round(required_exp / (83*1.1*3)); // calc LP and FP var LP = 25 + Math.floor(Math.min(desired_rank, 300) / 2) + Math.floor(Math.max(desired_rank - 300, 0) / 3); // calc friend slots @@ -372,6 +381,14 @@ function calculate_rank() { $("#rank-result-songs-normal").text(normal_count); $("#rank-result-songs-hard").text(hard_count); $("#rank-result-songs-expert").text(expert_count); + $("#rank-result-songs-easy-mf").text(m3_easy_count); + $("#rank-result-songs-normal-mf").text(m3_normal_count); + $("#rank-result-songs-hard-mf").text(m3_hard_count); + $("#rank-result-songs-expert-mf").text(m3_expert_count); + $("#rank-result-songs-easy-mfb").text(m3b_easy_count); + $("#rank-result-songs-normal-mfb").text(m3b_normal_count); + $("#rank-result-songs-hard-mfb").text(m3b_hard_count); + $("#rank-result-songs-expert-mfb").text(m3b_expert_count); // rank-results-lp">- LP and ResultsEXP required:   -

You will need to play the following number of songs in order to get this amount of EXP:
-
- EASY:   -
- NORMAL:   -
- HARD:   -
- EXPERT:   -
+
(Single / 3xMedFes / 3xMedFes w/EXP boost)

+ EASY:   - / - / -
+ NORMAL:   - / - / -
+ HARD:   - / - / -
+ EXPERT:   - / - / -

At this rank you will have - LP and - friend slots.

From 503736105216a98b9ab1824a4fdfa4ce626a5313 Mon Sep 17 00:00:00 2001 From: Donald Burr Date: Fri, 15 Jan 2016 13:09:29 -0800 Subject: [PATCH 15/17] Fix "same attribute" calculation in card level calc The actual amount of EXP required does not change, it's just that if fed same attribute cards, they give 120 EXP rather than 100 EXP. --- web_app/js/sif_tools.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/web_app/js/sif_tools.js b/web_app/js/sif_tools.js index 5b22f41..bc89759 100644 --- a/web_app/js/sif_tools.js +++ b/web_app/js/sif_tools.js @@ -970,15 +970,17 @@ function calculate_card() { for (level = current_level + 1; level <= target_level; level++) { required_exp += get_exp_table_entry(rarity, level); } - if (same_attribute) { - // feeding cards of same attribute get a 1.2x bonus, so reduce the needed exp by that - required_exp = Math.floor(required_exp / 1.2); - } // subtract what we have required_exp -= current_exp; var resultString = sprintf("To get a %s card from level %d (with %d EXP) to %d requires %d EXP.
", rarity, current_level, current_exp, target_level, required_exp); // calculate equiv N cards - var number_of_n_cards = Math.round(required_exp / 100) + 1; + // FINDME + if (same_attribute) { + n_card_factor = 120; + } else { + n_card_factor = 100; + } + var number_of_n_cards = Math.round(required_exp / n_card_factor) + 1; resultString += sprintf("(the equivalent of about %d level-1 N cards fed to it)", number_of_n_cards); // output the result $("#card-result-summary").html(resultString); @@ -989,12 +991,8 @@ function calculate_card() { return; } var real_exp_to_feed = exp_to_feed; - if (same_attribute) { - // feeding cards of same attribute get a 1.2x bonus, so account for that - real_exp_to_feed = Math.round(real_exp_to_feed * 1.2); - } // ready to rock - var resultsString = "FOO"; + var resultsString = ""; // XXX do some calculating var exp_tally = current_exp; var level = 0; From f7b0cf4739bbc9d772f57b6c25aa3704ff1f9e4b Mon Sep 17 00:00:00 2001 From: Donald Burr Date: Sat, 5 Mar 2016 21:18:47 -0800 Subject: [PATCH 16/17] Add gem calc --- command_line/rankcalc.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/command_line/rankcalc.py b/command_line/rankcalc.py index e31b367..f8c0b63 100755 --- a/command_line/rankcalc.py +++ b/command_line/rankcalc.py @@ -30,25 +30,29 @@ import getopt from math import floor # algorithm from: http://decaf.kouhi.me/lovelive/index.php?title=Gameplay#Ranks -def calc(game_version, starting_rank, starting_exp, desired_rank): +def calc(game_version, starting_rank, starting_exp, desired_rank, half_exp): required_exp = 0 for rank in range(starting_rank, desired_rank): required_exp_for_next_rank = round(34.45 * rank - 551) # account for half EXP on JP (only if rank < 100) - if game_version == "JP" and rank < 100: + if half_exp and rank < 100: required_exp_for_next_rank = required_exp_for_next_rank // 2 #print "AT RANK %d NEED %d EXP" % (rank, required_exp_for_next_rank) required_exp = required_exp + required_exp_for_next_rank # account for exp we already have required_exp = required_exp - starting_exp - print "To get from rank %d (with %d EXP) to rank %d on %s requires %d EXP." % (starting_rank, starting_exp, desired_rank, game_version, required_exp) - print "Equivalent to playing the following number of songs of difficulty level:" - # round up because you can't play half of a song (although you can play a song half-assedly :P and I often do :P) easy_count = (required_exp // 12) + 1 normal_count = (required_exp // 26) + 1 hard_count = (required_exp // 46) + 1 ex_count = (required_exp // 83) + 1 - print "EASY (%d) NORMAL (%d) HARD (%d) EXPERT (%d)" % (easy_count, normal_count, hard_count, ex_count) + easy_3x_count = (required_exp // (12*3)) + 1 + normal_3x_count = (required_exp // (26*3)) + 1 + hard_3x_count = (required_exp // (46*3)) + 1 + ex_3x_count = (required_exp // (83*3)) + 1 + easy_3xb_count = (required_exp // (12*3)) + 1 + normal_3xb_count = (required_exp // (26*3)) + 1 + hard_3xb_count = (required_exp // (46*3)) + 1 + ex_3xb_count = (required_exp // (83*3)) + 1 # calc LP LP = 25 + floor(min(desired_rank, 300) / 2) + floor(max(desired_rank - 300, 0) / 3) # calc friend slots From 7326c989963c059b4ca9faa49715e70cd727d121 Mon Sep 17 00:00:00 2001 From: Donald Burr Date: Sat, 5 Mar 2016 21:35:23 -0800 Subject: [PATCH 17/17] Revert "Add gem calc" This reverts commit f7b0cf4739bbc9d772f57b6c25aa3704ff1f9e4b. --- command_line/rankcalc.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/command_line/rankcalc.py b/command_line/rankcalc.py index f8c0b63..e31b367 100755 --- a/command_line/rankcalc.py +++ b/command_line/rankcalc.py @@ -30,29 +30,25 @@ import getopt from math import floor # algorithm from: http://decaf.kouhi.me/lovelive/index.php?title=Gameplay#Ranks -def calc(game_version, starting_rank, starting_exp, desired_rank, half_exp): +def calc(game_version, starting_rank, starting_exp, desired_rank): required_exp = 0 for rank in range(starting_rank, desired_rank): required_exp_for_next_rank = round(34.45 * rank - 551) # account for half EXP on JP (only if rank < 100) - if half_exp and rank < 100: + if game_version == "JP" and rank < 100: required_exp_for_next_rank = required_exp_for_next_rank // 2 #print "AT RANK %d NEED %d EXP" % (rank, required_exp_for_next_rank) required_exp = required_exp + required_exp_for_next_rank # account for exp we already have required_exp = required_exp - starting_exp + print "To get from rank %d (with %d EXP) to rank %d on %s requires %d EXP." % (starting_rank, starting_exp, desired_rank, game_version, required_exp) + print "Equivalent to playing the following number of songs of difficulty level:" + # round up because you can't play half of a song (although you can play a song half-assedly :P and I often do :P) easy_count = (required_exp // 12) + 1 normal_count = (required_exp // 26) + 1 hard_count = (required_exp // 46) + 1 ex_count = (required_exp // 83) + 1 - easy_3x_count = (required_exp // (12*3)) + 1 - normal_3x_count = (required_exp // (26*3)) + 1 - hard_3x_count = (required_exp // (46*3)) + 1 - ex_3x_count = (required_exp // (83*3)) + 1 - easy_3xb_count = (required_exp // (12*3)) + 1 - normal_3xb_count = (required_exp // (26*3)) + 1 - hard_3xb_count = (required_exp // (46*3)) + 1 - ex_3xb_count = (required_exp // (83*3)) + 1 + print "EASY (%d) NORMAL (%d) HARD (%d) EXPERT (%d)" % (easy_count, normal_count, hard_count, ex_count) # calc LP LP = 25 + floor(min(desired_rank, 300) / 2) + floor(max(desired_rank - 300, 0) / 3) # calc friend slots