Toggle menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

MediaWiki:Gadget-calculator-crystals-core.js: Difference between revisions

MediaWiki interface page
Content added Content deleted
(added calculate button and song ranks)
(added event shop calc)
 
(45 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: [
startDateWidget,
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 eventTierWidget = new OO.ui.FieldLayout(
var eventTierOptions = [ 'T1-10', 'T20-100', 'T200-500', 'T1000',
new OO.ui.ButtonSelectWidget( {
'T2000-5000', 'T10000-50000', 'T100000', 'T200000', 'T300000',
items: [],
'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
} );
} ),
} ),
} );
{
label: 'Average tier',
align: 'inline'
}
);
var eventFieldset = new OO.ui.FieldsetLayout( {
var eventFieldset = new OO.ui.FieldsetLayout( {
label: 'Events',
label: 'Events',
items: [
items: [
eventTierWidget
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 rank',
align: 'inline'
}
);
var songRankWidget = new OO.ui.ButtonSelectWidget( {
var songComboOptions = [ 'Full combo (combo 4)', 'Combo 3', 'Combo 2',
items: songRankOptions.map( function( option, index ) {
'Combo 1' ];
var songComboHardWidget = new OO.ui.FieldLayout(
return new OO.ui.ButtonOptionWidget( {
data: option,
new OO.ui.ButtonSelectWidget( {
items: [],
label: option,
selected: index === 0
} ),
{
} );
} )
label: 'Average song combo (on hard)',
} );
align: 'inline'
}
);
var songComboOptions = [
var songComboExpertWidget = new OO.ui.FieldLayout(
'Combo 4 (full combo)',
new OO.ui.ButtonSelectWidget( {
items: [],
'Combo 3',
} ),
'Combo 2',
'Combo 1',
{
'None',
label: 'Average song combo (on expert)',
];
align: 'inline'

}
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 songComboMasterWidget = new OO.ui.FieldLayout(
var songComboExpertWidget = new OO.ui.ButtonSelectWidget( {
items: songComboOptions.map( function( option, index ) {
new OO.ui.ButtonSelectWidget( {
return new OO.ui.ButtonOptionWidget( {
items: [],
data: songComboOptions.length - 1 - index,
} ),
label: option,
{
selected: index === 0
label: 'Average song combo (on master)',
align: 'inline'
} );
}
} )
);
} );
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( {
var songFieldset = new OO.ui.FieldsetLayout( {
label: 'Songs',
label: 'Songs',
items: [
items: [
songRankWidget,
new OO.ui.FieldLayout(
songComboHardWidget,
songCoverWidget,
songComboExpertWidget,
{
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 clStampMenuItems = [];
options: [
var clStampOptions = [ 'Crystals', 'Song cards', 'Miracle gems',
{ label: 'Yes', data: 'true' },
'Magic cloth', 'Magic thread', 'Mysterious seeds',
{ label: 'No', data: 'false' }
'Intermediate practice scores', 'Coins' ];
],
for (var i = 0; i < clStampOptions.length; i++) {
value: 'true'
clStampMenuItems.push(new OO.ui.MenuOptionWidget( {
} );
data: clStampOptions[i],
label: clStampOptions[i]
} ));
}
var clStampWidget = new OO.ui.DropdownWidget( {
menu: {
items: clStampMenuItems
}
} );
var clFieldset = new OO.ui.FieldsetLayout( {
var clFieldset = new OO.ui.FieldsetLayout( {
label: 'Challenge Stamp reward',
label: 'Challenge Live',
items: [
items: [
clStampWidget
new OO.ui.FieldLayout(
clStampWidget,
{
label: 'Crystals selected as Challenge Stamp reward?',
align: 'inline'
}
)
]
]
} );
} );
/***************************************************************************
/*
* Everything
* Everything
*/
*/
var submitButton = new OO.ui.ButtonInputWidget( {
var submitButton = new OO.ui.ButtonInputWidget( {
label: 'Calculate',
label: 'Calculate',
Line 149: Line 361:
icon: 'mathematics'
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( {
var fieldset = new OO.ui.FieldsetLayout( {
Line 156: Line 537:
songFieldset,
songFieldset,
clFieldset,
clFieldset,
new OO.ui.FieldLayout(
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 );
Cookies help us deliver our services. By using our services, you agree to our use of cookies.