MediaWiki:Gadget-calc-cooking.js: Difference between revisions

From Idle Clans wiki
No edit summary
No edit summary
 
(9 intermediate revisions by 2 users not shown)
Line 1: Line 1:
$(function () {
$(function () {
var idleClans = window.idleClans;
// Web assembly code to calculate the experience.
var WASM_CODE = "AGFzbQEAAAABIwZgAn9/AX9gBH9/f38AYAF/AX9gAXwBfGADfHx/AXxgAX8AAwsKAAEBAwQAAgACBQQFAXABCQkF" +
"AwEAEQYJAX8BQYCAwAALBxkCBm1lbW9yeQIADGNhbGN1bGF0ZV94cAAECQ4BAEEBCwgJAQUGCQIHCArWCwqnBAEHfyABKAIAIgMEQCAAQQJ" +
"0IQcDQCADKAIIIgJBAXEEfwNAIAMgAkF+cTYCCCADKAIEIgZBfHEiBQR/QQAgBSAFLQAAQQFxGwVBAAshBAJAIAMoAgAiAkECcQ0AIAJBfH" +
"EiCEUNACAIIAgoAgRBA3EgBXI2AgQgAygCBCIGQXxxIQUgAygCACECCyAFBEAgBSAFKAIAQQNxIAJBfHFyNgIAIAMoAgQhBiADKAIAIQILI" +
"AMgBkEDcTYCBCADIAJBA3E2AgAgAkECcQRAIAQgBCgCAEECcjYCAAsgASAENgIAIAQiAygCCCICQQFxDQALIARBCGoFIANBCGoLIQQCQCAD" +
"KAIAQXxxIgUgBGsgB0kNAAJ/IAUgB2tBfHEiAiAEQZiAwAAgAEGogMAAKAIAEQAAQQJ0akEIakkEQCAEKAIAIQIgBEEDcQ0CIAEgAkF8cTY" +
"CACADIgIoAgAMAQtBACEAIAJBADYCACACQQhrIgJCADcCACACIAMoAgBBfHE2AgACQCADKAIAIgFBAnENACABQXxxIgFFDQAgASABKAIEQQ" +
"NxIAJyNgIEIAIoAgRBA3EhAAsgAiAAIANyNgIEIAQgBCgCAEF+cTYCACADIAMoAgAiAEEDcSACciIBNgIAIABBAnFFBEAgAigCAAwBCyADI" +
"AFBfXE2AgAgAigCAEECcgshACACIABBAXI2AgAgAkEIag8LIAEgAjYCACACIgMNAAsLQQAL3AEBA38jAEEQayIDJAAgAyABKAIAIgYoAgA2" +
"AgwjAEEQayIBJAACQEGAECACQQJqIgIgAmwiAiACQYAQTRsiBSADQQxqIgIQACIEDQAgAUEIakGYgMAAIAVBBEGkgMAAKAIAEQEAQQAhBCA" +
"BKAIIDQAgASgCDCIEIAIoAgA2AgggAiAENgIAIAUgAhAAIQQLIAFBEGokACAEIQEgBiADKAIMNgIAIAEEfyABQgA3AgQgASABIAVBAnRqQQ" +
"JyNgIAQQAFQQELIQIgACABNgIEIAAgAjYCACADQRBqJAALagACfyACQQJ0IgEgA0EDdEGAgAFqIgIgASACSxtBh4AEaiIBQRB2QAAiAkF/R" +
"gRAQQAhAkEBDAELIAJBEHQiAkIANwIEIAIgAiABQYCAfHFqQQJyNgIAQQALIQMgACACNgIEIAAgAzYCAAshACAAvUKAgICAgICAgIB/g0L/" +
"////////7z+EvyAAoJ0LpgQCAn0BfyACQQBHIAC2IQMgAbYiBEMAAAAAXgRAIARDAADIQpVDAACAP5IgA5S7RAAAAAAAACRAoiIAIAAQAyI" +
"AoZlEAAAAAAAA4D9hBHwgAEQAAAAAAADgw2YhBSAAIABEAAAAAAAA8L+gQv///////////wACfiAAmUQAAAAAAADgQ2MEQCAAsAwBC0KAgI" +
"CAgICAgIB/C0KAgICAgICAgIB/IAUbIABE////////30NkG0IAIAAgAGEbQgGDUBsFIAALRAAAAAAAACRAo7YhAwsEfSADQ2Zmpj+Uu0QAA" +
"AAAAAAkQKIiACAAEAMiAKGZRAAAAAAAAOA/YQR8IABEAAAAAAAA4MNmIQIgACAARAAAAAAAAPC/oEL///////////8AAn4gAJlEAAAAAAAA" +
"4ENjBEAgALAMAQtCgICAgICAgICAfwtCgICAgICAgICAfyACGyAARP///////99DZBtCACAAIABhG0IBg1AbBSAAC0QAAAAAAAAkQKO2BSA" +
"DC7tEAAAAAAAAJECiIgAgABADIgChmUQAAAAAAADgP2EEfCAARAAAAAAAAODDZiECIAAgAEQAAAAAAADwv6BC////////////AAJ+IACZRA" +
"AAAAAAAOBDYwRAIACwDAELQoCAgICAgICAgH8LQoCAgICAgICAgH8gAhsgAET////////fQ2QbQgAgACAAYRtCAYNQGwUgAAtEAAAAAAAAJ" +
"ECjtrsLBAAgAQsEAEEACwUAQYAECwQAQQELAwABCws2AQBBgIDAAAstAQAAAAQAAAAEAAAAAgAAAAMAAAAEAAAABQAAAAAAAAABAAAABgAA" +
"AAcAAAAIAG8JcHJvZHVjZXJzAghsYW5ndWFnZQEEUnVzdAAMcHJvY2Vzc2VkLWJ5AwVydXN0Yx0xLjc4LjAgKDliMDA5NTZlNSAyMDI0LTA" +
"0LTI5KQZ3YWxydXMGMC4yMC4zDHdhc20tYmluZGdlbgYwLjIuOTIALA90YXJnZXRfZmVhdHVyZXMCKw9tdXRhYmxlLWdsb2JhbHMrCHNpZ2" +
"4tZXh0";
var WASM = null;
var CLAN_HOUSES = ["Tent", "Barn", "Windmill", "House", "Manor", "Castle"];
var CLAN_HOUSES = ["Tent", "Barn", "Windmill", "House", "Manor", "Castle"];
var PERSONAL_HOUSES = ["Cardboard box", "Tent", "Van Down by the River", "Small cabin", "House"];
var PERSONAL_HOUSES = ["Cardboard box", "Tent", "Van Down by the River", "Small cabin", "House"];
Line 7: Line 35:
var utils = idleClans.util;
var utils = idleClans.util;
var data = idleClans.data;
var data = idleClans.data;
function initializeWebAssembly() {
if (WASM != null) {
utils.debug("WebAssembly already initialized.");
return Promise.resolve(true);
}
return new Promise(function (resolve, reject) {
var buffer = Uint8Array.from(atob(WASM_CODE), function (c) { return c.charCodeAt(0); });
WebAssembly.instantiate(buffer).then(function (result) {
WASM = result.instance.exports;
resolve(true);
}).catch(function (error) {
console.error("Failed to initialize WebAssembly:", error);
reject(false);
});
});
}


function addDropdown($element, $dropdown, title, alignment) {
function addDropdown($element, $dropdown, title, alignment) {
Line 62: Line 108:
return task.name;
return task.name;
});
});
// Add "Salect task" as the first option.
// Add "Select task" as the first option.
taskOptions.splice(15, 0, {label: "==== Dishes ====", header: true});
taskOptions.splice(15, 0, {label: "==== Dishes ====", header: true});
taskOptions.splice(11, 0, {label: "==== Meat ====", header: true});
taskOptions.splice(11, 0, {label: "==== Meat ====", header: true});
taskOptions.splice(0, 0, {label: "==== Fish ====", header: true});
taskOptions.splice(0, 0, {label: "==== Fish ====", header: true});
var $taskDropdown = utils.ui.dropdown(taskOptions, undefined, "Salect task");
var $taskDropdown = utils.ui.dropdown(taskOptions, undefined, "Select task");
// Button
// Button
var $calculateButton = utils.ui.buttonInput("Calculate");
var $calculateButton = utils.ui.buttonInput("Calculate");
Line 76: Line 122:
$calculateButton.setDisabled(false);
$calculateButton.setDisabled(false);
});
});
// Add the elements to the calculator.
$element.append("<div class='calc-title'>Cooking Calculator</div>");
var $settings = $("<div class='calc-category'></div>");
// - Xp
addHeader($settings, "Upgrades & Boosts");
addDropdown($settings, $clanHouseDropdown, "Clan House");
addDropdown($settings, $personalHouseDropdown, "Personal House");
addDropdown($settings, $dailyAdDropdown, "Daily Ad");
// - Speed
addHeader($settings, "Equipment");
addDropdown($settings, $skillItemDropdown, "Skilling Item");
addDropdown($settings, $skillCapeDropdown, "Skill Cape");
// - Enchantments
addHeader($settings, "Enchantments");
var $enchantments = $("<div class='calc-enchantments'></div>");
var $enchantmentsContainerL = $("<div class='calc-enchantments-container'></div>");
var $enchantmentsContainerR = $("<div class='calc-enchantments-container'></div>");
$enchantmentsContainerL.append("<div class='calc-label-center'>Amulet</div>", $amuletDropdown.$element);
$enchantmentsContainerL.append("<div class='calc-label-center'>Ring</div>", $ringDropdown.$element);
$enchantmentsContainerR.append("<div class='calc-label-center'>Earring</div>", $earringDropdown.$element);
$enchantmentsContainerR.append("<div class='calc-label-center'>Bracelet</div>", $braceletDropdown.$element);
$enchantments.append($enchantmentsContainerL);
$enchantments.append($enchantmentsContainerR);
$settings.append($enchantments);
addDropdown($settings, $poakDropdown, "Potion of Ancient Knowledge", "center")
$element.append($settings);
// Task
var $task = $("<div class='calc-category calc-plain-category' style='margin-top: 0.5rem;margin-bottom: 0.5rem'></div>");
addDropdown($task, $taskDropdown, "Task");
var $buttonDiv = $("<div class='calc-button'></div>");
$buttonDiv.append($calculateButton.$element);
$task.append($buttonDiv);
$element.append($task);
// Result
var $results = $("<div class='calc-result'></div>");
var $xpPerTaskResult = $("<span>???</span>");
var $timePerTaskResult = $("<span>???</span>");
var $xpPerHourResult = $("<span>???</span>");
var $tasksPerHourResult = $("<span>???</span>");
addHeader($results, "Result");
addResultSpacer($results);
addResultEntry($results, "XP/task", $xpPerTaskResult);
addResultEntry($results, "Time/task", $timePerTaskResult);
addResultSpacer($results);
addResultEntry($results, "XP/hour", $xpPerHourResult);
addResultEntry($results, "Tasks/hour", $tasksPerHourResult);
addResultSpacer($results);
$element.append($results);
// Handle button
$calculateButton.on("click", function () {
// Xp
var clanHouseData = getDropdownValue($clanHouseDropdown);
var personalHouseData = getDropdownValue($personalHouseDropdown);
var dailyAdData = getDropdownValue($dailyAdDropdown);
// Speed
var skillItemData = getDropdownValue($skillItemDropdown);
var skillCapeData = getDropdownValue($skillCapeDropdown);
// - Enchantments
var amuletData = getDropdownValue($amuletDropdown);
var earringData = getDropdownValue($earringDropdown);
var ringData = getDropdownValue($ringDropdown);
var braceletData = getDropdownValue($braceletDropdown);
var poakData = getDropdownValue($poakDropdown);
// Task
var task = data.tasks.getTask("COOKING", getDropdownValue($taskDropdown));
if (task === null) {
mw.error("Invalid task selected.");
return;
}
var maxDailyAdHours = data.boosts.other.DAILY_AD_HOURS * data.boosts.other.DAILY_AD_AMOUNT;
var dailyAdBoost = data.boosts.experience.DAILY_AD;
// Get the selected values.
// - Xp
var clanHouse = data.boosts.experience.CLAN_HOUSE[clanHouseData];
var personalHouse = data.boosts.experience.PERSONAL_HOUSE[personalHouseData];
var dailyAd = dailyAdData === 0 ? 0 : // None
dailyAdData === 1 ?
dailyAdData : // 30%
(dailyAdBoost * maxDailyAdHours) / 24; // 24/7
// - Speed
var skillItem = data.boosts.speed.SKILL_ITEM[skillItemData];
var skillCape = data.boosts.speed.SKILL_CAPE[skillCapeData];
var enchantment =
data.boosts.speed.getEnchantmentBoost(amuletData, poakData === 1) +
data.boosts.speed.getEnchantmentBoost(earringData, poakData === 1) +
data.boosts.speed.getEnchantmentBoost(ringData, poakData === 1) +
data.boosts.speed.getEnchantmentBoost(braceletData, poakData === 1);
// Do the calculation.
var baseXp = task.exp;
var baseSpeed = task.time;
var xpPerTask = WASM.calculate_xp(baseXp, (clanHouse * 100) + (personalHouse * 100), dailyAd * 100);
//var xpPerTask = utils.number.bankersRound(baseXp * (1 + clanHouse + personalHouse) * (1 + dailyAd), 1);
var timePerTask = baseSpeed * (1 - enchantment - skillItem - skillCape);
var xpPerHour = xpPerTask / timePerTask * 3600;
var tasksPerHour = 3600 / timePerTask;
// Update the results.
$xpPerTaskResult.text(utils.formatNumber(xpPerTask));
$timePerTaskResult.text(utils.number.toFixedSmall(timePerTask, 3) + "s");
$xpPerHourResult.text(utils.formatNumber(utils.number.toFixedSmall(xpPerHour, 1)));
$tasksPerHourResult.text(utils.number.toFixedSmall(tasksPerHour, 1));
});
}
// Initialize
function init() {
var $calculators = $(".calc\\:cooking");
if (!$calculators.length) return;
utils.debug("Found " + $calculators.length + " cooking calculators.");
initializeWebAssembly().then(function (result) {
if (!result) {
console.error("[CALCULATOR] Failed to initialize calculator, web assembly failed.")
return;
}
$calculators.each(function () { createCalculator($(this)); });
});
}
init();
});

Latest revision as of 11:06, 12 June 2024

$(function () {
	var idleClans = window.idleClans;
	// Web assembly code to calculate the experience.
	var WASM_CODE = "AGFzbQEAAAABIwZgAn9/AX9gBH9/f38AYAF/AX9gAXwBfGADfHx/AXxgAX8AAwsKAAEBAwQAAgACBQQFAXABCQkF" +
		"AwEAEQYJAX8BQYCAwAALBxkCBm1lbW9yeQIADGNhbGN1bGF0ZV94cAAECQ4BAEEBCwgJAQUGCQIHCArWCwqnBAEHfyABKAIAIgMEQCAAQQJ" +
		"0IQcDQCADKAIIIgJBAXEEfwNAIAMgAkF+cTYCCCADKAIEIgZBfHEiBQR/QQAgBSAFLQAAQQFxGwVBAAshBAJAIAMoAgAiAkECcQ0AIAJBfH" +
		"EiCEUNACAIIAgoAgRBA3EgBXI2AgQgAygCBCIGQXxxIQUgAygCACECCyAFBEAgBSAFKAIAQQNxIAJBfHFyNgIAIAMoAgQhBiADKAIAIQILI" +
		"AMgBkEDcTYCBCADIAJBA3E2AgAgAkECcQRAIAQgBCgCAEECcjYCAAsgASAENgIAIAQiAygCCCICQQFxDQALIARBCGoFIANBCGoLIQQCQCAD" +
		"KAIAQXxxIgUgBGsgB0kNAAJ/IAUgB2tBfHEiAiAEQZiAwAAgAEGogMAAKAIAEQAAQQJ0akEIakkEQCAEKAIAIQIgBEEDcQ0CIAEgAkF8cTY" +
		"CACADIgIoAgAMAQtBACEAIAJBADYCACACQQhrIgJCADcCACACIAMoAgBBfHE2AgACQCADKAIAIgFBAnENACABQXxxIgFFDQAgASABKAIEQQ" +
		"NxIAJyNgIEIAIoAgRBA3EhAAsgAiAAIANyNgIEIAQgBCgCAEF+cTYCACADIAMoAgAiAEEDcSACciIBNgIAIABBAnFFBEAgAigCAAwBCyADI" +
		"AFBfXE2AgAgAigCAEECcgshACACIABBAXI2AgAgAkEIag8LIAEgAjYCACACIgMNAAsLQQAL3AEBA38jAEEQayIDJAAgAyABKAIAIgYoAgA2" +
		"AgwjAEEQayIBJAACQEGAECACQQJqIgIgAmwiAiACQYAQTRsiBSADQQxqIgIQACIEDQAgAUEIakGYgMAAIAVBBEGkgMAAKAIAEQEAQQAhBCA" +
		"BKAIIDQAgASgCDCIEIAIoAgA2AgggAiAENgIAIAUgAhAAIQQLIAFBEGokACAEIQEgBiADKAIMNgIAIAEEfyABQgA3AgQgASABIAVBAnRqQQ" +
		"JyNgIAQQAFQQELIQIgACABNgIEIAAgAjYCACADQRBqJAALagACfyACQQJ0IgEgA0EDdEGAgAFqIgIgASACSxtBh4AEaiIBQRB2QAAiAkF/R" +
		"gRAQQAhAkEBDAELIAJBEHQiAkIANwIEIAIgAiABQYCAfHFqQQJyNgIAQQALIQMgACACNgIEIAAgAzYCAAshACAAvUKAgICAgICAgIB/g0L/" +
		"////////7z+EvyAAoJ0LpgQCAn0BfyACQQBHIAC2IQMgAbYiBEMAAAAAXgRAIARDAADIQpVDAACAP5IgA5S7RAAAAAAAACRAoiIAIAAQAyI" +
		"AoZlEAAAAAAAA4D9hBHwgAEQAAAAAAADgw2YhBSAAIABEAAAAAAAA8L+gQv///////////wACfiAAmUQAAAAAAADgQ2MEQCAAsAwBC0KAgI" +
		"CAgICAgIB/C0KAgICAgICAgIB/IAUbIABE////////30NkG0IAIAAgAGEbQgGDUBsFIAALRAAAAAAAACRAo7YhAwsEfSADQ2Zmpj+Uu0QAA" +
		"AAAAAAkQKIiACAAEAMiAKGZRAAAAAAAAOA/YQR8IABEAAAAAAAA4MNmIQIgACAARAAAAAAAAPC/oEL///////////8AAn4gAJlEAAAAAAAA" +
		"4ENjBEAgALAMAQtCgICAgICAgICAfwtCgICAgICAgICAfyACGyAARP///////99DZBtCACAAIABhG0IBg1AbBSAAC0QAAAAAAAAkQKO2BSA" +
		"DC7tEAAAAAAAAJECiIgAgABADIgChmUQAAAAAAADgP2EEfCAARAAAAAAAAODDZiECIAAgAEQAAAAAAADwv6BC////////////AAJ+IACZRA" +
		"AAAAAAAOBDYwRAIACwDAELQoCAgICAgICAgH8LQoCAgICAgICAgH8gAhsgAET////////fQ2QbQgAgACAAYRtCAYNQGwUgAAtEAAAAAAAAJ" +
		"ECjtrsLBAAgAQsEAEEACwUAQYAECwQAQQELAwABCws2AQBBgIDAAAstAQAAAAQAAAAEAAAAAgAAAAMAAAAEAAAABQAAAAAAAAABAAAABgAA" +
		"AAcAAAAIAG8JcHJvZHVjZXJzAghsYW5ndWFnZQEEUnVzdAAMcHJvY2Vzc2VkLWJ5AwVydXN0Yx0xLjc4LjAgKDliMDA5NTZlNSAyMDI0LTA" +
		"0LTI5KQZ3YWxydXMGMC4yMC4zDHdhc20tYmluZGdlbgYwLjIuOTIALA90YXJnZXRfZmVhdHVyZXMCKw9tdXRhYmxlLWdsb2JhbHMrCHNpZ2" +
		"4tZXh0";
	var WASM = null;

	var CLAN_HOUSES = ["Tent", "Barn", "Windmill", "House", "Manor", "Castle"];
	var PERSONAL_HOUSES = ["Cardboard box", "Tent", "Van Down by the River", "Small cabin", "House"];
	var SKILL_CAPE = ["None", "Tier 1", "Tier 2", "Tier 3", "Tier 4"];
	var ENCHANTMENTS = ["None", "Common", "Rare", "Exceptional"];

	var utils = idleClans.util;
	var data = idleClans.data;

	function initializeWebAssembly() {
		if (WASM != null) {
			utils.debug("WebAssembly already initialized.");
			return Promise.resolve(true);
		}

		return new Promise(function (resolve, reject) {
			var buffer = Uint8Array.from(atob(WASM_CODE), function (c) { return c.charCodeAt(0); });
			WebAssembly.instantiate(buffer).then(function (result) {
				WASM = result.instance.exports;
				resolve(true);
			}).catch(function (error) {
				console.error("Failed to initialize WebAssembly:", error);
				reject(false);
			});
		});
	}

	function addDropdown($element, $dropdown, title, alignment) {
		alignment = alignment || "left";

		$element.append("<div class='calc-label-" + alignment + "'>" + title + "</div>");
		$element.append($dropdown.$element);
	}

	function addHeader($element, title) {
		$element.append("<div class='calc-header'>" + title + "</div>");
	}

	function addResultEntry($element, label, $result) {
		var $entry = $("<div class='calc-result-entry'></div>");
		$entry.append($("<div class='label'><span>" + label + "</span></div>"), $result);
		var $value = $("<div class='value'></div>");
		$value.append($result);
		$entry.append($value);

		$element.append($entry);
	}

	function addResultSpacer($element) {
		$element.append("<div class='calc-result-spacer'></div>");
	}

	function getDropdownValue($dropdown) {
		return $dropdown.getMenu().findSelectedItem().getData();
	}

	function createCalculator($element) {
		$element.addClass("idle-calculator");

		// XP
		var $clanHouseDropdown = utils.ui.dropdown(utils.tierify(CLAN_HOUSES, "None"), 0);
		var $personalHouseDropdown = utils.ui.dropdown(utils.tierify(PERSONAL_HOUSES, "None"), 0);
		var $dailyAdDropdown = utils.ui.dropdown(["None", "30%", "24/7 (10%)"], 0);

		// Speed
		var skillingItemOptions = utils.tierify(
			[ function(i) {return utils.getRefinedName("cooking pan", i)}, 7 ],
			"None");
		var $skillItemDropdown = utils.ui.dropdown(skillingItemOptions, 0);
		var $skillCapeDropdown = utils.ui.dropdown(SKILL_CAPE, 0);
		// - Enchantments
		var $amuletDropdown = utils.ui.dropdown(ENCHANTMENTS, 0);
		var $earringDropdown = utils.ui.dropdown(ENCHANTMENTS, 0);
		var $ringDropdown = utils.ui.dropdown(ENCHANTMENTS, 0);
		var $braceletDropdown = utils.ui.dropdown(ENCHANTMENTS, 0);
		var $poakDropdown = utils.ui.dropdown(["No", "Yes"], 0);

		// Task
		var taskOptions = idleClans.data.tasks.COOKING.map(function (task) {
			return task.name;
		});
		// Add "Select task" as the first option.
		taskOptions.splice(15, 0, {label: "==== Dishes ====", header: true});
		taskOptions.splice(11, 0, {label: "==== Meat ====", header: true});
		taskOptions.splice(0, 0, {label: "==== Fish ====", header: true});
		var $taskDropdown = utils.ui.dropdown(taskOptions, undefined, "Select task");
		// Button
		var $calculateButton = utils.ui.buttonInput("Calculate");
		// - Disable the button if no task is selected.
		if ($taskDropdown.getMenu().findSelectedItem() === null)
			$calculateButton.setDisabled(true);
		// - Enable the button if a task is selected.
		$taskDropdown.on("labelChange", function () {
			$calculateButton.setDisabled(false);
		});

		// Add the elements to the calculator.

		$element.append("<div class='calc-title'>Cooking Calculator</div>");
		var $settings = $("<div class='calc-category'></div>");

		// - Xp

		addHeader($settings, "Upgrades & Boosts");
		addDropdown($settings, $clanHouseDropdown, "Clan House");
		addDropdown($settings, $personalHouseDropdown, "Personal House");
		addDropdown($settings, $dailyAdDropdown, "Daily Ad");

		// - Speed

		addHeader($settings, "Equipment");
		addDropdown($settings, $skillItemDropdown, "Skilling Item");
		addDropdown($settings, $skillCapeDropdown, "Skill Cape");

		// - Enchantments

		addHeader($settings, "Enchantments");
		var $enchantments = $("<div class='calc-enchantments'></div>");
		var $enchantmentsContainerL = $("<div class='calc-enchantments-container'></div>");
		var $enchantmentsContainerR = $("<div class='calc-enchantments-container'></div>");

		$enchantmentsContainerL.append("<div class='calc-label-center'>Amulet</div>", $amuletDropdown.$element);
		$enchantmentsContainerL.append("<div class='calc-label-center'>Ring</div>", $ringDropdown.$element);
		$enchantmentsContainerR.append("<div class='calc-label-center'>Earring</div>", $earringDropdown.$element);
		$enchantmentsContainerR.append("<div class='calc-label-center'>Bracelet</div>", $braceletDropdown.$element);
		$enchantments.append($enchantmentsContainerL);
		$enchantments.append($enchantmentsContainerR);
		$settings.append($enchantments);
		addDropdown($settings, $poakDropdown, "Potion of Ancient Knowledge", "center")

		$element.append($settings);

		// Task

		var $task = $("<div class='calc-category calc-plain-category' style='margin-top: 0.5rem;margin-bottom: 0.5rem'></div>");
		addDropdown($task, $taskDropdown, "Task");
		var $buttonDiv = $("<div class='calc-button'></div>");
		$buttonDiv.append($calculateButton.$element);
		$task.append($buttonDiv);
		$element.append($task);

		// Result

		var $results = $("<div class='calc-result'></div>");
		var $xpPerTaskResult = $("<span>???</span>");
		var $timePerTaskResult = $("<span>???</span>");
		var $xpPerHourResult = $("<span>???</span>");
		var $tasksPerHourResult = $("<span>???</span>");

		addHeader($results, "Result");
		addResultSpacer($results);
		addResultEntry($results, "XP/task", $xpPerTaskResult);
		addResultEntry($results, "Time/task", $timePerTaskResult);
		addResultSpacer($results);
		addResultEntry($results, "XP/hour", $xpPerHourResult);
		addResultEntry($results, "Tasks/hour", $tasksPerHourResult);
		addResultSpacer($results);
		$element.append($results);

		// Handle button
		$calculateButton.on("click", function () {
			// Xp
			var clanHouseData = getDropdownValue($clanHouseDropdown);
			var personalHouseData = getDropdownValue($personalHouseDropdown);
			var dailyAdData = getDropdownValue($dailyAdDropdown);
			// Speed
			var skillItemData = getDropdownValue($skillItemDropdown);
			var skillCapeData = getDropdownValue($skillCapeDropdown);
			// - Enchantments
			var amuletData = getDropdownValue($amuletDropdown);
			var earringData = getDropdownValue($earringDropdown);
			var ringData = getDropdownValue($ringDropdown);
			var braceletData = getDropdownValue($braceletDropdown);
			var poakData = getDropdownValue($poakDropdown);
			// Task
			var task = data.tasks.getTask("COOKING", getDropdownValue($taskDropdown));

			if (task === null) {
				mw.error("Invalid task selected.");
				return;
			}

			var maxDailyAdHours = data.boosts.other.DAILY_AD_HOURS * data.boosts.other.DAILY_AD_AMOUNT;
			var dailyAdBoost = data.boosts.experience.DAILY_AD;

			// Get the selected values.
			// - Xp
			var clanHouse = data.boosts.experience.CLAN_HOUSE[clanHouseData];
			var personalHouse = data.boosts.experience.PERSONAL_HOUSE[personalHouseData];
			var dailyAd = dailyAdData === 0 ? 0 : // None
				dailyAdData === 1 ?
					dailyAdData : // 30%
					(dailyAdBoost * maxDailyAdHours) / 24; // 24/7
			// - Speed
			var skillItem = data.boosts.speed.SKILL_ITEM[skillItemData];
			var skillCape = data.boosts.speed.SKILL_CAPE[skillCapeData];
			var enchantment =
				data.boosts.speed.getEnchantmentBoost(amuletData, poakData === 1) +
				data.boosts.speed.getEnchantmentBoost(earringData, poakData === 1) +
				data.boosts.speed.getEnchantmentBoost(ringData, poakData === 1) +
				data.boosts.speed.getEnchantmentBoost(braceletData, poakData === 1);

			// Do the calculation.

			var baseXp = task.exp;
			var baseSpeed = task.time;

			var xpPerTask = WASM.calculate_xp(baseXp, (clanHouse * 100) + (personalHouse * 100), dailyAd * 100);
			//var xpPerTask = utils.number.bankersRound(baseXp * (1 + clanHouse + personalHouse) * (1 + dailyAd), 1);
			var timePerTask = baseSpeed * (1 - enchantment - skillItem - skillCape);
			var xpPerHour = xpPerTask / timePerTask * 3600;
			var tasksPerHour = 3600 / timePerTask;

			// Update the results.
			$xpPerTaskResult.text(utils.formatNumber(xpPerTask));
			$timePerTaskResult.text(utils.number.toFixedSmall(timePerTask, 3) + "s");
			$xpPerHourResult.text(utils.formatNumber(utils.number.toFixedSmall(xpPerHour, 1)));
			$tasksPerHourResult.text(utils.number.toFixedSmall(tasksPerHour, 1));
		});
	}

	// Initialize
	function init() {
		var $calculators = $(".calc\\:cooking");
		if (!$calculators.length) return;
		utils.debug("Found " + $calculators.length + " cooking calculators.");

		initializeWebAssembly().then(function (result) {
			if (!result) {
				console.error("[CALCULATOR] Failed to initialize calculator, web assembly failed.")
				return;
			}

			$calculators.each(function () { createCalculator($(this)); });
		});
	}
	init();
});