Files
2026-07-11 04:45:19 +00:00

4049 lines
188 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
(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('📊 功能模塊: 好感系統 | 性愛系統 | 月相系統 | 季節系統 | 作物系統 | 畜牧系統 | 懷孕系統 | 幼兒系統 | 排泄系統 | 任務系統 | 入眠系統 | 狀態效果');
})();