Tip of the Day
Long pressing an item opens multi-selection options in the inventory.
MediaWiki:Gadget-DryCalc.js: Difference between revisions
From Walkscape Walkthrough
mNo edit summary |
mNo edit summary |
||
| Line 2: | Line 2: | ||
(function () { | (function () { | ||
'use strict'; | 'use strict'; | ||
// ----------------------------- | |||
// Utilities | |||
// ----------------------------- | |||
function clampNumber(n, min, max) { | function clampNumber(n, min, max) { | ||
| Line 19: | Line 23: | ||
} | } | ||
function | function formatInt(x) { | ||
if (!isFinite(x)) return '—'; | |||
return String(Math.round(x)); | |||
} | |||
function calcBinomialSingle(p, t) { | |||
// For independent trials with success prob p: | |||
// P(0) = (1-p)^t, P(>=1) = 1 - P(0), E[drops]=t*p | |||
var q = 1 - p; | var q = 1 - p; | ||
var p0 = Math.pow(q, t); | var p0 = Math.pow(q, t); | ||
| Line 28: | Line 39: | ||
function trialsForConfidence(p, c) { | function trialsForConfidence(p, c) { | ||
// Smallest integer t such that 1 - (1-p)^t >= c | |||
if (!(p > 0 && p < 1)) return NaN; | if (!(p > 0 && p < 1)) return NaN; | ||
if (!(c > 0 && c < 1)) return NaN; | if (!(c > 0 && c < 1)) return NaN; | ||
| Line 34: | Line 46: | ||
} | } | ||
function normalize(mode, wearInput, oneInX, percent, count) { | |||
// Returns: { p, t, unit, perDrop, modeLabel, warnings[] } | |||
var warnings = []; | var warnings = []; | ||
var t = clampNumber(count, 0, 1e18); | |||
if (!(t >= 0)) { | |||
return { | |||
p: NaN, t: NaN, unit: '—', perDrop: NaN, | |||
modeLabel: '—', | |||
warnings: ['Please enter a valid count.'] | |||
}; | |||
} | |||
if (mode === 'wear') { | if (mode === 'wear') { | ||
var wear = clampNumber(wearInput, 1, 1e18); | var wear = clampNumber(wearInput, 1, 1e18); | ||
if (!(wear >= 1)) return { | if (!(wear >= 1)) { | ||
return { | return { | ||
p: NaN, t: NaN, unit: 'steps', perDrop: NaN, | |||
modeLabel: 'WEAR (steps)', | |||
warnings: ['Please enter a valid WEAR value (steps per drop).'] | |||
}; | |||
} | |||
return { | |||
p: 1 / wear, | |||
t: t, | |||
unit: 'steps', | |||
perDrop: wear, | |||
modeLabel: 'WEAR (steps)', | |||
warnings: warnings | |||
}; | |||
} | } | ||
if (mode === 'actions') { | |||
var x = clampNumber(oneInX, 1, 1e18); | |||
if (!(x >= 1)) { | |||
return { | |||
return { | p: NaN, t: NaN, unit: 'actions', perDrop: NaN, | ||
modeLabel: 'Actions (1 in X)', | |||
warnings: ['Please enter a valid “1 in X” value.'] | |||
}; | |||
} | |||
return { | |||
p: 1 / x, | |||
t: t, | |||
unit: 'actions', | |||
perDrop: x, | |||
modeLabel: 'Actions (1 in X)', | |||
warnings: warnings | |||
}; | |||
} | } | ||
if (mode === 'percent') { | |||
var pct = clampNumber(percent, 0, 100); | var pct = clampNumber(percent, 0, 100); | ||
if (!(pct > 0 && pct <= 100)) return { | if (!(pct > 0 && pct <= 100)) { | ||
return { | |||
p: NaN, t: NaN, unit: 'actions', perDrop: NaN, | |||
return { | modeLabel: 'Percent (%)', | ||
warnings: ['Please enter a valid percent (0 < % ≤ 100).'] | |||
}; | |||
} | |||
var p = pct / 100; | |||
return { | |||
p: p, | |||
t: t, | |||
unit: 'actions', | |||
perDrop: 1 / p, | |||
modeLabel: 'Percent (%)', | |||
warnings: warnings | |||
}; | |||
} | } | ||
return { | |||
p: NaN, t: NaN, unit: '—', perDrop: NaN, | |||
modeLabel: '—', | |||
warnings: ['Unknown input mode.'] | |||
}; | |||
} | |||
// ----------------------------- | |||
// UI | |||
// ----------------------------- | |||
var CONF_LEVELS = [ | |||
{ c: 0.50, label: '50%' }, | |||
{ c: 0.90, label: '90%' }, | |||
{ c: 0.95, label: '95%' }, | |||
{ c: 0.99, label: '99%' } | |||
]; | |||
function buildUI($host) { | function buildUI($host) { | ||
var | // Optional prefills via data-* | ||
var | var defaultMode = String($host.data('mode') || 'wear').toLowerCase(); | ||
if (['wear', 'actions', 'percent'].indexOf(defaultMode) === -1) defaultMode = 'wear'; | |||
var preWear = $host.data('wear'); | |||
var preOneIn = $host.data('onein'); | |||
var prePercent = $host.data('percent'); | |||
var preCount = $host.data('count'); | |||
// | // Container | ||
var $ | var $card = $('<div>').addClass('ws-dropcalc-card'); | ||
var $ | var $title = $('<div>').addClass('ws-dropcalc-title').text('Drop Chance Calculator'); | ||
function | // Mode (radio) | ||
var $ | var groupName = 'ws-dropcalc-mode-' + Math.random().toString(36).slice(2); | ||
var $ | |||
function makeRadio(id, value, label) { | |||
var $wrap = $('<label>').addClass('ws-dropcalc-radio'); | |||
var $input = $('<input>').attr({ type: 'radio', name: groupName, id: id, value: value }); | |||
var $text = $('<span>').text(label); | |||
$wrap.append($input, $text); | |||
return $wrap; | |||
} | } | ||
var $ | var $modeRow = $('<div>').addClass('ws-dropcalc-row'); | ||
var $ | var $modeLabel = $('<div>').addClass('ws-dropcalc-label').text('Input mode'); | ||
$ | var $radios = $('<div>').addClass('ws-dropcalc-radio-wrap'); | ||
var $rWear = makeRadio(groupName + '-wear', 'wear', 'WEAR (steps per item)'); | |||
var $rAct = makeRadio(groupName + '-actions', 'actions', 'Actions (1 in X)'); | |||
var $rPct = makeRadio(groupName + '-percent', 'percent', 'Percent (%)'); | |||
$radios.append($rWear, $rAct, $rPct); | |||
$modeRow.append($modeLabel, $radios); | |||
// Inputs | // Inputs | ||
var $wearRow = $('<div>').addClass('ws- | var $wearRow = $('<div>').addClass('ws-dropcalc-row'); | ||
var $wearLabel = $('<label>').addClass('ws- | var $wearLabel = $('<label>').addClass('ws-dropcalc-label').text('WEAR (steps per drop)'); | ||
var $wear = $('<input>').addClass('ws- | var $wear = $('<input>').addClass('ws-dropcalc-input').attr({ type: 'number', min: '1', step: '1' }); | ||
$wearRow.append($wearLabel, $wear); | $wearRow.append($wearLabel, $wear); | ||
var $oneInRow = $('<div>').addClass('ws- | var $oneInRow = $('<div>').addClass('ws-dropcalc-row'); | ||
var $oneInLabel = $('<label>').addClass('ws- | var $oneInLabel = $('<label>').addClass('ws-dropcalc-label').text('Rate (1 in X)'); | ||
var $oneIn = $('<input>').addClass('ws- | var $oneIn = $('<input>').addClass('ws-dropcalc-input').attr({ type: 'number', min: '1', step: '1' }); | ||
$oneInRow.append($oneInLabel, $oneIn); | $oneInRow.append($oneInLabel, $oneIn); | ||
var $pctRow = $('<div>').addClass('ws- | var $pctRow = $('<div>').addClass('ws-dropcalc-row'); | ||
var $pctLabel = $('<label>').addClass('ws- | var $pctLabel = $('<label>').addClass('ws-dropcalc-label').text('Chance (%)'); | ||
var $pct = $('<input>').addClass('ws- | var $pct = $('<input>').addClass('ws-dropcalc-input').attr({ type: 'number', min: '0', max: '100', step: '0.001' }); | ||
$pctRow.append($pctLabel, $ | var $pctHint = $('<div>').addClass('ws-dropcalc-hint').text('Example: 0.102 means 0.102%'); | ||
var $pctWrap = $('<div>').addClass('ws-dropcalc-col').append($pct, $pctHint); | |||
$pctRow.append($pctLabel, $pctWrap); | |||
var $ | var $countRow = $('<div>').addClass('ws-dropcalc-row'); | ||
var $ | var $countLabel = $('<label>').addClass('ws-dropcalc-label'); // text set dynamically | ||
var $ | var $count = $('<input>').addClass('ws-dropcalc-input').attr({ type: 'number', min: '0', step: '1' }); | ||
$ | $countRow.append($countLabel, $count); | ||
// Output | |||
var $ | var $out = $('<div>').addClass('ws-dropcalc-out'); | ||
// Prefill values (reasonable defaults if none supplied) | |||
$wear.val(isFinite(preWear) ? preWear : 62745); | |||
$oneIn.val(isFinite(preOneIn) ? preOneIn : 980); | |||
$pct.val(isFinite(prePercent) ? prePercent : 0.102); | |||
$count.val(isFinite(preCount) ? preCount : 0); | |||
// Select default mode | |||
$card.find('input[name="' + groupName + '"][value="' + defaultMode + '"]').prop('checked', true); | |||
function getMode() { | function getMode() { | ||
return $ | return $card.find('input[name="' + groupName + '"]:checked').val() || 'wear'; | ||
} | } | ||
| Line 131: | Line 214: | ||
$oneInRow.toggle(mode === 'actions'); | $oneInRow.toggle(mode === 'actions'); | ||
$pctRow.toggle(mode === 'percent'); | $pctRow.toggle(mode === 'percent'); | ||
} | } | ||
| Line 139: | Line 220: | ||
setVisibility(mode); | setVisibility(mode); | ||
var | var countVal = parseFloat($count.val()); | ||
var unit = (mode === 'wear') ? 'steps' : 'actions'; | |||
$countLabel.text('Completed (' + unit + ')'); | |||
var norm = | var norm = normalize( | ||
mode, | mode, | ||
parseFloat($wear.val()), | parseFloat($wear.val()), | ||
parseFloat($oneIn.val()), | parseFloat($oneIn.val()), | ||
parseFloat($pct.val()), | parseFloat($pct.val()), | ||
countVal | |||
); | ); | ||
$out.empty(); | $out.empty(); | ||
// Warnings | |||
if (norm.warnings && norm.warnings.length) { | if (norm.warnings && norm.warnings.length) { | ||
norm.warnings.forEach(function (w) { | norm.warnings.forEach(function (w) { | ||
$out.append($('<div>').addClass('ws- | $out.append($('<div>').addClass('ws-dropcalc-warn').text(w)); | ||
}); | }); | ||
} | } | ||
if (!( | // If invalid, do not compute further | ||
if (!(norm.p > 0 && norm.p < 1) || !(norm.t >= 0)) return; | |||
var r = calcBinomialSingle(norm.p, norm.t); | |||
var | var expectedPerDropLabel = (mode === 'wear') | ||
? 'WEAR (steps per drop)' | |||
: 'Expected actions per drop'; | |||
var | var expectedPerDropValue = (mode === 'percent') | ||
? formatNumber(norm.perDrop, 2) | |||
: formatInt(norm.perDrop); | |||
var multiple = norm.t / norm.perDrop; | |||
var | |||
$out.append( | $out.append( | ||
$('<div>').addClass('ws- | $('<div>').addClass('ws-dropcalc-metric') | ||
.append($('<div>').addClass('ws- | .append($('<div>').addClass('ws-dropcalc-k').text(expectedPerDropLabel)) | ||
.append($('<div>').addClass('ws- | .append($('<div>').addClass('ws-dropcalc-v').text(expectedPerDropValue + ' ' + unit)), | ||
$('<div>').addClass('ws- | $('<div>').addClass('ws-dropcalc-metric') | ||
.append($('<div>').addClass('ws- | .append($('<div>').addClass('ws-dropcalc-k').text('You are at')) | ||
.append($('<div>').addClass('ws- | .append($('<div>').addClass('ws-dropcalc-v').text(formatNumber(multiple, 2) + '× expected')), | ||
$('<div>').addClass('ws-dropcalc-metric') | |||
.append($('<div>').addClass('ws- | .append($('<div>').addClass('ws-dropcalc-k').text('Expected drops so far')) | ||
.append($('<div>').addClass('ws- | .append($('<div>').addClass('ws-dropcalc-v').text(formatNumber(r.mu, 2))), | ||
$('<div>').addClass('ws- | $('<div>').addClass('ws-dropcalc-metric') | ||
.append($('<div>').addClass('ws- | .append($('<div>').addClass('ws-dropcalc-k').text('Chance of at least 1 drop by now')) | ||
.append($('<div>').addClass('ws- | .append($('<div>').addClass('ws-dropcalc-v').text(formatPercent(r.pge1))), | ||
$('<div>').addClass('ws- | $('<div>').addClass('ws-dropcalc-metric') | ||
.append($('<div>').addClass('ws- | .append($('<div>').addClass('ws-dropcalc-k').text('Chance you would be dry (0 drops)')) | ||
.append($('<div>').addClass('ws- | .append($('<div>').addClass('ws-dropcalc-v').text(formatPercent(r.p0))), | ||
$('<div>').addClass('ws-dropcalc-subtitle').text('Milestones (chance of ≥1 drop)') | |||
); | |||
var $milestones = $('<div>').addClass('ws-dropcalc-milestones'); | |||
CONF_LEVELS.forEach(function (it) { | |||
var needed = trialsForConfidence(norm.p, it.c); | |||
$milestones.append( | |||
$('<div>').addClass('ws-dropcalc-chip') | |||
.text(it.label + ': ' + (isFinite(needed) ? needed : '—') + ' ' + unit) | |||
); | |||
}); | |||
$out.append($milestones); | |||
// Optional informational line for percent mode | |||
if (mode === 'percent') { | |||
$('<div>').addClass('ws- | $out.append( | ||
$('<div>').addClass('ws-dropcalc-note') | |||
.text('Percent mode assumes the chance is per action.') | |||
) | ); | ||
} | |||
} | } | ||
$card.on('change input', 'input', render); | |||
$ | |||
$ | $card.append( | ||
$title, | $title, | ||
$modeRow, | $modeRow, | ||
| Line 219: | Line 308: | ||
$oneInRow, | $oneInRow, | ||
$pctRow, | $pctRow, | ||
$ | $countRow, | ||
$out | $out | ||
); | ); | ||
$host.empty().append($ | $host.empty().append($card); | ||
render(); | render(); | ||
} | } | ||
function init($content) { | function init($content) { | ||
$content.find('.ws- | $content.find('.ws-dropcalc').each(function () { | ||
buildUI($(this)); | buildUI($(this)); | ||
}); | }); | ||
Revision as of 01:38, 29 December 2025
/* global mw, $ */
(function () {
'use strict';
// -----------------------------
// Utilities
// -----------------------------
function clampNumber(n, min, max) {
if (!isFinite(n)) return NaN;
return Math.min(Math.max(n, min), max);
}
function formatPercent(x) {
if (!isFinite(x)) return '—';
return (x * 100).toFixed(2) + '%';
}
function formatNumber(x, decimals) {
if (!isFinite(x)) return '—';
var d = (typeof decimals === 'number') ? decimals : 2;
return x.toFixed(d);
}
function formatInt(x) {
if (!isFinite(x)) return '—';
return String(Math.round(x));
}
function calcBinomialSingle(p, t) {
// For independent trials with success prob p:
// P(0) = (1-p)^t, P(>=1) = 1 - P(0), E[drops]=t*p
var q = 1 - p;
var p0 = Math.pow(q, t);
var pge1 = 1 - p0;
var mu = t * p;
return { p0: p0, pge1: pge1, mu: mu };
}
function trialsForConfidence(p, c) {
// Smallest integer t such that 1 - (1-p)^t >= c
if (!(p > 0 && p < 1)) return NaN;
if (!(c > 0 && c < 1)) return NaN;
var t = Math.log(1 - c) / Math.log(1 - p);
return Math.ceil(t);
}
function normalize(mode, wearInput, oneInX, percent, count) {
// Returns: { p, t, unit, perDrop, modeLabel, warnings[] }
var warnings = [];
var t = clampNumber(count, 0, 1e18);
if (!(t >= 0)) {
return {
p: NaN, t: NaN, unit: '—', perDrop: NaN,
modeLabel: '—',
warnings: ['Please enter a valid count.']
};
}
if (mode === 'wear') {
var wear = clampNumber(wearInput, 1, 1e18);
if (!(wear >= 1)) {
return {
p: NaN, t: NaN, unit: 'steps', perDrop: NaN,
modeLabel: 'WEAR (steps)',
warnings: ['Please enter a valid WEAR value (steps per drop).']
};
}
return {
p: 1 / wear,
t: t,
unit: 'steps',
perDrop: wear,
modeLabel: 'WEAR (steps)',
warnings: warnings
};
}
if (mode === 'actions') {
var x = clampNumber(oneInX, 1, 1e18);
if (!(x >= 1)) {
return {
p: NaN, t: NaN, unit: 'actions', perDrop: NaN,
modeLabel: 'Actions (1 in X)',
warnings: ['Please enter a valid “1 in X” value.']
};
}
return {
p: 1 / x,
t: t,
unit: 'actions',
perDrop: x,
modeLabel: 'Actions (1 in X)',
warnings: warnings
};
}
if (mode === 'percent') {
var pct = clampNumber(percent, 0, 100);
if (!(pct > 0 && pct <= 100)) {
return {
p: NaN, t: NaN, unit: 'actions', perDrop: NaN,
modeLabel: 'Percent (%)',
warnings: ['Please enter a valid percent (0 < % ≤ 100).']
};
}
var p = pct / 100;
return {
p: p,
t: t,
unit: 'actions',
perDrop: 1 / p,
modeLabel: 'Percent (%)',
warnings: warnings
};
}
return {
p: NaN, t: NaN, unit: '—', perDrop: NaN,
modeLabel: '—',
warnings: ['Unknown input mode.']
};
}
// -----------------------------
// UI
// -----------------------------
var CONF_LEVELS = [
{ c: 0.50, label: '50%' },
{ c: 0.90, label: '90%' },
{ c: 0.95, label: '95%' },
{ c: 0.99, label: '99%' }
];
function buildUI($host) {
// Optional prefills via data-*
var defaultMode = String($host.data('mode') || 'wear').toLowerCase();
if (['wear', 'actions', 'percent'].indexOf(defaultMode) === -1) defaultMode = 'wear';
var preWear = $host.data('wear');
var preOneIn = $host.data('onein');
var prePercent = $host.data('percent');
var preCount = $host.data('count');
// Container
var $card = $('<div>').addClass('ws-dropcalc-card');
var $title = $('<div>').addClass('ws-dropcalc-title').text('Drop Chance Calculator');
// Mode (radio)
var groupName = 'ws-dropcalc-mode-' + Math.random().toString(36).slice(2);
function makeRadio(id, value, label) {
var $wrap = $('<label>').addClass('ws-dropcalc-radio');
var $input = $('<input>').attr({ type: 'radio', name: groupName, id: id, value: value });
var $text = $('<span>').text(label);
$wrap.append($input, $text);
return $wrap;
}
var $modeRow = $('<div>').addClass('ws-dropcalc-row');
var $modeLabel = $('<div>').addClass('ws-dropcalc-label').text('Input mode');
var $radios = $('<div>').addClass('ws-dropcalc-radio-wrap');
var $rWear = makeRadio(groupName + '-wear', 'wear', 'WEAR (steps per item)');
var $rAct = makeRadio(groupName + '-actions', 'actions', 'Actions (1 in X)');
var $rPct = makeRadio(groupName + '-percent', 'percent', 'Percent (%)');
$radios.append($rWear, $rAct, $rPct);
$modeRow.append($modeLabel, $radios);
// Inputs
var $wearRow = $('<div>').addClass('ws-dropcalc-row');
var $wearLabel = $('<label>').addClass('ws-dropcalc-label').text('WEAR (steps per drop)');
var $wear = $('<input>').addClass('ws-dropcalc-input').attr({ type: 'number', min: '1', step: '1' });
$wearRow.append($wearLabel, $wear);
var $oneInRow = $('<div>').addClass('ws-dropcalc-row');
var $oneInLabel = $('<label>').addClass('ws-dropcalc-label').text('Rate (1 in X)');
var $oneIn = $('<input>').addClass('ws-dropcalc-input').attr({ type: 'number', min: '1', step: '1' });
$oneInRow.append($oneInLabel, $oneIn);
var $pctRow = $('<div>').addClass('ws-dropcalc-row');
var $pctLabel = $('<label>').addClass('ws-dropcalc-label').text('Chance (%)');
var $pct = $('<input>').addClass('ws-dropcalc-input').attr({ type: 'number', min: '0', max: '100', step: '0.001' });
var $pctHint = $('<div>').addClass('ws-dropcalc-hint').text('Example: 0.102 means 0.102%');
var $pctWrap = $('<div>').addClass('ws-dropcalc-col').append($pct, $pctHint);
$pctRow.append($pctLabel, $pctWrap);
var $countRow = $('<div>').addClass('ws-dropcalc-row');
var $countLabel = $('<label>').addClass('ws-dropcalc-label'); // text set dynamically
var $count = $('<input>').addClass('ws-dropcalc-input').attr({ type: 'number', min: '0', step: '1' });
$countRow.append($countLabel, $count);
// Output
var $out = $('<div>').addClass('ws-dropcalc-out');
// Prefill values (reasonable defaults if none supplied)
$wear.val(isFinite(preWear) ? preWear : 62745);
$oneIn.val(isFinite(preOneIn) ? preOneIn : 980);
$pct.val(isFinite(prePercent) ? prePercent : 0.102);
$count.val(isFinite(preCount) ? preCount : 0);
// Select default mode
$card.find('input[name="' + groupName + '"][value="' + defaultMode + '"]').prop('checked', true);
function getMode() {
return $card.find('input[name="' + groupName + '"]:checked').val() || 'wear';
}
function setVisibility(mode) {
$wearRow.toggle(mode === 'wear');
$oneInRow.toggle(mode === 'actions');
$pctRow.toggle(mode === 'percent');
}
function render() {
var mode = getMode();
setVisibility(mode);
var countVal = parseFloat($count.val());
var unit = (mode === 'wear') ? 'steps' : 'actions';
$countLabel.text('Completed (' + unit + ')');
var norm = normalize(
mode,
parseFloat($wear.val()),
parseFloat($oneIn.val()),
parseFloat($pct.val()),
countVal
);
$out.empty();
// Warnings
if (norm.warnings && norm.warnings.length) {
norm.warnings.forEach(function (w) {
$out.append($('<div>').addClass('ws-dropcalc-warn').text(w));
});
}
// If invalid, do not compute further
if (!(norm.p > 0 && norm.p < 1) || !(norm.t >= 0)) return;
var r = calcBinomialSingle(norm.p, norm.t);
var expectedPerDropLabel = (mode === 'wear')
? 'WEAR (steps per drop)'
: 'Expected actions per drop';
var expectedPerDropValue = (mode === 'percent')
? formatNumber(norm.perDrop, 2)
: formatInt(norm.perDrop);
var multiple = norm.t / norm.perDrop;
$out.append(
$('<div>').addClass('ws-dropcalc-metric')
.append($('<div>').addClass('ws-dropcalc-k').text(expectedPerDropLabel))
.append($('<div>').addClass('ws-dropcalc-v').text(expectedPerDropValue + ' ' + unit)),
$('<div>').addClass('ws-dropcalc-metric')
.append($('<div>').addClass('ws-dropcalc-k').text('You are at'))
.append($('<div>').addClass('ws-dropcalc-v').text(formatNumber(multiple, 2) + '× expected')),
$('<div>').addClass('ws-dropcalc-metric')
.append($('<div>').addClass('ws-dropcalc-k').text('Expected drops so far'))
.append($('<div>').addClass('ws-dropcalc-v').text(formatNumber(r.mu, 2))),
$('<div>').addClass('ws-dropcalc-metric')
.append($('<div>').addClass('ws-dropcalc-k').text('Chance of at least 1 drop by now'))
.append($('<div>').addClass('ws-dropcalc-v').text(formatPercent(r.pge1))),
$('<div>').addClass('ws-dropcalc-metric')
.append($('<div>').addClass('ws-dropcalc-k').text('Chance you would be dry (0 drops)'))
.append($('<div>').addClass('ws-dropcalc-v').text(formatPercent(r.p0))),
$('<div>').addClass('ws-dropcalc-subtitle').text('Milestones (chance of ≥1 drop)')
);
var $milestones = $('<div>').addClass('ws-dropcalc-milestones');
CONF_LEVELS.forEach(function (it) {
var needed = trialsForConfidence(norm.p, it.c);
$milestones.append(
$('<div>').addClass('ws-dropcalc-chip')
.text(it.label + ': ' + (isFinite(needed) ? needed : '—') + ' ' + unit)
);
});
$out.append($milestones);
// Optional informational line for percent mode
if (mode === 'percent') {
$out.append(
$('<div>').addClass('ws-dropcalc-note')
.text('Percent mode assumes the chance is per action.')
);
}
}
$card.on('change input', 'input', render);
$card.append(
$title,
$modeRow,
$wearRow,
$oneInRow,
$pctRow,
$countRow,
$out
);
$host.empty().append($card);
render();
}
function init($content) {
$content.find('.ws-dropcalc').each(function () {
buildUI($(this));
});
}
mw.hook('wikipage.content').add(init);
})();
