(async function() { if (window._mvuFinalStableV110) return; await waitGlobalInitialized('Mvu'); console.log('✅ MVU ZOD 最終穩定後處理 v11.0 已啓用'); var _ = window._; var get = function(o, p) { return _.get(o, p); }; var set = function(o, p, v) { return _.set(o, p, v); }; var isProcessing = false; var lastSnapshot = null; var fixCount = 0; var delCount = 0; var lastLogHash = ''; // ==================== Debug 模式開關 ==================== var DEBUG_MODE = false; // ← 設為 true 開啟詳細 Log function debugLog() { if (DEBUG_MODE) { console.log('[🔍]', ...arguments); } } function keyLog() { console.log('[📌]', ...arguments); } function errorLog() { console.error('[❌]', ...arguments); } // ==================== 整數路徑(根據新設計更新) ==================== var integerPaths = [ '生理.當前生命值', '精神.當前精神值', '生理.碳水', '生理.脂肪', '生理.氮平衡', '生理.蛋白質', '生理.維生素', '生理.水分', '生理.排泄欲', '精神.壓力', '精神.無聊', '精神.孤獨', '精神.睡眠', '精神.性慾', '生理.飽腹感', '狀態.表皮溼度', '狀態.疲勞', '狀態.清潔', '生理.免疫負荷', '狀態.免疫力', '同伴.*.生理狀態.流產風險累積', '同伴.*.生理狀態.流產次數', '同伴.*.生理狀態.上次流產日', '同伴.*.生理狀態.流產待處理' ]; var decayRates = [0.06, 0.15, 0.35, 0.8, 2.4]; // ==================== 擴充功能配置常數(不變) ==================== var MOON_PHASE_CONFIG = { LUNAR_CYCLE: 29.53, PHASES: [ { emoji: '🌑新月', min: 27, max: 29.53 }, { emoji: '🌒娥眉月', min: 0, max: 3 }, { emoji: '🌓上弦月', min: 4, max: 7 }, { emoji: '🌔盈凸月', min: 8, max: 11 }, { emoji: '🌕滿月', min: 12, max: 16 }, { emoji: '🌖虧凸月', min: 17, max: 20 }, { emoji: '🌗下弦月', min: 21, max: 23 }, { emoji: '🌘殘月', min: 24, max: 26 } ] }; var SEASON_CONFIG = { SPRING_DAYS: 30, SUMMER_DAYS: 30, AUTUMN_DAYS: 30, WINTER_DAYS: 30, SEASON_ORDER: ['春季', '夏季', '秋季', '冬季'] }; var SEASON_GROWTH_BONUS = { '春季': 15, '夏季': 20, '秋季': 0, '冬季': -50 }; var PREGNANCY_CONFIG = { STAGES: [ { name: '懷孕初期', maxDays: 30 }, { name: '懷孕中期', maxDays: 60 }, { name: '懷孕後期', maxDays: 90 } ], AUTO_BIRTH: true }; var LIVESTOCK_CONFIG = { GROWTH_STAGES: { '幼年期': { minAge: 0, maxAge: 3, canBreed: false }, '成長期': { minAge: 3, maxAge: 12, canBreed: false }, '成年期': { minAge: 12, maxAge: 72, canBreed: true }, '老年期': { minAge: 72, maxAge: 999, canBreed: false } }, LIFESPAN: { '野雞': 60, '野山羊': 120, '馬鹿': 180, '野兔': 84 }, SEASON_PRODUCTION_BONUS: { '春季': 10, '夏季': 5, '秋季': 0, '冬季': -20 }, BREEDING_CONFIG: { BASE_BREEDING_CHANCE: 0.25, MIN_BREEDING_AGE: 12, BREEDING_COOLDOWN: { '野雞': 30, '野山羊': 45, '馬鹿': 60, '野兔': 20 }, GENDER_RATIO: { male: 0.48, female: 0.52 } } }; var AFFECTION_SYSTEM = { STAGES: { 1: { name: '陌生', range: [1,1], 模糊地帶: false, intimacyTolerance: 0, romanticThreshold: 0 }, 2: { name: '朋友-穩定期', range: [2,2], 模糊地帶: false, intimacyTolerance: 10, romanticThreshold: 10 }, 3: { name: '朋友-親密臨界區', range: [3,3], 模糊地帶: true, intimacyTolerance: 30, romanticThreshold: 20 }, 4: { name: '摯友-深度期', range: [4,5], 模糊地帶: false, intimacyTolerance: 40, romanticThreshold: 30 }, 6: { name: '摯友-戀人臨界區', range: [6,7], 模糊地帶: true, intimacyTolerance: 65, romanticThreshold: 50 }, 8: { name: '戀人-新晉期', range: [8,8], 模糊地帶: false, intimacyTolerance: 85, romanticThreshold: 70 }, 9: { name: '戀人-穩定臨界區', range: [9,9], 模糊地帶: true, intimacyTolerance: 95, romanticThreshold: 85 }, 10: { name: '摯愛', range: [10,999], 模糊地帶: false, intimacyTolerance: 100, romanticThreshold: 100 } }, getStage: function(level) { var l = Number(level); if (isNaN(l) || l < 1) return this.STAGES[1]; for (var key in this.STAGES) { var stage = this.STAGES[key]; if (l >= stage.range[0] && l <= stage.range[1]) return stage; } return this.STAGES[10]; }, getStageName: function(level) { return this.getStage(level).name; }, isInGrayZone: function(level) { return this.getStage(level).模糊地帶; } }; var SEXUAL_CONFIG = { CORRUPTION_STAGES: { 0: { name: '初次被迫', minCount: 1, maxCount: 1 }, 1: { name: '重複暴露', minCount: 2, maxCount: 3 }, 2: { name: '生理轉折點', minCount: 4, maxCount: 4 }, 3: { name: '矛盾加劇', minCount: 5, maxCount: 7 }, 4: { name: '開始沉淪', minCount: 8, maxCount: 999 } } }; // ======================== 工具函數 ======================== function int(v) { return Number.isFinite(v) ? Math.round(v) : v; } function clamp(v, min, max) { return Math.max(min, Math.min(max, v)); } function v(val, def) { return Number.isFinite(val) ? val : (Number.isFinite(def) ? def : 0); } // ========== 🔴 新增:性愛結果格式轉換(向後兼容) ========== function ensureSexResultArray(vars) { var raw = get(vars, '系統.性愛結果'); // 如果是空值或 undefined,初始化為空陣列 if (raw === undefined || raw === null) { set(vars, '系統.性愛結果', []); return []; } // 如果已經是陣列,直接返回 if (Array.isArray(raw)) { return raw; } // 🔴 重點:如果是物件(舊格式),轉換為陣列 if (typeof raw === 'object' && Object.keys(raw).length > 0) { // 檢查是否是舊格式(有 角色 字段) if (raw.角色) { var migrated = [raw]; set(vars, '系統.性愛結果', migrated); keyLog('🔄 性愛結果格式轉換:物件 → 陣列(1筆記錄)'); return migrated; } } // 其他情況(空物件或異常),設為空陣列 set(vars, '系統.性愛結果', []); return []; } function parseTime(str) { var m = /^(\d{1,2}):(\d{2})$/.exec(str); return m ? parseInt(m[1]) * 60 + parseInt(m[2]) : null; } function parseFullTime(str) { var m = /第(\d+)天\s+(\d{1,2}):(\d{2})/.exec(str); if (!m) return null; var d = parseInt(m[1]), h = parseInt(m[2]), mm = parseInt(m[3]); return { day: d, hour: h, minute: mm, total: d * 1440 + h * 60 + mm }; } function getCurrentTime(vars) { var day = get(vars, '系統.經過天數'), time = get(vars, '系統.當前時間'); if (!Number.isFinite(day) || typeof time != 'string') return null; var m = parseTime(time); return m === null ? null : { day: day, hour: Math.floor(m / 60), minute: m % 60, total: day * 1440 + m }; } function fixTimeFormat(vars) { var currentTime = get(vars, '系統.當前時間'); if (!currentTime || typeof currentTime !== 'string') return false; // 匹配字串結尾的 HH:MM 或 H:MM var timePattern = /(\d{1,2}):(\d{2})$/; var match = timePattern.exec(currentTime); if (match) { var hour = match[1].padStart(2, '0'); var minute = match[2]; var formattedTime = hour + ':' + minute; // 只有當原本不是純 HH:MM 格式時才修正 if (currentTime !== formattedTime) { set(vars, '系統.當前時間', formattedTime); keyLog('⏰ 時間格式修正: "' + currentTime + '" → "' + formattedTime + '"'); fixCount++; return true; } } return false; } function scaleValue(oldVal, oldMax, newMax) { if (!Number.isFinite(oldVal) || !Number.isFinite(oldMax) || !Number.isFinite(newMax) || oldMax <= 0 || newMax <= 0) return oldVal; return Math.round(oldVal / oldMax * newMax); } function calculateTimeDifference(time1, time2) { if (!time1 || !time2) return 0; var h1 = parseInt(time1.split(':')[0]), m1 = parseInt(time1.split(':')[1]); var h2 = parseInt(time2.split(':')[0]), m2 = parseInt(time2.split(':')[1]); var minutes = (h2 * 60 + m2) - (h1 * 60 + m1); if (minutes < 0) minutes += 1440; return minutes / 60; } function isItemInvalid(item) { // 檢查是否為有效物件 if (!item || typeof item !== 'object') return true; // 🔴 空物件直接視為無效 if (Object.keys(item).length === 0) return true; var q = get(item, '數量'), s = get(item, '狀態'), w = get(item, '重量'); // 有數量欄位但數量無效 if (q !== undefined && !(Number(q) > 0)) return true; // 有狀態欄位但狀態無效 if (s !== undefined && !(Number(s) > 0)) return true; // 有重量欄位但重量無效 if (w !== undefined && !(Number(w) > 0)) return true; // 🔴 沒有任何關鍵欄位(數量、狀態、重量),視為結構異常的無效物品 if (q === undefined && s === undefined && w === undefined) return true; return false; } function tempFactor(t) { if (t < 0) return 0.1; if (t < 10) return 0.3; if (t < 20) return 0.6; if (t < 30) return 1; if (t < 40) return 1.5; return 1.8; } function updateDesc(desc, val) { var d = desc || ''; var patterns = [ new RegExp('(看起來不太新鮮的樣子)$'), new RegExp('(開始有些變質的跡象)$'), new RegExp('(明顯變質了,需要儘快處理)$'), new RegExp('(已經壞了,可能不適合使用)$'), new RegExp('(腐爛了,使用可能會有危險)$') ]; patterns.forEach(function(r) { if (r.test(d)) d = d.replace(r, ''); }); d = d.trim(); if (val >= 50) return d; if (val >= 40) return d + '(看起來不太新鮮的樣子)'; if (val >= 30) return d + '(開始有些變質的跡象)'; if (val >= 20) return d + '(明顯變質了,需要儘快處理)'; if (val >= 10) return d + '(已經壞了,可能不適合使用)'; return d + '(腐爛了,使用可能會有危險)'; } function checkRequirement(req) { if (!_.isPlainObject(req)) return req; if (typeof req.進度 == 'number' && typeof req.目標 == 'number') { if (req.進度 >= req.目標 && !req.已完成) req.已完成 = true; else if (req.進度 < req.目標 && req.已完成) req.已完成 = false; } return req; } function logChange(action, detail) { var hash = action + ':' + detail; if (hash === lastLogHash) return; lastLogHash = hash; console.log('🔧 ' + action + ': ' + detail); } function updateStatus(vars, status, msg) { var display; switch (status) { case 'fixed': display = '✅ 已修正'; break; case 'normal': display = '✅ 無需修正'; break; case 'skipped': display = '⏭️ 未執行'; break; case 'error': display = '❌ 有錯誤'; break; default: display = '🔄 未知'; } set(vars, '系統.腳本修正狀態', display); console.log('📊 腳本狀態: ' + display + ' - ' + msg); } // ==================== 整數化與上下限修正(獨立函數) ==================== function applyIntegerAndBounds(vars) { var changed = false; var allNumericPaths = [ '生理.當前生命值', '生理.生命值上限', '生理.碳水', '生理.脂肪', '生理.氮平衡', '生理.蛋白質', '生理.維生素', '生理.水分', '生理.排泄欲', '生理.飽腹感', '生理.體溫', '精神.當前精神值', '精神.精神值上限', '精神.壓力', '精神.無聊', '精神.孤獨', '精神.睡眠', '精神.性慾', '狀態.表皮溼度', '狀態.疲勞', '狀態.清潔', '狀態.免疫力', '生理.免疫負荷', '狀態.當前負重', '狀態.負重上限', '狀態.疼痛', '狀態.感染', '系統.求生點數', '系統.奇蹟點數', '系統.經過天數' ]; function toInt(value, allowDecimal) { if (!Number.isFinite(value)) return value; if (allowDecimal === 'temp') return Math.round(value * 10) / 10; if (allowDecimal === 'immune') return Math.round(value); // 免疫負荷直接四捨五入 return Math.round(value); } allNumericPaths.forEach(function(p) { var val = get(vars, p); if (typeof val === 'number' && Number.isFinite(val)) { var allowDecimal = (p === '生理.體溫') ? 'temp' : false; var newVal = toInt(val, allowDecimal); if (newVal !== val) { set(vars, p, newVal); logChange('整數化', p + ': ' + val + ' → ' + newVal); changed = true; } } }); // 同伴整數化 var companions = get(vars, '同伴'); if (_.isPlainObject(companions)) { for (var name in companions) { if (name.startsWith('$')) continue; var compPaths = [ '同伴.' + name + '.生理.水分', '同伴.' + name + '.生理.飽腹感', '同伴.' + name + '.生理.排泄欲', '同伴.' + name + '.精神.壓力', '同伴.' + name + '.精神.睡眠', '同伴.' + name + '.狀態.疲勞', '同伴.' + name + '.狀態.清潔', '同伴.' + name + '.生理.當前生命值', '同伴.' + name + '.精神.當前精神值' ]; compPaths.forEach(function(p) { var val = get(vars, p); if (typeof val === 'number' && Number.isFinite(val)) { var newVal = Math.round(val); if (newVal !== val) { set(vars, p, newVal); logChange('同伴整數化', p + ': ' + val + ' → ' + newVal); changed = true; } } }); } } // 🔴 強制修正主角脂肪(新增) var finalFat = get(vars, '生理.脂肪'); if (typeof finalFat === 'number' && !Number.isInteger(finalFat)) { keyLog('⚠️ 警告:整數化後生理.脂肪 仍然是 ' + finalFat); set(vars, '生理.脂肪', Math.round(finalFat)); keyLog('🔧 強制修正生理.脂肪: ' + finalFat + ' → ' + Math.round(finalFat)); changed = true; } // 同時也強制修正其他常見有小數的欄位 var finalNitrogen = get(vars, '生理.氮平衡'); if (typeof finalNitrogen === 'number' && !Number.isInteger(finalNitrogen)) { set(vars, '生理.氮平衡', Math.round(finalNitrogen)); keyLog('🔧 強制修正生理.氮平衡: ' + finalNitrogen + ' → ' + Math.round(finalNitrogen)); changed = true; } var finalVitamin = get(vars, '生理.維生素'); if (typeof finalVitamin === 'number' && !Number.isInteger(finalVitamin)) { set(vars, '生理.維生素', Math.round(finalVitamin)); keyLog('🔧 強制修正生理.維生素: ' + finalVitamin + ' → ' + Math.round(finalVitamin)); changed = true; } var finalExcretion = get(vars, '生理.排泄欲'); if (typeof finalExcretion === 'number' && !Number.isInteger(finalExcretion)) { set(vars, '生理.排泄欲', Math.round(finalExcretion)); keyLog('🔧 強制修正生理.排泄欲: ' + finalExcretion + ' → ' + Math.round(finalExcretion)); changed = true; } var finalFullness = get(vars, '生理.飽腹感'); if (typeof finalFullness === 'number' && !Number.isInteger(finalFullness)) { set(vars, '生理.飽腹感', Math.round(finalFullness)); keyLog('🔧 強制修正生理.飽腹感: ' + finalFullness + ' → ' + Math.round(finalFullness)); changed = true; } var finalSleep = get(vars, '精神.睡眠'); if (typeof finalSleep === 'number' && !Number.isInteger(finalSleep)) { set(vars, '精神.睡眠', Math.round(finalSleep)); keyLog('🔧 強制修正精神.睡眠: ' + finalSleep + ' → ' + Math.round(finalSleep)); changed = true; } var finalFatigue = get(vars, '狀態.疲勞'); if (typeof finalFatigue === 'number' && !Number.isInteger(finalFatigue)) { set(vars, '狀態.疲勞', Math.round(finalFatigue)); keyLog('🔧 強制修正狀態.疲勞: ' + finalFatigue + ' → ' + Math.round(finalFatigue)); changed = true; } if (changed) { keyLog('🔧 整數化完成,共修正 ' + fixCount + ' 處'); } else { keyLog('🔧 整數化執行完畢,無需修正'); } return changed; } // ==================== 擴充輔助函數 ==================== function rollDice(formula) { if (!formula || typeof formula !== 'string') return 0; var m = formula.match(/^(\d+)d(\d+)$/); if (!m) { var n = parseInt(formula); return isNaN(n) ? 0 : n; } var count = parseInt(m[1]), sides = parseInt(m[2]); if (count <= 0 || sides <= 0) return 0; var total = 0; for (var i = 0; i < count; i++) total += Math.floor(Math.random() * sides) + 1; return total; } function generateRandomGender() { return Math.random() < LIVESTOCK_CONFIG.BREEDING_CONFIG.GENDER_RATIO.male ? '雄性' : '雌性'; } function getAffectionStageForSex(level) { var stage = AFFECTION_SYSTEM.getStage(level); var map = { '陌生': '初階期', '朋友-穩定期': '初階期', '朋友-親密臨界區': '初階期', '摯友-深度期': '中階期', '摯友-戀人臨界區': '中階期', '戀人-新晉期': '高階期', '戀人-穩定臨界區': '高階期', '摯愛': '頂階頂' }; return map[stage.name] || '初階期'; } function getSeasonGrowthBonus(season) { return SEASON_GROWTH_BONUS[season] || 0; } function extractCropBonusFromObject(obj) { if (!obj || typeof obj !== 'object') return 0; var bonus = Number(obj['作物加成']); return (!isNaN(bonus) && bonus > 0) ? bonus : 0; } function extractCropBonusFromCarriedItems(items) { if (!_.isPlainObject(items)) return 0; var total = 0; for (var name in items) { if (name.startsWith('$')) continue; total += extractCropBonusFromObject(items[name]); } return total; } function calculateActualGrowthDays(vars, cropName, baseDays) { var totalBonus = 0; var season = get(vars, '環境.季節'); totalBonus += getSeasonGrowthBonus(season); var crops = get(vars, '作物'); if (_.isPlainObject(crops)) { for (var name in crops) { if (name.startsWith('$') || name === cropName) continue; totalBonus += extractCropBonusFromObject(crops[name]); } } var companions = get(vars, '同伴'); if (_.isPlainObject(companions)) { for (var name in companions) { if (name.startsWith('$')) continue; totalBonus += extractCropBonusFromCarriedItems(get(companions[name], '攜帶物品')); } } if (totalBonus <= 0) return baseDays; if (totalBonus <= 50) return Math.max(1, baseDays - Math.floor(baseDays * (totalBonus / 100))); return Math.max(1, Math.floor(baseDays / (1 + totalBonus / 100))); } function calculateGrowthStage(cropData, currentDay, actualDays) { var planted = v(cropData.種植日期, 0); var growthCycle = v(cropData.成長週期, 0); var hasBonus = extractCropBonusFromObject(cropData) > 0; if (growthCycle < 0 || hasBonus) return '特殊作物'; var progress = Math.min(100, Math.round(((currentDay - planted) / actualDays) * 100)); if (progress < 30) return '幼苗期'; if (progress < 60) return '生長期'; if (progress < 100) return '熟成期'; return '可收割'; } function calculateLivestockGrowthStage(data) { var age = v(data.年齡, 12); age = Math.max(12, age); for (var stage in LIVESTOCK_CONFIG.GROWTH_STAGES) { var cfg = LIVESTOCK_CONFIG.GROWTH_STAGES[stage]; if (age >= cfg.minAge && age < cfg.maxAge) return stage; } return '成年期'; } function calculateActualProductionCycle(vars, animalName, baseCycle) { var totalBonus = 0; var season = get(vars, '環境.季節'); totalBonus += LIVESTOCK_CONFIG.SEASON_PRODUCTION_BONUS[season] || 0; var facilities = get(vars, '營地.設施'); if (_.isPlainObject(facilities)) { for (var name in facilities) { if (name.startsWith('$')) continue; var bonus = Number(facilities[name].畜牧加成); if (!isNaN(bonus) && bonus > 0) totalBonus += bonus; } } if (totalBonus <= 0) return baseCycle; var reduction = Math.min(totalBonus / 100, 0.5); return Math.max(1, Math.floor(baseCycle * (1 - reduction))); } // ==================== 作物與畜牧保護系統 ==================== var PROTECTED_CROP_HARVEST_ACTIONS = ['RESET']; var CROP_REQUIRED_FIELDS = ['種植日期', '收成日期', '生長階段', '成長週期']; function isSustainableCrop(cropData) { return cropData && cropData.harvestAction && PROTECTED_CROP_HARVEST_ACTIONS.indexOf(cropData.harvestAction) !== -1; } function fixCropData(vars, cropName, cropData, currentDay) { var changed = false; var growthCycle = cropData.成長週期 || 30; var fixedData = {}; for (var i = 0; i < CROP_REQUIRED_FIELDS.length; i++) { var field = CROP_REQUIRED_FIELDS[i]; var value = cropData[field]; if (value === undefined || value === null) { switch(field) { case '種植日期': fixedData[field] = currentDay; logChange('作物修復', cropName + ' 種植日期設為 ' + currentDay); break; case '收成日期': fixedData[field] = currentDay + growthCycle; logChange('作物修復', cropName + ' 收成日期設為 ' + (currentDay + growthCycle)); break; case '生長階段': fixedData[field] = '幼苗期'; logChange('作物修復', cropName + ' 生長階段設為 幼苗期'); break; case '成長週期': fixedData[field] = growthCycle; logChange('作物修復', cropName + ' 成長週期設為 ' + growthCycle); break; } changed = true; } } if (!cropData.harvestAction || PROTECTED_CROP_HARVEST_ACTIONS.indexOf(cropData.harvestAction) === -1) { if (isSustainableCrop(cropData) || cropData.harvestAction === 'RESET') { fixedData.harvestAction = 'RESET'; logChange('作物修復', cropName + ' harvestAction 設為 RESET'); changed = true; } } if (changed) { for (var key in fixedData) { set(vars, '作物.' + cropName + '.' + key, fixedData[key]); } fixCount++; return true; } return false; } function checkAndRestoreSustainableCrops(vars, oldVars) { if (!oldVars) return false; var changed = false; var oldCrops = get(oldVars, '作物') || {}; var currentCrops = get(vars, '作物') || {}; var currentDay = v(get(vars, '系統.經過天數'), 0); for (var name in oldCrops) { if (name.startsWith('$')) continue; var oldCrop = oldCrops[name]; if (isSustainableCrop(oldCrop) && !currentCrops[name]) { keyLog('🌱 檢測到永續作物被刪除: ' + name + ',正在恢復...'); var growthCycle = oldCrop.成長週期 || 30; var restored = { 種植日期: currentDay, 收成日期: currentDay + growthCycle, 生長階段: '幼苗期', harvestAction: 'RESET', 成長週期: growthCycle, 收穫次數: (oldCrop.收穫次數 || 0) + 1 }; set(vars, '作物.' + name, restored); logChange('作物恢復', name + ' 已恢復,收成次數 +1'); changed = true; fixCount++; } } return changed; } function verifyAndFixAllCrops(vars) { var changed = false; var crops = get(vars, '作物'); if (!_.isPlainObject(crops)) return false; var currentDay = v(get(vars, '系統.經過天數'), 0); for (var name in crops) { if (name.startsWith('$')) continue; var crop = crops[name]; if (!_.isPlainObject(crop)) continue; if (fixCropData(vars, name, crop, currentDay)) changed = true; if (crop.種植日期 && crop.收成日期 && crop.種植日期 > crop.收成日期) { var growthCycle = crop.成長週期 || 30; var newHarvest = crop.種植日期 + growthCycle; set(vars, '作物.' + name + '.收成日期', newHarvest); logChange('作物修復', name + ' 收成日期已修正 ' + crop.收成日期 + ' → ' + newHarvest); changed = true; fixCount++; } if (crop.種植日期 > currentDay) { set(vars, '作物.' + name + '.種植日期', currentDay); logChange('作物修復', name + ' 種植日期不能大於當前天數,設為 ' + currentDay); changed = true; fixCount++; } } if (changed) debugLog('作物驗證修復完成'); return changed; } var LIVESTOCK_REQUIRED_FIELDS = ['年齡', '飼養數量', '生長階段', '飼養開始日']; function fixLivestockData(vars, animalName, animalData, currentDay) { var changed = false; for (var i = 0; i < LIVESTOCK_REQUIRED_FIELDS.length; i++) { var field = LIVESTOCK_REQUIRED_FIELDS[i]; var value = animalData[field]; if (value === undefined || value === null) { switch(field) { case '年齡': set(vars, '畜牧.' + animalName + '.年齡', 12); logChange('畜牧修復', animalName + ' 年齡設為 12'); changed = true; break; case '飼養數量': set(vars, '畜牧.' + animalName + '.飼養數量', { 雄性: 1, 雌性: 1 }); logChange('畜牧修復', animalName + ' 飼養數量設為 雄:1, 雌:1'); changed = true; break; case '生長階段': set(vars, '畜牧.' + animalName + '.生長階段', '成年期'); logChange('畜牧修復', animalName + ' 生長階段設為 成年期'); changed = true; break; case '飼養開始日': set(vars, '畜牧.' + animalName + '.飼養開始日', currentDay - 12); logChange('畜牧修復', animalName + ' 飼養開始日設為 ' + (currentDay - 12)); changed = true; break; } } } var livestock = get(vars, '畜牧'); var animal = livestock[animalName]; if (animal && animal.飼養數量) { var males = v(animal.飼養數量.雄性, 0); var females = v(animal.飼養數量.雌性, 0); if (males < 0 || females < 0) { set(vars, '畜牧.' + animalName + '.飼養數量.雄性', Math.max(0, males)); set(vars, '畜牧.' + animalName + '.飼養數量.雌性', Math.max(0, females)); logChange('畜牧修復', animalName + ' 數量修正為非負值 雄:' + Math.max(0, males) + ', 雌:' + Math.max(0, females)); changed = true; } } var startDay = v(animalData.飼養開始日, currentDay - 12); if (startDay > currentDay) { set(vars, '畜牧.' + animalName + '.飼養開始日', currentDay - 12); logChange('畜牧修復', animalName + ' 飼養開始日修正 ' + startDay + ' → ' + (currentDay - 12)); changed = true; } var calculatedAge = Math.max(12, 12 + Math.floor((currentDay - startDay) / 30)); var currentAge = v(animalData.年齡, calculatedAge); if (Math.abs(currentAge - calculatedAge) > 6) { set(vars, '畜牧.' + animalName + '.年齡', calculatedAge); logChange('畜牧修復', animalName + ' 年齡修正 ' + currentAge + ' → ' + calculatedAge); changed = true; } var expectedStage = calculateLivestockGrowthStage({ 年齡: calculatedAge }); var currentStage = animalData.生長階段; if (currentStage && expectedStage && currentStage !== expectedStage) { set(vars, '畜牧.' + animalName + '.生長階段', expectedStage); logChange('畜牧修復', animalName + ' 生長階段修正 ' + currentStage + ' → ' + expectedStage); changed = true; } if (animalData.產出物 && animalData.產出物.週期) { var cycle = v(animalData.產出物.週期, 7); if (cycle <= 0) { set(vars, '畜牧.' + animalName + '.產出物.週期', 7); logChange('畜牧修復', animalName + ' 產出週期修正為 7'); changed = true; } } if (changed) fixCount++; return changed; } function verifyAndFixAllLivestock(vars) { var changed = false; var livestock = get(vars, '畜牧'); if (!_.isPlainObject(livestock)) return false; var currentDay = v(get(vars, '系統.經過天數'), 0); for (var name in livestock) { if (name.startsWith('$')) continue; var animal = livestock[name]; if (!_.isPlainObject(animal)) continue; if (fixLivestockData(vars, name, animal, currentDay)) changed = true; } if (changed) debugLog('畜牧驗證修復完成'); return changed; } // ==================== 新生兒生成系統(修改版:放在子嗣下) ==================== function generateNewborn(vars, motherName, motherPath) { var litter = 1; var dice = Math.floor(Math.random() * 100) + 1; if (dice <= 10) { litter = 2; keyLog('👶👶 驚喜!' + motherName + ' 生下了雙胞胎!'); } else { keyLog('👶 ' + motherName + ' 生下了1個寶寶'); } // ✅ 修改:使用「子嗣」而非「同伴」 var children = get(vars, '子嗣'); if (!_.isPlainObject(children)) { children = {}; set(vars, '子嗣', children); } var birthCount = get(vars, motherPath + '.生育次數') || 0; set(vars, motherPath + '.生育次數', birthCount + litter); for (var i = 0; i < litter; i++) { var gender = Math.random() < 0.5 ? '女' : '男'; // 計算現有子女數量(只計算同一個母親的孩子) var existingChildren = 0; for (var name in children) { if (name.startsWith('$')) continue; var data = children[name]; if (data && data.母親 === motherName) existingChildren++; } var childNumber = existingChildren + i + 1; var childName = motherName + '的孩子' + childNumber; var finalName = childName; var counter = 1; while (children[finalName]) finalName = childName + '_' + counter++; var newborn = { 人物背景: motherName + ' 的孩子', 健康度: 80, 水分值: 100, 飽腹感: 100, 疲勞度: 0, 排泄欲: 0, 狀態: '新生兒', 關係: '子女', 屬性: {}, 技能: {}, 天賦特性: {}, 裝備: { 頭部: {}, 頸部: {}, 胸部: {}, 其他: {}, 背部: {}, 左手: {}, 右手: {}, 腰部: {}, 腿部: {}, 腳部: {} }, 攜帶物品: {}, 劇情演出: [], 敏感部位: {}, 淪陷: { 強制次數: 0, 當前階段: 0, 抵抗值: 100, 順從值: 0 }, 心理狀態: { 當前所想: '' }, 性癖好: [], 性愛人設: '', 重要經歷: {}, 罪惡感: {}, 生育次數: 0, 母親: motherName, 父親: get(vars, motherPath + '.生理狀態.授精者') || '未知' // ✅ 新增:記錄父親 }; if (gender === '女') { newborn.服裝 = { 上衣: '', 下裝: '', 內衣: '', 內褲: '', 鞋子: '', 絲襪: '' }; newborn.身體特徵 = { 類型: 'female', 體型: '嬰兒型', 三圍: {}, 乳房: {}, 私處: {} }; newborn.性經驗 = { 性交經驗: '無', 口交經驗: '無', 肛交經驗: '無', 性行為次數: 0, 性伴侶數量: 0, 處女: '是' }; newborn.生理狀態 = { 陰蒂狀態: '正常', 子宮狀態: '正常', 月經週期: 1, 懷孕狀態: '未懷孕', 懷孕天數: 0 }; newborn.性別 = '女'; } else { newborn.服裝 = { 上衣: '', 下裝: '', 內衣: '', 內褲: '', 鞋子: '', 襪子: '' }; newborn.身體特徵 = { 類型: 'male', 陰莖: { 長度: 0, 粗度: 0, 類型: '', 充血程度: '' } }; newborn.性經驗 = { 性交經驗: '無', 口交經驗: '無', 肛交經驗: '無', 性行為次數: 0, 性伴侶數量: 0, 處男: '是' }; newborn.生理狀態 = { 勃起狀態: '未勃起', 射精狀態: '未射精' }; newborn.性別 = '男'; } // ✅ 修改:寫入「子嗣」而非「同伴」 set(vars, '子嗣.' + finalName, newborn); logChange('新生兒', motherName + ' 生下 ' + finalName + ' (' + gender + ')'); } keyLog('👶 ' + motherName + ' 共生下 ' + litter + ' 個寶寶'); return litter; } // ======================== 路徑確保 ======================== function ensurePaths(vars) { // 強制刪除已廢棄的狀態.飽腹感 if (get(vars, '狀態.飽腹感') !== undefined) { _.unset(vars, '狀態.飽腹感'); console.log('🗑️ 已刪除廢棄路徑: 狀態.飽腹感'); } if (!_.isPlainObject(get(vars, '任務'))) { set(vars, '任務', { 主線: {}, 支線: {}, 日常: {}, 緊急: {}, 羈絆: {} }); } var comps = get(vars, '同伴'); if (_.isPlainObject(comps)) { for (var n in comps) { if (n.startsWith('$')) continue; if (!_.isPlainObject(get(vars, '任務.羈絆.' + n))) { set(vars, '任務.羈絆.' + n, {}); } } } var pets = get(vars, '寵物'); if (_.isPlainObject(pets)) { for (var n in pets) { if (n.startsWith('$')) continue; if (!_.isPlainObject(get(vars, '寵物.' + n + '.攜帶物品'))) { set(vars, '寵物.' + n + '.攜帶物品', {}); } } } var camps = get(vars, '營地'); if (_.isPlainObject(camps)) { for (var c in camps) { if (c.startsWith('$')) continue; ['位置', '樣式', '物品', '食材', '醫藥', '材料', '牲畜', '設施', '奇物', '武器'].forEach(function(f) { if (get(vars, '營地.' + c + '.' + f) === undefined) { set(vars, '營地.' + c + '.' + f, (f === '位置' || f === '樣式') ? '' : {}); } }); } } var equip = get(vars, '裝備'); if (_.isPlainObject(equip)) { ['頭部', '頸部', '胸部', '其他', '背部', '左手', '右手', '腰部', '腿部', '腳部'].forEach(function(p) { if (get(vars, '裝備.' + p) === undefined) set(vars, '裝備.' + p, {}); }); } if (_.isPlainObject(comps)) { for (var n in comps) { if (n.startsWith('$')) continue; var eq = get(vars, '同伴.' + n + '.裝備'); if (!_.isPlainObject(eq)) { eq = {}; ['頭部', '頸部', '胸部', '其他', '背部', '左手', '右手', '腰部', '腿部', '腳部'].forEach(function(p) { eq[p] = {}; }); set(vars, '同伴.' + n + '.裝備', eq); } else { ['頭部', '頸部', '胸部', '其他', '背部', '左手', '右手', '腰部', '腿部', '腳部'].forEach(function(p) { if (get(vars, '同伴.' + n + '.裝備.' + p) === undefined) { set(vars, '同伴.' + n + '.裝備.' + p, {}); } }); } if (!Array.isArray(get(vars, '同伴.' + n + '.劇情演出'))) { set(vars, '同伴.' + n + '.劇情演出', []); } var gender = get(vars, '同伴.' + n + '.基礎信息.性別'); if (gender === '女' || gender === 'female') { if (get(vars, '同伴.' + n + '.生育次數') === undefined) { set(vars, '同伴.' + n + '.生育次數', 0); debugLog('初始化 同伴.' + n + '.生育次數 = 0'); } } } } var userGender = get(vars, '基礎信息.性別'); if (userGender === '女' || userGender === 'female') { if (get(vars, '生育次數') === undefined) { set(vars, '生育次數', 0); debugLog('初始化 主角.生育次數 = 0'); } } if (get(vars, '系統.前次時間') === undefined) { set(vars, '系統.前次時間', '00:00'); debugLog('初始化 系統.前次時間 = 00:00'); } if (get(vars, '系統.前次天數') === undefined) { set(vars, '系統.前次天數', 1); debugLog('初始化 系統.前次天數 = 1'); } if (!_.isPlainObject(get(vars, '養殖'))) set(vars, '養殖', {}); var shop = get(vars, '商店'); if (!_.isPlainObject(shop)) set(vars, '商店', {}); } // ======================== 等級升級處理 ======================== function processLevelUp(vars, basePath) { var obj = get(vars, basePath); if (!_.isPlainObject(obj)) return; var isAttr = basePath.indexOf('屬性') !== -1, baseExp = isAttr ? 10 : 100; for (var k in obj) { if (k.startsWith('$')) continue; var item = obj[k]; if (!_.isPlainObject(item)) continue; var lv = int(get(item, '等級')) || 0, exp = int(get(item, '當前經驗')) || 0, changed = false, oldLv = lv; while (exp >= Math.max(1, lv * baseExp)) { exp -= lv * baseExp; lv++; changed = true; } if (changed) { set(vars, basePath + '.' + k + '.等級', lv); set(vars, basePath + '.' + k + '.當前經驗', exp); logChange('等級提升', basePath + '.' + k + ' ' + oldLv + ' → ' + lv); fixCount++; } var need = Math.max(1, lv * baseExp); if (get(item, '升級所需經驗') !== need) { set(vars, basePath + '.' + k + '.升級所需經驗', need); } } } // ======================== 同伴邊界限制 ======================== function processCompanionLimits(vars) { var comps = get(vars, '同伴'); if (_.isPlainObject(comps)) { for (var n in comps) { if (n.startsWith('$')) continue; var base = '同伴.' + n; // ========== 邊界修正(更新為正確路徑) ========== // 生理數值 var bounds = [ { p: base + '.生理.水分', min: 0, max: 100 }, { p: base + '.生理.飽腹感', min: 0, max: 100 }, { p: base + '.生理.排泄欲', min: 0, max: 100 }, // 精神數值 { p: base + '.精神.壓力', min: 0, max: 100 }, { p: base + '.精神.睡眠', min: 0, max: 100 }, // 次生屬性 { p: base + '.狀態.疲勞', min: 0, max: 4 * (get(vars, base + '.屬性.耐力.等級') || 20) }, { p: base + '.狀態.清潔', min: 0, max: 100 }, // 核心狀態(生命/精神值) { p: base + '.生理.當前生命值', min: 0, max: get(vars, base + '.生理.生命值上限') || 100 }, { p: base + '.精神.當前精神值', min: 0, max: get(vars, base + '.精神.精神值上限') || 100 } ]; bounds.forEach(function(b) { var val = get(vars, b.p); if (typeof val === 'number' && Number.isFinite(val)) { var maxVal = typeof b.max === 'function' ? b.max(vars) : b.max; var minVal = b.min; var newVal = clamp(val, minVal, maxVal); if (!Number.isInteger(newVal)) newVal = Math.round(newVal); if (newVal !== val) { set(vars, b.p, newVal); logChange('同伴邊界修正', b.p + ' ' + val + ' → ' + newVal); fixCount++; } } }); // ========== 技能經驗修正 ========== var skills = get(vars, base + '.技能'); if (_.isPlainObject(skills)) { for (var skillName in skills) { if (skillName.startsWith('$')) continue; var exp = get(vars, base + '.技能.' + skillName + '.當前經驗'); if (typeof exp === 'number' && exp < 0) { set(vars, base + '.技能.' + skillName + '.當前經驗', 0); logChange('技能經驗修正', base + '.' + skillName + ' 經驗 ' + exp + ' → 0'); fixCount++; } } } // ✅ 在這裡添加屬性經驗修正(在技能經驗修正之後) var attrs = ['力量', '敏捷', '耐力', '智力', '感知', '意志']; for (var i = 0; i < attrs.length; i++) { var attr = attrs[i]; var exp = get(vars, base + '.屬性.' + attr + '.當前經驗'); if (typeof exp === 'number' && exp < 0) { set(vars, base + '.屬性.' + attr + '.當前經驗', 0); logChange('同伴屬性經驗修正', base + '.' + attr + ' 經驗 ' + exp + ' → 0'); fixCount++; } } // ========== 等級升級 ========== processLevelUp(vars, base + '.屬性'); processLevelUp(vars, base + '.技能'); } } // 寵物邊界修正(如果有的話) var pets = get(vars, '寵物'); if (_.isPlainObject(pets)) { for (var n in pets) { if (n.startsWith('$')) continue; var base = '寵物.' + n; var fav = get(vars, base + '.好感度'); if (Number.isFinite(fav)) { var newFav = clamp(fav, 0, 100); if (!Number.isInteger(newFav)) newFav = Math.round(newFav); if (newFav !== fav) { set(vars, base + '.好感度', newFav); logChange('邊界修正', base + '.好感度 ' + fav + ' → ' + newFav); fixCount++; } } processLevelUp(vars, base + '.屬性'); processLevelUp(vars, base + '.技能'); } } } // ======================== 物品狀態上限修正 ======================== function fixItemStatusLimits(vars) { var paths = ['攜帶物品', '裝備', '捷徑或裝置']; paths.forEach(function(p) { var items = get(vars, p); if (!_.isPlainObject(items)) return; for (var k in items) { if (k.startsWith('$')) continue; var s = get(items[k], '狀態'); if (Number.isFinite(s) && s > 100) { set(vars, p + '.' + k + '.狀態', 100); logChange('物品狀態上限', p + '.' + k + ' ' + s + ' → 100'); fixCount++; } } }); var camps = get(vars, '營地'); if (_.isPlainObject(camps)) { for (var c in camps) { if (c.startsWith('$')) continue; var cats = ['物品', '食材', '醫藥', '材料', '牲畜', '設施', '奇物', '武器']; cats.forEach(function(cat) { var items = get(vars, '營地.' + c + '.' + cat); if (!_.isPlainObject(items)) return; for (var k in items) { if (k.startsWith('$')) continue; var s = get(items[k], '狀態'); if (Number.isFinite(s) && s > 100) { set(vars, '營地.' + c + '.' + cat + '.' + k + '.狀態', 100); logChange('物品狀態上限', '營地.' + c + '.' + cat + '.' + k + ' ' + s + ' → 100'); fixCount++; } } }); } } } // ======================== 清理無效物品 ======================== function cleanupAllItems(vars) { var paths = ['攜帶物品', '捷徑或裝置']; // 🔴 移除 '裝備' function fixItemNumbers(item, itemPath) { if (!_.isPlainObject(item)) return; var changed = false; var s = get(item, '狀態'); if (Number.isFinite(s) && !Number.isInteger(s)) { var newS = Math.round(s); set(item, '狀態', newS); logChange('狀態取整', itemPath + ' 狀態 ' + s + ' → ' + newS); fixCount++; changed = true; } var w = get(item, '重量'); if (Number.isFinite(w)) { var newW = Math.round(w * 100) / 100; if (newW !== w) { set(item, '重量', newW); logChange('重量修正', itemPath + ' 重量 ' + w + ' → ' + newW); fixCount++; changed = true; } } var q = get(item, '數量'); if (Number.isFinite(q)) { var rounded = Math.round(q); if (Math.abs(q - rounded) < 1e-10 && rounded !== q) { set(item, '數量', rounded); logChange('數量誤差修正', itemPath + ' 數量 ' + q + ' → ' + rounded); fixCount++; changed = true; } } return changed; } // 🔴 只處理攜帶物品和捷徑,不處理裝備 paths.forEach(function(p) { var items = get(vars, p); if (!_.isPlainObject(items)) return; var toDel = []; for (var k in items) { if (k.startsWith('$')) continue; var item = items[k]; fixItemNumbers(item, p + '.' + k); if (isItemInvalid(item)) toDel.push(k); } if (toDel.length) { toDel.forEach(function(k) { _.unset(vars, p + '.' + k); }); logChange('清理異常物品', p + ': ' + toDel.join(', ')); delCount += toDel.length; } }); // 營地物品處理(保持不變) var camps = get(vars, '營地'); if (_.isPlainObject(camps)) { for (var c in camps) { if (c.startsWith('$')) continue; var cats = ['物品', '食材', '醫藥', '材料', '牲畜', '設施', '奇物', '武器']; cats.forEach(function(cat) { var items = get(vars, '營地.' + c + '.' + cat); if (!_.isPlainObject(items)) return; var toDel = []; for (var k in items) { if (k.startsWith('$')) continue; var item = items[k]; fixItemNumbers(item, '營地.' + c + '.' + cat + '.' + k); if (isItemInvalid(item)) toDel.push(k); } if (toDel.length) { toDel.forEach(function(k) { _.unset(vars, '營地.' + c + '.' + cat + '.' + k); }); logChange('清理營地異常物品', c + '/' + cat + ': ' + toDel.join(', ')); delCount += toDel.length; } }); } } // 同伴攜帶物品(保持不變) var comps = get(vars, '同伴'); if (_.isPlainObject(comps)) { for (var n in comps) { if (n.startsWith('$')) continue; var items = get(vars, '同伴.' + n + '.攜帶物品'); if (!_.isPlainObject(items)) continue; var toDel = []; for (var k in items) { if (k.startsWith('$')) continue; var item = items[k]; fixItemNumbers(item, '同伴.' + n + '.攜帶物品.' + k); if (isItemInvalid(item)) toDel.push(k); } if (toDel.length) { toDel.forEach(function(k) { _.unset(vars, '同伴.' + n + '.攜帶物品.' + k); }); logChange('清理同伴異常物品', n + ': ' + toDel.join(', ')); delCount += toDel.length; } } } // 寵物攜帶物品(保持不變) var pets = get(vars, '寵物'); if (_.isPlainObject(pets)) { for (var n in pets) { if (n.startsWith('$')) continue; var items = get(vars, '寵物.' + n + '.攜帶物品'); if (!_.isPlainObject(items)) continue; var toDel = []; for (var k in items) { if (k.startsWith('$')) continue; var item = items[k]; fixItemNumbers(item, '寵物.' + n + '.攜帶物品.' + k); if (isItemInvalid(item)) toDel.push(k); } if (toDel.length) { toDel.forEach(function(k) { _.unset(vars, '寵物.' + n + '.攜帶物品.' + k); }); logChange('清理寵物異常物品', n + ': ' + toDel.join(', ')); delCount += toDel.length; } } } } // ======================== 物品腐敗處理 ======================== function decayLocation(vars, basePath, hours, factor) { var items = get(vars, basePath); if (!_.isPlainObject(items)) return; (function walk(path, obj) { if (!_.isPlainObject(obj)) return; for (var k in obj) { if (k.startsWith('$')) continue; var v = obj[k]; if (_.isPlainObject(v)) { var shelf = int(get(v, '保質期')); if (shelf >= 1 && shelf <= 5) { var state = int(get(v, '狀態')); if (state > 0) { var decay = decayRates[5 - shelf] * hours * factor, newState = Math.max(0, Math.round(state - decay)); if (newState !== state) { set(vars, path + '.' + k + '.狀態', newState); var desc = get(v, '描述'), newDesc = updateDesc(desc, newState); if (newDesc !== desc) set(vars, path + '.' + k + '.描述', newDesc); } } } walk(path + '.' + k, v); } } })(basePath, items); } function processItemDecay(vars) { if (!lastSnapshot) return; var cur = getCurrentTime(vars); if (!cur) return; var prevDay = lastSnapshot.day, prevTime = lastSnapshot.time; if (!prevDay || !prevTime) return; var prevMins = prevDay * 1440 + parseTime(prevTime), curMins = cur.total, delta = curMins - prevMins; if (delta <= 0) return; var hours = delta / 60, avgTemp = int(get(vars, '環境.體感溫度')) || 25, factor = tempFactor(avgTemp); ['攜帶物品', '裝備', '捷徑或裝置'].forEach(function(p) { decayLocation(vars, p, hours, factor); }); var camps = get(vars, '營地'); if (_.isPlainObject(camps)) { for (var c in camps) { if (c.startsWith('$')) continue; ['物品', '食材', '醫藥', '材料', '奇物', '武器'].forEach(function(cat) { decayLocation(vars, '營地.' + c + '.' + cat, hours, factor); }); } } var comps = get(vars, '同伴'); if (_.isPlainObject(comps)) { for (var n in comps) { if (n.startsWith('$')) continue; decayLocation(vars, '同伴.' + n + '.攜帶物品', hours, factor); } } var pets = get(vars, '寵物'); if (_.isPlainObject(pets)) { for (var n in pets) { if (n.startsWith('$')) continue; decayLocation(vars, '寵物.' + n + '.攜帶物品', hours, factor); } } } // ==================== 月相系統 ==================== function calculateMoonPhase(currentDay) { var cycle = MOON_PHASE_CONFIG.LUNAR_CYCLE; var phases = MOON_PHASE_CONFIG.PHASES; var pos = currentDay % cycle; for (var i = 0; i < phases.length; i++) { if (pos >= phases[i].min && pos <= phases[i].max) return phases[i].emoji; } return '🌑新月'; } function updateMoonPhase(vars) { var currentDay = v(get(vars, '系統.經過天數'), 0); var newPhase = calculateMoonPhase(currentDay); var currentPhase = get(vars, '環境.月相'); if (newPhase !== currentPhase) { set(vars, '環境.月相', newPhase); keyLog('月相更新: ' + currentPhase + ' → ' + newPhase); debugLog('月相計算 - 當前天數: ' + currentDay + ', 新月相: ' + newPhase); logChange('月相更新', currentPhase + ' → ' + newPhase); fixCount++; return true; } debugLog('月相無變化: ' + currentPhase); return false; } // ==================== 季節系統 ==================== function calculateSeason(currentDay) { var total = SEASON_CONFIG.SPRING_DAYS + SEASON_CONFIG.SUMMER_DAYS + SEASON_CONFIG.AUTUMN_DAYS + SEASON_CONFIG.WINTER_DAYS; var pos = currentDay % total; var acc = 0; acc += SEASON_CONFIG.SPRING_DAYS; if (pos < acc) return '春季'; acc += SEASON_CONFIG.SUMMER_DAYS; if (pos < acc) return '夏季'; acc += SEASON_CONFIG.AUTUMN_DAYS; if (pos < acc) return '秋季'; return '冬季'; } function updateSeason(vars) { var currentDay = v(get(vars, '系統.經過天數'), 0); var newSeason = calculateSeason(currentDay); var currentSeason = get(vars, '環境.季節'); if (newSeason !== currentSeason) { set(vars, '環境.季節', newSeason); keyLog('季節更新: ' + currentSeason + ' → ' + newSeason); debugLog('季節計算 - 當前天數: ' + currentDay + ', 新季節: ' + newSeason); logChange('季節更新', currentSeason + ' → ' + newSeason); fixCount++; return true; } debugLog('季節無變化: ' + currentSeason); return false; } // ==================== 懷孕系統 ==================== function updatePregnancyStage(vars, characterPath) { var prefix = characterPath ? characterPath + '.' : ''; var pregnant = get(vars, prefix + '生理狀態.懷孕狀態'); // 🔴 加入 '流產' 到排除清單 if (!pregnant || pregnant === '未懷孕' || pregnant === '' || pregnant === '流產') { return false; } // 🔴 使用計算函數獲取懷孕天數 var days = calculatePregnancyDays(vars, characterPath); // 如果懷孕天數為 0 且懷孕日不存在,可能出了問題 if (days === 0 && get(vars, prefix + '生理狀態.懷孕日') === undefined) { // 回退到讀取儲存的天數 days = get(vars, prefix + '生理狀態.懷孕天數') || 0; } var newStage = null; if (days <= 30) { newStage = '懷孕初期'; } else if (days <= 60) { newStage = '懷孕中期'; } else { newStage = '懷孕後期'; } // 自動分娩(超過 90 天) if (days > 90) { var name = characterPath ? characterPath.split('.').pop() : '主角'; var motherPath = characterPath || '主角'; keyLog('🤰 ' + name + ' 懷孕 ' + days + ' 天,自動分娩!'); var litterCount = generateNewborn(vars, name, motherPath); set(vars, prefix + '生理狀態.懷孕狀態', '未懷孕'); set(vars, prefix + '生理狀態.懷孕天數', 0); set(vars, prefix + '生理狀態.懷孕日', 0); // 🔴 清空懷孕日 set(vars, prefix + '生理狀態.月經週期', 1); logChange('分娩', name + ' 順利分娩,生下 ' + litterCount + ' 胎'); fixCount++; return true; } // 更新懷孕狀態 if (newStage && newStage !== pregnant) { keyLog('🤰 ' + (characterPath || '主角') + ' 懷孕階段更新: ' + pregnant + ' → ' + newStage + ' (第' + days + '天)'); set(vars, prefix + '生理狀態.懷孕狀態', newStage); logChange('懷孕階段', (characterPath || '主角') + ' ' + pregnant + ' → ' + newStage); fixCount++; return true; } return false; } function advancePregnancy(vars, days) { // 現在天數由 calculatePregnancyDays 計算,這個函數只需要更新階段 var changed = false; // 更新主角 if (updatePregnancyStage(vars, '')) changed = true; // 更新同伴 var companions = get(vars, '同伴'); if (_.isPlainObject(companions)) { for (var name in companions) { if (name.startsWith('$')) continue; if (updatePregnancyStage(vars, '同伴.' + name)) changed = true; } } return changed; } function calculatePregnancy(vars, characterName, isInternalEjaculation, contraceptive) { var base = '同伴.' + characterName; // 1. 先檢查性別 var gender = get(vars, base + '.基礎信息.性別'); if (gender !== '女' && gender !== 'female' && gender !== '女性') { keyLog('🔍 [2] 性別不符,跳過 (性別=' + gender + ')'); return false; } // 2. 再檢查懷孕狀態(流產視為未懷孕) var pregnancyState = get(vars, base + '.生理狀態.懷孕狀態'); if (pregnancyState && pregnancyState !== '未懷孕' && pregnancyState !== '流產') { keyLog('🔍 [4] 已懷孕,跳過'); return false; } // 3. 有效避孕檢查 if (contraceptive === '保險套' || contraceptive === '避孕藥') { keyLog('🔍 [5] 有效避孕,懷孕機率=0%'); set(vars, base + '.生理狀態.懷孕天數', 0); return false; } // 4. 獲取授精者 var sexResults = ensureSexResultArray(vars); var inseminator = '未知'; for (var i = 0; i < sexResults.length; i++) { if (sexResults[i].角色 === characterName && sexResults[i].授精者) { inseminator = sexResults[i].授精者; break; } } if (inseminator === '未知') { inseminator = get(vars, '基礎信息.名字') || '未知'; } keyLog('🔍 [1] 懷孕判斷入口: 角色=' + characterName + ', 性別=' + gender); keyLog('🔍 [6] 通過所有檢查,開始計算機率'); var isPullOut = (contraceptive === '體外射精'); var cycleDay = get(vars, base + '.生理狀態.月經週期') || 14; var cyclePhase = '安全期'; if (cycleDay >= 1 && cycleDay <= 5) cyclePhase = '月經期'; else if (cycleDay >= 10 && cycleDay <= 16) cyclePhase = '危險期'; keyLog('🔍 [7] 月經週期: 第' + cycleDay + '天, 階段: ' + cyclePhase); var baseChance = 0; if (cyclePhase === '危險期') { baseChance = isInternalEjaculation ? 25 : 8; } else if (cyclePhase === '安全期') { baseChance = isInternalEjaculation ? 2 : 0.5; } else { baseChance = isInternalEjaculation ? 0.3 : 0; } keyLog('🔍 [8] 基礎機率: ' + baseChance + '%'); if (isPullOut && isInternalEjaculation === false) { baseChance = Math.floor(baseChance * 0.25); keyLog('🔍 [9] 體外射精,機率降低至 ' + baseChance + '%'); } if (contraceptive === '安全期') { var originalChance = baseChance; baseChance = Math.min(baseChance, 2); if (originalChance !== baseChance) { keyLog('🔍 [10] 安全期避孕,機率從 ' + originalChance + '% 限制為 ' + baseChance + '%'); } } var talentBonus = 0; var talents = get(vars, base + '.天賦特性'); if (_.isPlainObject(talents)) { for (var talentName in talents) { if (talentName.startsWith('$')) continue; var talent = talents[talentName]; if (talent && talent.加值 && talent.加值.懷孕) { var bonusValue = talent.加值.懷孕; if (typeof bonusValue === 'number') talentBonus += Math.floor(bonusValue / 10); } } } keyLog('🔍 [11] 天賦加成: ' + talentBonus + '%'); var finalChance = Math.min(99, Math.max(0, baseChance + talentBonus)); var dice = Math.floor(Math.random() * 100) + 1; var isPregnant = dice <= finalChance; keyLog('🔍 [12] 最終機率: ' + finalChance + '%, 骰子: ' + dice + ', 結果: ' + (isPregnant ? '懷孕' : '未懷孕')); if (isPregnant) { var currentPregnancyState = get(vars, base + '.生理狀態.懷孕狀態'); if (!currentPregnancyState || currentPregnancyState === '未懷孕' || currentPregnancyState === '流產') { set(vars, base + '.生理狀態.懷孕狀態', '懷孕初期'); } var currentDay = get(vars, '系統.經過天數') || 0; set(vars, base + '.生理狀態.懷孕日', currentDay); var pregnancyDays = 0; set(vars, base + '.生理狀態.懷孕天數', pregnancyDays); set(vars, base + '.生理狀態.授精者', inseminator); keyLog('🤰 ' + characterName + ' 懷孕了! (懷孕日: 第' + currentDay + '天)'); logChange('懷孕', characterName + ' 在第 ' + currentDay + ' 天懷孕,授精者: ' + inseminator); } else { set(vars, base + '.生理狀態.懷孕天數', 0); keyLog('❌ ' + characterName + ' 未懷孕 (週期: ' + cyclePhase + ', 機率: ' + finalChance + '%)'); } return isPregnant; } // ==================== 計算懷孕天數(基於懷孕日) ==================== function calculatePregnancyDays(vars, characterPath) { var prefix = characterPath ? characterPath + '.' : ''; var pregnant = get(vars, prefix + '生理狀態.懷孕狀態'); // 如果沒有懷孕,直接返回 0 if (!pregnant || pregnant === '未懷孕' || pregnant === '') { return 0; } var pregnancyDay = get(vars, prefix + '生理狀態.懷孕日'); var currentDay = get(vars, '系統.經過天數') || 0; // 如果沒有懷孕日記錄,回退到原本的懷孕天數 if (pregnancyDay === undefined || pregnancyDay === null) { return get(vars, prefix + '生理狀態.懷孕天數') || 0; } // 🔴 計算懷孕天數 = 當前天數 - 懷孕當天數 var days = Math.max(0, currentDay - pregnancyDay); // 同步更新懷孕天數欄位(保持一致性) set(vars, prefix + '生理狀態.懷孕天數', days); return days; } function updateMenstrualCycle(vars) { var changed = false; var companions = get(vars, '同伴'); if (_.isPlainObject(companions)) { for (var name in companions) { if (name.startsWith('$')) continue; var gender = get(vars, '同伴.' + name + '.基礎信息.性別'); if (gender === '女' || gender === 'female' || gender === '女性') { var pregnant = get(vars, '同伴.' + name + '.生理狀態.懷孕狀態'); var currentCycle = get(vars, '同伴.' + name + '.生理狀態.月經週期'); var newCycle; // 🔴 新增:流產後重置月經週期 if (pregnant === '流產') { newCycle = 1; debugLog('同伴 ' + name + ' 流產,月經週期重置為 1'); } else if (currentCycle === undefined || currentCycle === null) { newCycle = Math.floor(Math.random() * 28) + 1; } else { newCycle = currentCycle + 1; if (newCycle > 28) newCycle = 1; } set(vars, '同伴.' + name + '.生理狀態.月經週期', newCycle); changed = true; } } } // 主角同理 var userGender = get(vars, '基礎信息.性別'); if (userGender === '女' || userGender === 'female') { var userPregnant = get(vars, '生理狀態.懷孕狀態'); var userCycle = get(vars, '生理狀態.月經週期'); var newUserCycle; // 🔴 新增:流產後重置月經週期 if (userPregnant === '流產') { newUserCycle = 1; debugLog('主角流產,月經週期重置為 1'); } else if (userCycle === undefined || userCycle === null) { newUserCycle = Math.floor(Math.random() * 28) + 1; } else { newUserCycle = userCycle + 1; if (newUserCycle > 28) newUserCycle = 1; } set(vars, '生理狀態.月經週期', newUserCycle); changed = true; } if (changed) debugLog('月經週期更新完成'); return changed; } // ==================== 作物系統 ==================== function updateCropGrowthStages(vars) { var changed = false; var currentDay = v(get(vars, '系統.經過天數'), 0); var crops = get(vars, '作物'); if (!_.isPlainObject(crops)) return false; for (var name in crops) { if (name.startsWith('$')) continue; var data = crops[name]; if (!_.isPlainObject(data)) continue; var growthCycle = v(data.成長週期, 0); var planted = v(data.種植日期, 0); if (planted <= 0) continue; var actualDays = growthCycle > 0 ? calculateActualGrowthDays(vars, name, growthCycle) : Math.abs(growthCycle); var stage = calculateGrowthStage(data, currentDay, actualDays); var currentStage = get(vars, '作物.' + name + '.生長階段'); if (stage !== currentStage) { set(vars, '作物.' + name + '.生長階段', stage); keyLog('🌱 作物 ' + name + ' 生長階段: ' + currentStage + ' → ' + stage); changed = true; if (stage === '可收割' && !get(vars, '作物.' + name + '.已成熟')) { set(vars, '作物.' + name + '.已成熟', true); keyLog('🌾 作物 ' + name + ' 已成熟,可收割!'); } } if (growthCycle > 0 && planted > 0) { var maturity = planted + actualDays; if (get(vars, '作物.' + name + '.收成日期') !== maturity) { set(vars, '作物.' + name + '.收成日期', maturity); debugLog('作物 ' + name + ' 收成日期更新: ' + maturity); changed = true; } } } if (changed) debugLog('作物系統更新完成'); return changed; } // ==================== 畜牧系統 ==================== function checkAndProcessBreeding(vars, currentDay, specificAnimal) { var updated = false; var livestock = get(vars, '畜牧'); if (!_.isPlainObject(livestock)) return { success: false, updated: false }; var bred = []; var animals = specificAnimal ? [[specificAnimal, livestock[specificAnimal]]] : Object.entries(livestock); for (var i = 0; i < animals.length; i++) { var name = animals[i][0], data = animals[i][1]; if (name.startsWith('$') || !_.isPlainObject(data)) continue; if (!data.繁殖 || !data.繁殖.可繁殖) continue; var stage = calculateLivestockGrowthStage(data); if (stage === '幼年期' || stage === '成長期') continue; var males = v(data.飼養數量?.雄性, 0), females = v(data.飼養數量?.雌性, 0); if (males <= 0 || females <= 0) continue; if (data.繁殖.懷孕中) continue; var lastBreed = v(data.繁殖.上次繁殖日, 0); var cooldown = LIVESTOCK_CONFIG.BREEDING_CONFIG.BREEDING_COOLDOWN[name] || 30; if (lastBreed > 0 && currentDay - lastBreed < cooldown) continue; var chance = LIVESTOCK_CONFIG.BREEDING_CONFIG.BASE_BREEDING_CHANCE; var ageMonths = v(data.年齡, 12); if (ageMonths >= 12 && ageMonths < 36) chance = 0.4; else if (ageMonths >= 36) chance = 0.2; if (Math.random() > chance) continue; set(vars, '畜牧.' + name + '.繁殖.懷孕中', true); set(vars, '畜牧.' + name + '.繁殖.上次繁殖日', currentDay); updated = true; bred.push(name); keyLog('🐄 ' + name + ' 成功懷孕!'); } if (updated) logChange('畜牧繁殖', '懷孕動物: ' + bred.join(', ')); return { success: bred.length > 0, updated: updated, bredAnimals: bred }; } function checkAndProcessBirth(vars, currentDay, specificAnimal) { var updated = false; var livestock = get(vars, '畜牧'); if (!_.isPlainObject(livestock)) return { success: false, updated: false }; var birthed = []; var animals = specificAnimal ? [[specificAnimal, livestock[specificAnimal]]] : Object.entries(livestock); for (var i = 0; i < animals.length; i++) { var name = animals[i][0], data = animals[i][1]; if (name.startsWith('$') || !_.isPlainObject(data)) continue; if (!data.繁殖?.懷孕中) continue; var lastBreed = v(data.繁殖.上次繁殖日, 0); var pregnancyCycle = v(data.繁殖.懷孕週期, 0); if (lastBreed <= 0 || pregnancyCycle <= 0) continue; var dueDay = lastBreed + pregnancyCycle; if (currentDay < dueDay) continue; var litter = rollDice(data.繁殖?.每胎數量 || '1d2'); if (litter <= 0) litter = 1; var males = 0, females = 0; for (var j = 0; j < litter; j++) { if (generateRandomGender() === '雄性') males++; else females++; } var curMales = v(data.飼養數量?.雄性, 0); var curFemales = v(data.飼養數量?.雌性, 0); set(vars, '畜牧.' + name + '.飼養數量.雄性', curMales + males); set(vars, '畜牧.' + name + '.飼養數量.雌性', curFemales + females); set(vars, '畜牧.' + name + '.繁殖.懷孕中', false); updated = true; birthed.push(name); keyLog('🐣 ' + name + ' 生產了 ' + litter + ' 隻幼崽 (雄:' + males + ', 雌:' + females + ')'); } if (updated) logChange('畜牧生產', '生產動物: ' + birthed.join(', ')); return { success: birthed.length > 0, updated: updated, birthedAnimals: birthed }; } function updateLivestockStates(vars) { var changed = false; var currentDay = v(get(vars, '系統.經過天數'), 0); var livestock = get(vars, '畜牧'); if (!_.isPlainObject(livestock)) return false; for (var name in livestock) { if (name.startsWith('$')) continue; var data = livestock[name]; if (!_.isPlainObject(data)) continue; var stage = calculateLivestockGrowthStage(data); if (stage !== get(vars, '畜牧.' + name + '.生長階段')) { set(vars, '畜牧.' + name + '.生長階段', stage); keyLog('🐑 ' + name + ' 生長階段: → ' + stage); changed = true; } var startDay = v(data.飼養開始日, 0); if (startDay > 0) { var age = Math.max(12, 12 + Math.floor((currentDay - startDay) / 30)); if (age !== v(data.年齡, 0)) { set(vars, '畜牧.' + name + '.年齡', age); debugLog('畜牧 ' + name + ' 年齡更新: ' + age + '個月'); changed = true; } } if (data.產出物 && data.產出物.週期) { var baseCycle = v(data.產出物.週期, 0); if (baseCycle > 0) { var actualCycle = calculateActualProductionCycle(vars, name, baseCycle); var nextDay = v(data.產出物.下次產出日, 0); if (nextDay <= 0 && startDay > 0) { nextDay = startDay + actualCycle; set(vars, '畜牧.' + name + '.產出物.下次產出日', nextDay); debugLog('畜牧 ' + name + ' 初始化產出日: ' + nextDay); changed = true; } else if (currentDay > nextDay && nextDay > 0) { var cycles = Math.floor((currentDay - nextDay) / actualCycle) + 1; set(vars, '畜牧.' + name + '.產出物.下次產出日', nextDay + cycles * actualCycle); keyLog('🥚 ' + name + ' 產出物已生產!'); changed = true; } if (actualCycle !== baseCycle) { set(vars, '畜牧.' + name + '.產出物.週期', actualCycle); debugLog('畜牧 ' + name + ' 產出週期更新: ' + baseCycle + ' → ' + actualCycle); changed = true; } } } var breedResult = checkAndProcessBreeding(vars, currentDay, name); if (breedResult.updated) changed = true; var birthResult = checkAndProcessBirth(vars, currentDay, name); if (birthResult.updated) changed = true; } if (changed) debugLog('畜牧系統更新完成'); return changed; } // ==================== 商店歷史記錄 ==================== // ==================== 商店歷史記錄 ==================== function recordShopHistory(vars) { var shop = get(vars, '商店'); if (!_.isPlainObject(shop)) return; var history = get(vars, '系統.商店歷史'); // 🔴 更新為 9 個類別(夥伴拆成 4 個獨立類別) var categories = ['材料', '工具', '奇物', '夥伴-技術與後勤', '夥伴-戰鬥與探索', '夥伴-反派與妖魅', '夥伴-純愛與保護控', '藥物', '盲盒']; var maxLength = 16; // 轉換舊格式(如果還是陣列) if (Array.isArray(history)) { var newHistory = { "材料": [], "工具": [], "奇物": [], "夥伴-技術與後勤": [], "夥伴-戰鬥與探索": [], "夥伴-反派與妖魅": [], "夥伴-純愛與保護控": [], "藥物": [], "盲盒": [] }; for (var i = 0; i < history.length; i++) { var day = history[i]; if (day.商品 && Array.isArray(day.商品)) { for (var j = 0; j < day.商品.length; j++) { var item = day.商品[j]; var cat = item.類別; // 🔴 舊版「夥伴」轉換為四個夥伴類別時,需要判斷歸屬,但舊格式沒有子類別資訊 // 所以統一歸入「夥伴-技術與後勤」作為預設(或可根據名稱關鍵字判斷) if (cat === '夥伴') { cat = '夥伴-技術與後勤'; } var rawName = item.名稱; var cleanName = rawName.replace(/\([^)]*\)/g, '').trim(); if (newHistory[cat] && newHistory[cat].indexOf(cleanName) === -1) { newHistory[cat].push(cleanName); } } } } history = newHistory; set(vars, '系統.商店歷史', history); keyLog('🔄 舊格式轉換完成(已刪除括弧,夥伴類別已拆分)'); } // 確保格式正確(包含 9 個類別) if (!_.isPlainObject(history)) { history = { "材料": [], "工具": [], "奇物": [], "夥伴-技術與後勤": [], "夥伴-戰鬥與探索": [], "夥伴-反派與妖魅": [], "夥伴-純愛與保護控": [], "藥物": [], "盲盒": [] }; set(vars, '系統.商店歷史', history); } // 🔴 同一天不重複記錄 var currentDay = get(vars, '系統.經過天數'); var lastRecordDay = get(vars, '系統.上次記錄天數'); if (currentDay === lastRecordDay) { debugLog('同一天跳過商店歷史記錄,天數=' + currentDay); return; } // 記錄當天商品(9 個類別) var anyChange = false; for (var i = 0; i < categories.length; i++) { var cat = categories[i]; var item = shop[cat]; if (item && typeof item === 'object' && item['名稱']) { var rawName = item['名稱']; var cleanName = rawName.replace(/\([^)]*\)/g, '').trim(); var list = history[cat] || []; if (list.length >= maxLength) { list.shift(); } list.push(cleanName); history[cat] = list; anyChange = true; } } if (anyChange) { set(vars, '系統.商店歷史', history); set(vars, '系統.上次記錄天數', currentDay); keyLog('📜 商店歷史已更新,天數=' + currentDay + ',共9個類別'); } } // ==================== 性愛系統 ==================== function calculateVoluntary(affectionLevel, interactionType, statusEffects, comfort, isSudden, hasTrauma, specialStatus) { var base = 50 + (affectionLevel * 5); if (interactionType === '情感推進') base += 10; else if (interactionType === '強制欺騙') base -= 40; if (affectionLevel >= 10) base += 20; else if (affectionLevel >= 7) base += 10; else if (affectionLevel >= 4) base += 5; else if (affectionLevel === 1) base -= 10; if (statusEffects && statusEffects.indexOf('恐懼') !== -1) base -= 15; if (statusEffects && statusEffects.indexOf('壓力') !== -1) base -= 10; if (statusEffects && statusEffects.indexOf('興致') !== -1) base += 15; if (comfort > 80) base += 5; if (comfort < 30) base -= 10; if (isSudden) base -= 20; if (hasTrauma) base -= 20; if (specialStatus === '自責傾向') base -= 10; else if (specialStatus === '共犯') base += 20; return Math.max(0, Math.min(100, base)); } function getResultGrade(voluntary) { if (voluntary >= 90) return { grade: '高度自願-主動', description: '主動推進 + 愉悅表現' }; if (voluntary >= 80) return { grade: '高度自願-積極', description: '積極配合 + 正向反饋' }; if (voluntary >= 70) return { grade: '高度自願-害羞', description: '害羞但允許 + 輕微緊張' }; if (voluntary >= 60) return { grade: '矛盾-逐漸接受', description: '初始抗拒→逐漸接受' }; if (voluntary >= 50) return { grade: '矛盾-明顯猶豫', description: '明顯猶豫 + 內心掙扎' }; if (voluntary >= 40) return { grade: '矛盾-持續輕拒', description: '持續輕拒 + 不情願' }; if (voluntary >= 30) return { grade: '非自願-忍耐', description: '被動承受 + 事後懊悔' }; if (voluntary >= 20) return { grade: '非自願-無力反抗', description: '持續抗拒但漸弱' }; if (voluntary >= 10) return { grade: '非自願-被迫接受', description: '恐懼服從 + 無奈' }; return { grade: '非自願-激烈反抗', description: '激烈抗拒 + 創傷反應' }; } function getCorruptionStage(forcedCount, affectionLevel) { var adj = forcedCount; if (affectionLevel >= 16) adj += 3; else if (affectionLevel >= 13) adj += 2; else if (affectionLevel >= 10) adj += 1; for (var stage in SEXUAL_CONFIG.CORRUPTION_STAGES) { var cfg = SEXUAL_CONFIG.CORRUPTION_STAGES[stage]; if (adj >= cfg.minCount && adj <= cfg.maxCount) { return { stage: parseInt(stage), name: cfg.name }; } } return { stage: 4, name: '開始沉淪' }; } function calculateAffectionChange(voluntary, affectionLevel, interactionType, specialStatus, isSuccess) { var expChange = 0, levelChange = 0, newStatus = specialStatus, trauma = ''; if (!isSuccess) { if (voluntary < 30) { return { expChange: -999, levelChange: -1, newSpecialStatus: specialStatus, traumaMark: '嚴重創傷' }; } return { expChange: 0, levelChange: 0, newSpecialStatus: specialStatus, traumaMark: '' }; } var stage = getAffectionStageForSex(affectionLevel); var isForced = (interactionType === '強制欺騙' && voluntary < 40); if (voluntary >= 70) { expChange = Math.min(100, 30 + Math.floor(voluntary / 2)); } else if (voluntary >= 40) { if (voluntary > 50) expChange = Math.min(50, Math.floor((voluntary - 50) * 5)); else if (voluntary < 50) expChange = Math.max(-20, Math.floor((voluntary - 50) * 4)); } if (voluntary < 40 && isForced) { expChange = 0; if (stage === '初階期') { levelChange = -1; trauma = '關係動搖'; } else if (stage === '中階期') { levelChange = 0; trauma = '關係動搖'; } else if (stage === '高階期') { expChange = -999; newStatus = '自責傾向'; trauma = '自責傾向觸發'; } else if (stage === '頂階初') { expChange = 40; newStatus = '扭曲依賴'; trauma = '扭曲依賴觸發'; } else if (stage === '頂階中') { expChange = 100; if (Math.random() < 0.1) levelChange = 1; newStatus = '徹底沉淪'; trauma = '徹底沉淪觸發'; } else if (stage === '頂階頂') { expChange = 150; levelChange = 1; newStatus = '共犯'; trauma = '共犯覺醒觸發'; } } return { expChange: expChange, levelChange: levelChange, newSpecialStatus: newStatus, traumaMark: trauma }; } function processSexualInteraction(vars, data) { var name = data.characterName; var base = '同伴.' + name; var affection = v(get(vars, base + '.技能.好感(對阿彬).等級'), 1); var affectionExp = v(get(vars, base + '.技能.好感(對阿彬).當前經驗'), 0); var forced = v(get(vars, base + '.淪陷.強制次數'), 0); var special = get(vars, base + '.淪陷.特殊狀態') || ''; var voluntary = calculateVoluntary( affection, data.interactionType || '情感推進', data.statusEffects || [], data.comfort || 50, data.isSudden || false, special.indexOf('關係動搖') !== -1 || special.indexOf('嚴重創傷') !== -1, special ); if (special === '徹底沉淪' && data.interactionType === '強制欺騙') { voluntary = calculateVoluntary(affection, '情感推進', data.statusEffects || [], data.comfort || 50, data.isSudden || false, false, special); } var grade = getResultGrade(voluntary); var forcedInc = false; if (data.interactionType === '強制欺騙' && voluntary < 30 && special !== '徹底沉淪') { forced++; forcedInc = true; } var newCorruption = getCorruptionStage(forced, affection); var change = calculateAffectionChange(voluntary, affection, data.interactionType || '情感推進', special, data.isSuccess !== false); var newExp = affectionExp + change.expChange; var newLevel = affection + change.levelChange; if (change.expChange === -999) newExp = 0; newLevel = Math.max(1, newLevel); if (newExp !== affectionExp) set(vars, base + '.技能.好感(對阿彬).當前經驗', newExp); if (newLevel !== affection) set(vars, base + '.技能.好感(對阿彬).等級', newLevel); if (forcedInc) set(vars, base + '.淪陷.強制次數', forced); if (newCorruption.stage !== get(vars, base + '.淪陷.當前階段', 0)) { set(vars, base + '.淪陷.當前階段', newCorruption.stage); } if (change.newSpecialStatus !== special) { set(vars, base + '.淪陷.特殊狀態', change.newSpecialStatus); } var result = { 角色: name, 完成: data.isSuccess !== false ? true : false, // 改為「完成」 內射: data.isInternalEjaculation === true, 避孕措施: data.避孕措施 || '無', 類型: data.interactionType || '情感推進', 自願度: voluntary, 自願分級: grade.grade, 淪陷階段: { 階段: newCorruption.stage, 名稱: newCorruption.name }, 關係變化: { 經驗變化: change.expChange, 等級變化: change.levelChange }, 特殊狀態: change.newSpecialStatus || '無', 描寫建議: grade.description }; // 🔴 改為陣列版本 var results = ensureSexResultArray(vars); results.push(result); set(vars, '系統.性愛結果', results); keyLog('💕 性愛事件已記錄 (' + results.length + ' 筆)'); keyLog('💕 性愛事件 - ' + name + ': 自願度=' + voluntary + ', 結果=' + grade.grade); logChange('性愛事件', name + ' 自願度=' + voluntary); var isInternalEjaculation = data.是否內射 === true; var isSuccess = data.是否成功 !== false; return result; } // ======================== 時間流逝與等級處理 ======================== function updateDayOnTimePassed(vars) { keyLog('跨日檢查開始'); var prevTime = get(vars, '系統.前次時間'); var curTime = get(vars, '系統.當前時間'); var prevDay = get(vars, '系統.前次天數'); var curDay = get(vars, '系統.經過天數'); if (curDay > prevDay) { keyLog('✓ 偵測到天數外部更新: ' + prevDay + ' → ' + curDay); advancePregnancy(vars, 1); updateMenstrualCycle(vars); updateMoonPhase(vars); updateSeason(vars); updateCropGrowthStages(vars); updateLivestockStates(vars); return true; } var prevMinutes = parseTime(prevTime); var curMinutes = parseTime(curTime); if (prevMinutes === null || curMinutes === null) return false; var isTimeRewind = curMinutes < prevMinutes; var isSameDay = curDay === prevDay; if (isTimeRewind && isSameDay) { var newDay = curDay + 1; set(vars, '系統.經過天數', newDay); keyLog('✓ 跨日成功!天數: ' + curDay + ' → ' + newDay + ' (' + prevTime + ' → ' + curTime + ')'); logChange('天數修正', curDay + ' → ' + newDay); fixCount++; advancePregnancy(vars, 1); updateMenstrualCycle(vars); updateMoonPhase(vars); updateSeason(vars); updateCropGrowthStages(vars); updateLivestockStates(vars); return true; } return false; } function processTimeAndLevel(vars, oldVars) { updateDayOnTimePassed(vars); processLevelUp(vars, '屬性'); processLevelUp(vars, '技能'); // 主角屬性經驗邊界修正 var playerAttrs = ['力量', '敏捷', '耐力', '智力', '感知', '意志']; for (var i = 0; i < playerAttrs.length; i++) { var attr = playerAttrs[i]; var exp = get(vars, '屬性.' + attr + '.當前經驗'); if (typeof exp === 'number' && exp < 0) { set(vars, '屬性.' + attr + '.當前經驗', 0); logChange('主角屬性經驗修正', '屬性.' + attr + ' 經驗 ' + exp + ' → 0'); fixCount++; } } // 根據生效效果計算屬性加值(正面加成 + 負面減值) var curEffs = get(vars, '狀態.生效效果') || {}; var endBonus = 0, strBonus = 0, willBonus = 0, intBonus = 0, perBonus = 0; // ==================== 水分 ==================== if (curEffs.水分) { switch (curEffs.水分.狀態) { case '水分充足': endBonus += 2; break; case '輕微口渴': intBonus -= 2; perBonus -= 2; break; case '明顯口渴': strBonus -= 5; endBonus -= 5; break; case '輕度脫水': strBonus -= 10; endBonus -= 10; break; case '嚴重脫水': strBonus -= 15; endBonus -= 15; break; } } // ==================== 飽腹感 ==================== if (curEffs.飽腹感) { switch (curEffs.飽腹感.狀態) { case '飽足': endBonus += 2; break; case '過飽': perBonus -= 5; break; case '輕微飢餓': intBonus -= 2; perBonus -= 2; break; case '明顯飢餓': strBonus -= 5; endBonus -= 5; intBonus -= 5; perBonus -= 5; break; case '極度飢餓': strBonus -= 15; endBonus -= 15; intBonus -= 15; perBonus -= 15; break; case '虛脫': strBonus -= 25; endBonus -= 25; intBonus -= 25; perBonus -= 25; willBonus -= 25; break; } } // ==================== 睡眠 ==================== if (curEffs.睡眠) { switch (curEffs.睡眠.狀態) { case '精力充沛': endBonus += 1; intBonus += 5; perBonus += 5; break; case '疲倦': strBonus -= 5; endBonus -= 5; intBonus -= 5; perBonus -= 5; willBonus -= 5; break; case '非常疲憊': strBonus -= 15; endBonus -= 15; intBonus -= 15; perBonus -= 15; willBonus -= 15; break; case '精疲力竭': strBonus -= 30; endBonus -= 30; intBonus -= 30; perBonus -= 30; willBonus -= 30; break; } } // ==================== 疲勞 ==================== if (curEffs.疲勞) { switch (curEffs.疲勞.狀態) { case '精力十足': strBonus += 2; break; case '微微出汗': strBonus -= 2; endBonus -= 2; break; case '有些疲憊': strBonus -= 5; endBonus -= 5; break; case '相當疲勞': strBonus -= 10; endBonus -= 10; break; case '筋疲力盡': strBonus -= 15; endBonus -= 15; break; } } // ==================== 壓力 ==================== if (curEffs.壓力) { switch (curEffs.壓力.狀態) { case '心神沉靜': willBonus += 5; intBonus += 5; break; case '高度焦慮': willBonus -= 5; intBonus -= 5; break; case '不堪重負': willBonus -= 10; intBonus -= 10; break; case '精神崩潰': willBonus -= 20; intBonus -= 20; perBonus -= 10; break; } } // ==================== 無聊 ==================== if (curEffs.無聊) { switch (curEffs.無聊.狀態) { case '興致盎然': intBonus += 3; perBonus += 3; break; case '感到無聊': intBonus -= 2; perBonus -= 2; break; case '非常無聊': intBonus -= 5; perBonus -= 5; break; case '極度無聊': intBonus -= 10; perBonus -= 10; willBonus -= 5; break; } } // ==================== 性慾 ==================== if (curEffs.性慾) { switch (curEffs.性慾.狀態) { case '消退期': intBonus += 2; perBonus += 2; break; case '反應期': break; // 無影響 case '持續期': intBonus -= 2; perBonus -= 2; break; case '興奮期': intBonus -= 5; willBonus -= 5; break; case '高潮期': intBonus -= 10; willBonus -= 10; break; case '失控期': intBonus -= 20; willBonus -= 20; break; } } // ==================== 排泄欲 ==================== if (curEffs.排泄欲) { switch (curEffs.排泄欲.狀態) { case '輕微便意': intBonus -= 2; perBonus -= 2; break; case '明顯便意': intBonus -= 5; perBonus -= 5; break; case '快拉出來了': intBonus -= 10; perBonus -= 10; willBonus -= 5; break; case '大小便失禁': intBonus -= 20; perBonus -= 20; willBonus -= 10; break; } } // ==================== 體溫 ==================== if (curEffs.體溫) { switch (curEffs.體溫.狀態) { case '體溫過低': strBonus -= 5; endBonus -= 5; intBonus -= 5; break; case '嚴重失溫': strBonus -= 10; endBonus -= 10; intBonus -= 10; willBonus -= 10; break; case '發燒': strBonus -= 5; endBonus -= 5; intBonus -= 5; willBonus -= 5; break; case '高熱': strBonus -= 10; endBonus -= 10; intBonus -= 10; willBonus -= 10; break; } } // ==================== 疼痛 ==================== if (curEffs.疼痛) { switch (curEffs.疼痛.狀態) { case '輕微疼痛': intBonus -= 2; perBonus -= 2; break; case '劇烈疼痛': strBonus -= 10; endBonus -= 10; intBonus -= 10; perBonus -= 10; willBonus -= 10; break; case '極度痛苦': strBonus -= 15; endBonus -= 15; intBonus -= 15; perBonus -= 15; willBonus -= 15; break; } } // ==================== 清潔 ==================== if (curEffs.清潔) { switch (curEffs.清潔.狀態) { case '有點髒': break; // 無屬性影響,主要影響免疫負荷 case '污穢不堪': break; // 無屬性影響,主要影響免疫負荷 } } // ==================== 維生素 ==================== if (curEffs.維生素) { switch (curEffs.維生素.狀態) { case '維生素缺乏': endBonus -= 5; intBonus -= 2; break; case '維生素嚴重缺乏': endBonus -= 10; intBonus -= 5; perBonus -= 5; break; } } // ==================== 氮平衡 ==================== if (curEffs.氮平衡) { switch (curEffs.氮平衡.狀態) { case '負氮平衡': strBonus -= 5; endBonus -= 5; break; case '嚴重負氮平衡': strBonus -= 10; endBonus -= 10; break; } } // 應用屬性加值 set(vars, '屬性.耐力.加值', clamp(endBonus, -50, 50)); set(vars, '屬性.力量.加值', clamp(strBonus, -50, 50)); set(vars, '屬性.意志.加值', clamp(willBonus, -50, 50)); set(vars, '屬性.智力.加值', clamp(intBonus, -50, 50)); set(vars, '屬性.感知.加值', clamp(perBonus, -50, 50)); debugLog('屬性加值更新 - 力量:' + strBonus + ', 耐力:' + endBonus + ', 意志:' + willBonus + ', 智力:' + intBonus + ', 感知:' + perBonus); var end = int(get(vars, '屬性.耐力.等級')) || 20; var will = int(get(vars, '屬性.意志.等級')) || 20; var str = int(get(vars, '屬性.力量.等級')) || 20; var hpMax = 4 * end, spMax = 4 * will, baseCarry = 2 * str, carryBonus = 0; var equip = get(vars, '裝備'); if (_.isPlainObject(equip)) { _.forEach(equip, function(slot, slotName) { if (slotName.startsWith('$')) return; if (_.isPlainObject(slot)) { var bonus = int(get(slot, '負重加成')); if (Number.isFinite(bonus) && bonus > 0) carryBonus += bonus; } }); } var carryMax = Math.round(baseCarry * (carryBonus > 0 ? carryBonus : 1)); if (lastSnapshot && lastSnapshot.hpMax && lastSnapshot.hpMax !== hpMax) { var newHp = scaleValue(lastSnapshot.hpCur, lastSnapshot.hpMax, hpMax); if (newHp !== get(vars, '生理.當前生命值')) { set(vars, '生理.當前生命值', newHp); logChange('生命值上限變化', '當前生命值按比例調整'); } } set(vars, '生理.生命值上限', hpMax); set(vars, '精神.精神值上限', spMax); set(vars, '狀態.負重上限', carryMax); var totalWeight = 0; var items = get(vars, '攜帶物品'); if (_.isPlainObject(items)) { _.forEach(items, function(item, key) { if (key.startsWith('$')) return; var q = get(item, '數量'), w = get(item, '重量'); if (Number.isFinite(q) && Number.isFinite(w) && q > 0 && w > 0) totalWeight += q * w; }); } if (_.isPlainObject(equip)) { _.forEach(equip, function(slot, slotName) { if (slotName.startsWith('$')) return; if (_.isPlainObject(slot)) { var w = get(slot, '重量'); if (Number.isFinite(w) && w > 0) totalWeight += w; } }); } set(vars, '狀態.當前負重', Math.round(totalWeight * 100) / 100); var hpCur = get(vars, '生理.當前生命值'); if (hpCur > hpMax) { set(vars, '生理.當前生命值', hpMax); logChange('邊界修正', '當前生命值 ' + hpCur + ' → ' + hpMax); fixCount++; } var spCur = get(vars, '精神.當前精神值'); if (spCur > spMax) { set(vars, '精神.當前精神值', spMax); logChange('邊界修正', '當前精神值 ' + spCur + ' → ' + spMax); fixCount++; } var fatigueMax = 4 * end, fatigue = get(vars, '狀態.疲勞'); if (fatigue > fatigueMax) { set(vars, '狀態.疲勞', fatigueMax); logChange('邊界修正', '疲勞 ' + fatigue + ' → ' + fatigueMax); fixCount++; } else if (fatigue < 0) { set(vars, '狀態.疲勞', 0); logChange('邊界修正', '疲勞 ' + fatigue + ' → 0'); fixCount++; } var carbs = get(vars, '生理.碳水'); if (carbs > 2000) { var excess = carbs - 2000, toFat = Math.round(excess * 0.75); set(vars, '生理.碳水', 2000); var oldFat = get(vars, '生理.脂肪') || 0; set(vars, '生理.脂肪', oldFat + toFat); logChange('碳水轉脂肪', '超出' + excess + 'kcal → +' + toFat + 'kcal脂肪'); fixCount++; } var bounds = [ { p: '屬性.力量.加值', min: -50, max: 50 }, { p: '屬性.敏捷.加值', min: -50, max: 50 }, { p: '屬性.耐力.加值', min: -50, max: 50 }, { p: '屬性.智力.加值', min: -50, max: 50 }, { p: '屬性.感知.加值', min: -50, max: 50 }, { p: '屬性.意志.加值', min: -50, max: 50 }, { p: '生理.水分', min: 0, max: 100 }, { p: '生理.碳水', min: 0, max: 2000 }, { p: '生理.脂肪', min: 0, max: 100000 }, { p: '生理.蛋白質', min: 0, max: 2400 }, { p: '生理.維生素', min: 0, max: 100 }, { p: '生理.氮平衡', min: -100, max: 100 }, { p: '生理.免疫負荷', min: 0, max: 100 }, { p: '生理.體溫', min: 35, max: 40 }, { p: '生理.排泄欲', min: 0, max: 100 }, { p: '精神.壓力', min: 0, max: 100 }, { p: '精神.無聊', min: 0, max: 100 }, { p: '精神.孤獨', min: 0, max: 100 }, { p: '精神.睡眠', min: 0, max: 100 }, { p: '精神.性慾', min: 0, max: 100 }, { p: '生理.飽腹感', min: 0, max: 100 }, { p: '狀態.表皮溼度', min: 0, max: 100 }, { p: '狀態.感染', min: 0, max: 100 }, { p: '狀態.清潔', min: 0, max: 100 }, { p: '狀態.疼痛', min: 0, max: 100 }, { p: '系統.生存點數', min: 0, max: 99999 }, { p: '系統.奇蹟點數', min: 0, max: 99999 } ]; bounds.forEach(function(b) { var val = get(vars, b.p); if (Number.isFinite(val)) { var nv = clamp(val, b.min, b.max); if (nv !== val) { set(vars, b.p, nv); logChange('邊界修正', b.p + ' ' + val + ' → ' + nv); fixCount++; } } }); processCompanionLimits(vars); } // ======================== 狀態效果生成 ======================== var statusCache = null; function processStatusEffects(vars) { var cur = getCurrentTime(vars); if (!cur) return; var water = int(get(vars, '生理.水分')) || 0; var excretion = int(get(vars, '生理.排泄欲')) || 0; var stress = int(get(vars, '精神.壓力')) || 0; var lust = int(get(vars, '精神.性慾')) || 0; var boring = int(get(vars, '精神.無聊')) || 0; var sleep = int(get(vars, '精神.睡眠')) || 0; var vit = int(get(vars, '生理.維生素')) || 0; var temp = int(get(vars, '生理.體溫')) || 37; var full = int(get(vars, '生理.飽腹感')) || 0; var fatigue = int(get(vars, '狀態.疲勞')) || 0; var nitrogen = int(get(vars, '生理.氮平衡')) || 0; var skin = int(get(vars, '狀態.表皮溼度')) || 0; var clean = int(get(vars, '狀態.清潔')) || 0; var pain = int(get(vars, '狀態.疼痛')) || 0; var end = int(get(vars, '屬性.耐力.等級')) || 20; var will = int(get(vars, '屬性.意志.等級')) || 20; var str = int(get(vars, '屬性.力量.等級')) || 20; var maxFatigue = 4 * end; var fatiguePerc = maxFatigue > 0 ? fatigue / maxFatigue * 100 : 0; var effects = {}; var statuses = {}; function add(k, name, desc) { effects[k] = { 狀態: name, 效果: desc }; statuses[k] = { name: name, value: arguments[3] }; } // 排泄欲效果 if (excretion >= 100) add('排泄欲', '大小便失禁', '達到極限無法控制,直接在原地大小便失禁。', excretion); else if (excretion >= 71) add('排泄欲', '快拉出來了', '已經到了極限邊緣,頻繁放臭屁、漏尿,急著要找地方解放。', excretion); else if (excretion >= 51) add('排泄欲', '明顯便意', '腦中都是想要找地方拉屎或放尿,注意力難以集中。', excretion); else if (excretion >= 31) add('排泄欲', '輕微便意', '有點尿意想上廁所,但是還能忍耐。', excretion); else add('排泄欲', '消化正常', '無特殊效果。', excretion); // 水分 if (water > 90) add('水分', '水分充足', '身體水分充足,精神飽滿。所有與耐力相關的檢定獲得+5背景加值。', water); else if (water > 70) add('水分', '狀態正常', '沒什麼特別的感覺。', water); else if (water > 50) add('水分', '輕微口渴', '喉嚨有點幹,下意識想找水喝,注意力有點不集中。所有智力、感知相關檢定受到-2減值。', water); else if (water > 30) add('水分', '明顯口渴', '口乾舌燥,感覺有些煩躁,身體在強烈要求補水。所有重體力相關檢定(如力量、耐力)受到-5減值。', water); else if (water > 10) add('水分', '輕度脫水', '嘴脣乾裂,頭暈乏力,身體因缺水而變得遲鈍。所有重體力相關檢定受到-10減值。', water); else if (water > 0) add('水分', '嚴重脫水', '嚴重缺水,視野開始模糊,身體機能受到明顯影響,必須立刻補充水分。所有重體力相關檢定受到-15減值。', water); else add('水分', '瀕死:脫水', '身體因嚴重缺水而瀕臨崩潰。', water); // 壓力 if (stress < 10) add('壓力', '心神沉靜', '思維清晰,內心平靜。所有與意志和智力相關的檢定獲得+5加值', stress); else if (stress < 50) add('壓力', '狀態正常', '無特殊效果', stress); else if (stress < 80) add('壓力', '高度焦慮', '壓力極大,內心充滿焦慮和不安。所有檢定受到 -5 減值。', stress); else if (stress < 100) add('壓力', '不堪重負', '巨大的壓力使你瀕臨崩潰邊緣。所有檢定受到 -10 減值。', stress); else add('壓力', '精神崩潰', '精神防線徹底崩潰,角色陷入恐慌、偏執或絕望狀態。', stress); // 性慾 if (lust < 10) add('性慾', '消退期', '日常工作狀態,此時專注力提升,反應能力非常好。', lust); else if (lust < 25) add('性慾', '反應期', '開始會注意異性的身體曲線,被觸碰時會有輕微感覺。', lust); else if (lust < 45) add('性慾', '持續期', '盯著女性的胸部、臀部看,陰莖時不時半勃起,出現舔唇動作。', lust); else if (lust < 65) add('性慾', '興奮期', '主動觸碰女性身體,做出親密接觸,渴望更進一步。', lust); else if (lust < 85) add('性慾', '高潮期', '強烈渴望,克制力降到最低點,只要女性不強烈抗拒就會直接發生性愛。', lust); else add('性慾', '失控期', '理智幾乎喪失,陰莖硬到發痛。即使女性抗拒也會強行發生性愛。', lust); // 無聊 if (boring < 10) add('無聊', '興致盎然', '對周圍的一切充滿好奇和探索欲。技能經驗獲取幅度提升20%', boring); else if (boring < 50) add('無聊', '狀態正常', '無特殊效果', boring); else if (boring < 80) add('無聊', '感到無聊', '缺乏精神刺激讓你感到煩悶。', boring); else if (boring < 95) add('無聊', '非常無聊', '感到極度乏味,對什麼都提不起興趣。', boring); else add('無聊', '極度無聊', '單調的生活令人窒息,你迫切需要做點什麼來改變現狀。', boring); // 睡眠 if (sleep > 90) add('睡眠', '精力充沛', '充足的睡眠讓你恢復了所有精力。疲勞恢復速度加倍,精神屬性和技能相關檢定+5', sleep); else if (sleep > 60) add('睡眠', '狀態正常', '無特殊效果', sleep); else if (sleep > 30) add('睡眠', '疲倦', '開始感到身體和精神的疲勞。所有屬性檢定受到 -5 減值', sleep); else if (sleep > 0) add('睡眠', '非常疲憊', '嚴重缺乏睡眠,眼皮沉重,反應遲鈍。所有屬性檢定受到 -15 減值。', sleep); else add('睡眠', '精疲力竭', '身體達到極限,隨時可能因過度疲勞而昏倒。', sleep); // 維生素 if (vit > 80) add('維生素', '維生素充足', '維生素充足', vit); else if (vit > 30) add('維生素', '狀態正常', '無特殊效果', vit); else if (vit > 0) add('維生素', '維生素缺乏', '身體出現多種不良反應,更快疲勞等', vit); else add('維生素', '維生素嚴重缺乏', '出現壞血病、夜盲症等嚴重疾病', vit); // 體溫 if (temp > 40) add('體溫', '高熱', '身體滾燙,意識模糊,嚴重高燒讓人難以思考。所有屬性檢定-10減值。', temp); else if (temp > 38) add('體溫', '發燒', '身體發燙,感到乏力、惡寒,身體非常不適。所有屬性檢定-5減值。', temp); else if (temp > 36) add('體溫', '狀態正常', '體溫正常,沒什麼不適。', temp); else if (temp > 34) add('體溫', '體溫過低', '身體開始顫抖,手腳冰冷,思維和行動變得遲緩。所有屬性檢定-5減值。', temp); else add('體溫', '嚴重失溫', '身體劇烈顫抖,意識開始模糊,再這樣下去會有生命危險。所有屬性檢定-10減值。', temp); // 飽腹感 if (full > 95) add('飽腹感', '過飽', '吃撐了,行動有點不方便。所有敏捷和耐力相關檢定受到-5減值。', full); else if (full > 85) add('飽腹感', '飽足', '吃飽喝足,感覺充滿能量,體力充沛,疲勞感不易累積。', full); else if (full > 60) add('飽腹感', '狀態正常', '沒什麼特別的感覺。', full); else if (full > 30) add('飽腹感', '輕微飢餓', '肚子有點空,開始期待下一頓飯。', full); else if (full > 10) add('飽腹感', '明顯飢餓', '肚子在咕咕叫,有點想吃東西了,注意力難以集中。所有屬性檢定受到-5減值。', full); else if (full > 0) add('飽腹感', '極度飢餓', '餓得前胸貼後背,身體因爲缺乏能量而感到虛弱,必須儘快進食。所有屬性檢定受到-15減值。', full); else add('飽腹感', '虛脫', '身體因能量儲備完全耗盡而極度虛弱,各項機能開始下降,身體正在消耗自身以維持生命。必須立即進食以阻止進一步惡化。', full); // 疲勞 if (fatiguePerc < 10) add('疲勞', '精力十足', '渾身是勁,感覺還能再戰三百回合。所有與力量和耐力相關檢定獲得+5背景加值。', fatigue); else if (fatiguePerc <= 30) add('疲勞', '狀態正常', '身體狀態良好,沒什麼特別的感覺。', fatigue); else if (fatiguePerc <= 50) add('疲勞', '微微出汗', '身體開始發熱,呼吸稍微加快,但還遠未到累的時候。', fatigue); else if (fatiguePerc <= 70) add('疲勞', '有些疲憊', '肌肉微微發酸,呼吸比平時稍重,是時候考慮歇一歇了。所有重體力相關檢定受到-5減值。', fatigue); else if (fatiguePerc <= 90) add('疲勞', '相當疲勞', '身體變得沉重,每一下動作都需要更多力氣,疲倦感十分明顯。所有重體力相關檢定受到-10減值。', fatigue); else add('疲勞', '筋疲力盡', '身體非常沉重,每一下動作都變得吃力,只想找個地方坐下來喘口氣。所有重體力相關檢定受到-15減值。', fatigue); // 氮平衡 if (nitrogen > 75) add('氮平衡', '正氮平衡', '身體處於合成代謝狀態,有利於組織生長和修復。1.5倍力量經驗增長,更快從疲勞恢復', nitrogen); else if (nitrogen > -50) add('氮平衡', '狀態正常', '無特殊效果', nitrogen); else if (nitrogen > -90) add('氮平衡', '負氮平衡', '身體處於分解代謝狀態,肌肉開始流失,感到無力。力量和耐力相關檢定-5減值', nitrogen); else add('氮平衡', '嚴重負氮平衡', '嚴重的組織蛋白分解,身體虛弱。力量和耐力相關檢定-10減值', nitrogen); // 表皮溼度 if (skin > 80) add('表皮溼度', '全身溼透', '衣物和皮膚完全溼透,非常不適,體溫流失速度加快。在寒冷環境中更容易觸發"體溫過低"狀態', skin); else if (skin > 40) add('表皮溼度', '潮溼', '皮膚和衣物感到潮溼,略有不適', skin); else if (skin > 20) add('表皮溼度', '狀態正常', '無特殊效果', skin); else add('表皮溼度', '皮膚乾燥', '皮膚缺水,感到緊繃和輕微的瘙癢', skin); // 清潔 if (clean > 90) add('清潔', '乾淨整潔', '全身清爽,心情愉悅。精神值自然恢復速度提升10%', clean); else if (clean > 40) add('清潔', '狀態正常', '無特殊效果', clean); else if (clean > 10) add('清潔', '有點髒', '身上有污垢或異味,讓人不適。', clean); else add('清潔', '污穢不堪', '全身骯髒,散發惡臭,大大增加感染疾病的風險。', clean); // 疼痛 if (pain > 90) add('疼痛', '極度痛苦', '難以忍受的劇痛,幾乎無法思考和行動。所有檢定受到-15減值,有概率因劇痛而昏厥', pain); else if (pain > 50) add('疼痛', '劇烈疼痛', '強烈的疼痛感嚴重干擾了專注力。所有檢定受到-10減值', pain); else if (pain > 10) add('疼痛', '輕微疼痛', '持續的隱痛讓人煩躁,分散注意力。所有與智力、感知相關的檢定受到-2減值', pain); else add('疼痛', '狀態正常', '無特殊效果', pain); var ignoreList = ['狀態正常', '沒什麼特別的感覺', '無特殊感覺', '無特殊效果', '體溫正常', '皮膚乾燥', '興致盎然', '精力充沛', '心神沉靜', '乾淨整潔']; for (var k in statuses) { if (ignoreList.includes(statuses[k].name)) delete statuses[k]; } var curEff = get(vars, '狀態.生效效果') || {}; for (var k in effects) { var newEff = effects[k], old = curEff[k]; if (!old || old.狀態 !== newEff.狀態 || old.效果 !== newEff.效果) { set(vars, '狀態.生效效果.' + k, newEff); logChange('生效效果更新', k + ': ' + newEff.狀態); fixCount++; } } for (var k in curEff) { if (k.startsWith('$')) continue; if (!effects[k] && curEff[k] !== undefined) { set(vars, '狀態.生效效果.' + k, null); } } // 持續傷害計算(路徑已更新) if (statusCache && cur) { var prev = statusCache.time; var delta = (cur.total - prev.total) / 60; if (delta > 0 && statusCache.statuses) { var oldStats = statusCache.statuses; var damageStates = [ '明顯口渴', '輕度脫水', '嚴重脫水', '過飽', '明顯飢餓', '極度飢餓', '虛脫', '相當疲勞', '筋疲力盡', '高度焦慮', '不堪重負', '感到無聊', '非常無聊', '極度無聊', '高熱', '發燒', '體溫過低', '嚴重失溫', '極度痛苦', '劇烈疼痛', '有點髒', '污穢不堪' ]; for (var k in statuses) { var s = statuses[k], old = oldStats[k]; var effectiveName = s.name; if (old && old.name !== s.name && damageStates.includes(old.name)) effectiveName = old.name; if (damageStates.includes(effectiveName)) { debugLog('持續傷害觸發: ' + effectiveName + ', 時間差: ' + delta.toFixed(2) + '小時'); switch(effectiveName) { case '明顯口渴': var newSp1 = clamp((get(vars, '精神.當前精神值')||0) - 0.5 * delta, 0, 100); set(vars, '精神.當前精神值', newSp1); logChange('持續傷害', '明顯口渴:精神值 -' + (0.5 * delta).toFixed(2) + ' → ' + newSp1.toFixed(2)); fixCount++; break; case '輕度脫水': var newHp2 = clamp((get(vars, '生理.當前生命值')||0) - 0.5 * delta, 0, 1000); var newSp2 = clamp((get(vars, '精神.當前精神值')||0) - 0.5 * delta, 0, 100); set(vars, '生理.當前生命值', newHp2); set(vars, '精神.當前精神值', newSp2); logChange('持續傷害', '輕度脫水:生命/精神值各 -' + (0.5 * delta).toFixed(2)); fixCount++; break; case '嚴重脫水': var newHp3 = clamp((get(vars, '生理.當前生命值')||0) - 1 * delta, 0, 1000); var newSp3 = clamp((get(vars, '精神.當前精神值')||0) - 1 * delta, 0, 100); var newLoad3 = clamp(get(vars, '生理.免疫負荷') + 0.5 * delta, 0, 100); set(vars, '生理.當前生命值', newHp3); set(vars, '精神.當前精神值', newSp3); set(vars, '生理.免疫負荷', newLoad3); logChange('持續傷害', '嚴重脫水:生命/精神值各 -' + (1 * delta).toFixed(2) + ',免疫負荷 +' + (0.5 * delta).toFixed(2)); fixCount++; break; case '過飽': var newFat4 = clamp(get(vars, '狀態.疲勞') + 1 * delta, 0, 4 * end); set(vars, '狀態.疲勞', newFat4); logChange('持續傷害', '過飽:疲勞值 +' + (1 * delta).toFixed(2)); fixCount++; break; case '明顯飢餓': var newSp5 = clamp((get(vars, '精神.當前精神值')||0) - 0.5 * delta, 0, 100); set(vars, '精神.當前精神值', newSp5); logChange('持續傷害', '明顯飢餓:精神值 -' + (0.5 * delta).toFixed(2)); fixCount++; break; case '極度飢餓': var newHp6 = clamp((get(vars, '生理.當前生命值')||0) - 0.5 * delta, 0, 1000); var newSp6 = clamp((get(vars, '精神.當前精神值')||0) - 1 * delta, 0, 100); set(vars, '生理.當前生命值', newHp6); set(vars, '精神.當前精神值', newSp6); if (get(vars, '環境.體感溫度') < get(vars, '狀態.最低舒適溫度')) { var newTemp6 = clamp(get(vars, '生理.體溫') - 0.5 * delta, 35, 40); set(vars, '生理.體溫', newTemp6); logChange('持續傷害', '極度飢餓(寒冷環境):生命值 -' + (0.5 * delta).toFixed(2) + ',精神值 -' + (1 * delta).toFixed(2) + ',體溫 -0.5℃'); } else { logChange('持續傷害', '極度飢餓:生命值 -' + (0.5 * delta).toFixed(2) + ',精神值 -' + (1 * delta).toFixed(2)); } fixCount++; break; case '虛脫': var newHp7 = clamp((get(vars, '生理.當前生命值')||0) - 1 * delta, 0, 1000); var newSp7 = clamp((get(vars, '精神.當前精神值')||0) - 2 * delta, 0, 100); set(vars, '生理.當前生命值', newHp7); set(vars, '精神.當前精神值', newSp7); if (get(vars, '環境.體感溫度') < get(vars, '狀態.最低舒適溫度')) { var newTemp7 = clamp(get(vars, '生理.體溫') - 0.5 * delta, 35, 40); set(vars, '生理.體溫', newTemp7); logChange('持續傷害', '虛脫(寒冷環境):生命值 -' + (1 * delta).toFixed(2) + ',精神值 -' + (2 * delta).toFixed(2) + ',體溫 -0.5℃'); } else { logChange('持續傷害', '虛脫:生命值 -' + (1 * delta).toFixed(2) + ',精神值 -' + (2 * delta).toFixed(2)); } fixCount++; break; case '相當疲勞': var newImm8 = clamp(get(vars, '狀態.免疫力') - 0.5 * delta, 0, 100); set(vars, '狀態.免疫力', newImm8); logChange('持續傷害', '相當疲勞:免疫力 -' + (0.5 * delta).toFixed(2) + ' → ' + newImm8.toFixed(2)); fixCount++; break; case '筋疲力盡': var newImm9 = clamp(get(vars, '狀態.免疫力') - 1 * delta, 0, 100); var newSleep9 = clamp(get(vars, '精神.睡眠') - 0.5 * delta, 0, 100); set(vars, '狀態.免疫力', newImm9); set(vars, '精神.睡眠', newSleep9); logChange('持續傷害', '筋疲力盡:免疫力 -' + (1 * delta).toFixed(2) + ' → ' + newImm9.toFixed(2) + ',睡眠值 -' + (0.5 * delta).toFixed(2) + ' → ' + newSleep9.toFixed(2)); fixCount++; break; case '高度焦慮': var newSp10 = clamp((get(vars, '精神.當前精神值')||0) - 0.3 * delta, 0, 100); set(vars, '精神.當前精神值', newSp10); logChange('持續傷害', '高度焦慮:精神值 -' + (0.3 * delta).toFixed(2) + ' → ' + newSp10.toFixed(2)); fixCount++; break; case '不堪重負': var newSp11 = clamp((get(vars, '精神.當前精神值')||0) - 0.5 * delta, 0, 100); set(vars, '精神.當前精神值', newSp11); logChange('持續傷害', '不堪重負:精神值 -' + (0.5 * delta).toFixed(2) + ' → ' + newSp11.toFixed(2)); fixCount++; break; case '感到無聊': var newSp12 = clamp((get(vars, '精神.當前精神值')||0) - 0.3 * delta, 0, 100); set(vars, '精神.當前精神值', newSp12); logChange('持續傷害', '感到無聊:精神值 -' + (0.3 * delta).toFixed(2) + ' → ' + newSp12.toFixed(2)); fixCount++; break; case '非常無聊': var newSp13 = clamp((get(vars, '精神.當前精神值')||0) - 0.5 * delta, 0, 100); set(vars, '精神.當前精神值', newSp13); logChange('持續傷害', '非常無聊:精神值 -' + (0.5 * delta).toFixed(2) + ' → ' + newSp13.toFixed(2)); fixCount++; break; case '極度無聊': var newSp14 = clamp((get(vars, '精神.當前精神值')||0) - 0.8 * delta, 0, 100); set(vars, '精神.當前精神值', newSp14); logChange('持續傷害', '極度無聊:精神值 -' + (0.8 * delta).toFixed(2) + ' → ' + newSp14.toFixed(2)); fixCount++; break; case '高熱': var newHp15 = clamp((get(vars, '生理.當前生命值')||0) - 1 * delta, 0, 1000); var newSp15 = clamp((get(vars, '精神.當前精神值')||0) - 1 * delta, 0, 100); set(vars, '生理.當前生命值', newHp15); set(vars, '精神.當前精神值', newSp15); logChange('持續傷害', '高熱:生命/精神值各 -' + (1 * delta).toFixed(2)); fixCount++; break; case '發燒': var newHp16 = clamp((get(vars, '生理.當前生命值')||0) - 0.5 * delta, 0, 1000); var newSp16 = clamp((get(vars, '精神.當前精神值')||0) - 0.5 * delta, 0, 100); set(vars, '生理.當前生命值', newHp16); set(vars, '精神.當前精神值', newSp16); logChange('持續傷害', '發燒:生命/精神值各 -' + (0.5 * delta).toFixed(2)); fixCount++; break; case '體溫過低': var newFat17 = clamp(get(vars, '狀態.疲勞') + 1.5 * delta, 0, 4 * end); set(vars, '狀態.疲勞', newFat17); logChange('持續傷害', '體溫過低:疲勞值 +' + (1.5 * delta).toFixed(2) + ' → ' + newFat17.toFixed(2)); fixCount++; break; case '嚴重失溫': var newHp18 = clamp((get(vars, '生理.當前生命值')||0) - 1.5 * delta, 0, 1000); var newFat18 = clamp(get(vars, '狀態.疲勞') + 2.5 * delta, 0, 4 * end); set(vars, '生理.當前生命值', newHp18); set(vars, '狀態.疲勞', newFat18); logChange('持續傷害', '嚴重失溫:生命值 -' + (1.5 * delta).toFixed(2) + ',疲勞值 +' + (2.5 * delta).toFixed(2)); fixCount++; break; case '極度痛苦': var newSp19 = clamp((get(vars, '精神.當前精神值')||0) - 1.5 * delta, 0, 100); set(vars, '精神.當前精神值', newSp19); logChange('持續傷害', '極度痛苦:精神值 -' + (1.5 * delta).toFixed(2) + ' → ' + newSp19.toFixed(2)); fixCount++; break; case '劇烈疼痛': var newSp20 = clamp((get(vars, '精神.當前精神值')||0) - 0.5 * delta, 0, 100); set(vars, '精神.當前精神值', newSp20); logChange('持續傷害', '劇烈疼痛:精神值 -' + (0.5 * delta).toFixed(2) + ' → ' + newSp20.toFixed(2)); fixCount++; break; case '有點髒': var newLoad21 = clamp(get(vars, '生理.免疫負荷') + 0.3 * delta, 0, 100); set(vars, '生理.免疫負荷', newLoad21); logChange('持續傷害', '有點髒:免疫負荷 +' + (0.3 * delta).toFixed(2) + ' → ' + newLoad21.toFixed(2)); fixCount++; break; case '污穢不堪': var newLoad22 = clamp(get(vars, '生理.免疫負荷') + 0.5 * delta, 0, 100); set(vars, '生理.免疫負荷', newLoad22); logChange('持續傷害', '污穢不堪:免疫負荷 +' + (0.5 * delta).toFixed(2) + ' → ' + newLoad22.toFixed(2)); fixCount++; break; } } } } } // 計算最終生命上限和免疫力 var baseHpMax = 4 * end, immunity = end; if (statuses.氮平衡 && statuses.氮平衡.name === '嚴重負氮平衡') { baseHpMax -= 10; immunity -= 10; } if (statuses.維生素) { switch(statuses.維生素.name) { case '維生素充足': immunity += 5; break; case '維生素缺乏': immunity -= 5; break; case '維生素嚴重缺乏': immunity -= 10; break; } } if (statuses.體溫) { switch(statuses.體溫.name) { case '發燒': immunity += 5; break; case '高熱': immunity += 10; break; } } var oldStatsForImmunity = (statusCache && statusCache.statuses) ? statusCache.statuses : {}; if (statuses.疲勞 && statuses.疲勞.name === '相當疲勞' && (!oldStatsForImmunity.疲勞 || oldStatsForImmunity.疲勞.name !== '相當疲勞')) immunity -= 2; if (statuses.疲勞 && statuses.疲勞.name === '筋疲力盡' && (!oldStatsForImmunity.疲勞 || oldStatsForImmunity.疲勞.name !== '筋疲力盡')) immunity -= 4; var spMax = 4 * will; var spCur2 = get(vars, '精神.當前精神值'); if (spCur2 > spMax) { set(vars, '精神.當前精神值', spMax); logChange('邊界修正', '當前精神值 ' + spCur2 + ' → ' + spMax); fixCount++; } var immLoadCurr = get(vars, '生理.免疫負荷'); if (immLoadCurr > 100) { set(vars, '生理.免疫負荷', 100); logChange('邊界修正', '免疫負荷 ' + immLoadCurr + ' → 100'); fixCount++; } else if (immLoadCurr < 0) { set(vars, '生理.免疫負荷', 0); logChange('邊界修正', '免疫負荷 ' + immLoadCurr + ' → 0'); fixCount++; } var curImmunity = get(vars, '狀態.免疫力'); var finalImm = clamp(immunity, 0, 100); if (curImmunity !== finalImm) { set(vars, '狀態.免疫力', finalImm); logChange('免疫力修正', curImmunity + ' → ' + finalImm); fixCount++; } statusCache = { time: cur, statuses: statuses }; } // ======================== 清理已完結的劇情演出 ======================== function cleanupCompletedPerformances(vars) { var comps = get(vars, '同伴'); if (!_.isPlainObject(comps)) return; for (var name in comps) { if (name.startsWith('$')) continue; var performances = get(vars, '同伴.' + name + '.劇情演出'); if (!Array.isArray(performances) || performances.length === 0) continue; // 收集需要刪除的主題 var themesToDelete = {}; // 第一遍:分析每個主題的狀態 for (var i = 0; i < performances.length; i++) { var perf = performances[i]; if (typeof perf !== 'string') continue; // 解析格式:事件-主題-序號-狀態:描述 var match = perf.match(/^事件-([^-]+)-(\d+)-(.+?):(.+)$/); if (!match) continue; var theme = match[1]; var seq = parseInt(match[2]); var status = match[3]; var desc = match[4]; if (!themesToDelete[theme]) { themesToDelete[theme] = { maxSeq: 0, hasCompleted: false, indices: [] }; } themesToDelete[theme].indices.push(i); if (seq > themesToDelete[theme].maxSeq) { themesToDelete[theme].maxSeq = seq; } if (status === '已完成') { themesToDelete[theme].hasCompleted = true; } } // 第二遍:從後往前刪除(避免索引錯亂) var indicesToRemove = []; for (var theme in themesToDelete) { var info = themesToDelete[theme]; // 規則:當序號達到 5 且最後一個事件已完成時,刪除整個主題 if (info.maxSeq >= 5 && info.hasCompleted) { indicesToRemove.push(...info.indices); logChange('劇情演出清理', name + ' 主題「' + theme + '」序號已達 ' + info.maxSeq + ',已完結,刪除 ' + info.indices.length + ' 條'); fixCount++; } } // 從後往前刪除(保持索引有效) if (indicesToRemove.length > 0) { indicesToRemove.sort((a, b) => b - a); for (var i = 0; i < indicesToRemove.length; i++) { performances.splice(indicesToRemove[i], 1); } set(vars, '同伴.' + name + '.劇情演出', performances); } } } // ======================== 任務和商店處理 ======================== function processTasksAndShop(vars) { function updateTaskCompletion(tp) { var task = get(vars, tp); if (!_.isPlainObject(task) || !task.狀態) return; var changed = false; var baseReqs = get(task, '任務要求'); if (_.isPlainObject(baseReqs)) { var allDone = true; for (var k in baseReqs) { if (k.startsWith('$')) continue; var req = baseReqs[k], newReq = checkRequirement(req); if (newReq !== req) { set(vars, tp + '.任務要求.' + k, newReq); changed = true; } if (!newReq.已完成) allDone = false; } var curDone = get(task, '完成情況.基礎要求完成'); if (allDone !== curDone) { set(vars, tp + '.完成情況.基礎要求完成', allDone); changed = true; } } var advReqs = get(task, '進階目標'); if (_.isPlainObject(advReqs)) { var allDone = true; for (var k in advReqs) { if (k.startsWith('$')) continue; var req = advReqs[k], newReq = checkRequirement(req); if (newReq !== req) { set(vars, tp + '.進階目標.' + k, newReq); changed = true; } if (!newReq.已完成) allDone = false; } var curDone = get(task, '完成情況.進階目標完成'); if (allDone !== curDone) { set(vars, tp + '.完成情況.進階目標完成', allDone); changed = true; } } return changed; } function updateTaskStatus(tp, type) { var task = get(vars, tp); if (!_.isPlainObject(task) || !task.狀態) return; var status = get(task, '狀態'), baseDone = get(task, '完成情況.基礎要求完成', false), advDone = get(task, '完成情況.進階目標完成', false), hasAdv = _.isPlainObject(get(task, '進階目標')) && Object.keys(get(task, '進階目標')).filter(function(k) { return !k.startsWith('$'); }).length > 0, endTimeStr = get(task, '結束時間', ''), curTime = getCurrentTime(vars); var correct = status, endTime = endTimeStr ? parseFullTime(endTimeStr) : null, expired = endTime && curTime && curTime.total >= endTime.total; if (expired) { if (baseDone) { correct = '待結算'; if (hasAdv && !advDone) { var s = get(task, '結算狀態') || { 基礎獎勵已發放: false, 進階獎勵已發放: false, 失敗懲罰已發放: false }; if (!s.進階獎勵已發放) set(vars, tp + '.結算狀態', { ...s, 進階獎勵已發放: true }); } } else { if (type === '緊急') { correct = '已失敗'; var s = get(task, '結算狀態') || { 基礎獎勵已發放: false, 進階獎勵已發放: false, 失敗懲罰已發放: false }; if (s.失敗懲罰已發放 === undefined || s.失敗懲罰已發放 === false) { set(vars, tp + '.結算狀態', { ...s, 失敗懲罰已發放: false }); } } else { correct = '已放棄'; } } } else { if (!baseDone) correct = '進行中'; else if (hasAdv && !advDone) correct = '進階中'; else correct = '待結算'; } if (status !== correct) { set(vars, tp + '.狀態', correct); logChange('任務狀態修正', tp + ' ' + status + ' → ' + correct); } } // ========== 主線、支線、日常、緊急、背德(統一處理) ========== // 🔴 加入「背德」 var types = ['主線', '支線', '日常', '緊急', '背德']; for (var i = 0; i < types.length; i++) { var t = types[i], p = '任務.' + t; var task = get(vars, p); if (_.isPlainObject(task) && task.狀態) { updateTaskCompletion(p); updateTaskStatus(p, t); } } // ========== 羈絆任務(保持原本的獨立迴圈,不做任何修改) ========== var bond = get(vars, '任務.羈絆'); if (_.isPlainObject(bond)) { for (var n in bond) { if (n.startsWith('$')) continue; var p = '任務.羈絆.' + n; var task = bond[n]; if (_.isPlainObject(task) && task.狀態) { updateTaskCompletion(p); updateTaskStatus(p, '羈絆'); } } } // ========== 清理 ========== function cleanup() { // 清理主類型任務(加入「背德」) var types = ['主線', '支線', '日常', '緊急', '背德']; for (var i = 0; i < types.length; i++) { var t = types[i], p = '任務.' + t; var task = get(vars, p); if (!_.isPlainObject(task) || _.isEmpty(task)) continue; var baseReward = get(task, '結算狀態.基礎獎勵已發放', false); var advReward = get(task, '結算狀態.進階獎勵已發放', false); var hasAdv = _.isPlainObject(get(task, '進階目標')) && Object.keys(get(task, '進階目標')).filter(function(k) { return !k.startsWith('$'); }).length > 0; var allDone = baseReward && (!hasAdv || advReward); if (allDone) { _.set(vars, p, {}); logChange('任務清理', p + ' 設爲空對象'); continue; } var status = get(task, '狀態'); if (status === '待結算' && baseReward && (!hasAdv || advReward)) { _.set(vars, p, {}); logChange('任務清理', p + ' 設爲空對象'); } else if (status === '已放棄') { _.set(vars, p, {}); logChange('任務清理', p + ' 設爲空對象'); } else if (status === '已失敗' && get(task, '結算狀態.失敗懲罰已發放', false)) { _.set(vars, p, {}); logChange('任務清理', p + ' 設爲空對象'); } } // 羈絆任務清理(保持原本不變) var bond2 = get(vars, '任務.羈絆'); if (_.isPlainObject(bond2)) { for (var n in bond2) { if (n.startsWith('$')) continue; var p = '任務.羈絆.' + n; var task = bond2[n]; if (!_.isPlainObject(task) || _.isEmpty(task)) continue; var baseReward = get(task, '結算狀態.基礎獎勵已發放', false); var advReward = get(task, '結算狀態.進階獎勵已發放', false); var hasAdv = _.isPlainObject(get(task, '進階目標')) && Object.keys(get(task, '進階目標')).filter(function(k) { return !k.startsWith('$'); }).length > 0; var allDone = baseReward && (!hasAdv || advReward); if (allDone) { _.set(vars, p, {}); logChange('羈絆任務清理', p + ' 設爲空對象'); continue; } var status = get(task, '狀態'); if (status === '待結算' && baseReward && (!hasAdv || advReward)) { _.set(vars, p, {}); logChange('羈絆任務清理', p + ' 設爲空對象'); } else if (status === '已放棄') { _.set(vars, p, {}); logChange('羈絆任務清理', p + ' 設爲空對象'); } } } } cleanup(); } // ======================== 同伴生效效果(改寫版 - 適用你的路徑) ======================== function processCompanionStatusEffects(vars) { var comps = get(vars, '同伴'); if (!_.isPlainObject(comps)) return; var cur = getCurrentTime(vars); if (!cur) return; // 獲取主角的維生素和氮平衡(同伴會受影響) var playerVit = int(get(vars, '生理.維生素')) || 0; var playerNitro = int(get(vars, '生理.氮平衡')) || 0; for (var n in comps) { if (n.startsWith('$')) continue; var base = '同伴.' + n; // ========== 讀取數據(你的路徑) ========== var water = int(get(vars, base + '.生理.水分')) || 0; var full = int(get(vars, base + '.生理.飽腹感')) || 0; var excretion = int(get(vars, base + '.生理.排泄欲')) || 0; var stress = int(get(vars, base + '.精神.壓力')) || 0; var sleepVal = int(get(vars, base + '.精神.睡眠')) || 0; var fatigue = int(get(vars, base + '.狀態.疲勞')) || 0; var cleanVal = int(get(vars, base + '.狀態.清潔')) || 0; var endLv = int(get(vars, base + '.屬性.耐力.等級')) || 20; var maxFat = 4 * endLv; var fatPerc = maxFat > 0 ? fatigue / maxFat * 100 : 0; // 生命/精神值(用於持續傷害) var hpCur = int(get(vars, base + '.生理.當前生命值')) || 100; var hpMaxV = int(get(vars, base + '.生理.生命值上限')) || 100; var spCur2 = int(get(vars, base + '.精神.當前精神值')) || 100; var spMaxV = int(get(vars, base + '.精神.精神值上限')) || 100; // ========== 生理週期(你的路徑) ========== var menses = int(get(vars, base + '.生理狀態.月經週期')) || 14; var pregnant = get(vars, base + '.生理狀態.懷孕狀態'); var isPregnant = (pregnant && pregnant !== '未懷孕' && pregnant !== '流產'); var preDays = isPregnant ? (get(vars, base + '.生理狀態.懷孕天數') || 0) : 0; // ========== 🔴 新增:月經週期詳細 Log ========== debugLog('🩸 [月經週期] ' + n + ' 的月經週期計算開始'); debugLog(' ├─ 原始週期值: ' + menses); debugLog(' ├─ 懷孕狀態: ' + (pregnant || '未懷孕')); debugLog(' ├─ 是否懷孕: ' + (isPregnant ? '是 (第' + preDays + '天)' : '否')); // 計算階段 var cyclePhase = ''; var phaseDesc = ''; if (isPregnant) { cyclePhase = '懷孕中'; phaseDesc = '受孕概率較高,部分同伴可能表現出性慾變化(依人設而定)。'; debugLog(' └─ 🔴 結果: 懷孕中,跳過月經週期判斷'); } else { var cycleDay = menses; if (cycleDay >= 1 && cycleDay <= 5) { cyclePhase = '月經期'; phaseDesc = '體力消耗+20%。腹部不適,感知檢定-2。情緒敏感,對互動反應可能更易煩躁。'; debugLog(' ├─ 📊 階段判斷: 第' + cycleDay + '天 → 月經期 (1-5天)'); } else if (cycleDay >= 10 && cycleDay <= 16) { cyclePhase = '危險期(易孕期)'; phaseDesc = '受孕概率較高,部分同伴可能表現出性慾變化(依人設而定)。'; debugLog(' ├─ 📊 階段判斷: 第' + cycleDay + '天 → 🔴 危險期 (10-16天)'); debugLog(' ├─ ⚠️ 懷孕風險: 高 (內射機率約25%)'); } else if (cycleDay >= 6 && cycleDay <= 9) { cyclePhase = '安全期'; phaseDesc = '受孕概率較低。'; debugLog(' ├─ 📊 階段判斷: 第' + cycleDay + '天 → 🟢 安全期 (6-9天)'); debugLog(' ├─ ⚠️ 懷孕風險: 低 (內射機率約2%)'); } else if (cycleDay >= 17 && cycleDay <= 28) { cyclePhase = '安全期'; phaseDesc = '受孕概率較低。'; debugLog(' ├─ 📊 階段判斷: 第' + cycleDay + '天 → 🟢 安全期 (17-28天)'); debugLog(' ├─ ⚠️ 懷孕風險: 低 (內射機率約2%)'); } else { cyclePhase = '狀態正常'; phaseDesc = '無特殊效果。'; debugLog(' ├─ 📊 階段判斷: 第' + cycleDay + '天 → 狀態正常 (範圍外)'); } if (cyclePhase !== '狀態正常') { add('生理週期', cyclePhase, phaseDesc); debugLog(' └─ ✅ 已更新生效效果: 生理週期 → ' + cyclePhase); } else { // 移除生理週期效果 if (get(vars, base + '.狀態.生效效果.生理週期')) { _.unset(vars, base + '.狀態.生效效果.生理週期'); debugLog(' └─ 🗑️ 已移除生理週期效果 (狀態正常)'); } else { debugLog(' └─ ℹ️ 狀態正常,無需更新'); } } } // 移除懷孕效果(如果有殘留) if (get(vars, base + '.狀態.生效效果.懷孕')) { _.unset(vars, base + '.狀態.生效效果.懷孕'); debugLog(' └─ 🗑️ 已清除殘留的懷孕效果'); } // ========== 排泄系統(單一排泄欲拆分為尿意/便意) ========== var pee = excretion; var poo = excretion; // ========== 狀態效果生成 ========== var effects = {}; var statuses = {}; // ========== 好感效果生成 ========== var affection = get(vars, base + '.技能.好感(對阿彬).等級') || 1; var affectionStage = ''; var affectionDesc = ''; function add(k, name, desc) { effects[k] = { 狀態: name, 效果: desc }; statuses[k] = { name: name }; } // ----- 好感 ----- if (affection >= 10) { affectionStage = '夫妻'; affectionDesc = '完全信賴對方,願意為對方付出一切,對任何要求都會順從,包括特殊性癖和背德行為。'; } else if (affection == 9) { affectionStage = '愛侶'; affectionDesc = '會主動親近、撒嬌、討抱,願意配合正常體位的性行為,對特殊性行為會猶豫但可能嘗試。'; } else if (affection == 8) { affectionStage = '伴侶'; affectionDesc = '會主動親近、撒嬌、討抱,願意配合正常體位的性行為,對特殊性行為明確說不。'; } else if (affection == 7) { affectionStage = '熱戀'; affectionDesc = '渴望更多肢體接觸,不排斥正常體位的性行為(不接受特殊性行為,特殊人設例外)。'; } else if (affection == 6) { affectionStage = '戀人'; affectionDesc = '期待肢體接觸,對性行為半推半就,嘴上拒絕但身體不排斥,需要對方主動引導。'; } else if (affection == 5) { affectionStage = '曖昧期'; affectionDesc = '願意嘗試非插入式性行為(口交、手交、體外摩擦),對插入式性行為仍有猶豫,需要引導。'; } else if (affection == 4) { affectionStage = '知己'; affectionDesc = '會主動靠近對方,享受獨處時光,可以接受愛撫和深吻,但對插入式性行為仍有抗拒。'; } else if (affection == 3) { affectionStage = '好友'; affectionDesc = '會主動開啟話題,回應熱情,願意分享想法,可以接受親吻、牽手、擁抱等肢體接觸。'; } else if (affection == 2) { affectionStage = '認識'; affectionDesc = '願意回應對方,保持基本禮貌距離,不主動展開話題,但對方提問時會回答。'; } else { affectionStage = '陌生'; affectionDesc = '對不熟悉的人保持防備,回應簡短,不主動開啟話題,避免眼神接觸和肢體靠近。'; } add('好感度', affectionStage, affectionDesc); // ----- 水分 ----- if (water > 90) add('水分', '水分充足', '身體水分充足,精神飽滿。所有與耐力相關的檢定獲得+5背景加值。'); else if (water > 70) add('水分', '狀態正常', '沒什麼特別的感覺。'); else if (water > 50) add('水分', '輕微口渴', '喉嚨有點幹,下意識想找水喝,注意力有點不集中。所有智力、感知相關檢定受到-2減值。'); else if (water > 30) add('水分', '明顯口渴', '口乾舌燥,感覺有些煩躁,身體在強烈要求補水。所有重體力相關檢定(如力量、耐力)受到-5減值。'); else if (water > 10) add('水分', '輕度脫水', '嘴脣乾裂,頭暈乏力,身體因缺水而變得遲鈍。所有重體力相關檢定受到-10減值。'); else if (water > 0) add('水分', '嚴重脫水', '嚴重缺水,視野開始模糊,身體機能受到明顯影響,必須立刻補充水分。所有重體力相關檢定受到-15減值。'); else add('水分', '瀕死:脫水', '身體因嚴重缺水而瀕臨崩潰。'); // ----- 飽腹感 ----- if (full > 95) add('飽腹感', '過飽', '吃撐了,行動有點不方便。所有敏捷和耐力相關檢定受到-5減值。'); else if (full > 85) add('飽腹感', '飽足', '吃飽喝足,感覺充滿能量,體力充沛,疲勞感不易累積。'); else if (full > 60) add('飽腹感', '狀態正常', '沒什麼特別的感覺。'); else if (full > 30) add('飽腹感', '輕微飢餓', '肚子有點空,開始期待下一頓飯。'); else if (full > 10) add('飽腹感', '明顯飢餓', '肚子在咕咕叫,有點想吃東西了,注意力難以集中。所有屬性檢定受到-5減值。'); else if (full > 0) add('飽腹感', '極度飢餓', '餓得前胸貼後背,身體因爲缺乏能量而感到虛弱,必須儘快進食。所有屬性檢定受到-15減值。'); else add('飽腹感', '虛脫', '身體因能量儲備完全耗盡而極度虛弱,各項機能開始下降,身體正在消耗自身以維持生命。必須立即進食以阻止進一步惡化。'); // ----- 疲勞 ----- if (fatPerc < 10) add('疲勞', '精力十足', '渾身是勁,感覺還能再戰三百回合。所有與力量和敏捷相關檢定獲得+5背景加值。'); else if (fatPerc <= 30) add('疲勞', '狀態正常', '身體狀態良好,沒什麼特別感覺。'); else if (fatPerc <= 50) add('疲勞', '微微出汗', '身體開始發熱,呼吸稍微加快,但還遠未到累的時候。'); else if (fatPerc <= 70) add('疲勞', '有些疲憊', '肌肉微微發酸,呼吸比平時稍重,是時候考慮歇一歇了。所有重體力相關檢定受到-5減值。'); else if (fatPerc <= 90) add('疲勞', '相當疲勞', '身體變得沉重,每一下動作都需要更多力氣,疲倦感十分明顯。所有重體力相關檢定受到-10減值。'); else add('疲勞', '筋疲力盡', '身體非常沉重,每一下動作都變得吃力,只想找個地方坐下來喘口氣。所有重體力相關檢定受到-15減值。'); // ----- 壓力 ----- if (stress < 10) add('壓力', '心神沉靜', '思維清晰,內心平靜。意志、智力檢定獲得+5加值。'); else if (stress < 50) add('壓力', '狀態正常', '無特殊效果。'); else if (stress < 80) add('壓力', '高度焦慮', '壓力極大,內心充滿焦慮和不安。所有檢定受到-5減值。'); else if (stress < 100) add('壓力', '不堪重負', '巨大的壓力使她瀕臨崩潰邊緣。所有檢定受到-10減值。'); else add('壓力', '精神崩潰', '精神防線徹底崩潰,陷入恐慌、偏執或絕望狀態。'); // ----- 睡眠 ----- if (sleepVal > 90) add('睡眠', '精力充沛', '充足的睡眠讓她恢復了所有精力。疲勞恢復速度加倍,精神屬性和技能相關檢定+5。'); else if (sleepVal > 60) add('睡眠', '狀態正常', '無特殊效果。'); else if (sleepVal > 30) add('睡眠', '疲倦', '開始感到身體和精神的疲勞。所有屬性檢定受到-5減值。'); else if (sleepVal > 0) add('睡眠', '非常疲憊', '嚴重缺乏睡眠,眼皮沉重,反應遲鈍。所有屬性檢定受到-15減值。'); else add('睡眠', '精疲力竭', '身體達到極限,隨時可能因過度疲勞而昏倒。'); // ----- 清潔 ----- if (cleanVal > 90) add('清潔', '乾淨整潔', '全身清爽,心情愉悅。所有與意志、智力相關的檢定獲得+2背景加值。'); else if (cleanVal > 40) add('清潔', '狀態正常', '無特殊效果。'); else if (cleanVal > 10) add('清潔', '有點髒', '身上有污垢或異味,讓人不適。'); else add('清潔', '污穢不堪', '全身骯髒,散發惡臭,大大增加感染疾病的風險。'); // ----- 維生素(受主角影響) ----- if (playerVit > 80) add('維生素', '維生素充足', '維生素充足。'); else if (playerVit > 30) add('維生素', '狀態正常', '無特殊效果。'); else if (playerVit > 0) add('維生素', '維生素缺乏', '身體出現多種不良反應,更快疲勞等。'); else add('維生素', '維生素嚴重缺乏', '出現壞血病、夜盲症等嚴重疾病。'); // ----- 氮平衡(受主角影響) ----- if (playerNitro >= 75) add('氮平衡', '正氮平衡', '蛋白質充足,力量相關檢定效果增強,更快從疲勞恢復。'); else if (playerNitro >= 0) add('氮平衡', '狀態正常', '無特殊效果。'); else if (playerNitro >= -50) add('氮平衡', '負氮平衡', '蛋白質不足,肌肉分解流失。力量下降,免疫力下降,傷口難以癒合,更難恢復耐力。'); else add('氮平衡', '嚴重負氮平衡', '蛋白質嚴重缺乏,身體機能大幅受損。'); // ----- 排泄需求(從單一排泄欲轉換) ----- var peeStage = '', peeDesc = '', pooStage = '', pooDesc = ''; if (pee < 20) { peeStage = '狀態正常'; peeDesc = '暫無尿意'; } else if (pee < 40) { peeStage = '輕微尿意'; peeDesc = '開始有排尿的感覺,尚可忍受'; } else if (pee < 60) { peeStage = '尿急'; peeDesc = '排尿需求變得強烈,注意力輕微分散(智力、感知檢定-2)'; } else if (pee < 80) { peeStage = '憋不住'; peeDesc = '排尿需求非常緊迫,難以集中精神(智力、感知檢定-5)'; } else { peeStage = '快要失禁'; peeDesc = '隨時可能失禁,必須立即排尿(智力、感知檢定-10)'; } if (poo < 20) { pooStage = '狀態正常'; pooDesc = '暫無便意'; } else if (poo < 40) { pooStage = '輕微便意'; pooDesc = '開始有排便的感覺,尚可忍受'; } else if (poo < 60) { pooStage = '急需排便'; pooDesc = '排便需求強烈,腹部不適(智力、感知檢定-2)'; } else if (poo < 80) { pooStage = '忍不住'; pooDesc = '排便需求非常緊迫,必須立即解決(智力、感知檢定-5)'; } else { pooStage = '即將失禁'; pooDesc = '隨時可能失禁,必須立即排便(智力、感知檢定-10)'; } var excreteDesc = ''; if (peeStage === '狀態正常' && pooStage === '狀態正常') { excreteDesc = '暫無排泄需求,身體感覺輕鬆。'; } else { excreteDesc = peeDesc + '。同時,' + pooDesc; } var dominant = (pee > poo) ? peeStage : pooStage; add('排泄需求', dominant, excreteDesc); // ----- 懷孕狀態和生理週期 ----- var preDays = isPregnant ? (get(vars, base + '.生理狀態.懷孕天數') || 0) : 0; if (isPregnant) { var preStage, preDesc; if (preDays <= 30) { preStage = '懷孕初期'; preDesc = '體力消耗+30%。飽腹感消耗每小時額外+2。全屬性檢定-5減值。排泄需求尿意累積速度+50%。可能出現噁心、食慾變化。'; } else if (preDays <= 60) { preStage = '懷孕中期'; preDesc = '體力消耗+20%。飽腹感消耗每小時額外+3。蛋白質和維生素需求+30%。力量/敏捷檢定-10減值。腹部開始隆起。'; } else { preStage = '懷孕後期'; preDesc = '體力消耗+40%。無法奔跑、無法重體力活動。力量/敏捷檢定-20減值。睡眠恢復效率-30%。尿意累積速度+100%。'; } add('懷孕', preStage, preDesc); // 移除生理週期效果(懷孕期間) if (get(vars, base + '.狀態.生效效果.生理週期')) { _.unset(vars, base + '.狀態.生效效果.生理週期'); } } else { // 非懷孕時,顯示生理週期效果 var cycleDay = get(vars, base + '.生理狀態.月經週期') || 14; var phase, phaseDesc; if (cycleDay >= 1 && cycleDay <= 5) { phase = '生理期中'; phaseDesc = '體力消耗+20%。腹部不適,感知檢定-2。情緒敏感,對互動反應可能更易煩躁。'; } else if (cycleDay >= 10 && cycleDay <= 16) { phase = '易孕期'; phaseDesc = '受孕概率較高,部分同伴可能表現出性慾變化(依人設而定)。'; } else { phase = '狀態正常'; phaseDesc = '無特殊效果。'; } if (phase !== '狀態正常') { add('生理週期', phase, phaseDesc); } // 移除懷孕效果(如果有殘留) if (get(vars, base + '.狀態.生效效果.懷孕')) { _.unset(vars, base + '.狀態.生效效果.懷孕'); } } // ========== 寫入生效效果 ========== var curEff = get(vars, base + '.狀態.生效效果') || {}; // 更新或新增效果 for (var k in effects) { var newEff = effects[k]; var old = curEff[k]; if (!old || old.狀態 !== newEff.狀態 || old.效果 !== newEff.效果) { set(vars, base + '.狀態.生效效果.' + k, newEff); debugLog('同伴生效效果更新', n + ': ' + k + ' → ' + newEff.狀態); fixCount++; } } // 清理過期的效果(保留懷孕和生理週期,因為已單獨處理) for (var k in curEff) { if (k === '懷孕' || k === '生理週期') continue; if (!effects[k] && curEff[k] !== undefined) { _.unset(vars, base + '.狀態.生效效果.' + k); debugLog('同伴生效效果移除', n + ': ' + k); fixCount++; } } // ========== 持續傷害計算 ========== if (statusCache && cur) { var delta = 0; try { var prevT = statusCache.time; if (prevT && prevT.total) delta = (cur.total - prevT.total) / 60; } catch(e) {} if (delta > 0) { var damageStates = [ '明顯口渴', '輕度脫水', '嚴重脫水', '過飽', '明顯飢餓', '極度飢餓', '虛脫', '相當疲勞', '筋疲力盡', '高度焦慮', '不堪重負', '有點髒', '污穢不堪', '維生素缺乏', '維生素嚴重缺乏', '負氮平衡', '嚴重負氮平衡' ]; for (var k in statuses) { var s = statuses[k]; if (s && damageStates.includes(s.name)) { debugLog('[同伴持續傷害] ' + n + ' ' + s.name + ' 時間差:' + delta.toFixed(2) + '小時'); switch (s.name) { case '明顯口渴': set(vars, base + '.精神.當前精神值', Math.round(clamp(spCur2 - 0.5 * delta, 0, spMaxV))); break; case '輕度脫水': set(vars, base + '.生理.當前生命值', Math.round(clamp(hpCur - 0.5 * delta, 0, hpMaxV))); set(vars, base + '.精神.當前精神值', Math.round(clamp(spCur2 - 0.5 * delta, 0, spMaxV))); break; case '嚴重脫水': set(vars, base + '.生理.當前生命值', Math.round(clamp(hpCur - 1 * delta, 0, hpMaxV))); set(vars, base + '.精神.當前精神值', Math.round(clamp(spCur2 - 1 * delta, 0, spMaxV))); break; case '過飽': set(vars, base + '.狀態.疲勞', Math.round(clamp(fatigue + 1 * delta, 0, maxFat))); break; case '明顯飢餓': set(vars, base + '.精神.當前精神值', Math.round(clamp(spCur2 - 0.5 * delta, 0, spMaxV))); break; case '極度飢餓': set(vars, base + '.生理.當前生命值', Math.round(clamp(hpCur - 0.5 * delta, 0, hpMaxV))); set(vars, base + '.精神.當前精神值', Math.round(clamp(spCur2 - 1 * delta, 0, spMaxV))); break; case '虛脫': set(vars, base + '.生理.當前生命值', Math.round(clamp(hpCur - 1 * delta, 0, hpMaxV))); set(vars, base + '.精神.當前精神值', Math.round(clamp(spCur2 - 2 * delta, 0, spMaxV))); break; case '高度焦慮': set(vars, base + '.精神.當前精神值', Math.round(clamp(spCur2 - 0.3 * delta, 0, spMaxV))); break; case '不堪重負': set(vars, base + '.精神.當前精神值', Math.round(clamp(spCur2 - 0.5 * delta, 0, spMaxV))); break; case '有點髒': set(vars, base + '.精神.睡眠', Math.round(clamp(sleepVal - 0.2 * delta, 0, 100))); break; case '污穢不堪': set(vars, base + '.精神.睡眠', Math.round(clamp(sleepVal - 0.3 * delta, 0, 100))); set(vars, base + '.狀態.疲勞', Math.round(clamp(fatigue + 0.3 * delta, 0, maxFat))); break; case '維生素缺乏': set(vars, base + '.狀態.疲勞', Math.round(clamp(fatigue + 0.3 * delta, 0, maxFat))); break; case '維生素嚴重缺乏': set(vars, base + '.狀態.疲勞', Math.round(clamp(fatigue + 0.5 * delta, 0, maxFat))); set(vars, base + '.生理.當前生命值', Math.round(clamp(hpCur - 0.3 * delta, 0, hpMaxV))); break; case '負氮平衡': set(vars, base + '.狀態.疲勞', Math.round(clamp(fatigue + 0.2 * delta, 0, maxFat))); break; case '嚴重負氮平衡': set(vars, base + '.生理.當前生命值', Math.round(clamp(hpCur - 0.5 * delta, 0, hpMaxV))); set(vars, base + '.狀態.疲勞', Math.round(clamp(fatigue + 0.5 * delta, 0, maxFat))); break; } } } } } } } // ==================== 流產後續清理 ==================== function processMiscarriageCleanup(vars) { var companions = get(vars, '同伴'); if (!_.isPlainObject(companions)) return; var currentDay = get(vars, '系統.經過天數') || 0; for (var name in companions) { if (name.startsWith('$')) continue; var pending = get(vars, '同伴.' + name + '.生理狀態.流產待處理'); if (pending !== true) continue; keyLog('💔 檢測到 ' + name + ' 流產待處理,執行後續清理...'); // 0. 🔴 新增:月經週期重置(雙重保障) set(vars, '同伴.' + name + '.生理狀態.月經週期', 1); keyLog('🔄 ' + name + ' 月經週期重置為 1'); // 1. 清除未出生的子嗣(母親 = name 且沒有出生日期) var children = get(vars, '子嗣'); if (_.isPlainObject(children)) { var toRemove = []; for (var childName in children) { if (childName.startsWith('$')) continue; var child = children[childName]; if (child.母親 === name && !child.出生日期) { toRemove.push(childName); } } for (var i = 0; i < toRemove.length; i++) { _.unset(vars, '子嗣.' + toRemove[i]); keyLog('🗑️ 清除未出生子嗣: ' + toRemove[i]); } } // 2. 觸發劇情演出(如果有) var performances = get(vars, '同伴.' + name + '.劇情演出') || []; var hasEvent = false; for (var i = 0; i < performances.length; i++) { if (typeof performances[i] === 'string' && performances[i].indexOf('事件-流產-') !== -1) { hasEvent = true; break; } } if (!hasEvent) { performances.push('事件-流產-1-待觸發:流產後的情感創傷與關係變化'); set(vars, '同伴.' + name + '.劇情演出', performances); keyLog('📝 為 ' + name + ' 生成流產劇情演出'); } // 3. 清除待處理標記(避免重複執行) set(vars, '同伴.' + name + '.生理狀態.流產待處理', false); keyLog('✅ ' + name + ' 流產後續清理完成'); } } // ======================== 主修正函數 ======================== function applyCorrections(vars, oldVars) { fixCount = 0; delCount = 0; debugLog('修正開始'); applyIntegerAndBounds(vars); try { ensurePaths(vars); fixTimeFormat(vars); processStatusEffects(vars); processCompanionStatusEffects(vars); processMiscarriageCleanup(vars); processTimeAndLevel(vars, oldVars); // 🔴 每次更新都重新計算懷孕天數和階段 updatePregnancyStage(vars, ''); // 主角 var companions = get(vars, '同伴'); if (_.isPlainObject(companions)) { for (var name in companions) { if (name.startsWith('$')) continue; updatePregnancyStage(vars, '同伴.' + name); } } fixItemStatusLimits(vars); cleanupAllItems(vars); processItemDecay(vars); // 時間差計算(每小時更新) if (lastSnapshot) { var prevTime = lastSnapshot.time; var curTime = get(vars, '系統.當前時間'); if (prevTime && curTime) { var hoursDiff = calculateTimeDifference(prevTime, curTime); if (hoursDiff > 0 && hoursDiff < 24) { debugLog('時間差計算: ' + hoursDiff + ' 小時'); // ========== 自動入睡/喚醒規則(22:00-07:00)========== var currentTime = get(vars, '系統.當前時間'); if (currentTime && typeof currentTime === 'string') { var hour = parseInt(currentTime.split(':')[0]); if (!isNaN(hour)) { var shouldBeSleeping = (hour >= 22 || hour < 8); var isCurrentlySleeping = get(vars, '狀態.入睡') === true; if (shouldBeSleeping && !isCurrentlySleeping) { set(vars, '狀態.入睡', true); keyLog('😴 自動入睡:時間 ' + currentTime + ' 進入睡眠狀態'); } else if (!shouldBeSleeping && isCurrentlySleeping) { set(vars, '狀態.入睡', false); keyLog('⏰ 自動喚醒:時間 ' + currentTime + ' 結束睡眠狀態'); } } } var isSleeping = get(vars, '狀態.入睡') === true; var sleepMultiplier = isSleeping ? 0.5 : 1.0; if (isSleeping) { keyLog('😴 當前為睡眠狀態,消耗減半'); } // 水分消耗 var water = get(vars, '生理.水分') || 0; set(vars, '生理.水分', Math.max(0, water - 1.5 * hoursDiff * sleepMultiplier)); // 碳水消耗 var carbs = get(vars, '生理.碳水') || 0; set(vars, '生理.碳水', Math.max(0, carbs - 100 * hoursDiff * sleepMultiplier)); // 脂肪消耗 var fat = get(vars, '生理.脂肪') || 0; set(vars, '生理.脂肪', Math.max(0, fat - 100 * hoursDiff * sleepMultiplier)); // 氮平衡 var nitrogen = get(vars, '生理.氮平衡') || 0; set(vars, '生理.氮平衡', Math.max(-100, nitrogen - 1 * hoursDiff * sleepMultiplier)); // 維生素 var vitamin = get(vars, '生理.維生素') || 0; set(vars, '生理.維生素', Math.max(0, vitamin - 0.5 * hoursDiff * sleepMultiplier)); // 睡眠值 var sleep = get(vars, '精神.睡眠') || 0; if (isSleeping) { set(vars, '精神.睡眠', Math.min(100, sleep + 8 * hoursDiff)); } else { set(vars, '精神.睡眠', Math.max(0, sleep - 2 * hoursDiff)); } // 飽腹感 var fullness = get(vars, '生理.飽腹感') || 0; set(vars, '生理.飽腹感', Math.max(0, fullness - 4 * hoursDiff * sleepMultiplier)); // 疲勞 var end = int(get(vars, '屬性.耐力.等級')) || 20; var fatigueMax = 4 * end; var fatigue = get(vars, '狀態.疲勞') || 0; if (isSleeping) { set(vars, '狀態.疲勞', Math.max(0, fatigue - 4 * hoursDiff)); } else { set(vars, '狀態.疲勞', Math.min(fatigueMax, fatigue + 2 * hoursDiff)); } // 排泄欲 var excretionVal = get(vars, '生理.排泄欲') || 0; set(vars, '生理.排泄欲', Math.min(100, excretionVal + 2 * hoursDiff * sleepMultiplier)); // 同伴的日常消耗 var companions = get(vars, '同伴'); if (_.isPlainObject(companions)) { for (var name in companions) { if (name.startsWith('$')) continue; var compEnd = int(get(vars, '同伴.' + name + '.屬性.耐力.等級')) || 20; var compFatigueMax = 4 * compEnd; var compFullness = get(vars, '同伴.' + name + '.生理.飽腹感') || 0; var compWater = get(vars, '同伴.' + name + '.生理.水分') || 0; var compExcretion = get(vars, '同伴.' + name + '.生理.排泄欲') || 0; var compFatigue = get(vars, '同伴.' + name + '.狀態.疲勞') || 0; var compClean = get(vars, '同伴.' + name + '.狀態.清潔') || 0; var compSleep = get(vars, '同伴.' + name + '.精神.睡眠') || 100; var compStress = get(vars, '同伴.' + name + '.精神.壓力') || 0; var compIsSleeping = isSleeping; var newFullness = compFullness - 4 * hoursDiff * (compIsSleeping ? 0.5 : 1); set(vars, '同伴.' + name + '.生理.飽腹感', Math.max(0, Math.min(100, newFullness))); var newWater = compWater - 1.5 * hoursDiff * (compIsSleeping ? 0.5 : 1); set(vars, '同伴.' + name + '.生理.水分', Math.max(0, Math.min(100, newWater))); var newExcretion = compExcretion + 2 * hoursDiff * (compIsSleeping ? 0.5 : 1); set(vars, '同伴.' + name + '.生理.排泄欲', Math.min(100, newExcretion)); var newFatigue; if (compIsSleeping) { newFatigue = compFatigue - 4 * hoursDiff; set(vars, '同伴.' + name + '.狀態.疲勞', Math.max(0, Math.min(compFatigueMax, newFatigue))); } else { newFatigue = compFatigue + 2 * hoursDiff; set(vars, '同伴.' + name + '.狀態.疲勞', Math.min(compFatigueMax, newFatigue)); } var newClean = compClean - 0.5 * hoursDiff; set(vars, '同伴.' + name + '.狀態.清潔', Math.max(0, Math.min(100, newClean))); var newSleep; if (compIsSleeping) { newSleep = compSleep + 8 * hoursDiff; set(vars, '同伴.' + name + '.精神.睡眠', Math.min(100, newSleep)); } else { newSleep = compSleep - 2 * hoursDiff; set(vars, '同伴.' + name + '.精神.睡眠', Math.max(0, newSleep)); } var newStress = compStress + 0.2 * hoursDiff; if (newClean < 30) newStress += 0.3 * hoursDiff; if (newFullness < 30) newStress += 0.3 * hoursDiff; set(vars, '同伴.' + name + '.精神.壓力', Math.min(100, Math.max(0, newStress))); } } if (isSleeping) { keyLog('😴 睡眠狀態消耗 (主角): 水分-' + (1.5 * hoursDiff * sleepMultiplier).toFixed(1) + ', 飽腹感-' + (4 * hoursDiff * sleepMultiplier).toFixed(1) + ', 疲勞-' + (4 * hoursDiff).toFixed(1) + ' (恢復)'); } else { keyLog('⏰ 自動消耗 (主角): 水分-' + (1.5 * hoursDiff).toFixed(1) + ', 飽腹感-' + (4 * hoursDiff).toFixed(1) + ', 疲勞+' + (2 * hoursDiff).toFixed(1)); } var companionCount = 0; var sampleCompName = ''; if (_.isPlainObject(companions)) { for (var name in companions) { if (name.startsWith('$')) continue; companionCount++; if (sampleCompName === '') sampleCompName = name; } } if (companionCount > 0) { keyLog('👥 同伴消耗 (共 ' + companionCount + ' 位): 以 ' + sampleCompName + ' 為例 - 水分-1.5/時, 飽腹-4/時, 疲勞+' + (isSleeping ? '4(恢復)' : '2') + ', 清潔-0.5/時'); } } } } // 🔴 第二次整數化移到這裡(在所有消耗計算完成之後) applyIntegerAndBounds(vars); updateMoonPhase(vars); updateSeason(vars); recordShopHistory(vars); updateCropGrowthStages(vars); updateLivestockStates(vars); processTasksAndShop(vars); processCompanionLimits(vars); var currentTime = get(vars, '系統.當前時間'); var currentDay = get(vars, '系統.經過天數'); if (currentTime) set(vars, '系統.前次時間', currentTime); if (currentDay !== undefined) set(vars, '系統.前次天數', currentDay); var total = fixCount + delCount; var status = total > 0 ? 'fixed' : 'normal'; var msg = total > 0 ? '修正' + fixCount + '處,清理' + delCount + '件物品' : '所有數據均符合規則,無需修正'; if (total > 0) keyLog('修正完成 - ' + msg); else debugLog('修正完成 - ' + msg); checkAndRestoreSustainableCrops(vars, oldVars); verifyAndFixAllCrops(vars); verifyAndFixAllLivestock(vars); cleanupCompletedPerformances(vars); updateStatus(vars, status, msg); } catch(e) { errorLog('修正錯誤:', e.message); if (DEBUG_MODE) console.error(e.stack); updateStatus(vars, 'error', e.message); } } // ======================== 事件掛載 ======================== eventOn(Mvu.events.VARIABLE_UPDATE_STARTED, function(vars) { var d = vars.stat_data; var currentDay = get(d, '系統.經過天數'); var currentTime = get(d, '系統.當前時間'); lastSnapshot = { day: currentDay, time: currentTime, hpMax: get(d, '生理.生命值上限'), hpCur: get(d, '生理.當前生命值'), spMax: get(d, '精神.精神值上限'), spCur: get(d, '精神.當前精神值') }; debugLog('記錄前次狀態: 天數=' + currentDay + ', 時間=' + currentTime); fixCount = 0; delCount = 0; lastLogHash = ''; }); eventOn(Mvu.events.VARIABLE_UPDATE_ENDED, function(newVars, oldVars) { if (isProcessing) return; isProcessing = true; try { // ========== 1. 執行修正 ========== applyCorrections(newVars.stat_data, oldVars.stat_data); var vars = newVars.stat_data; // ========== 2. 確保性愛結果是陣列 ========== var sexResults = ensureSexResultArray(vars); // ========== 3. 如果沒有性愛結果,直接結束 ========== if (sexResults.length === 0) { return; } // ========== 4. 處理每一筆性愛結果 ========== var remaining = []; var INITIAL_COUNTDOWN = 2; for (var i = 0; i < sexResults.length; i++) { var result = sexResults[i]; // ============================================= // 情況 A:新事件(沒有刪除倒數) // ============================================= if (result.刪除倒數 === undefined || result.刪除倒數 === null) { // ----- 以下:原本的「補充計算」邏輯(完整保留)----- var name = result.角色; if (name) { var base = '同伴.' + name; // 1. 獲取計算參數 var affection = v(get(vars, base + '.技能.好感(對阿彬).等級'), 1); var forced = v(get(vars, base + '.淪陷.強制次數'), 0); var special = get(vars, base + '.淪陷.特殊狀態') || ''; var comfort = get(vars, '環境.舒適度') || 50; var statusEffects = []; var compEff = get(vars, base + '.狀態.生效效果'); if (_.isPlainObject(compEff)) { for (var k in compEff) { if (k.startsWith('$')) continue; if (compEff[k] && compEff[k].狀態) { statusEffects.push(compEff[k].狀態); } } } var isSudden = (result.有預警 === false); var hasTrauma = (special.indexOf('關係動搖') !== -1 || special.indexOf('嚴重創傷') !== -1); // 2. 計算自願度 var voluntary = calculateVoluntary( affection, result.情境 || '情感推進', statusEffects, comfort, isSudden, hasTrauma, special ); var grade = getResultGrade(voluntary); // 3. 計算淪陷階段 var newCorruption = getCorruptionStage(forced, affection); // 4. 計算關係變化 var change = calculateAffectionChange( voluntary, affection, result.情境 || '情感推進', special, result.完成 !== false ); // 5. 建立完整的 updatedResult(和原本完全一樣) var updatedResult = { 角色: result.角色, 完成: result.完成, 內射: result.內射, 避孕措施: result.避孕措施 || '無', 情境: result.情境 || '情感推進', 自願度: voluntary, 自願分級: grade.grade, 描寫建議: grade.description, 淪陷階段: { 階段: newCorruption.stage, 名稱: newCorruption.name }, 關係變化: { 經驗變化: change.expChange, 等級變化: change.levelChange }, 特殊狀態: change.newSpecialStatus || '無', 刪除倒數: INITIAL_COUNTDOWN, 授精者: result.授精者 || '阿彬' }; // 寫回更新後的結果 sexResults[i] = updatedResult; result = updatedResult; // 6. 執行懷孕判斷(和原本完全一樣) if (result.完成 === true && result.內射 === true) { calculatePregnancy(vars, name, true, result.避孕措施); } // 7. 執行好感度更新(和原本完全一樣) if (change && (change.expChange !== 0 || change.levelChange !== 0)) { var currentExp = get(vars, base + '.技能.好感(對阿彬).當前經驗') || 0; var currentLevel = get(vars, base + '.技能.好感(對阿彬).等級') || 1; var newExp = Math.max(0, currentExp + change.expChange); var newLevel = Math.max(1, currentLevel + change.levelChange); if (newExp !== currentExp) { set(vars, base + '.技能.好感(對阿彬).當前經驗', newExp); } if (newLevel !== currentLevel) { set(vars, base + '.技能.好感(對阿彬).等級', newLevel); } } // 8. 執行淪陷階段更新(和原本完全一樣) if (newCorruption.stage !== undefined) { var currentStage = get(vars, base + '.淪陷.當前階段') || 0; if (newCorruption.stage !== currentStage) { set(vars, base + '.淪陷.當前階段', newCorruption.stage); } } // 9. 執行特殊狀態更新(和原本完全一樣) if (change.newSpecialStatus && change.newSpecialStatus !== special) { set(vars, base + '.淪陷.特殊狀態', change.newSpecialStatus); } } // 保留這筆記錄(倒數還沒歸零) remaining.push(result); continue; } // ============================================= // 情況 B:已有倒數 → 減數或清除 // ============================================= result.刪除倒數--; if (result.刪除倒數 > 0) { remaining.push(result); } // 倒數歸零則不保留(自動清除) } // ========== 5. 寫回更新後的陣列 ========== set(vars, '系統.性愛結果', remaining); } catch(e) { errorLog('處理錯誤:', e); if (DEBUG_MODE) console.error(e.stack); } finally { isProcessing = false; } }); window.processSexualInteraction = processSexualInteraction; window.getResultGrade = getResultGrade; window.calculateVoluntary = calculateVoluntary; window.calculatePregnancy = calculatePregnancy; window.updateAllCrops = function(vars) { return updateCropGrowthStages(vars); }; window.updateAllLivestock = function(vars) { return updateLivestockStates(vars); }; window.calculateMoonPhase = calculateMoonPhase; window.calculateSeason = calculateSeason; window._mvuFinalStableV110 = true; console.log('✅ 所有擴充功能已載入 (v11.0 路徑更新)'); console.log('📊 功能模塊: 好感系統 | 性愛系統 | 月相系統 | 季節系統 | 作物系統 | 畜牧系統 | 懷孕系統 | 幼兒系統 | 排泄系統 | 任務系統 | 入眠系統 | 狀態效果'); })();