// ==UserScript==
// @name 猫国建设者全能小助手 (GUI版 v7.8.24 - 统一模式版)
// @namespace http://tampermonkey.net/
// @version 7.8.24
// @description 修复云存储功能,改为多种fallback方法确保存档成功。基于v7.8.23改进,将手稿、概要、蓝图的自动化逻辑升级为"定时/阈值"双模式切换。
// @author AI Assistant
// @match *://kittensgame.com/web/*
// @updateURL https://raw.githubusercontent.com/DearPeter/kittens-game-script/main/auto.js
// @downloadURL https://raw.githubusercontent.com/DearPeter/kittens-game-script/main/auto.js
// @grant none
// ==/UserScript==
(function() {
'use strict';
console.log('>>> 猫国建设者全能小助手 GUI版 v7.8.24 (统一模式版) 正在加载... <<<');
// ==========================================
// 1. 配置中心与存储 (Configuration & Storage)
// ==========================================
const STORAGE_KEY = 'KG_AutoAssist_Config_v7_8';
const PROFILES_KEY = 'KG_AutoAssist_Profiles_v1';
const UI_STATE_KEY = 'KG_AutoAssist_UIState';
const defaultConfig = {
global: { enabled: true },
starchart: { enabled: true },
unicornPasture: { enabled: false },
wood: { enabled: true, type: 'percent', thresholdPercent: 90 },
minerals: { enabled: true, type: 'percent', thresholdPercent: 90 },
coal: { enabled: true, type: 'percent', thresholdPercent: 90 },
iron: { enabled: true, type: 'percent', thresholdPercent: 90 },
catnipWood: { enabled: false, type: 'percent', thresholdPercent: 90 },
oilKerosene: { enabled: false, type: 'percent', thresholdPercent: 90 },
eludium: { enabled: false, type: 'percent', thresholdPercent: 90 },
titaniumAlloy: { enabled: false, type: 'percent', thresholdPercent: 90 },
uraniumThorium: { enabled: false, type: 'percent', thresholdPercent: 90 },
smartHunterGold: { enabled: false },
// 模式切换型配置 (Mode Switch Items)
hunters: { enabled: true, mode: 'interval', intervalMinutes: 5, thresholdPercent: 95 },
autoTrade: { enabled: false, mode: 'interval', intervalMinutes: 20, thresholdPercent: 95, targetRace: 'zebras' },
// [修改] 手稿、概要、蓝图统一为模式切换型
manuscript: { enabled: true, mode: 'interval', intervalMinutes: 3, thresholdPercent: 98 },
compendium: { enabled: true, mode: 'interval', intervalMinutes: 60, thresholdPercent: 98 },
blueprint: { enabled: false, mode: 'interval', intervalMinutes: 60, thresholdPercent: 98 },
// 智能级联交易
smartTrade: {
enabled: false,
p1: { race: 'dragons', percent: 95 },
p2: { race: 'zebras', percent: 90 },
p3: { race: 'sharks', percent: 0 }
},
// 列维坦
leviathans: {
autoTrade: false,
autoFeed: false,
minUnobtainium: 15000
},
emergencyTradeCatnip: { enabled: false, type: 'percent', thresholdPercent: 60 },
parchment: { enabled: true, type: 'fixed', thresholdFixed: 15000 },
scaffold: { enabled: false, type: 'fixed', thresholdFixed: 10000 },
praise: { enabled: true, intervalMinutes: 60 },
cloudSave: { enabled: true, intervalMinutes: 10 },
ui: { fabHidden: false, posX: 'auto', posY: '20px' }
};
let config = loadConfig();
function loadConfig() {
try {
const saved = localStorage.getItem(STORAGE_KEY);
if (saved) {
const parsed = JSON.parse(saved);
// 兼容性合并
if (!parsed.global) parsed.global = defaultConfig.global;
// [新增] 确保手稿等拥有 mode 字段 (从旧版本迁移)
['hunters', 'autoTrade', 'manuscript', 'compendium', 'blueprint'].forEach(k => {
if (!parsed[k] || !parsed[k].mode) {
parsed[k] = { ...defaultConfig[k], ...parsed[k], mode: 'interval' };
// 如果旧版本有 triggerOnFull=true,迁移为 threshold 模式
if (parsed[k].triggerOnFull) {
parsed[k].mode = 'threshold';
parsed[k].thresholdPercent = 98;
delete parsed[k].triggerOnFull;
}
}
});
if (!parsed.leviathans) parsed.leviathans = defaultConfig.leviathans;
for (const key in defaultConfig) {
if (parsed[key] === undefined) parsed[key] = defaultConfig[key];
}
return { ...defaultConfig, ...parsed };
}
} catch (e) { console.error('读取配置失败:', e); }
return JSON.parse(JSON.stringify(defaultConfig));
}
function saveConfig() {
try { localStorage.setItem(STORAGE_KEY, JSON.stringify(config)); } catch (e) { console.error('保存配置失败:', e); }
}
// --- 档案管理 ---
function getProfiles() { try { return JSON.parse(localStorage.getItem(PROFILES_KEY) || '{}'); } catch(e){return {};} }
function saveProfile(name) {
if (!name || !name.trim()) return false;
const profiles = getProfiles();
profiles[name] = JSON.parse(JSON.stringify(config));
localStorage.setItem(PROFILES_KEY, JSON.stringify(profiles));
return true;
}
function loadProfile(name) {
const profiles = getProfiles();
if (profiles[name]) {
config = { ...defaultConfig, ...profiles[name] };
saveConfig(); createUI();
Object.keys(timers).forEach(key => updateSpecificTimer(key));
return true;
}
return false;
}
function deleteProfile(name) {
const profiles = getProfiles();
if (profiles[name]) { delete profiles[name]; localStorage.setItem(PROFILES_KEY, JSON.stringify(profiles)); return true; }
return false;
}
const RACE_RES_MAP = {
"lizards": "wood", "sharks": "catnip", "griffins": "wood", "nagas": "minerals",
"zebras": "titanium", "spiders": "coal", "dragons": "uranium", "leviathans": "timeCrystal"
};
const capResourceMap = {
wood: 'wood', minerals: 'minerals', coal: 'coal', iron: 'iron',
catnipWood: 'catnip', emergencyTradeCatnip: 'catnip', oilKerosene: 'oil',
eludium: 'unobtainium',
titaniumAlloy: 'titanium',
uraniumThorium: 'uranium'
};
function getActualThreshold(configKey) {
const itemConfig = config[configKey];
if (itemConfig.type === 'fixed') return itemConfig.thresholdFixed;
if (itemConfig.type === 'percent') {
const resName = capResourceMap[configKey];
try {
const resData = gamePage.resPool.get(resName);
if (resData && resData.maxValue > 0) return Math.floor(resData.maxValue * (itemConfig.thresholdPercent / 100));
} catch (e) {}
}
return 9999999999;
}
// ==========================================
// 2. 界面构建器 (UI Builder)
// ==========================================
function createUI() {
if (typeof gamePage === 'undefined' || !gamePage.diplomacy || !gamePage.diplomacy.races) return;
const existingPanel = document.getElementById('kg-auto-assist-panel');
if (existingPanel) existingPanel.remove();
const existingFab = document.getElementById('kg-auto-assist-fab');
if (existingFab) existingFab.remove();
if (!config.ui.fabHidden) { createFAB(); } else { createMainPanel(); }
}
function createFAB() {
const fab = document.createElement('div');
fab.id = 'kg-auto-assist-fab';
fab.style.cssText = `
position: fixed; bottom: 30px; right: 30px;
width: 50px; height: 50px;
background-color: #1a73e8; color: #fff;
border-radius: 50%;
text-align: center; line-height: 50px; font-size: 24px;
cursor: pointer; z-index: 2147483647; user-select: none;
box-shadow: 0 4px 12px rgba(26,115,232,0.4);
transition: all 0.3s ease; border: none;
`;
fab.innerHTML = '🐱';
fab.title = '点击打开全能小助手';
fab.addEventListener('click', () => { config.ui.fabHidden = true; saveConfig(); createUI(); });
document.body.appendChild(fab);
}
function injectStyles() {
const styleId = 'kg-assist-styles';
if (document.getElementById(styleId)) return;
const style = document.createElement('style');
style.id = styleId;
style.innerHTML = `
.kg-tab-nav { display: flex; border-bottom: 1px solid #e0e0e0; background: transparent; padding: 0 8px; }
.kg-tab-btn {
flex: 1; background: transparent; border: none; color: #5f6368;
padding: 12px 0; cursor: pointer; border-bottom: 3px solid transparent;
font-size: 13px; font-weight: 500; font-family: 'Roboto', 'Segoe UI', sans-serif;
transition: all 0.2s; outline: none; border-radius: 4px 4px 0 0;
}
.kg-tab-btn:hover { background: #f1f3f4; color: #202124; }
.kg-tab-btn.active { color: #1a73e8; border-bottom: 3px solid #1a73e8; background: #e8f0fe; }
.kg-tab-content { display: none; padding: 16px 8px; animation: kg-fade 0.2s ease-in-out; }
.kg-tab-content.active { display: block; }
@keyframes kg-fade { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: translateY(0); } }
#kg-auto-assist-panel ::-webkit-scrollbar { width: 6px; }
#kg-auto-assist-panel ::-webkit-scrollbar-thumb { background: #dadce0; border-radius: 3px; }
#kg-auto-assist-panel ::-webkit-scrollbar-thumb:hover { background: #bdc1c6; }
`;
document.head.appendChild(style);
}
function createMainPanel() {
injectStyles();
const panel = document.createElement('div');
panel.id = 'kg-auto-assist-panel';
const topPos = config.ui.posY !== 'auto' ? config.ui.posY : '20px';
const leftPos = config.ui.posX !== 'auto' ? config.ui.posX : 'auto';
const rightPos = config.ui.posX === 'auto' ? '20px' : 'auto';
panel.style.cssText = `
position: fixed; top: ${topPos}; left: ${leftPos}; right: ${rightPos};
width: 460px;
background-color: #ffffff; color: #3c4043;
border-radius: 16px; z-index: 9999;
font-family: 'Roboto', 'Segoe UI', Arial, sans-serif; font-size: 13px;
box-shadow: 0 8px 24px rgba(0,0,0,0.15);
overflow: hidden; transition: box-shadow 0.3s;
`;
// --- Header ---
const header = document.createElement('div');
header.style.cssText = `display: flex; justify-content: space-between; align-items: center; padding: 12px 16px; cursor: move; background: #ffffff; border-bottom: 1px solid #f1f3f4;`;
const titleDiv = document.createElement('div');
titleDiv.innerHTML = '🐱 小助手 v7.8.24';
const globalToggleDiv = document.createElement('div');
globalToggleDiv.style.cssText = 'display: flex; align-items: center; gap: 8px;';
const globalLabel = document.createElement('span'); globalLabel.innerText = '全局开关'; globalLabel.style.cssText = 'font-size: 13px; color: #5f6368;';
const toggleSwitch = document.createElement('label');
toggleSwitch.style.cssText = `position: relative; display: inline-block; width: 50px; height: 24px;`;
const toggleInput = document.createElement('input');
toggleInput.type = 'checkbox'; toggleInput.checked = config.global.enabled; toggleInput.style.cssText = 'opacity: 0; width: 0; height: 0;';
const toggleSpan = document.createElement('span');
toggleSpan.style.cssText = `position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: ${config.global.enabled ? '#1a73e8' : '#dadce0'}; transition: .4s; border-radius: 24px;`;
toggleSpan.innerHTML = '';
toggleSwitch.appendChild(toggleInput); toggleSwitch.appendChild(toggleSpan);
toggleInput.addEventListener('change', function() {
config.global.enabled = this.checked; saveConfig();
toggleSpan.style.backgroundColor = this.checked ? '#1a73e8' : '#dadce0';
toggleSpan.querySelector('span').style.transform = this.checked ? 'translateX(26px)' : 'translateX(0)';
});
globalToggleDiv.appendChild(globalLabel); globalToggleDiv.appendChild(toggleSwitch);
const closeBtn = document.createElement('button');
closeBtn.innerHTML = '✖';
closeBtn.style.cssText = 'background:none; border:none; color:#5f6368; cursor:pointer; font-size: 16px; padding: 4px;';
closeBtn.addEventListener('click', () => { config.ui.fabHidden = false; saveConfig(); createUI(); });
header.appendChild(titleDiv); header.appendChild(globalToggleDiv); header.appendChild(closeBtn);
panel.appendChild(header);
// --- Tabs ---
const tabNav = document.createElement('div');
tabNav.className = 'kg-tab-nav';
const tabs = [
{ id: 'tab-res', label: '资源转化' },
{ id: 'tab-act', label: '自动活动' },
{ id: 'tab-trade', label: '贸易外交' },
{ id: 'tab-profile', label: '档案管理' }
];
const contentContainer = document.createElement('div');
contentContainer.style.padding = '0 8px 8px 8px'; contentContainer.style.background = '#ffffff';
let activeTabIndex = parseInt(localStorage.getItem(UI_STATE_KEY) || '0');
if(activeTabIndex >= tabs.length) activeTabIndex = 0;
const tabContents = [];
tabs.forEach((tab, index) => {
const btn = document.createElement('button');
btn.className = `kg-tab-btn ${index === activeTabIndex ? 'active' : ''}`; btn.innerText = tab.label;
btn.onclick = () => {
document.querySelectorAll('.kg-tab-btn').forEach(b => b.classList.remove('active'));
document.querySelectorAll('.kg-tab-content').forEach(c => c.classList.remove('active'));
btn.classList.add('active'); tabContents[index].classList.add('active'); localStorage.setItem(UI_STATE_KEY, index);
};
tabNav.appendChild(btn);
const contentDiv = document.createElement('div');
contentDiv.className = `kg-tab-content ${index === activeTabIndex ? 'active' : ''}`;
contentDiv.id = tab.id; tabContents.push(contentDiv); contentContainer.appendChild(contentDiv);
});
panel.appendChild(tabNav); panel.appendChild(contentContainer);
// UI Creation Helper
function createControlItem(label, configKey, uiType = 'none') {
const row = document.createElement('div');
row.style.cssText = 'display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; padding: 2px 4px;';
const isInterval = uiType === 'interval';
const isHybridThreshold = uiType === 'hybrid';
const isModeSwitch = uiType === 'modeSwitch';
const leftSide = document.createElement('label');
leftSide.style.cssText = 'display: flex; align-items: center; cursor: pointer; flex-grow: 1; color: #3c4043; font-weight: 400;';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox'; checkbox.checked = config[configKey].enabled;
checkbox.style.marginRight = '10px'; checkbox.style.accentColor = '#1a73e8';
checkbox.addEventListener('change', (e) => {
config[configKey].enabled = e.target.checked;
saveConfig();
if (isInterval || isModeSwitch) updateSpecificTimer(configKey);
});
leftSide.appendChild(checkbox); leftSide.appendChild(document.createTextNode(label)); row.appendChild(leftSide);
const rightSide = document.createElement('div');
rightSide.style.cssText = 'display: flex; align-items: center; justify-content: flex-end;';
const inputStyle = 'background: #f1f3f4; color: #202124; border: 1px solid transparent; border-radius: 6px; padding: 4px 6px; font-family: inherit; font-size: 12px; transition: 0.2s; outline: none;';
if (isModeSwitch) {
// --- 模式切换型 UI (猎人/交易/手稿/蓝图/概要) ---
const modeSelect = document.createElement('select');
modeSelect.style.cssText = 'width: 60px; margin-right: 6px; ' + inputStyle;
const opt1 = document.createElement('option'); opt1.value = 'interval'; opt1.text = '定时';
const opt2 = document.createElement('option'); opt2.value = 'threshold'; opt2.text = '阈值'; // 统称为阈值
modeSelect.appendChild(opt1); modeSelect.appendChild(opt2);
modeSelect.value = config[configKey].mode;
const valueContainer = document.createElement('div');
valueContainer.style.display = 'flex'; valueContainer.style.alignItems = 'center';
const renderValueInput = () => {
valueContainer.innerHTML = '';
if (modeSelect.value === 'interval') {
const input = document.createElement('input'); input.type = 'number'; input.value = config[configKey].intervalMinutes; input.min = 1;
input.style.cssText = 'width: 40px; text-align: right; ' + inputStyle;
input.addEventListener('change', (e) => { config[configKey].intervalMinutes = Math.max(1, parseInt(e.target.value)||1); saveConfig(); updateSpecificTimer(configKey); });
valueContainer.appendChild(input);
const unit = document.createElement('span'); unit.innerText = '分'; unit.style.cssText='margin-left:4px; font-size:12px; color:#5f6368;';
valueContainer.appendChild(unit);
} else {
// 阈值模式:使用百分比滑块
const range = document.createElement('input'); range.type = 'range'; range.min = '1'; range.max = '100';
range.value = config[configKey].thresholdPercent;
range.style.cssText = 'width: 60px; height: 4px; background: #dadce0; outline: none; border-radius: 2px; accent-color: #1a73e8; margin-right: 4px;';
const text = document.createElement('span'); text.innerText = `${config[configKey].thresholdPercent}%`; text.style.cssText='font-size:12px; color:#5f6368; width: 30px;';
range.addEventListener('input', (e) => {
config[configKey].thresholdPercent = parseInt(e.target.value);
text.innerText = `${config[configKey].thresholdPercent}%`;
saveConfig();
});
valueContainer.appendChild(range); valueContainer.appendChild(text);
}
};
renderValueInput();
modeSelect.addEventListener('change', (e) => {
config[configKey].mode = e.target.value;
renderValueInput();
saveConfig();
updateSpecificTimer(configKey); // 切换模式时重置定时器逻辑
});
// 交易特殊处理:目标种族选择
if(configKey === 'autoTrade') {
const raceSelect = document.createElement('select');
raceSelect.style.cssText = 'width: 70px; margin-right: 6px; ' + inputStyle;
if (gamePage.diplomacy && gamePage.diplomacy.races) {
gamePage.diplomacy.races.forEach(race => { if (race.unlocked) { const o = document.createElement('option'); o.value = race.name; o.text = race.title; raceSelect.appendChild(o); }});
}
raceSelect.value = config.autoTrade.targetRace;
raceSelect.addEventListener('change', (e) => { config.autoTrade.targetRace = e.target.value; saveConfig(); });
rightSide.appendChild(raceSelect);
}
rightSide.appendChild(modeSelect);
rightSide.appendChild(valueContainer);
} else if (isHybridThreshold) {
// ... (略,保持原有逻辑) ...
const itemType = config[configKey].type;
if (itemType === 'percent') {
const sliderContainer = document.createElement('div'); sliderContainer.style.cssText = 'display:flex; align-items:center; width: 240px;';
const rangeInput = document.createElement('input'); rangeInput.type = 'range'; rangeInput.min = '1'; rangeInput.max = '100'; rangeInput.value = config[configKey].thresholdPercent;
rangeInput.style.cssText = 'flex-grow:1; cursor: pointer; height: 4px; background: #dadce0; outline: none; border-radius: 2px; accent-color: #1a73e8;';
const percentText = document.createElement('span'); percentText.style.cssText = 'font-size: 12px; width: 140px; text-align: left; color: #5f6368; margin-left: 10px; font-variant-numeric: tabular-nums;';
const updatePercentText = (percentVal) => {
const resName = capResourceMap[configKey]; let actualVal = 'N/A';
try { const resData = gamePage.resPool.get(resName); if (resData && resData.maxValue > 0) actualVal = Math.floor(resData.maxValue * (percentVal / 100)); } catch (e) {}
percentText.innerText = `${percentVal}% (${actualVal})`;
};
updatePercentText(rangeInput.value);
rangeInput.addEventListener('input', (e) => { const val = parseInt(e.target.value); config[configKey].thresholdPercent = val; updatePercentText(val); saveConfig(); });
sliderContainer.appendChild(rangeInput); sliderContainer.appendChild(percentText); rightSide.appendChild(sliderContainer);
} else if (itemType === 'fixed') {
const input = document.createElement('input'); input.type = 'number'; input.value = config[configKey].thresholdFixed;
input.style.cssText = 'width: 70px; text-align: right; ' + inputStyle;
input.addEventListener('change', (e) => { config[configKey].thresholdFixed = parseInt(e.target.value) || 0; saveConfig(); });
rightSide.appendChild(input);
}
} else if (isInterval) {
const input = document.createElement('input'); input.type = 'number'; input.value = config[configKey].intervalMinutes; input.min = 1;
input.style.cssText = 'width: 50px; text-align: right; ' + inputStyle;
input.addEventListener('change', (e) => { config[configKey].intervalMinutes = Math.max(1, parseInt(e.target.value) || 1); saveConfig(); updateSpecificTimer(configKey); });
rightSide.appendChild(input);
const minLabel = document.createElement('span'); minLabel.innerText = '分'; minLabel.style.cssText = 'color: #5f6368; margin-left: 4px; font-size: 12px;';
rightSide.appendChild(minLabel);
}
row.appendChild(rightSide);
return row;
}
// T1: Resources
const t1 = tabContents[0];
t1.appendChild(createControlItem('木材 -> 木梁 (上限%)', 'wood', 'hybrid'));
t1.appendChild(createControlItem('矿物 -> 石板 (上限%)', 'minerals', 'hybrid'));
t1.appendChild(createControlItem('煤炭 -> 钢铁 (上限%)', 'coal', 'hybrid'));
t1.appendChild(createControlItem('铁 -> 金属板 (上限%)', 'iron', 'hybrid'));
t1.appendChild(createControlItem('猫薄荷 -> 木头 (上限%)', 'catnipWood', 'hybrid'));
t1.appendChild(createControlItem('石油 -> 煤油 (上限%)', 'oilKerosene', 'hybrid'));
t1.appendChild(document.createElement('hr')).style.cssText = 'border: 0; border-top: 1px solid #f1f3f4; margin: 8px 0;';
t1.appendChild(createControlItem('难得素 -> E合金 (上限%)', 'eludium', 'hybrid'));
t1.appendChild(createControlItem('钛 -> 合金 (上限%)', 'titaniumAlloy', 'hybrid'));
t1.appendChild(createControlItem('铀 -> 钍 (上限%)', 'uraniumThorium', 'hybrid'));
t1.appendChild(document.createElement('hr')).style.cssText = 'border: 0; border-top: 1px solid #f1f3f4; margin: 8px 0;';
t1.appendChild(createControlItem('木梁 -> 脚手架 (固定值)', 'scaffold', 'hybrid'));
t1.appendChild(createControlItem('毛皮 ->羊皮纸 (固定值)', 'parchment', 'hybrid'));
// T2: Activities
const t2 = tabContents[1];
t2.appendChild(createControlItem('自动点星图', 'starchart'));
t2.appendChild(createControlItem('自动升级独角兽牧场', 'unicornPasture'));
t2.appendChild(document.createElement('hr')).style.cssText = 'border: 0; border-top: 1px solid #f1f3f4; margin: 8px 0;';
t2.appendChild(createControlItem('自动派猎人', 'hunters', 'modeSwitch'));
t2.appendChild(createControlItem('智能猎人 (金满停/低开)', 'smartHunterGold'));
t2.appendChild(document.createElement('hr')).style.cssText = 'border: 0; border-top: 1px solid #f1f3f4; margin: 8px 0;';
t2.appendChild(createControlItem('自动赞美太阳 (Timer)', 'praise', 'interval'));
// [修改] 现在使用 modeSwitch 类型,支持定时/阈值切换
t2.appendChild(createControlItem('自动合成手稿', 'manuscript', 'modeSwitch'));
t2.appendChild(createControlItem('自动合成概要', 'compendium', 'modeSwitch'));
t2.appendChild(createControlItem('自动合成蓝图', 'blueprint', 'modeSwitch'));
t2.appendChild(document.createElement('hr')).style.cssText = 'border: 0; border-top: 1px solid #f1f3f4; margin: 8px 0;';
t2.appendChild(createControlItem('定时云存储', 'cloudSave', 'interval'));
// T3: Trade
const t3 = tabContents[2];
t3.appendChild(createControlItem('猫薄荷 < 阈值 -> 交易鲨鱼(1次)', 'emergencyTradeCatnip', 'hybrid'));
t3.appendChild(createControlItem('自动交易', 'autoTrade', 'modeSwitch'));
const hr = document.createElement('hr'); hr.style.cssText = 'border: 0; border-top: 1px solid #e0e0e0; margin: 16px 0;'; t3.appendChild(hr);
const tradeHeader = document.createElement('div'); tradeHeader.innerHTML = '智能级联交易 (Smart Cascade)'; tradeHeader.style.marginBottom = '8px'; tradeHeader.style.color = '#1a73e8'; t3.appendChild(tradeHeader);
const stRow = document.createElement('div'); stRow.style.cssText = 'display:flex; justify-content:space-between; align-items:center; margin-bottom:8px;';
const stLabel = document.createElement('label'); stLabel.style.cursor = 'pointer'; stLabel.style.color = '#3c4043';
const stCb = document.createElement('input'); stCb.type = 'checkbox'; stCb.checked = config.smartTrade.enabled; stCb.style.marginRight = '8px'; stCb.style.accentColor = '#1a73e8';
stCb.addEventListener('change', (e) => { config.smartTrade.enabled = e.target.checked; saveConfig(); });
stLabel.appendChild(stCb); stLabel.appendChild(document.createTextNode('启用级联逻辑')); stRow.appendChild(stLabel); t3.appendChild(stRow);
function createPriorityRow(label, pKey, isFinal = false) {
const container = document.createElement('div'); container.style.cssText = 'margin-bottom: 8px; padding-left: 12px; border-left: 2px solid #dadce0;';
const topRow = document.createElement('div'); topRow.style.cssText = 'display:flex; justify-content:space-between; align-items:center; margin-bottom:4px;';
const lbl = document.createElement('span'); lbl.innerHTML = label; lbl.style.fontSize='12px'; lbl.style.color = '#5f6368';
const raceSelect = document.createElement('select'); raceSelect.style.cssText = 'width: 110px; background: #f1f3f4; color: #202124; border: 1px solid transparent; border-radius: 6px; padding: 2px 4px; outline:none; font-size: 11px;';
if (gamePage.diplomacy && gamePage.diplomacy.races) { gamePage.diplomacy.races.forEach(race => { if (race.unlocked) { const opt = document.createElement('option'); opt.value = race.name; opt.text = race.title || race.name; raceSelect.appendChild(opt); }}); }
raceSelect.value = config.smartTrade[pKey].race;
topRow.appendChild(lbl); topRow.appendChild(raceSelect); container.appendChild(topRow);
if (!isFinal) {
const btmRow = document.createElement('div'); btmRow.style.cssText = 'display:flex; align-items:center;';
const range = document.createElement('input'); range.type = 'range'; range.min = '1'; range.max = '100'; range.value = config.smartTrade[pKey].percent;
range.style.cssText = 'flex-grow:1; height:4px; background:#dadce0; cursor:pointer; margin-right:8px; accent-color: #1a73e8;';
const valDisplay = document.createElement('span'); valDisplay.style.cssText = 'font-size:11px; color:#5f6368; width: 130px; text-align:right; white-space:nowrap; font-variant-numeric: tabular-nums;';
const updateDisplay = () => {
const race = raceSelect.value; const pct = parseInt(range.value);
config.smartTrade[pKey].race = race; config.smartTrade[pKey].percent = pct;
const resName = RACE_RES_MAP[race] || 'unknown'; let actual = 'N/A';
if (resName !== 'unknown') { try { const res = gamePage.resPool.get(resName); if (res && res.maxValue > 0) { actual = Math.floor(res.maxValue * (pct / 100)); if (actual > 1000000) actual = (actual/1000000).toFixed(2) + 'M'; else if (actual > 1000) actual = (actual/1000).toFixed(1) + 'K'; } else { actual = "无上限"; } } catch(e){} }
let resLabel = resName === 'unknown' ? '' : ` (${resName})`; valDisplay.innerText = `${pct}% ${resLabel} ≈ ${actual}`;
};
raceSelect.addEventListener('change', () => { updateDisplay(); saveConfig(); }); range.addEventListener('input', () => { updateDisplay(); saveConfig(); }); updateDisplay();
btmRow.appendChild(range); btmRow.appendChild(valDisplay); container.appendChild(btmRow);
} else { raceSelect.addEventListener('change', () => { config.smartTrade[pKey].race = raceSelect.value; saveConfig(); }); }
return container;
}
t3.appendChild(createPriorityRow("优先级 1 (P1):", 'p1')); t3.appendChild(createPriorityRow("优先级 2 (P2):", 'p2')); t3.appendChild(createPriorityRow("优先级 3 (兜底):", 'p3', true));
const stTip = document.createElement('div'); stTip.innerText = "* 逻辑: P1满 -> P2, P2满 -> P3\n* P3 无需设置阈值"; stTip.style.fontSize = '11px'; stTip.style.color = '#70757a'; stTip.style.paddingLeft = '12px'; t3.appendChild(stTip);
// 列维坦区域
const hrLev = document.createElement('hr'); hrLev.style.cssText = 'border: 0; border-top: 1px solid #e0e0e0; margin: 16px 0;'; t3.appendChild(hrLev);
const levHeader = document.createElement('div'); levHeader.innerHTML = '🌌 虚空主宰 (Leviathans)'; levHeader.style.marginBottom = '8px'; t3.appendChild(levHeader);
// 控件: 优先交易列维坦
const levTradeRow = document.createElement('div'); levTradeRow.style.cssText = 'display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; padding: 2px 4px;';
const levTradeLabel = document.createElement('label'); levTradeLabel.style.cssText = 'display: flex; align-items: center; cursor: pointer; color: #3c4043; font-size: 13px;';
const levTradeCb = document.createElement('input'); levTradeCb.type = 'checkbox'; levTradeCb.checked = config.leviathans.autoTrade;
levTradeCb.style.marginRight = '8px'; levTradeCb.style.accentColor = '#673ab7';
levTradeCb.addEventListener('change', (e) => { config.leviathans.autoTrade = e.target.checked; saveConfig(); });
levTradeLabel.appendChild(levTradeCb); levTradeLabel.appendChild(document.createTextNode('优先交易 (> 15k 难得素)'));
levTradeLabel.title = "当列维坦在场且难得素大于 15000 时,暂停 E 合金合成,优先全部交易";
levTradeRow.appendChild(levTradeLabel); t3.appendChild(levTradeRow);
// 控件: 自动喂食
const levFeedRow = document.createElement('div'); levFeedRow.style.cssText = 'display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; padding: 2px 4px;';
const levFeedLabel = document.createElement('label'); levFeedLabel.style.cssText = 'display: flex; align-items: center; cursor: pointer; color: #3c4043; font-size: 13px;';
const levFeedCb = document.createElement('input'); levFeedCb.type = 'checkbox'; levFeedCb.checked = config.leviathans.autoFeed;
levFeedCb.style.marginRight = '8px'; levFeedCb.style.accentColor = '#673ab7';
levFeedCb.addEventListener('change', (e) => { config.leviathans.autoFeed = e.target.checked; saveConfig(); });
levFeedLabel.appendChild(levFeedCb); levFeedLabel.appendChild(document.createTextNode('自动喂食死角兽'));
levFeedRow.appendChild(levFeedLabel); t3.appendChild(levFeedRow);
// T4: Profiles
const t4 = tabContents[3];
const profileHeader = document.createElement('div'); profileHeader.innerHTML = '📂 配置档案管理 (Profiles)'; profileHeader.style.marginBottom = '12px'; profileHeader.style.color = '#1a73e8'; t4.appendChild(profileHeader);
const btnStyle = 'border:none; color:white; font-size:12px; cursor:pointer; padding:6px 12px; border-radius:4px; transition: opacity 0.2s;';
const inputStyle2 = 'flex-grow:1; background:#f1f3f4; color:#202124; border:1px solid transparent; margin-right:8px; padding:6px; font-size:12px; border-radius:4px; outline:none;';
const saveRow = document.createElement('div'); saveRow.style.cssText = 'display:flex; justify-content:space-between; margin-bottom:12px;';
const nameInput = document.createElement('input'); nameInput.placeholder = '输入配置名称'; nameInput.style.cssText = inputStyle2;
const saveBtn = document.createElement('button'); saveBtn.innerText = '保存'; saveBtn.style.cssText = 'background:#188038; ' + btnStyle;
saveBtn.addEventListener('click', () => { if (saveProfile(nameInput.value)) { alert(`✅ [${nameInput.value}] 保存成功`); createUI(); } });
saveRow.appendChild(nameInput); saveRow.appendChild(saveBtn); t4.appendChild(saveRow);
const loadRow = document.createElement('div'); loadRow.style.cssText = 'display:flex; justify-content:space-between; align-items:center;';
const profileSelect = document.createElement('select'); profileSelect.style.cssText = inputStyle2;
Object.keys(getProfiles()).forEach(pName => { const opt = document.createElement('option'); opt.value = pName; opt.text = pName; profileSelect.appendChild(opt); });
const loadBtn = document.createElement('button'); loadBtn.innerText = '读取'; loadBtn.style.cssText = 'background:#1a73e8; margin-right:8px; ' + btnStyle;
loadBtn.addEventListener('click', () => { if (profileSelect.value && confirm(`读取 [${profileSelect.value}]?`)) loadProfile(profileSelect.value); });
const delBtn = document.createElement('button'); delBtn.innerText = '删除'; delBtn.style.cssText = 'background:#d93025; ' + btnStyle;
delBtn.addEventListener('click', () => { if (profileSelect.value && confirm(`删除 [${profileSelect.value}]?`)) { deleteProfile(profileSelect.value); createUI(); } });
loadRow.appendChild(profileSelect); loadRow.appendChild(loadBtn); loadRow.appendChild(delBtn); t4.appendChild(loadRow);
document.body.appendChild(panel);
let isDragging = false; let offsetX, offsetY;
header.addEventListener('mousedown', (e) => { isDragging = true; offsetX = e.clientX - panel.offsetLeft; offsetY = e.clientY - panel.offsetTop; header.style.cursor = 'grabbing'; });
document.addEventListener('mousemove', (e) => { if (isDragging) { panel.style.left = (e.clientX - offsetX) + 'px'; panel.style.top = (e.clientY - offsetY) + 'px'; } });
document.addEventListener('mouseup', () => { if (isDragging) { isDragging = false; header.style.cursor = 'move'; config.ui.posX = panel.style.left; config.ui.posY = panel.style.top; saveConfig(); } });
}
// ==========================================
// 3. 自动化逻辑核心
// ==========================================
const timers = {};
function checkAndCraftThreshold(resName, craftTargetName, configKey) {
if (!config.global.enabled) return;
if (!config[configKey].enabled) return;
try {
const actualThreshold = getActualThreshold(configKey);
if (gamePage.resPool.get(resName).value > actualThreshold) {
gamePage.craftAll(craftTargetName);
}
} catch (e) {}
}
function runSmartTradeCascade() {
if (!config.global.enabled) return;
if (!config.smartTrade.enabled) return;
const cfg = config.smartTrade;
let targetRace = cfg.p1.race;
if (isRaceResourceFull(cfg.p1.race, cfg.p1.percent)) {
targetRace = cfg.p2.race;
if (isRaceResourceFull(cfg.p2.race, cfg.p2.percent)) {
targetRace = cfg.p3.race;
}
}
if (targetRace && config.autoTrade.targetRace !== targetRace) {
const race = gamePage.diplomacy.get(targetRace);
if (race && race.unlocked) {
console.log(`【级联交易】切换目标: [${config.autoTrade.targetRace}] -> [${targetRace}]`);
config.autoTrade.targetRace = targetRace;
saveConfig();
// 如果UI存在,更新UI
const sel = document.getElementById('kg-assist-select-autoTrade-race');
if (sel) sel.value = targetRace;
}
}
}
function isRaceResourceFull(raceName, thresholdPercent) {
const resName = RACE_RES_MAP[raceName];
if (!resName) return false;
try {
const res = gamePage.resPool.get(resName);
if (!res || res.maxValue <= 0) return false;
return (res.value / res.maxValue) >= (thresholdPercent / 100);
} catch(e) { return false; }
}
function clickButtonByLabel(labelText) {
const allButtons = document.querySelectorAll('.btnContent');
for (let i = 0; i < allButtons.length; i++) {
if (allButtons[i].textContent.includes(labelText)) {
const container = allButtons[i].closest('.btn');
if (container && !container.classList.contains('disabled') && !container.classList.contains('grayed')) {
allButtons[i].click();
return true;
}
}
}
return false;
}
function mainLoopTask() {
if (!config.global.enabled) return;
if (config.starchart.enabled) { try { const btn = document.getElementById('observeBtn'); if (btn && btn.style.display !== 'none') btn.click(); } catch (e) {} }
if (gamePage && gamePage.resPool && gamePage.bld) {
// [独角兽牧场]
if (config.unicornPasture.enabled) {
try {
const bldName = 'unicornPasture';
const prices = gamePage.bld.getPrices(bldName);
if (prices && prices.length > 0) {
let canAfford = true;
for (let i = 0; i < prices.length; i++) {
const res = gamePage.resPool.get(prices[i].name);
if (!res || res.value < prices[i].val) { canAfford = false; break; }
}
if (canAfford) {
const bldMeta = gamePage.bld.get(bldName);
const label = bldMeta ? bldMeta.label : 'Unicorn Pasture';
if (clickButtonByLabel(label)) console.log(`【自动化】🦄 自动点击: [${label}]`);
else if (label !== 'Unicorn Pasture') clickButtonByLabel('Unicorn Pasture');
}
}
} catch (e) {}
}
// [通用阈值检测函数]
const checkThreshold = (cfg, resName) => {
if (!cfg.enabled || cfg.mode !== 'threshold') return false;
try {
const r = gamePage.resPool.get(resName);
if (r && r.maxValue > 0) {
return (r.value / r.maxValue) >= (cfg.thresholdPercent / 100);
}
} catch(e) {}
return false;
};
// 1. 猎人 (Manpower)
if (checkThreshold(config.hunters, 'manpower')) {
let shouldHunt = true;
if (config.smartHunterGold.enabled) {
const gold = gamePage.resPool.get('gold');
const furs = gamePage.resPool.get('furs');
const ivory = gamePage.resPool.get('ivory');
if (gold.value >= gold.maxValue && furs.value > 1000 && ivory.value > 1000) {
shouldHunt = false;
}
}
if (shouldHunt) gamePage.village.huntAll();
}
// 2. 交易 (Manpower)
if (checkThreshold(config.autoTrade, 'manpower')) {
const targetId = config.autoTrade.targetRace;
const race = gamePage.diplomacy.races.find(r => r.name === targetId);
if (race && race.unlocked) gamePage.diplomacy.tradeAll(race);
}
// 3. 手稿 (Culture)
if (checkThreshold(config.manuscript, 'culture')) {
gamePage.craftAll('manuscript');
}
// 4. 概要 & 蓝图 (Science)
// 逻辑: 如果两者都开启了阈值模式,优先合蓝图,剩下的再合概要
const sciThresholdMetComp = checkThreshold(config.compendium, 'science');
const sciThresholdMetBlue = checkThreshold(config.blueprint, 'science');
if (sciThresholdMetBlue) {
gamePage.craftAll('blueprint');
}
// 重新检测(或直接继续),如果蓝图合完后科学还是多(或者没开蓝图),合概要
if (sciThresholdMetComp) {
// 如果蓝图刚刚运行过,这里再做一次阈值检查比较安全
if (checkThreshold(config.compendium, 'science')) {
gamePage.craftAll('compedium');
}
}
// ==========================================
checkAndCraftThreshold('wood', 'beam', 'wood');
checkAndCraftThreshold('minerals', 'slab', 'minerals');
checkAndCraftThreshold('coal', 'steel', 'coal');
checkAndCraftThreshold('iron', 'plate', 'iron');
checkAndCraftThreshold('beam', 'scaffold', 'scaffold');
checkAndCraftThreshold('furs', 'parchment', 'parchment');
checkAndCraftThreshold('oil', 'kerosene', 'oilKerosene');
// [列维坦 & E合金 互斥逻辑]
let suppressEludiumCrafting = false;
if (config.leviathans.autoTrade) {
try {
const leviRace = gamePage.diplomacy.get("leviathans");
const unobtainium = gamePage.resPool.get("unobtainium");
if (leviRace && leviRace.unlocked) {
if (unobtainium.value > config.leviathans.minUnobtainium) {
suppressEludiumCrafting = true;
gamePage.diplomacy.tradeAll(leviRace);
}
}
} catch (e) { console.error('列维坦交易检测错误:', e); }
}
if (config.leviathans.autoFeed) {
try {
const necrocorn = gamePage.resPool.get("necrocorn");
if (necrocorn && necrocorn.value >= 1) {
gamePage.diplomacy.feedElders();
console.log("🦄 自动喂食死角兽");
}
} catch(e) {}
}
if (!suppressEludiumCrafting) {
checkAndCraftThreshold('unobtainium', 'eludium', 'eludium');
}
checkAndCraftThreshold('titanium', 'alloy', 'titaniumAlloy');
checkAndCraftThreshold('uranium', 'thorium', 'uraniumThorium');
runSmartTradeCascade();
if (config.catnipWood.enabled) {
try {
const catnipRes = gamePage.resPool.get('catnip');
const currentThreshold = getActualThreshold('catnipWood');
if (catnipRes.value > currentThreshold) {
const targetAmount = catnipRes.value * 0.20;
const craftActions = Math.floor(targetAmount / 100);
if (craftActions > 0) gamePage.craft('wood', craftActions);
}
} catch (e) {}
}
if (config.emergencyTradeCatnip.enabled) {
try {
const catnipRes = gamePage.resPool.get('catnip');
const threshold = catnipRes.maxValue * (config.emergencyTradeCatnip.thresholdPercent / 100);
if (catnipRes.value < threshold) {
const race = gamePage.diplomacy.races.find(r => r.name === 'sharks');
if (race && race.unlocked) {
gamePage.diplomacy.trade(race, 1);
}
}
} catch (e) {}
}
}
}
const tasks = {
hunters: () => {
if (!config.global.enabled) return;
if (config.hunters.mode === 'threshold') return;
try { if (gamePage.village.huntAll) { gamePage.village.huntAll(); console.log(`【自动化】✅ 派出猎人 (Timer)`); } } catch (e) {}
},
autoTrade: () => {
if (!config.global.enabled) return;
if (config.autoTrade.mode === 'threshold') return;
const targetId = config.autoTrade.targetRace;
if (!targetId || !gamePage.diplomacy) return;
try {
const race = gamePage.diplomacy.races.find(r => r.name === targetId);
if (race && race.unlocked) {
gamePage.diplomacy.tradeAll(race);
console.log(`【自动化】🤝 定时交易: [${race.title}]`);
}
} catch (e) { console.error(`交易出错:`, e); }
},
praise: () => { if (!config.global.enabled) return; try { if (gamePage.resPool.get('faith').value > 0) { gamePage.religion.praise(); console.log(`【自动化】☀️ 赞美太阳`); } } catch (e) {} },
// [修改] 下面三个任务增加了模式检查,防止双重触发
manuscript: () => {
if (!config.global.enabled) return;
if (config.manuscript.mode === 'threshold') return;
try { gamePage.craftAll('manuscript'); console.log(`【自动化】📜 合成手稿 (Timer)`); } catch (e) {}
},
compendium: () => {
if (!config.global.enabled) return;
if (config.compendium.mode === 'threshold') return;
try { gamePage.craftAll('compedium'); console.log(`【自动化】📚 合成概要 (Timer)`); } catch (e) {}
},
blueprint: () => {
if (!config.global.enabled) return;
if (config.blueprint.mode === 'threshold') return;
try { gamePage.craftAll('blueprint'); console.log(`【自动化】📘 合成蓝图 (Timer)`); } catch (e) {}
},
cloudSave: () => {
if (!config.global.enabled) return;
if (!config.cloudSave.enabled) return;
try {
let saved = false;
// 方法1: 调用 gamePage.save() 本地存档
if (gamePage.save) {
gamePage.save();
console.log(`【自动化】☁️ 本地存档完成`);
saved = true;
}
// 方法2: 调用 gamePage.server 保存到服务器
if (gamePage.server) {
if (gamePage.server.save) {
gamePage.server.save();
console.log(`【自动化】☁️ 服务器存档完成`);
saved = true;
} else if (typeof gamePage.server.toggle === 'function') {
gamePage.server.toggle();
console.log(`【自动化】☁️ 服务器存档切换`);
saved = true;
} else if (typeof gamePage.server === 'function') {
gamePage.server();
console.log(`【自动化】☁️ 服务器存档调用`);
saved = true;
}
}
// 方法3: 查找并点击所有可能的存档按钮
const allButtons = document.querySelectorAll('button');
for (const btn of allButtons) {
const text = btn.textContent.toLowerCase().trim();
if (text.includes('save') || text.includes('存档') || text.includes('cloud') || text.includes('云')) {
if (btn.offsetParent !== null && !btn.disabled) {
btn.click();
console.log(`【自动化】☁️ 点击存档按钮: "${text}"`);
saved = true;
break;
}
}
}
// 方法4: 查找链接形式的存档
const allLinks = document.querySelectorAll('a');
for (const link of allLinks) {
const text = link.textContent.toLowerCase().trim();
if (text.includes('save') || text.includes('存档') || text.includes('cloud')) {
if (link.offsetParent !== null) {
link.click();
console.log(`【自动化】☁️ 点击存档链接: "${text}"`);
saved = true;
break;
}
}
}
if (saved) {
console.log(`【自动化】☁️ 云存储完成 [${new Date().toLocaleTimeString()}]`);
} else {
console.log(`【自动化】⚠️ 未找到存档按钮`);
}
} catch (e) {
console.error(`【自动化】❌ 云存储出错:`, e);
}
}
};
function updateSpecificTimer(key) {
if (timers[key]) clearInterval(timers[key]);
if (config[key].enabled) {
// [修改] 统一检查所有支持模式切换的 key
const switchableKeys = ['hunters', 'autoTrade', 'manuscript', 'compendium', 'blueprint'];
if (switchableKeys.includes(key) && config[key].mode === 'threshold') {
console.log(`[设置] ${key} 已切换为阈值模式,关闭定时器。`);
return;
}
const intervalMs = Math.max((config[key].intervalMinutes || 60) * 60 * 1000, 60000);
timers[key] = setInterval(tasks[key], intervalMs);
console.log(`[设置] ${key} 定时器已更新,间隔: ${config[key].intervalMinutes} 分钟。`);
}
}
function init() {
var checkReady = setInterval(function() {
if (typeof gamePage !== 'undefined' && gamePage.ui && gamePage.resPool) {
clearInterval(checkReady);
if (window.kgAutoGlobalTimer) clearInterval(window.kgAutoGlobalTimer);
Object.values(timers).forEach(clearInterval);
createUI();
window.kgAutoGlobalTimer = setInterval(mainLoopTask, 2000);
Object.keys(tasks).forEach(key => updateSpecificTimer(key));
console.log('>>> 🐱 全能小助手 v7.8.24 (统一模式版) 启动成功! <<<');
}
}, 1000);
}
window.stopKgAutoAssist = function() {
if (window.kgAutoGlobalTimer) clearInterval(window.kgAutoGlobalTimer);
Object.values(timers).forEach(clearInterval);
const panel = document.getElementById('kg-auto-assist-panel');
if (panel) panel.remove();
const fab = document.getElementById('kg-auto-assist-fab');
if (fab) fab.remove();
const style = document.getElementById('kg-assist-styles');
if (style) style.remove();
};
document.addEventListener('keydown', function(event) {
if (event.key === 'F12') {
console.log('【小助手日志】🔍 全局开关:', config.global.enabled);
console.log('【小助手日志】🔍 完整配置:', config);
}
});
init();
})();