feat(calculator): Satisfactory Calculator mit Overclock-Power
This commit is contained in:
@@ -509,6 +509,7 @@
|
|||||||
<script src="src/js/calculator.js"></script>
|
<script src="src/js/calculator.js"></script>
|
||||||
<script src="src/js/calc-scientific.js"></script>
|
<script src="src/js/calc-scientific.js"></script>
|
||||||
<script src="src/js/calc-converter.js"></script>
|
<script src="src/js/calc-converter.js"></script>
|
||||||
|
<script src="src/js/calc-satisfactory.js"></script>
|
||||||
<script src="src/js/timer.js"></script>
|
<script src="src/js/timer.js"></script>
|
||||||
<script src="src/js/image-ref.js"></script>
|
<script src="src/js/image-ref.js"></script>
|
||||||
<script src="src/js/bookmark-import.js"></script>
|
<script src="src/js/bookmark-import.js"></script>
|
||||||
|
|||||||
@@ -1452,6 +1452,88 @@ body.show-desc .bm-desc { display: block; }
|
|||||||
border-top: 1px solid var(--border);
|
border-top: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Calculator Game Modes (shared) */
|
||||||
|
.calc-game-subtabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.calc-game-subtab {
|
||||||
|
flex: 1;
|
||||||
|
padding: 5px 4px;
|
||||||
|
background: rgba(255,255,255,0.04);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-size: 10px;
|
||||||
|
font-family: 'Rajdhani', sans-serif;
|
||||||
|
cursor: pointer;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
transition: all 0.15s;
|
||||||
|
}
|
||||||
|
.calc-game-subtab:hover {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
.calc-game-subtab.active {
|
||||||
|
background: var(--accent-dim);
|
||||||
|
border-color: var(--accent);
|
||||||
|
color: var(--accent);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.calc-game-field {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
.calc-game-field label {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
min-width: 90px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.calc-game-input {
|
||||||
|
flex: 1;
|
||||||
|
padding: 5px 8px;
|
||||||
|
background: rgba(0,0,0,0.3);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 13px;
|
||||||
|
font-family: 'Rajdhani', monospace;
|
||||||
|
}
|
||||||
|
.calc-game-output {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 6px 8px;
|
||||||
|
background: rgba(0,0,0,0.2);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
.calc-game-output span:first-child {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
.calc-game-value {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--accent);
|
||||||
|
font-weight: 600;
|
||||||
|
font-family: 'Rajdhani', monospace;
|
||||||
|
}
|
||||||
|
.calc-game-warning {
|
||||||
|
font-size: 10px;
|
||||||
|
color: var(--danger);
|
||||||
|
padding: 2px 0;
|
||||||
|
}
|
||||||
|
.calc-game-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
TIMER WIDGET
|
TIMER WIDGET
|
||||||
============================================ */
|
============================================ */
|
||||||
|
|||||||
@@ -0,0 +1,184 @@
|
|||||||
|
/* =============================================
|
||||||
|
HELLION NEWTAB — calc-satisfactory.js
|
||||||
|
Satisfactory Calculator Modus
|
||||||
|
============================================= */
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const POWER_EXPONENT = 1.321928;
|
||||||
|
const SUB_MODES = ['itemsPerMin', 'power', 'machines'];
|
||||||
|
let _activeSubMode = 'itemsPerMin';
|
||||||
|
|
||||||
|
function createField(labelKey, defaultVal, opts) {
|
||||||
|
opts = opts || {};
|
||||||
|
const row = document.createElement('div');
|
||||||
|
row.className = 'calc-game-field';
|
||||||
|
const label = document.createElement('label');
|
||||||
|
label.textContent = t(labelKey);
|
||||||
|
const input = document.createElement('input');
|
||||||
|
input.type = 'number';
|
||||||
|
input.className = 'calc-game-input';
|
||||||
|
input.value = defaultVal;
|
||||||
|
if (opts.step) input.step = opts.step;
|
||||||
|
if (opts.min !== undefined) input.min = opts.min;
|
||||||
|
if (opts.max !== undefined) input.max = opts.max;
|
||||||
|
row.append(label, input);
|
||||||
|
return { row, input };
|
||||||
|
}
|
||||||
|
|
||||||
|
function createOutput(labelKey) {
|
||||||
|
const row = document.createElement('div');
|
||||||
|
row.className = 'calc-game-output';
|
||||||
|
const label = document.createElement('span');
|
||||||
|
label.textContent = t(labelKey);
|
||||||
|
const value = document.createElement('span');
|
||||||
|
value.className = 'calc-game-value';
|
||||||
|
row.append(label, value);
|
||||||
|
return { row, value };
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderItemsPerMin(container) {
|
||||||
|
const itemsField = createField('calculator.sat.items_per_craft', 1, { step: 1, min: 1 });
|
||||||
|
const timeField = createField('calculator.sat.craft_time', 4, { step: 0.1, min: 0.1 });
|
||||||
|
const clockField = createField('calculator.sat.clock_speed', 100, { step: 1, min: 1, max: 250 });
|
||||||
|
const output = createOutput('calculator.sat.output_per_min');
|
||||||
|
|
||||||
|
function calc() {
|
||||||
|
const items = parseFloat(itemsField.input.value) || 0;
|
||||||
|
const time = parseFloat(timeField.input.value) || 1;
|
||||||
|
const clock = parseFloat(clockField.input.value) || 100;
|
||||||
|
const result = (items * 60) / time * (clock / 100);
|
||||||
|
output.value.textContent = Calculator._formatResult(result) + ' items/min';
|
||||||
|
}
|
||||||
|
|
||||||
|
[itemsField, timeField, clockField].forEach(f => f.input.addEventListener('input', calc));
|
||||||
|
container.append(itemsField.row, timeField.row, clockField.row, output.row);
|
||||||
|
calc();
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderPower(container) {
|
||||||
|
const basePowerField = createField('calculator.sat.base_power', 30, { step: 1, min: 0.1 });
|
||||||
|
const clockField = createField('calculator.sat.clock_speed', 100, { step: 1, min: 1, max: 250 });
|
||||||
|
const powerOutput = createOutput('calculator.sat.power_usage');
|
||||||
|
const effOutput = createOutput('calculator.sat.efficiency');
|
||||||
|
|
||||||
|
function calc() {
|
||||||
|
const basePower = parseFloat(basePowerField.input.value) || 0;
|
||||||
|
const clock = parseFloat(clockField.input.value) || 100;
|
||||||
|
const ratio = clock / 100;
|
||||||
|
const power = basePower * Math.pow(ratio, POWER_EXPONENT);
|
||||||
|
const effPerItem = Math.pow(ratio, POWER_EXPONENT - 1);
|
||||||
|
|
||||||
|
powerOutput.value.textContent = Calculator._formatResult(power) + ' MW';
|
||||||
|
|
||||||
|
if (clock > 100) {
|
||||||
|
const overhead = (effPerItem - 1) * 100;
|
||||||
|
effOutput.value.textContent = '+' + Calculator._formatResult(overhead) + '% ' + t('calculator.sat.per_item');
|
||||||
|
effOutput.row.style.display = '';
|
||||||
|
} else {
|
||||||
|
effOutput.row.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[basePowerField, clockField].forEach(f => f.input.addEventListener('input', calc));
|
||||||
|
container.append(basePowerField.row, clockField.row, powerOutput.row, effOutput.row);
|
||||||
|
calc();
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderMachines(container) {
|
||||||
|
const targetField = createField('calculator.sat.target_output', 60, { step: 1, min: 1 });
|
||||||
|
const itemsField = createField('calculator.sat.items_per_craft', 1, { step: 1, min: 1 });
|
||||||
|
const timeField = createField('calculator.sat.craft_time', 4, { step: 0.1, min: 0.1 });
|
||||||
|
const clockField = createField('calculator.sat.clock_speed', 100, { step: 1, min: 1, max: 250 });
|
||||||
|
const basePowerField = createField('calculator.sat.base_power', 30, { step: 1, min: 0.1 });
|
||||||
|
const machinesOutput = createOutput('calculator.sat.machines_needed');
|
||||||
|
const totalPowerOutput = createOutput('calculator.sat.total_power');
|
||||||
|
|
||||||
|
function calc() {
|
||||||
|
const target = parseFloat(targetField.input.value) || 0;
|
||||||
|
const items = parseFloat(itemsField.input.value) || 1;
|
||||||
|
const time = parseFloat(timeField.input.value) || 1;
|
||||||
|
const clock = parseFloat(clockField.input.value) || 100;
|
||||||
|
const basePower = parseFloat(basePowerField.input.value) || 0;
|
||||||
|
const ratio = clock / 100;
|
||||||
|
const itemsPerMin = (items * 60) / time * ratio;
|
||||||
|
const machines = itemsPerMin > 0 ? Math.ceil(target / itemsPerMin) : 0;
|
||||||
|
const totalPower = machines * basePower * Math.pow(ratio, POWER_EXPONENT);
|
||||||
|
machinesOutput.value.textContent = machines;
|
||||||
|
totalPowerOutput.value.textContent = Calculator._formatResult(totalPower) + ' MW';
|
||||||
|
}
|
||||||
|
|
||||||
|
[targetField, itemsField, timeField, clockField, basePowerField].forEach(f => f.input.addEventListener('input', calc));
|
||||||
|
container.append(targetField.row, itemsField.row, timeField.row, clockField.row, basePowerField.row, machinesOutput.row, totalPowerOutput.row);
|
||||||
|
calc();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadState() {
|
||||||
|
const data = await Store.get(Calculator.STORAGE_KEY);
|
||||||
|
if (data && data.calculator && data.calculator.satisfactory) {
|
||||||
|
const s = data.calculator.satisfactory;
|
||||||
|
if (s.lastSubMode && SUB_MODES.includes(s.lastSubMode)) _activeSubMode = s.lastSubMode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveState() {
|
||||||
|
const data = await Store.get(Calculator.STORAGE_KEY) || {};
|
||||||
|
if (!data.calculator) data.calculator = {};
|
||||||
|
data.calculator.satisfactory = { lastSubMode: _activeSubMode };
|
||||||
|
await Store.set(Calculator.STORAGE_KEY, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderSubMode(container) {
|
||||||
|
container.textContent = '';
|
||||||
|
switch (_activeSubMode) {
|
||||||
|
case 'itemsPerMin': renderItemsPerMin(container); break;
|
||||||
|
case 'power': renderPower(container); break;
|
||||||
|
case 'machines': renderMachines(container); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Calculator.registerMode('satisfactory', {
|
||||||
|
label: '⚙️',
|
||||||
|
shortName: 'SAT',
|
||||||
|
titleKey: 'calculator.tab.satisfactory',
|
||||||
|
|
||||||
|
render(bodyEl) {
|
||||||
|
bodyEl.style.padding = '8px';
|
||||||
|
bodyEl.style.display = 'flex';
|
||||||
|
bodyEl.style.flexDirection = 'column';
|
||||||
|
bodyEl.style.gap = '8px';
|
||||||
|
|
||||||
|
loadState().then(() => {
|
||||||
|
const subContent = document.createElement('div');
|
||||||
|
subContent.className = 'calc-game-content';
|
||||||
|
|
||||||
|
const bar = document.createElement('div');
|
||||||
|
bar.className = 'calc-game-subtabs';
|
||||||
|
|
||||||
|
SUB_MODES.forEach(mode => {
|
||||||
|
const btn = document.createElement('button');
|
||||||
|
btn.type = 'button';
|
||||||
|
btn.className = 'calc-game-subtab' + (mode === _activeSubMode ? ' active' : '');
|
||||||
|
btn.textContent = t('calculator.sat.tab.' + mode);
|
||||||
|
btn.dataset.mode = mode;
|
||||||
|
btn.addEventListener('click', () => {
|
||||||
|
bar.querySelectorAll('.calc-game-subtab').forEach(b => b.classList.remove('active'));
|
||||||
|
btn.classList.add('active');
|
||||||
|
_activeSubMode = mode;
|
||||||
|
renderSubMode(subContent);
|
||||||
|
saveState();
|
||||||
|
});
|
||||||
|
bar.appendChild(btn);
|
||||||
|
});
|
||||||
|
|
||||||
|
bodyEl.append(bar, subContent);
|
||||||
|
renderSubMode(subContent);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
saveState();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
@@ -107,6 +107,21 @@ const STRINGS = {
|
|||||||
'calculator.conv.cat.volume': 'Volumen',
|
'calculator.conv.cat.volume': 'Volumen',
|
||||||
'calculator.conv.cat.speed': 'Geschwindigkeit',
|
'calculator.conv.cat.speed': 'Geschwindigkeit',
|
||||||
'calculator.conv.cat.area': 'Fläche',
|
'calculator.conv.cat.area': 'Fläche',
|
||||||
|
'calculator.tab.satisfactory': 'Satisfactory',
|
||||||
|
'calculator.sat.tab.itemsPerMin': 'Items/Min',
|
||||||
|
'calculator.sat.tab.power': 'Strom',
|
||||||
|
'calculator.sat.tab.machines': 'Maschinen',
|
||||||
|
'calculator.sat.items_per_craft': 'Items/Craft',
|
||||||
|
'calculator.sat.craft_time': 'Craftzeit (s)',
|
||||||
|
'calculator.sat.clock_speed': 'Taktrate (%)',
|
||||||
|
'calculator.sat.base_power': 'Grundleistung (MW)',
|
||||||
|
'calculator.sat.target_output': 'Ziel Output/Min',
|
||||||
|
'calculator.sat.output_per_min': 'Output',
|
||||||
|
'calculator.sat.power_usage': 'Stromverbrauch',
|
||||||
|
'calculator.sat.efficiency': 'Effizienz',
|
||||||
|
'calculator.sat.per_item': 'pro Item',
|
||||||
|
'calculator.sat.machines_needed': 'Maschinen benötigt',
|
||||||
|
'calculator.sat.total_power': 'Gesamtleistung',
|
||||||
|
|
||||||
// Timer
|
// Timer
|
||||||
'timer.title': 'Timer',
|
'timer.title': 'Timer',
|
||||||
@@ -439,6 +454,21 @@ const STRINGS = {
|
|||||||
'calculator.conv.cat.volume': 'Volume',
|
'calculator.conv.cat.volume': 'Volume',
|
||||||
'calculator.conv.cat.speed': 'Speed',
|
'calculator.conv.cat.speed': 'Speed',
|
||||||
'calculator.conv.cat.area': 'Area',
|
'calculator.conv.cat.area': 'Area',
|
||||||
|
'calculator.tab.satisfactory': 'Satisfactory',
|
||||||
|
'calculator.sat.tab.itemsPerMin': 'Items/Min',
|
||||||
|
'calculator.sat.tab.power': 'Power',
|
||||||
|
'calculator.sat.tab.machines': 'Machines',
|
||||||
|
'calculator.sat.items_per_craft': 'Items/Craft',
|
||||||
|
'calculator.sat.craft_time': 'Craft Time (s)',
|
||||||
|
'calculator.sat.clock_speed': 'Clock Speed (%)',
|
||||||
|
'calculator.sat.base_power': 'Base Power (MW)',
|
||||||
|
'calculator.sat.target_output': 'Target Output/Min',
|
||||||
|
'calculator.sat.output_per_min': 'Output',
|
||||||
|
'calculator.sat.power_usage': 'Power Usage',
|
||||||
|
'calculator.sat.efficiency': 'Efficiency',
|
||||||
|
'calculator.sat.per_item': 'per item',
|
||||||
|
'calculator.sat.machines_needed': 'Machines needed',
|
||||||
|
'calculator.sat.total_power': 'Total Power',
|
||||||
|
|
||||||
// Timer
|
// Timer
|
||||||
'timer.title': 'Timer',
|
'timer.title': 'Timer',
|
||||||
|
|||||||
Reference in New Issue
Block a user