// ==UserScript== // @name OI 教练模拟器 - 开发者工具 // @namespace http://tampermonkey.net/ // @version 1.1 // @description OI教练模拟器开发者工具,支持编辑学生属性、天赋、晋级状态、预算、声誉等 // @author Oracynx // @match *://*/* // @grant none // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/555808/OI%20%E6%95%99%E7%BB%83%E6%A8%A1%E6%8B%9F%E5%99%A8%20-%20%E5%BC%80%E5%8F%91%E8%80%85%E5%B7%A5%E5%85%B7.user.js // @updateURL https://update.greasyfork.icu/scripts/555808/OI%20%E6%95%99%E7%BB%83%E6%A8%A1%E6%8B%9F%E5%99%A8%20-%20%E5%BC%80%E5%8F%91%E8%80%85%E5%B7%A5%E5%85%B7.meta.js // ==/UserScript== (function () { 'use strict'; // 检测是否为OI教练模拟器 function isOICoachSimulator() { console.log('开始检测OI教练模拟器...'); // 检查关键特征 const hasGameObject = typeof window.game !== 'undefined'; console.log('检测到game对象:', hasGameObject); const hasStudents = hasGameObject && Array.isArray(window.game.students); console.log('检测到学生数据:', hasStudents); const hasWeek = hasGameObject && typeof window.game.week === 'number'; console.log('检测到周数:', hasWeek); const hasBudget = hasGameObject && typeof window.game.budget === 'number'; console.log('检测到预算:', hasBudget); // 放宽检测条件:只要有game对象和学生数据就认为是模拟器 const isSimulator = hasGameObject && hasStudents; console.log('最终检测结果:', isSimulator); if (!isSimulator) { console.log('检测失败原因:'); if (!hasGameObject) console.log('- 缺少game对象'); if (!hasStudents) console.log('- 缺少学生数据'); } return isSimulator; } // 主面板类 class OIDevTools { constructor() { this.isVisible = false; this.panel = null; this.currentTab = 'students'; this.init(); } init() { this.createPanel(); this.bindEvents(); this.injectStyles(); } createPanel() { this.panel = document.createElement('div'); this.panel.id = 'oi-dev-tools'; this.panel.innerHTML = this.getPanelHTML(); this.panel.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 1200px; height: 80vh; background: white; border: 2px solid #333; border-radius: 10px; box-shadow: 0 0 20px rgba(0,0,0,0.5); z-index: 10000; display: none; font-family: Arial, sans-serif; overflow: hidden; `; document.body.appendChild(this.panel); } getPanelHTML() { const studentsTabDisplay = this.currentTab === 'students' ? 'block' : 'none'; const gameTabDisplay = this.currentTab === 'game' ? 'block' : 'none'; const qualificationsTabDisplay = this.currentTab === 'qualifications' ? 'block' : 'none'; const talentsTabDisplay = this.currentTab === 'talents' ? 'block' : 'none'; const advancedTabDisplay = this.currentTab === 'advanced' ? 'block' : 'none'; const studentsActive = this.currentTab === 'students' ? 'active' : ''; const gameActive = this.currentTab === 'game' ? 'active' : ''; const qualificationsActive = this.currentTab === 'qualifications' ? 'active' : ''; const talentsActive = this.currentTab === 'talents' ? 'active' : ''; const advancedActive = this.currentTab === 'advanced' ? 'active' : ''; return '
' + '' + '
' + '

开发者工具

' + '
学生管理
' + '
游戏状态
' + '
晋级状态
' + '
天赋管理
' + '
高级功能
' + '
' + '' + '' + '
' + '
' + '' + '
' + '
' + this.getStudentsTabHTML() + '
' + '
' + this.getGameTabHTML() + '
' + '
' + this.getQualificationsTabHTML() + '
' + '
' + this.getTalentsTabHTML() + '
' + '
' + this.getAdvancedTabHTML() + '
' + '
' + '
'; } getStudentsTabHTML() { const students = window.game?.students || []; let tableRows = ''; students.forEach((student, index) => { tableRows += this.getStudentRowHTML(student, index); }); return '

学生管理

' + '
' + '' + '' + '
' + '
' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + tableRows + '' + '
姓名能力思维编码压力心理状态操作
' + '
'; } getStudentRowHTML(student, index) { const isActive = student.active !== false; const selectedActive = isActive ? 'selected' : ''; const selectedInactive = !isActive ? 'selected' : ''; return '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + ''; } getGameTabHTML() { const game = window.game || {}; return `

游戏状态编辑

基础信息

快速操作

`; } getQualificationsTabHTML() { const game = window.game || {}; const qualifications = game.qualification || {}; const students = game.students || []; const compOrder = window.COMPETITION_ORDER || ["CSP-S1", "CSP-S2", "NOIP", "省选", "NOI"]; return `

晋级状态管理

当前赛季: ${game.week > 13 ? '第二赛季' : '第一赛季'}

${compOrder.map(comp => this.getCompetitionQualificationHTML(comp, qualifications, students)).join('')}
`; } getCompetitionQualificationHTML(compName, qualifications, students) { const seasonIndex = window.game?.week > 13 ? 1 : 0; const qualified = qualifications[seasonIndex]?.[compName] || new Set(); const qualifiedArray = Array.from(qualified); return `

${compName}

${students.map(student => `
`).join('')}
`; } getTalentsTabHTML() { const talentManager = window.TalentManager; const registeredTalents = talentManager ? Object.keys(talentManager._talents || {}) : []; return `

天赋管理

已注册天赋

${registeredTalents.map(talent => `
${talent}
${talentManager.getTalent(talent)?.description || '暂无描述'}
`).join('')}

批量天赋操作

`; } getAdvancedTabHTML() { return `

高级功能

数据操作

调试功能

自定义命令

`; } injectStyles() { const style = document.createElement('style'); style.textContent = ` .tab-button { padding: 10px; margin-bottom: 5px; cursor: pointer; border-radius: 4px; transition: background-color 0.2s; } .tab-button:hover { background: #e0e0e0; } .tab-button.active { background: #2196F3; color: white; } input, select, textarea { border: 1px solid #ccc; border-radius: 4px; } input:focus, select:focus, textarea:focus { outline: none; border-color: #2196F3; box-shadow: 0 0 5px rgba(33, 150, 243, 0.3); } button { cursor: pointer; transition: background-color 0.2s; } button:hover { opacity: 0.9; } `; document.head.appendChild(style); } bindEvents() { // 标签切换 this.panel.addEventListener('click', (e) => { if (e.target.classList.contains('tab-button')) { this.switchTab(e.target.dataset.tab); } }); // 关闭面板 this.panel.addEventListener('click', (e) => { if (e.target.id === 'close-panel') { this.hide(); } }); // 保存游戏 this.panel.addEventListener('click', (e) => { if (e.target.id === 'save-game') { this.saveGame(); } }); // 学生管理事件 this.panel.addEventListener('click', (e) => { if (e.target.classList.contains('delete-student')) { this.deleteStudent(parseInt(e.target.dataset.index)); } else if (e.target.id === 'add-student') { this.addStudent(); } else if (e.target.id === 'refresh-students') { this.refreshStudents(); } else if (e.target.classList.contains('edit-talents')) { this.showEditTalentsModal(parseInt(e.target.dataset.index)); } }); // 学生属性实时更新 this.panel.addEventListener('change', (e) => { if (e.target.type === 'text' || e.target.type === 'number' || e.target.tagName === 'SELECT') { const index = parseInt(e.target.dataset.index); const field = e.target.dataset.field; const value = e.target.type === 'number' ? parseInt(e.target.value) : e.target.tagName === 'SELECT' ? e.target.value === 'true' : e.target.value; if (!isNaN(index) && field) { this.updateStudentField(index, field, value); } } }); // 游戏状态事件 this.panel.addEventListener('click', (e) => { const game = window.game; if (!game) return; if (e.target.id === 'add-budget') { game.budget = (game.budget || 0) + 10000; this.updateGameFields(); } else if (e.target.id === 'add-reputation') { game.reputation = Math.min(100, (game.reputation || 0) + 10); this.updateGameFields(); } else if (e.target.id === 'next-week') { game.week = (game.week || 1) + 1; this.updateGameFields(); } else if (e.target.id === 'reset-pressure') { this.resetAllPressure(); } else if (e.target.id === 'apply-game-changes') { this.applyGameChanges(); } }); // 晋级状态事件 this.panel.addEventListener('change', (e) => { if (e.target.type === 'checkbox' && e.target.dataset.comp && e.target.dataset.student) { this.updateQualification(e.target.dataset.comp, e.target.dataset.student, e.target.checked); } }); this.panel.addEventListener('click', (e) => { if (e.target.classList.contains('select-all')) { this.selectAllQualifications(e.target.dataset.comp); } else if (e.target.classList.contains('clear-all')) { this.clearAllQualifications(e.target.dataset.comp); } }); // 天赋管理事件 this.panel.addEventListener('click', (e) => { if (e.target.id === 'add-talent-all') { this.addTalentToAll(); } else if (e.target.id === 'remove-talent-all') { this.removeTalentFromAll(); } }); // 高级功能事件 this.panel.addEventListener('click', (e) => { if (e.target.id === 'export-save') { this.exportSave(); } else if (e.target.id === 'import-save') { this.importSave(); } else if (e.target.id === 'reset-game') { this.resetGame(); } else if (e.target.id === 'execute-command') { this.executeCustomCommand(); } }); } switchTab(tabName) { this.currentTab = tabName; // 更新标签按钮状态 this.panel.querySelectorAll('.tab-button').forEach(btn => { btn.classList.toggle('active', btn.dataset.tab === tabName); }); // 更新内容区域 this.panel.querySelectorAll('.tab-content').forEach(content => { content.style.display = content.id === `${tabName}-tab` ? 'block' : 'none'; }); // 刷新特定标签的数据 if (tabName === 'students') { this.refreshStudents(); } else if (tabName === 'game') { this.updateGameFields(); } } refreshStudents() { const studentsTab = this.panel.querySelector('#students-tab'); studentsTab.innerHTML = this.getStudentsTabHTML(); } updateStudentField(index, field, value) { const students = window.game?.students; if (students && students[index]) { // 如果修改的是姓名,需要处理晋级状态的数据同步 if (field === 'name') { const oldName = students[index].name; // 保存旧姓名 students[index][field] = value; this.updateQualificationNames(oldName, value); // 更新晋级状态中的姓名 this.showNotification(`已更新学生姓名从 "${oldName}" 到 "${value}"`); } else { students[index][field] = value; this.showNotification(`已更新 ${students[index].name} 的 ${field}`); } // 刷新游戏UI if (typeof window.renderAll === 'function') { window.renderAll(); } } } deleteStudent(index) { const students = window.game?.students; if (students && students[index]) { const studentName = students[index].name; if (confirm(`确定要删除学生 "${studentName}" 吗?`)) { students.splice(index, 1); this.refreshStudents(); this.showNotification(`已删除学生 ${studentName}`); } } } addStudent() { const students = window.game?.students || []; const newStudent = { name: `新生${students.length + 1}`, ability: 50, thinking: 50, coding: 50, pressure: 0, mental: 80, active: true, talents: new Set() }; students.push(newStudent); this.refreshStudents(); this.showNotification('已添加新学生'); } updateGameFields() { const game = window.game; if (!game) return; const weekInput = this.panel.querySelector('#game-week'); const budgetInput = this.panel.querySelector('#game-budget'); const reputationInput = this.panel.querySelector('#game-reputation'); const provinceSelect = this.panel.querySelector('#game-province-type'); if (weekInput) weekInput.value = game.week || 1; if (budgetInput) budgetInput.value = game.budget || 0; if (reputationInput) reputationInput.value = game.reputation || 0; if (provinceSelect) provinceSelect.value = game.province_type || '普通省'; } applyGameChanges() { const game = window.game; if (!game) return; const weekInput = this.panel.querySelector('#game-week'); const budgetInput = this.panel.querySelector('#game-budget'); const reputationInput = this.panel.querySelector('#game-reputation'); const provinceSelect = this.panel.querySelector('#game-province-type'); if (weekInput) game.week = parseInt(weekInput.value) || 1; if (budgetInput) game.budget = parseInt(budgetInput.value) || 0; if (reputationInput) game.reputation = parseInt(reputationInput.value) || 0; if (provinceSelect) game.province_type = provinceSelect.value; this.showNotification('游戏状态已更新'); // 刷新游戏UI if (typeof window.renderAll === 'function') { window.renderAll(); } } resetAllPressure() { const students = window.game?.students || []; students.forEach(student => { student.pressure = 0; }); // 刷新学生表格和游戏UI this.refreshStudents(); if (typeof window.renderAll === 'function') { window.renderAll(); } this.showNotification('已重置所有学生压力'); } updateQualification(compName, studentName, qualified) { const game = window.game; if (!game) return; const seasonIndex = game.week > 13 ? 1 : 0; if (!game.qualification) game.qualification = {}; if (!game.qualification[seasonIndex]) game.qualification[seasonIndex] = {}; if (!game.qualification[seasonIndex][compName]) game.qualification[seasonIndex][compName] = new Set(); if (qualified) { game.qualification[seasonIndex][compName].add(studentName); } else { game.qualification[seasonIndex][compName].delete(studentName); } // 刷新游戏UI if (typeof window.renderAll === 'function') { window.renderAll(); } } selectAllQualifications(compName) { const students = window.game?.students || []; students.forEach(student => { this.updateQualification(compName, student.name, true); }); // 更新UI const checkboxes = this.panel.querySelectorAll(`input[data-comp="${compName}"]`); checkboxes.forEach(checkbox => { checkbox.checked = true; }); this.showNotification(`已为所有学生添加 ${compName} 晋级资格`); } clearAllQualifications(compName) { const game = window.game; if (!game) return; const seasonIndex = game.week > 13 ? 1 : 0; if (game.qualification && game.qualification[seasonIndex]) { delete game.qualification[seasonIndex][compName]; } // 更新UI const checkboxes = this.panel.querySelectorAll(`input[data-comp="${compName}"]`); checkboxes.forEach(checkbox => { checkbox.checked = false; }); this.showNotification(`已清空 ${compName} 晋级资格`); } addTalentToAll() { const talentSelect = this.panel.querySelector('#bulk-talent-select'); const talentName = talentSelect?.value; if (!talentName) { alert('请先选择天赋'); return; } const students = window.game?.students || []; students.forEach(student => { if (!student.talents) student.talents = new Set(); student.talents.add(talentName); }); // 刷新学生表格和游戏UI this.refreshStudents(); if (typeof window.renderAll === 'function') { window.renderAll(); } this.showNotification(`已为所有学生添加天赋: ${talentName}`); } removeTalentFromAll() { const talentSelect = this.panel.querySelector('#bulk-talent-select'); const talentName = talentSelect?.value; if (!talentName) { alert('请先选择天赋'); return; } const students = window.game?.students || []; students.forEach(student => { if (student.talents) { student.talents.delete(talentName); } }); // 刷新学生表格和游戏UI this.refreshStudents(); if (typeof window.renderAll === 'function') { window.renderAll(); } this.showNotification(`已从所有学生移除天赋: ${talentName}`); } exportSave() { const gameData = window.game; if (!gameData) { alert('没有找到游戏数据'); return; } const dataStr = JSON.stringify(gameData, null, 2); const dataBlob = new Blob([dataStr], { type: 'application/json' }); const url = URL.createObjectURL(dataBlob); const link = document.createElement('a'); link.href = url; link.download = `oi-coach-save-${new Date().toISOString().split('T')[0]}.json`; link.click(); URL.revokeObjectURL(url); this.showNotification('存档已导出'); } importSave() { const input = document.createElement('input'); input.type = 'file'; input.accept = '.json'; input.onchange = (e) => { const file = e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = (event) => { try { const saveData = JSON.parse(event.target.result); Object.assign(window.game, saveData); this.showNotification('存档已导入'); // 刷新所有数据 this.refreshStudents(); this.updateGameFields(); // 刷新游戏UI if (typeof window.renderAll === 'function') { window.renderAll(); } } catch (error) { alert('导入失败: ' + error.message); } }; reader.readAsText(file); }; input.click(); } resetGame() { if (confirm('确定要重置游戏吗?所有进度将会丢失!')) { if (typeof window.resetGame === 'function') { window.resetGame(); } else { // 简单重置 window.game = { week: 1, budget: 50000, reputation: 50, students: [], qualification: {} }; } this.showNotification('游戏已重置'); this.refreshStudents(); this.updateGameFields(); } } executeCustomCommand() { const commandTextarea = this.panel.querySelector('#custom-command'); const command = commandTextarea.value.trim(); if (!command) { alert('请输入命令'); return; } try { const result = eval(command); console.log('命令执行结果:', result); this.showNotification('命令执行成功'); } catch (error) { alert('命令执行失败: ' + error.message); } } // 显示编辑天赋模态框 showEditTalentsModal(studentIndex) { const students = window.game?.students; if (!students || !students[studentIndex]) return; const student = students[studentIndex]; const talentManager = window.TalentManager; const registeredTalents = talentManager ? Object.keys(talentManager._talents || {}) : []; const studentTalents = student.talents ? Array.from(student.talents) : []; // 创建天赋列表HTML let talentsHTML = ''; registeredTalents.forEach(talentName => { const isSelected = studentTalents.includes(talentName); const talentDef = talentManager.getTalent(talentName); const description = talentDef?.description || '暂无描述'; const color = talentDef?.color || '#2b6cb0'; talentsHTML += `
`; }); const modalHTML = ` `; // 创建模态框 const modalContainer = document.createElement('div'); modalContainer.innerHTML = modalHTML; document.body.appendChild(modalContainer); // 绑定事件 const saveBtn = modalContainer.querySelector('#save-talents'); const cancelBtn = modalContainer.querySelector('#cancel-talents'); saveBtn.onclick = () => { // 获取选中的天赋 const checkboxes = modalContainer.querySelectorAll('input[type="checkbox"]'); const selectedTalents = []; checkboxes.forEach(checkbox => { if (checkbox.checked) { selectedTalents.push(checkbox.value); } }); // 更新学生天赋 if (!student.talents) student.talents = new Set(); student.talents.clear(); selectedTalents.forEach(talent => student.talents.add(talent)); // 移除模态框 modalContainer.remove(); // 刷新学生表格以更新视图 this.refreshStudents(); // 刷新游戏UI(如果存在) if (typeof window.renderAll === 'function') { window.renderAll(); } this.showNotification(`已更新 ${student.name} 的天赋`); }; cancelBtn.onclick = () => { modalContainer.remove(); }; // 点击背景关闭 modalContainer.querySelector('.modal').onclick = (e) => { if (e.target === e.currentTarget) { modalContainer.remove(); } }; } saveGame() { if (typeof window.saveGame === 'function') { window.saveGame(); this.showNotification('游戏已保存'); } else { alert('保存功能不可用'); } } showNotification(message) { // 创建通知元素 const notification = document.createElement('div'); notification.textContent = message; notification.style.cssText = ` position: fixed; top: 20px; right: 20px; background: #4CAF50; color: white; padding: 10px 20px; border-radius: 4px; z-index: 10001; box-shadow: 0 2px 5px rgba(0,0,0,0.2); `; document.body.appendChild(notification); // 3秒后自动移除 setTimeout(() => { if (notification.parentNode) { notification.parentNode.removeChild(notification); } }, 3000); } // 更新晋级状态中的学生姓名 updateQualificationNames(oldName, newName) { const game = window.game; if (!game || !game.qualification) return; // 遍历所有赛季和比赛,更新晋级状态中的姓名 for (const seasonIndex in game.qualification) { const season = game.qualification[seasonIndex]; for (const compName in season) { const qualifiedSet = season[compName]; if (qualifiedSet.has(oldName)) { qualifiedSet.delete(oldName); qualifiedSet.add(newName); console.log(`[晋级状态更新] 赛季${seasonIndex} ${compName}: "${oldName}" → "${newName}"`); } } } // 同时更新国家集训队相关数据 if (game.nationalTeamResults) { // 更新CTT成绩 if (game.nationalTeamResults.cttScores) { game.nationalTeamResults.cttScores.forEach(score => { if (score.studentName === oldName) { score.studentName = newName; } }); } // 更新CTS成绩 if (game.nationalTeamResults.ctsScores) { game.nationalTeamResults.ctsScores.forEach(score => { if (score.studentName === oldName) { score.studentName = newName; } }); } // 更新IOI晋级名单 if (game.nationalTeamResults.ioiQualified) { const index = game.nationalTeamResults.ioiQualified.indexOf(oldName); if (index !== -1) { game.nationalTeamResults.ioiQualified[index] = newName; } } } // 更新职业生涯记录 if (game.careerCompetitions) { game.careerCompetitions.forEach(comp => { comp.entries.forEach(entry => { if (entry.name === oldName) { entry.name = newName; } }); }); } this.showNotification(`已同步晋级状态数据: "${oldName}" → "${newName}"`); } show() { this.isVisible = true; this.panel.style.display = 'block'; this.refreshStudents(); this.updateGameFields(); } hide() { this.isVisible = false; this.panel.style.display = 'none'; } toggle() { if (this.isVisible) { this.hide(); } else { this.show(); } } } // 创建开发者工具按钮 function createDevButton() { const devButton = document.createElement('button'); devButton.innerHTML = '🔧 Dev'; devButton.id = 'oi-dev-button'; devButton.style.cssText = ` position: fixed; top: 10px; left: 10px; background: #4CAF50; color: white; border: none; border-radius: 4px; padding: 8px 16px; font-size: 14px; cursor: pointer; z-index: 9998; box-shadow: 0 2px 5px rgba(0,0,0,0.2); transition: background-color 0.2s; `; // 悬停效果 devButton.addEventListener('mouseenter', () => { devButton.style.background = '#45a049'; }); devButton.addEventListener('mouseleave', () => { devButton.style.background = '#4CAF50'; }); return devButton; } // 主函数 function main() { const isSimulator = isOICoachSimulator(); if (!isSimulator) { console.log('未检测到OI教练模拟器,开发者工具未启用'); return; } console.log('检测到OI教练模拟器,正在启用开发者工具...'); let devTools = null; // 创建开发者按钮 const devButton = createDevButton(); document.body.appendChild(devButton); // 按钮点击事件 devButton.addEventListener('click', () => { if (!devTools) { devTools = new OIDevTools(); } devTools.toggle(); }); } // 等待DOM加载完成后初始化 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', main); } else { main(); } })();