More actions
Content deleted Content added
added intermediate values |
added event shop calc |
||
(40 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
( function ( $, mw ) { |
( function ( $, mw ) { |
||
var calculationValues = { |
|||
/* |
|||
days: undefined, |
|||
* Date range |
|||
events: undefined, |
|||
songs: undefined |
|||
}; |
|||
/*************************************************************************** |
|||
* Calculation helpers |
|||
*/ |
*/ |
||
function estimateNumberOfEvents( days ) { |
|||
var startDateWidget = new OO.ui.FieldLayout( |
|||
// the median event length is 8 |
|||
new mw.widgets.DateInputWidget( { |
|||
// the average event length is 8.16 |
|||
type: 'date' |
|||
return Math.ceil( days / 8 ); |
|||
} ), |
|||
} |
|||
{ |
|||
label: 'Start date', |
|||
function estimateNumberOfCcs( startDay, endDay ) { |
|||
align: 'inline', |
|||
return 0; |
|||
help: 'test' |
|||
} |
|||
function estimateNumberOfEventSongs( numOfEvents ) { |
|||
// mixed events are roughtly 1/3 of all events |
|||
return Math.ceil( 2 * numOfEvents / 3); |
|||
} |
|||
function calculateLoginCrystals( numOfLogins ) { |
|||
var numOfDaySeven = Math.floor( numOfLogins / 7 ); |
|||
return ( numOfLogins - numOfDaySeven ) * 30 + numOfDaySeven * 100; |
|||
} |
|||
function calculateEventTierCrystals( numOfEvents, avgTier ) { |
|||
// this is VERY brittle because it's index based |
|||
var tierCrystals = [ 3000, 2500, 2000, 1500, 1000, 500, 250, 200, 150, |
|||
100, 50, 0 ]; |
|||
return numOfEvents * tierCrystals[avgTier]; |
|||
} |
|||
function calculateEventShopCrystals( |
|||
numOfMarathonEvents, |
|||
numOfCcEvents |
|||
) { |
|||
return numOfMarathonEvents * 500 + numOfCcEvents * 300; |
|||
} |
|||
function calculateCcCrystals( numOfCcEvents ) { |
|||
return numOfCcEvents * 150; |
|||
} |
|||
function calculateEventStoryCrystals( numOfEvents ) { |
|||
// 8 event stories, 50 crystals each |
|||
return numOfEvents * 8 * 50; |
|||
} |
|||
function calculateAfterLiveCrystals( numOfEvents ) { |
|||
return numOfEvents * 300; |
|||
} |
|||
function calculateAreaConvoCrystals( numOfEvents ) { |
|||
return numOfEvents * 0 * 10; |
|||
} |
|||
function calculateSongCrystals( |
|||
numOfSongs, |
|||
avgRank, |
|||
avgHardCombo, |
|||
avgExpertCombo, |
|||
avgMasterCombo |
|||
) { |
|||
var rankCrystals = { |
|||
'S': 50 + 30 + 20 + 10, |
|||
'A': 30 + 20 + 10, |
|||
'B': 20 + 10, |
|||
'C': 10 |
|||
}; |
|||
var crystalsFromRank = rankCrystals[avgRank]; |
|||
var comboCrystals = { |
|||
'hard': [ 0, 0, 0, 0, 50 ], |
|||
'expert': [ 0, 0, 0, 20, 50 + 20 ], |
|||
'master': [ 0, 0, 0, 20, 50 + 20 ] |
|||
}; |
|||
var crystalsFromHardCombo = comboCrystals.hard[avgHardCombo]; |
|||
var crystalsFromExpertCombo = comboCrystals.expert[avgExpertCombo]; |
|||
var crystalsFromMasterCombo = comboCrystals.master[avgMasterCombo]; |
|||
return numOfSongs * ( |
|||
crystalsFromRank + |
|||
crystalsFromHardCombo + |
|||
crystalsFromExpertCombo + |
|||
crystalsFromMasterCombo |
|||
); |
|||
} |
|||
function calculateClCrystals( numberOfPlays, crystalsSelected ) { |
|||
var numOfDaySeven = Math.floor( numberOfPlays / 7 ); |
|||
var partialCrystals = ( numberOfPlays - numOfDaySeven ) * 20; |
|||
if ( !crystalsSelected ) { |
|||
return partialCrystals; |
|||
} |
} |
||
); |
|||
return partialCrystals + numOfDaySeven * 100; |
|||
} |
|||
/*************************************************************************** |
|||
var endDateWidget = new OO.ui.FieldLayout( |
|||
* General helpers |
|||
new mw.widgets.DateInputWidget( { |
|||
*/ |
|||
type: 'date' |
|||
function getDateRelativeToTodayAsString( dayOffset ) { |
|||
} ), |
|||
var now = new Date(); |
|||
{ |
|||
now.setDate( now.getDate() + dayOffset ); |
|||
label: 'End date', |
|||
return now.toLocaleString('ja', { |
|||
align: 'inline' |
|||
year: 'numeric', |
|||
month: '2-digit', |
|||
day: '2-digit' |
|||
}); |
|||
} |
|||
function getDateFromString( dateString ) { |
|||
var date = new Date( dateString ).getTime(); |
|||
if ( isNaN( date ) ) { |
|||
return undefined; |
|||
} |
} |
||
return date; |
|||
); |
|||
} |
|||
function getDayDiffBetweenDates( start, end ) { |
|||
return Math.ceil( ( end - start ) / 86400000 ); |
|||
} |
|||
/*************************************************************************** |
|||
* HTML helpers |
|||
*/ |
|||
function makeRow( key, value ) { |
|||
var keyTd = document.createElement( 'td' ); |
|||
keyTd.setAttribute( 'width', '200px' ); |
|||
keyTd.innerText = key; |
|||
var valTd = document.createElement( 'td' ); |
|||
valTd.setAttribute( 'width', '75px' ); |
|||
valTd.style['text-align'] = 'right'; |
|||
valTd.innerText = value; |
|||
var row = document.createElement( 'tr' ); |
|||
row.appendChild( keyTd ); |
|||
row.appendChild( valTd ); |
|||
return row; |
|||
} |
|||
/*************************************************************************** |
|||
* Date range |
|||
*/ |
|||
var startDateWidget = new mw.widgets.DateInputWidget( { |
|||
type: 'date', |
|||
value: getDateRelativeToTodayAsString( 0 ), |
|||
mustBeAfter: getDateRelativeToTodayAsString( -1 ), |
|||
inputFormat: 'YYYY/MM/DD', |
|||
displayFormat: 'YYYY/MM/DD' |
|||
} ); |
|||
var endDateWidget = new mw.widgets.DateInputWidget( { |
|||
type: 'date', |
|||
mustBeAfter: getDateRelativeToTodayAsString( 0 ), |
|||
inputFormat: 'YYYY/MM/DD', |
|||
displayFormat: 'YYYY/MM/DD' |
|||
} ); |
|||
function handleDateRangeChange() { |
|||
var startDate = getDateFromString(startDateWidget.getValue()); |
|||
var endDate = getDateFromString(endDateWidget.getValue()); |
|||
var dateDiff = endDate - startDate; |
|||
} |
|||
var dateFieldset = new OO.ui.FieldsetLayout( { |
var dateFieldset = new OO.ui.FieldsetLayout( { |
||
label: 'Date range', |
label: 'Date range', |
||
items: [ |
items: [ |
||
new OO.ui.FieldLayout( |
|||
startDateWidget, |
|||
endDateWidget |
|||
{ |
|||
label: 'Start date', |
|||
align: 'inline', |
|||
help: 'test' |
|||
} |
|||
), |
|||
new OO.ui.FieldLayout( |
|||
endDateWidget, |
|||
{ |
|||
label: 'End date', |
|||
align: 'inline' |
|||
} |
|||
) |
|||
] |
] |
||
} ); |
} ); |
||
/*************************************************************************** |
|||
/* |
|||
* Event |
* Event |
||
*/ |
*/ |
||
// this is VERY brittle because it's index based |
|||
var eventTierOptions = [ 'T1-10', 'T20-100', 'T200-500', 'T1000', |
|||
var eventTierOptions = [ 'T1-10', 'T20-100', 'T200-500', 'T1000', |
|||
'T2000-5000', 'T10000-50000', 'T100000', 'T200000', 'T300000', |
|||
'T2000-5000', 'T10000-50000', 'T100000', 'T200000', 'T300000', |
|||
'T500000', 'T1000000' ]; |
|||
'T500000', 'T1000000', 'None' ]; |
|||
var eventTierWidget = new OO.ui.FieldLayout( |
|||
var eventTierWidget = new OO.ui.ButtonSelectWidget( { |
|||
items: eventTierOptions.map( function( option, index ) { |
|||
return new OO.ui.ButtonOptionWidget( { |
|||
data: index, |
|||
label: option, |
|||
selected: index === 0 |
|||
} ); |
|||
} ) |
} ); |
||
} ), |
} ), |
||
} ); |
|||
{ |
|||
label: 'Average tier', |
|||
align: 'inline' |
|||
} |
|||
); |
|||
var eventFieldset = new OO.ui.FieldsetLayout( { |
var eventFieldset = new OO.ui.FieldsetLayout( { |
||
label: 'Events', |
label: 'Events', |
||
items: [ |
items: [ |
||
new OO.ui.FieldLayout( |
|||
eventTierWidget, |
|||
{ |
|||
label: 'Average tier', |
|||
align: 'inline' |
|||
} |
|||
) |
|||
] |
] |
||
} ); |
} ); |
||
/*************************************************************************** |
|||
/* |
|||
* Song |
* Song |
||
*/ |
*/ |
||
var songCoverWidget = new OO.ui.NumberInputWidget( { |
|||
min: 0, |
|||
value: 0 |
|||
} ); |
|||
var songRankOptions = [ 'S', 'A', 'B', 'C' ]; |
var songRankOptions = [ 'S', 'A', 'B', 'C' ]; |
||
var songRankWidget = new OO.ui.FieldLayout( |
|||
new OO.ui.ButtonSelectWidget( { |
|||
items: songRankOptions.map( function( rank ) { |
|||
return new OO.ui.ButtonOptionWidget( { |
|||
data: rank, |
|||
label: rank |
|||
} ); |
|||
} ), |
|||
} ), |
|||
{ |
|||
label: 'Average song score rank', |
|||
align: 'inline' |
|||
} |
|||
); |
|||
var songRankWidget = new OO.ui.ButtonSelectWidget( { |
|||
var songComboOptions = [ 'Combo 4 (full combo)', 'Combo 3', 'Combo 2', |
|||
items: songRankOptions.map( function( option, index ) { |
|||
'Combo 1' ]; |
|||
return new OO.ui.ButtonOptionWidget( { |
|||
data: option, |
|||
new OO.ui.ButtonSelectWidget( { |
|||
label: option, |
|||
items: songComboOptions.map( function( combo ) { |
|||
selected: index === 0 |
|||
return new OO.ui.ButtonOptionWidget( { |
|||
} ); |
|||
} ) |
|||
label: combo |
|||
} ); |
|||
} ) |
|||
} ), |
|||
{ |
|||
label: 'Average song combo milestone (on hard)', |
|||
align: 'inline' |
|||
} |
|||
); |
|||
var songComboOptions = [ |
|||
var songComboExpertWidget = new OO.ui.FieldLayout( |
|||
'Combo 4 (full combo)', |
|||
new OO.ui.ButtonSelectWidget( { |
|||
'Combo 3', |
|||
items: songComboOptions.map( function( combo ) { |
|||
'Combo 2', |
|||
return new OO.ui.ButtonOptionWidget( { |
|||
'Combo 1', |
|||
'None', |
|||
label: combo |
|||
]; |
|||
} ); |
|||
} ) |
|||
var songComboHardWidget = new OO.ui.ButtonSelectWidget( { |
|||
} ), |
|||
items: songComboOptions.map( function( option, index ) { |
|||
{ |
|||
return new OO.ui.ButtonOptionWidget( { |
|||
label: 'Average song combo milestone (on expert)', |
|||
data: songComboOptions.length - 1 - index, |
|||
align: 'inline' |
|||
label: option, |
|||
} |
|||
selected: index === 0 |
|||
); |
|||
} ); |
|||
} ) |
|||
} ); |
|||
var |
var songComboExpertWidget = new OO.ui.ButtonSelectWidget( { |
||
items: songComboOptions.map( function( option, index ) { |
|||
new OO.ui.ButtonSelectWidget( { |
|||
return new OO.ui.ButtonOptionWidget( { |
|||
items: songComboOptions.map( function( combo ) { |
|||
data: songComboOptions.length - 1 - index, |
|||
return new OO.ui.ButtonOptionWidget( { |
|||
label: option, |
|||
selected: index === 0 |
|||
} ); |
|||
} ) |
|||
} ); |
|||
{ |
|||
var songComboMasterWidget = new OO.ui.ButtonSelectWidget( { |
|||
label: 'Average song combo milestone (on master)', |
|||
items: songComboOptions.map( function( option, index ) { |
|||
align: 'inline' |
|||
return new OO.ui.ButtonOptionWidget( { |
|||
} |
|||
data: songComboOptions.length - 1 - index, |
|||
); |
|||
label: option, |
|||
selected: index === 0 |
|||
} ); |
|||
} ) |
|||
} ); |
|||
var songFieldset = new OO.ui.FieldsetLayout( { |
var songFieldset = new OO.ui.FieldsetLayout( { |
||
label: 'Songs', |
label: 'Songs', |
||
items: [ |
items: [ |
||
new OO.ui.FieldLayout( |
|||
songCoverWidget, |
|||
{ |
|||
label: 'New cover songs', |
|||
songComboMasterWidget |
|||
align: 'inline' |
|||
} |
|||
), |
|||
new OO.ui.FieldLayout( |
|||
songRankWidget, |
|||
{ |
|||
label: 'Average score rank', |
|||
align: 'inline' |
|||
} |
|||
), |
|||
new OO.ui.FieldLayout( |
|||
songComboHardWidget, |
|||
{ |
|||
label: 'Average combo milestone (on hard)', |
|||
align: 'inline' |
|||
} |
|||
), |
|||
new OO.ui.FieldLayout( |
|||
songComboExpertWidget, |
|||
{ |
|||
label: 'Average combo milestone (on expert)', |
|||
align: 'inline' |
|||
} |
|||
), |
|||
new OO.ui.FieldLayout( |
|||
songComboMasterWidget, |
|||
{ |
|||
label: 'Average combo milestone (on master)', |
|||
align: 'inline' |
|||
} |
|||
) |
|||
] |
] |
||
} ); |
} ); |
||
/*************************************************************************** |
|||
/* |
|||
* Challenge Stamp |
* Challenge Stamp |
||
*/ |
*/ |
||
var clStampWidget = new OO.ui.RadioSelectInputWidget( { |
var clStampWidget = new OO.ui.RadioSelectInputWidget( { |
||
options: [ |
options: [ |
||
{ |
{ label: 'Yes', data: 'true' }, |
||
{ |
{ label: 'No', data: 'false' } |
||
], |
], |
||
value: 'true' |
value: 'true' |
||
Line 148: | Line 340: | ||
var clFieldset = new OO.ui.FieldsetLayout( { |
var clFieldset = new OO.ui.FieldsetLayout( { |
||
label: ' |
label: 'Challenge Live', |
||
items: [ |
items: [ |
||
new OO.ui.FieldLayout( |
|||
clStampWidget, |
|||
{ |
|||
label: 'Crystals selected as Challenge Stamp reward?', |
|||
align: 'inline' |
|||
} |
|||
) |
|||
] |
] |
||
} ); |
} ); |
||
/*************************************************************************** |
|||
/* |
|||
* Everything |
|||
* Intermediate values |
|||
*/ |
*/ |
||
var |
var submitButton = new OO.ui.ButtonInputWidget( { |
||
label: 'Calculate', |
|||
flags: [ 'primary', 'progressive' ], |
|||
min: 0 |
|||
align: 'left', |
|||
} ); |
|||
icon: 'mathematics' |
|||
var ivSongs = new OO.ui.NumberInputWidget( { |
|||
label: 'Number of songs', |
|||
min: 0 |
|||
} ); |
|||
var intermediateFieldset = new OO.ui.FieldsetLayout( { |
|||
label: 'Intermediate values', |
|||
items: [ |
|||
ivEvents, |
|||
ivSongs |
|||
] |
|||
} ); |
} ); |
||
function validate() {} |
|||
/* |
|||
* Everything |
|||
function calculate() { |
|||
*/ |
|||
var startDate = new Date( startDateWidget.getValue() ); |
|||
var submitButton = new OO.ui.FieldLayout( |
|||
var endDate = new Date( endDateWidget.getValue() ); |
|||
new OO.ui.ButtonInputWidget( { |
|||
label: 'Calculate', |
|||
var avgEventTier = parseInt( |
|||
flags: [ 'primary', 'progressive' ], |
|||
eventTierWidget.findSelectedItems().getData() |
|||
align: 'left', |
|||
); |
|||
icon: 'mathematics' |
|||
var numOfSongCovers = parseInt( songCoverWidget.getValue() ); |
|||
); |
|||
var avgSongRank = songRankWidget |
|||
.findSelectedItems() |
|||
.getData(); |
|||
var avgComboHard = songComboHardWidget |
|||
.findSelectedItems() |
|||
.getData(); |
|||
var avgComboExpert = songComboExpertWidget |
|||
.findSelectedItems() |
|||
.getData(); |
|||
var avgComboMaster = songComboMasterWidget |
|||
.findSelectedItems() |
|||
.getData(); |
|||
var clStampCrystalsChosen = clStampWidget.getValue() === 'true'; |
|||
// Begin internal values |
|||
var internalValues = {}; |
|||
internalValues.diffBetweenDates = getDayDiffBetweenDates( |
|||
startDate, |
|||
endDate |
|||
); |
|||
internalValues.numOfEvents = estimateNumberOfEvents( |
|||
internalValues.diffBetweenDates |
|||
); |
|||
internalValues.numOfCcEvents = estimateNumberOfCcs( startDate, endDate ); |
|||
internalValues.numOfMarathonEvents = internalValues.numOfEvents - |
|||
internalValues.numOfCcEvents; |
|||
internalValues.numOfSongCovers = numOfSongCovers; |
|||
internalValues.numOfSongComms = estimateNumberOfEventSongs( |
|||
internalValues.numOfEvents |
|||
); |
|||
internalValues.numOfSongs = internalValues.numOfSongCovers + |
|||
internalValues.numOfSongComms; |
|||
console.log(eventTierWidget.findSelectedItems().getData()); |
|||
console.log(songCoverWidget.getValue()); |
|||
console.log(songRankWidget.findSelectedItems().getData()); |
|||
console.log( |
|||
songComboHardWidget.findSelectedItems().getData(), |
|||
songComboExpertWidget.findSelectedItems().getData(), |
|||
songComboMasterWidget.findSelectedItems().getData() |
|||
); |
|||
console.log(clStampWidget.getValue()); |
|||
// Begin calculations |
|||
var crystals = {}; |
|||
crystals.logins = calculateLoginCrystals( |
|||
internalValues.diffBetweenDates |
|||
); |
|||
crystals.eventTiers = calculateEventTierCrystals( |
|||
internalValues.numOfEvents, |
|||
avgEventTier |
|||
); |
|||
crystals.eventShops = calculateEventShopCrystals( |
|||
internalValues.numOfMarathonEvents, |
|||
internalValues.numOfCcEvents |
|||
); |
|||
crystals.eventStories = calculateEventStoryCrystals( |
|||
internalValues.numOfEvents |
|||
); |
|||
crystals.vlives = calculateAfterLiveCrystals( |
|||
internalValues.numOfEvents |
|||
); |
|||
crystals.cc = calculateCcCrystals( internalValues.numOfCcEvents ); |
|||
crystals.areaConvo = calculateAreaConvoCrystals( |
|||
internalValues.numOfEvents |
|||
); |
|||
crystals.songs = calculateSongCrystals( |
|||
internalValues.numOfSongs, |
|||
avgSongRank, |
|||
avgComboHard, |
|||
avgComboExpert, |
|||
avgComboMaster |
|||
); |
|||
crystals.cl = calculateClCrystals( |
|||
internalValues.diffBetweenDates, |
|||
clStampCrystalsChosen |
|||
); |
|||
var fragment = document.createDocumentFragment(); |
|||
var crystalTable = makeCrystalTable( crystals ); |
|||
fragment.appendChild( crystalTable ); |
|||
var ivTable = makeInternalValuesTable( internalValues ); |
|||
fragment.appendChild( ivTable ); |
|||
$( '#result' ).html( fragment ); |
|||
} |
|||
function makeInternalValuesTable( values ) { |
|||
var fragment = document.createDocumentFragment(); |
|||
var caption = document.createElement( 'caption' ); |
|||
caption.innerText = 'Internal values'; |
|||
fragment.appendChild( caption ); |
|||
var rows = [ |
|||
{ name: 'Difference in dates', value: values.diffBetweenDates }, |
|||
{ name: 'Events (total, estimated)', value: values.numOfEvents }, |
|||
{ name: 'Events (Marathon)', value: values.numOfMarathonEvents }, |
|||
{ name: 'Events (Cheerful Carnival)', value: values.numOfCcEvents }, |
|||
{ name: 'Songs (total)', value: values.numOfSongs }, |
|||
{ name: 'Songs (commission, estimated)', value: values.numOfSongComms }, |
|||
{ name: 'Songs (cover)', value: values.numOfSongCovers }, |
|||
]; |
|||
rows.forEach( function ( row ) { |
|||
fragment.appendChild( makeRow( row.name, row.value ) ); |
|||
} ); |
|||
var table = document.createElement( 'table' ); |
|||
table.setAttribute( 'class', 'wikitable' ); |
|||
table.appendChild( fragment ); |
|||
return table; |
|||
} |
|||
function makeCrystalTable( crystals ) { |
|||
var totalCrystals = 0; |
|||
var fragment = document.createDocumentFragment(); |
|||
var caption = document.createElement( 'caption' ); |
|||
caption.innerText = 'Crystal income'; |
|||
fragment.appendChild( caption ); |
|||
var rows = [ |
|||
{ name: 'Daily logins', crystals: crystals.logins }, |
|||
{ name: 'Event tiers', crystals: crystals.eventTiers }, |
|||
{ name: 'Event Shops', crystals: crystals.eventShops }, |
|||
{ name: 'Event stories', crystals: crystals.eventStories }, |
|||
{ name: 'After lives', crystals: crystals.vlives }, |
|||
{ name: 'Cheerful Carnivals', crystals: crystals.cc }, |
|||
{ name: 'Song clears', crystals: crystals.songs }, |
|||
{ name: 'Challenge Live', crystals: crystals.cl } |
|||
]; |
|||
rows.forEach( function ( row ) { |
|||
fragment.appendChild( makeRow( row.name, row.crystals ) ); |
|||
totalCrystals += row.crystals; |
|||
} ); |
|||
var totalRow = makeRow ('Total crystals', totalCrystals ); |
|||
totalRow.style['border-top-width'] = '2px'; |
|||
totalRow.style['border-top-style'] = 'solid'; |
|||
totalRow.style['font-weight'] = 'bold'; |
|||
fragment.appendChild( totalRow ); |
|||
var table = document.createElement( 'table' ); |
|||
table.setAttribute( 'class', 'wikitable' ); |
|||
table.appendChild( fragment ); |
|||
return table; |
|||
} |
|||
submitButton.on( 'click', calculate ); |
|||
var fieldset = new OO.ui.FieldsetLayout( { |
var fieldset = new OO.ui.FieldsetLayout( { |
||
Line 191: | Line 537: | ||
songFieldset, |
songFieldset, |
||
clFieldset, |
clFieldset, |
||
new OO.ui.FieldLayout( |
|||
intermediateFieldset, |
|||
submitButton |
submitButton |
||
) |
|||
] |
] |
||
} ); |
} ); |
Latest revision as of 05:40, 25 August 2022
( function ( $, mw ) {
var calculationValues = {
days: undefined,
events: undefined,
songs: undefined
};
/***************************************************************************
* Calculation helpers
*/
function estimateNumberOfEvents( days ) {
// the median event length is 8
// the average event length is 8.16
return Math.ceil( days / 8 );
}
function estimateNumberOfCcs( startDay, endDay ) {
return 0;
}
function estimateNumberOfEventSongs( numOfEvents ) {
// mixed events are roughtly 1/3 of all events
return Math.ceil( 2 * numOfEvents / 3);
}
function calculateLoginCrystals( numOfLogins ) {
var numOfDaySeven = Math.floor( numOfLogins / 7 );
return ( numOfLogins - numOfDaySeven ) * 30 + numOfDaySeven * 100;
}
function calculateEventTierCrystals( numOfEvents, avgTier ) {
// this is VERY brittle because it's index based
var tierCrystals = [ 3000, 2500, 2000, 1500, 1000, 500, 250, 200, 150,
100, 50, 0 ];
return numOfEvents * tierCrystals[avgTier];
}
function calculateEventShopCrystals(
numOfMarathonEvents,
numOfCcEvents
) {
return numOfMarathonEvents * 500 + numOfCcEvents * 300;
}
function calculateCcCrystals( numOfCcEvents ) {
return numOfCcEvents * 150;
}
function calculateEventStoryCrystals( numOfEvents ) {
// 8 event stories, 50 crystals each
return numOfEvents * 8 * 50;
}
function calculateAfterLiveCrystals( numOfEvents ) {
return numOfEvents * 300;
}
function calculateAreaConvoCrystals( numOfEvents ) {
return numOfEvents * 0 * 10;
}
function calculateSongCrystals(
numOfSongs,
avgRank,
avgHardCombo,
avgExpertCombo,
avgMasterCombo
) {
var rankCrystals = {
'S': 50 + 30 + 20 + 10,
'A': 30 + 20 + 10,
'B': 20 + 10,
'C': 10
};
var crystalsFromRank = rankCrystals[avgRank];
var comboCrystals = {
'hard': [ 0, 0, 0, 0, 50 ],
'expert': [ 0, 0, 0, 20, 50 + 20 ],
'master': [ 0, 0, 0, 20, 50 + 20 ]
};
var crystalsFromHardCombo = comboCrystals.hard[avgHardCombo];
var crystalsFromExpertCombo = comboCrystals.expert[avgExpertCombo];
var crystalsFromMasterCombo = comboCrystals.master[avgMasterCombo];
return numOfSongs * (
crystalsFromRank +
crystalsFromHardCombo +
crystalsFromExpertCombo +
crystalsFromMasterCombo
);
}
function calculateClCrystals( numberOfPlays, crystalsSelected ) {
var numOfDaySeven = Math.floor( numberOfPlays / 7 );
var partialCrystals = ( numberOfPlays - numOfDaySeven ) * 20;
if ( !crystalsSelected ) {
return partialCrystals;
}
return partialCrystals + numOfDaySeven * 100;
}
/***************************************************************************
* General helpers
*/
function getDateRelativeToTodayAsString( dayOffset ) {
var now = new Date();
now.setDate( now.getDate() + dayOffset );
return now.toLocaleString('ja', {
year: 'numeric',
month: '2-digit',
day: '2-digit'
});
}
function getDateFromString( dateString ) {
var date = new Date( dateString ).getTime();
if ( isNaN( date ) ) {
return undefined;
}
return date;
}
function getDayDiffBetweenDates( start, end ) {
return Math.ceil( ( end - start ) / 86400000 );
}
/***************************************************************************
* HTML helpers
*/
function makeRow( key, value ) {
var keyTd = document.createElement( 'td' );
keyTd.setAttribute( 'width', '200px' );
keyTd.innerText = key;
var valTd = document.createElement( 'td' );
valTd.setAttribute( 'width', '75px' );
valTd.style['text-align'] = 'right';
valTd.innerText = value;
var row = document.createElement( 'tr' );
row.appendChild( keyTd );
row.appendChild( valTd );
return row;
}
/***************************************************************************
* Date range
*/
var startDateWidget = new mw.widgets.DateInputWidget( {
type: 'date',
value: getDateRelativeToTodayAsString( 0 ),
mustBeAfter: getDateRelativeToTodayAsString( -1 ),
inputFormat: 'YYYY/MM/DD',
displayFormat: 'YYYY/MM/DD'
} );
var endDateWidget = new mw.widgets.DateInputWidget( {
type: 'date',
mustBeAfter: getDateRelativeToTodayAsString( 0 ),
inputFormat: 'YYYY/MM/DD',
displayFormat: 'YYYY/MM/DD'
} );
function handleDateRangeChange() {
var startDate = getDateFromString(startDateWidget.getValue());
var endDate = getDateFromString(endDateWidget.getValue());
var dateDiff = endDate - startDate;
}
var dateFieldset = new OO.ui.FieldsetLayout( {
label: 'Date range',
items: [
new OO.ui.FieldLayout(
startDateWidget,
{
label: 'Start date',
align: 'inline',
help: 'test'
}
),
new OO.ui.FieldLayout(
endDateWidget,
{
label: 'End date',
align: 'inline'
}
)
]
} );
/***************************************************************************
* Event
*/
// this is VERY brittle because it's index based
var eventTierOptions = [ 'T1-10', 'T20-100', 'T200-500', 'T1000',
'T2000-5000', 'T10000-50000', 'T100000', 'T200000', 'T300000',
'T500000', 'T1000000', 'None' ];
var eventTierWidget = new OO.ui.ButtonSelectWidget( {
items: eventTierOptions.map( function( option, index ) {
return new OO.ui.ButtonOptionWidget( {
data: index,
label: option,
selected: index === 0
} );
} ),
} );
var eventFieldset = new OO.ui.FieldsetLayout( {
label: 'Events',
items: [
new OO.ui.FieldLayout(
eventTierWidget,
{
label: 'Average tier',
align: 'inline'
}
)
]
} );
/***************************************************************************
* Song
*/
var songCoverWidget = new OO.ui.NumberInputWidget( {
min: 0,
value: 0
} );
var songRankOptions = [ 'S', 'A', 'B', 'C' ];
var songRankWidget = new OO.ui.ButtonSelectWidget( {
items: songRankOptions.map( function( option, index ) {
return new OO.ui.ButtonOptionWidget( {
data: option,
label: option,
selected: index === 0
} );
} )
} );
var songComboOptions = [
'Combo 4 (full combo)',
'Combo 3',
'Combo 2',
'Combo 1',
'None',
];
var songComboHardWidget = new OO.ui.ButtonSelectWidget( {
items: songComboOptions.map( function( option, index ) {
return new OO.ui.ButtonOptionWidget( {
data: songComboOptions.length - 1 - index,
label: option,
selected: index === 0
} );
} )
} );
var songComboExpertWidget = new OO.ui.ButtonSelectWidget( {
items: songComboOptions.map( function( option, index ) {
return new OO.ui.ButtonOptionWidget( {
data: songComboOptions.length - 1 - index,
label: option,
selected: index === 0
} );
} )
} );
var songComboMasterWidget = new OO.ui.ButtonSelectWidget( {
items: songComboOptions.map( function( option, index ) {
return new OO.ui.ButtonOptionWidget( {
data: songComboOptions.length - 1 - index,
label: option,
selected: index === 0
} );
} )
} );
var songFieldset = new OO.ui.FieldsetLayout( {
label: 'Songs',
items: [
new OO.ui.FieldLayout(
songCoverWidget,
{
label: 'New cover songs',
align: 'inline'
}
),
new OO.ui.FieldLayout(
songRankWidget,
{
label: 'Average score rank',
align: 'inline'
}
),
new OO.ui.FieldLayout(
songComboHardWidget,
{
label: 'Average combo milestone (on hard)',
align: 'inline'
}
),
new OO.ui.FieldLayout(
songComboExpertWidget,
{
label: 'Average combo milestone (on expert)',
align: 'inline'
}
),
new OO.ui.FieldLayout(
songComboMasterWidget,
{
label: 'Average combo milestone (on master)',
align: 'inline'
}
)
]
} );
/***************************************************************************
* Challenge Stamp
*/
var clStampWidget = new OO.ui.RadioSelectInputWidget( {
options: [
{ label: 'Yes', data: 'true' },
{ label: 'No', data: 'false' }
],
value: 'true'
} );
var clFieldset = new OO.ui.FieldsetLayout( {
label: 'Challenge Live',
items: [
new OO.ui.FieldLayout(
clStampWidget,
{
label: 'Crystals selected as Challenge Stamp reward?',
align: 'inline'
}
)
]
} );
/***************************************************************************
* Everything
*/
var submitButton = new OO.ui.ButtonInputWidget( {
label: 'Calculate',
flags: [ 'primary', 'progressive' ],
align: 'left',
icon: 'mathematics'
} );
function validate() {}
function calculate() {
var startDate = new Date( startDateWidget.getValue() );
var endDate = new Date( endDateWidget.getValue() );
var avgEventTier = parseInt(
eventTierWidget.findSelectedItems().getData()
);
var numOfSongCovers = parseInt( songCoverWidget.getValue() );
var avgSongRank = songRankWidget
.findSelectedItems()
.getData();
var avgComboHard = songComboHardWidget
.findSelectedItems()
.getData();
var avgComboExpert = songComboExpertWidget
.findSelectedItems()
.getData();
var avgComboMaster = songComboMasterWidget
.findSelectedItems()
.getData();
var clStampCrystalsChosen = clStampWidget.getValue() === 'true';
// Begin internal values
var internalValues = {};
internalValues.diffBetweenDates = getDayDiffBetweenDates(
startDate,
endDate
);
internalValues.numOfEvents = estimateNumberOfEvents(
internalValues.diffBetweenDates
);
internalValues.numOfCcEvents = estimateNumberOfCcs( startDate, endDate );
internalValues.numOfMarathonEvents = internalValues.numOfEvents -
internalValues.numOfCcEvents;
internalValues.numOfSongCovers = numOfSongCovers;
internalValues.numOfSongComms = estimateNumberOfEventSongs(
internalValues.numOfEvents
);
internalValues.numOfSongs = internalValues.numOfSongCovers +
internalValues.numOfSongComms;
console.log(eventTierWidget.findSelectedItems().getData());
console.log(songCoverWidget.getValue());
console.log(songRankWidget.findSelectedItems().getData());
console.log(
songComboHardWidget.findSelectedItems().getData(),
songComboExpertWidget.findSelectedItems().getData(),
songComboMasterWidget.findSelectedItems().getData()
);
console.log(clStampWidget.getValue());
// Begin calculations
var crystals = {};
crystals.logins = calculateLoginCrystals(
internalValues.diffBetweenDates
);
crystals.eventTiers = calculateEventTierCrystals(
internalValues.numOfEvents,
avgEventTier
);
crystals.eventShops = calculateEventShopCrystals(
internalValues.numOfMarathonEvents,
internalValues.numOfCcEvents
);
crystals.eventStories = calculateEventStoryCrystals(
internalValues.numOfEvents
);
crystals.vlives = calculateAfterLiveCrystals(
internalValues.numOfEvents
);
crystals.cc = calculateCcCrystals( internalValues.numOfCcEvents );
crystals.areaConvo = calculateAreaConvoCrystals(
internalValues.numOfEvents
);
crystals.songs = calculateSongCrystals(
internalValues.numOfSongs,
avgSongRank,
avgComboHard,
avgComboExpert,
avgComboMaster
);
crystals.cl = calculateClCrystals(
internalValues.diffBetweenDates,
clStampCrystalsChosen
);
var fragment = document.createDocumentFragment();
var crystalTable = makeCrystalTable( crystals );
fragment.appendChild( crystalTable );
var ivTable = makeInternalValuesTable( internalValues );
fragment.appendChild( ivTable );
$( '#result' ).html( fragment );
}
function makeInternalValuesTable( values ) {
var fragment = document.createDocumentFragment();
var caption = document.createElement( 'caption' );
caption.innerText = 'Internal values';
fragment.appendChild( caption );
var rows = [
{ name: 'Difference in dates', value: values.diffBetweenDates },
{ name: 'Events (total, estimated)', value: values.numOfEvents },
{ name: 'Events (Marathon)', value: values.numOfMarathonEvents },
{ name: 'Events (Cheerful Carnival)', value: values.numOfCcEvents },
{ name: 'Songs (total)', value: values.numOfSongs },
{ name: 'Songs (commission, estimated)', value: values.numOfSongComms },
{ name: 'Songs (cover)', value: values.numOfSongCovers },
];
rows.forEach( function ( row ) {
fragment.appendChild( makeRow( row.name, row.value ) );
} );
var table = document.createElement( 'table' );
table.setAttribute( 'class', 'wikitable' );
table.appendChild( fragment );
return table;
}
function makeCrystalTable( crystals ) {
var totalCrystals = 0;
var fragment = document.createDocumentFragment();
var caption = document.createElement( 'caption' );
caption.innerText = 'Crystal income';
fragment.appendChild( caption );
var rows = [
{ name: 'Daily logins', crystals: crystals.logins },
{ name: 'Event tiers', crystals: crystals.eventTiers },
{ name: 'Event Shops', crystals: crystals.eventShops },
{ name: 'Event stories', crystals: crystals.eventStories },
{ name: 'After lives', crystals: crystals.vlives },
{ name: 'Cheerful Carnivals', crystals: crystals.cc },
{ name: 'Song clears', crystals: crystals.songs },
{ name: 'Challenge Live', crystals: crystals.cl }
];
rows.forEach( function ( row ) {
fragment.appendChild( makeRow( row.name, row.crystals ) );
totalCrystals += row.crystals;
} );
var totalRow = makeRow ('Total crystals', totalCrystals );
totalRow.style['border-top-width'] = '2px';
totalRow.style['border-top-style'] = 'solid';
totalRow.style['font-weight'] = 'bold';
fragment.appendChild( totalRow );
var table = document.createElement( 'table' );
table.setAttribute( 'class', 'wikitable' );
table.appendChild( fragment );
return table;
}
submitButton.on( 'click', calculate );
var fieldset = new OO.ui.FieldsetLayout( {
items: [
dateFieldset,
eventFieldset,
songFieldset,
clFieldset,
new OO.ui.FieldLayout(
submitButton
)
]
} );
$( '#calculator' ).append( fieldset.$element );
} )( jQuery, mediaWiki );