4049 lines
188 KiB
JSON
4049 lines
188 KiB
JSON
(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('📊 功能模塊: 好感系統 | 性愛系統 | 月相系統 | 季節系統 | 作物系統 | 畜牧系統 | 懷孕系統 | 幼兒系統 | 排泄系統 | 任務系統 | 入眠系統 | 狀態效果');
|
||
})(); |