Sync all projects

This commit is contained in:
github-actions[bot]
2026-07-13 06:22:17 +00:00
parent 5760fd2902
commit ed26d5b473
31 changed files with 28989 additions and 11123 deletions
+3918 -473
View File
@@ -1,506 +1,3951 @@
---
# (Variable Reference)
(async function() {
if (window._mvuFinalStableV110) return;
await waitGlobalInitialized('Mvu');
console.log(' MVU ZOD v11.1 ');
: #
: 0
: AI
: 200075%
: 200075%
: 751.5
: 2200
: 50
: 80500使100
: 36.5-37.5
:
: 100
:
var _ = window._;
var get = function(o, p) { return _.get(o, p); };
var set = function(o, p, v) { return _.set(o, p, v); };
: #
: 0
: AI
:
: 滿
: 調
: 滿
: 0
var isProcessing = false;
var lastSnapshot = null;
var fixCount = 0;
var delCount = 0;
var lastLogHash = '';
: # insert
# '': { '': '', '': { '': } }
#
# `{"op":"insert","path":"/天賦特性/夜視","value":{"描述":"在黑暗中也能清晰視物","加值":{"夜間觀察":20}}}`
// ==================== Debug 模式開關 ====================
var DEBUG_MODE = false; // ← 設為 true 開啟詳細 Log
: #
# '': { '': 1, '': 0, '': 100 , '': 0 }
# +25+50+75: *10
: #
: #
: #
: #
: #
: # 滿調
function debugLog() {
if (DEBUG_MODE) {
console.log('[🔍]', ...arguments);
}
}
: #
: 50%
: AI
:
:
: ''''''
:
: 100
:
:
:
:
: rimworld
: rimworld
: AI使
function keyLog() {
console.log('[📌]', ...arguments);
}
function errorLog() {
console.error('[]', ...arguments);
}
: #
# '': { '': , '': , '': }
#
# - ()
#
#
# - 5 ~ 10
# - 15 ~ 30
# - 25 ~ 50
# - 40 ~ 75
# - 50 ~ 100
# 1-20+
# "好感(對阿彬)": { "等級": 3, "當前經驗": 120, "升級所需經驗": 300 }
#
# - 1
# - 使 +25 ~ +250
# - 1 * 100
// ==================== 整數路徑(根據新設計更新) ====================
var integerPaths = [
'.', '.',
'.', '.', '.', '.', '.', '.', '.',
'.', '.', '.', '.', '.',
'.', '.', '.', '.', '.', '.',
'.*..', '.*..', '.*..', '.*..'
];
var decayRates = [0.06, 0.15, 0.35, 0.8, 2.4];
: # 穿
:
:
:
:
:
:
:
:
:
:
:
var SEASON_CONFIG = {
SPRING_DAYS: 30,
SUMMER_DAYS: 30,
AUTUMN_DAYS: 30,
WINTER_DAYS: 30,
SEASON_ORDER: ['', '', '', '']
};
_JSON_Patch:
# 穿{"op":"replace","path":"/裝備/背部","value":{"大容量登山包":{"狀態類型":"耐久度","狀態":100,"重量":1.8,"負重加成":1.5,"描述":"..."}}}
# {"op":"replace","path":"/裝備/背部","value":{}}
# 穿{"op":"replace","path":"/同伴/同伴名/裝備/腳部","value":{"簡易涼鞋":{"狀態類型":"耐久度","狀態":100,"重量":0.4,"描述":"..."}}}
var SEASON_GROWTH_BONUS = {
'': 15,
'': 20,
'': 0,
'': -50
};
: # <>:'': { '': , '': '', '': '//', '': , '': , '': '', '': , '': { '': }, '': '' }
//
var PREGNANCY_CONFIG = {
STAGES: [
{ name: '', maxDays: 30 },
{ name: '', maxDays: 60 },
{ name: '', maxDays: 90 }
],
AUTO_BIRTH: true
};
{"op":"insert","path":"/攜帶物品/野莓","value":{"數量":3,"狀態類型":"新鮮度","狀態":100,"重量":0.05,"描述":"酸甜的野生漿果"}}
1. { "op": "remove", "path": "/源位置/物品名" }
2. { "op": "insert", "path": "/目標位置/物品名", "value": { ... } }
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 }
}
};
: #
:
: 0-100/50
: 30
:
:
:
:
: 滿
: AI
: °C
: #
:
:
: 24HH:MM
:
: 100
: AI
: # AI
:
: true/false # true=false=
: true/false
: #
: /////
: /調//
:
: # (00:00)
# ----
# { "名稱": "文字","品階":"N/R/SR/SSR/UR", "描述": "文字", "需求點數": }
: ()
: 使便
:
: ()()()
: ''
-:
: ("某荒野求生漫畫")
:
滿:
-
-
-
- 使
:
-:
: ("某荒野求生漫畫")
:
滿:
-
-
-
-
:
-:
: /("某荒野求生漫畫")
:
滿:
-
-
-
- 滿
- 滿
:
: /
-:
: ("某荒野求生漫畫")
:
滿:
-
-
-
-
:
:
1.
2. '///'
3.
4.
5.
6. = + + 100~5000
1. > 使//
2. > 使
3. > 使
AI 使
{"op":"replace","path":"/商店/材料","value":{"名稱":"新商品名","品階":"N/R/SR/SSR/UR","描述":"...","需求點數":100}}
{"op":"replace","path":"/商店/夥伴-技術與後勤","value":{"名稱":"XXX","品階":"SR","描述":"出自《XXX》,求生優勢:...,性愛優勢:...","需求點數":1000}}
: #
# { "標題": "任務名稱", "描述": "任務詳細說明", "狀態": "進行中", "完成情況": {"基礎要求完成": false, "進階目標完成": false}, "結算狀態": {"基礎獎勵已發放": false, "進階獎勵已發放": false}, "區域限制": "", "開始時間": "第3天 06:00", "結束時間": "第6天 06:00", "任務要求": { "基礎要求1": { "描述": "...", "進度": 0, "目標": 1, "已完成": false }}, "進階目標": { "進階要求1": { "描述": "...", "進度": 0, "目標": 1, "已完成": false }}, "基礎獎勵": "獎勵描述", "進階獎勵": "獎勵描述" }
:
:
:
: ()
:
#
"背德": {
"標題": "曖昧的界線",
"描述": "千鶴對你的好感升級了,她開始用不同的眼光看你。但這份感情,可能傷害到和也⋯⋯",
"狀態": "進行中",
"完成情況": {"基礎要求完成": false, "進階目標完成": false},
"結算狀態": {"基礎獎勵已發放": false, "進階獎勵已發放": false},
"區域限制": "",
"開始時間": "第5天 14:00",
"結束時間": "第8天 14:00",
"任務要求": { "基礎要求1": { "描述": "在千鶴向你暗示好感時,做出選擇(接受/保持距離/拖延)", "進度": 0, "目標": 1, "已完成": false }},
"進階目標": { "進階要求1": { "描述": "處理好與和也的關係,不讓他知道(或讓他接受)這件事", "進度": 0, "目標": 1, "已完成": false }},
"基礎獎勵": "求生點數:80,千鶴好感經驗+90",
"進階獎勵": "劇情演出:[事件-千鶴的告白-1-未發放:千鶴在雨夜向你坦白心意]",
"失敗懲罰": "千鶴好感技能經驗-60,和也技能經驗好感-50,團隊氣氛惡化"
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).; }
};
: #
{ "位置": "", "樣式": "", "物品": {}, "食材": {}, "醫藥": {}, "材料": {}, "牲畜": {}, "設施": {}, "奇物": {}, "武器": {} }
-
-
- ///////
insertremove + insert/
1.
{"op":"insert","path":"/營地/聆風丘陵營地","value":{"位置":"聆風丘陵河畔","樣式":"簡易營地","物品":{},"食材":{},"醫藥":{},"材料":{},"牲畜":{},"設施":{},"奇物":{},"武器":{}}}
2.
{"op":"insert","path":"/營地/聆風丘陵營地/材料/木材","value":{"數量":20,"狀態類型":"耐久度","狀態":100,"重量":2,"描述":"堅硬的河畔楓原木"}}
3.
{"op":"insert","path":"/營地/聆風丘陵營地","value":{"位置":"聆風丘陵河畔","樣式":"簡易營地","物品":{},"食材":{},"醫藥":{},"材料":{"木材":{"數量":20,"狀態類型":"耐久度","狀態":100,"重量":2,"描述":"..."}},"牲畜":{},"設施":{},"奇物":{},"武器":{}}}
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 }
}
};
: # : '': { '': '', '': '', '': '', '': 100, '': 0, '': '' }
: "野豬屍體旁""河邊石頭上""戰鬥現場地面"
: N/R/SR/UR
: //
:
:
: "一把插在野豬屍體上的石制短矛,矛尖沾滿血跡"
// ======================== 工具函數 ========================
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;
}
: #
# {
# "淪陷": { "強制次數": 0, "當前階段": 0 },
# "狀態": {
# "當前狀態":"當前人物狀況的簡要說明(18個字內)",
# "疲勞":0,
# "清潔":0,
# "生效效果":{},
# },
# "生理": {
# "生命值上限": 100,
# "當前生命值": 100,
# "水分":100,
# "飽腹感":80,
# "排泄欲":0
# },
# "精神": {
# "精神值上限": 100,
# "當前精神值": 100,
# "壓力":0,
# "睡眠":100
# },
# "關係": "陌生人(8個字內)",
# "屬性": { "力量": {}, "敏捷": {}, "耐力": {}, "智力": {}, "感知": {}, "意志": {} },
# "技能": { "好感(對阿彬)": { "等級": 1, "當前經驗": 0, "升級所需經驗": 100 } },
# "裝備": { "頭部": {}, "頸部": {}, "胸部": {}, "背部": {}, "腰部": {}, "左手": {}, "右手": {}, "腿部": {}, "腳部": {} },
# "天賦特性": {"性愛天才": {"描述": "天生熟悉各種性愛技巧","加值":{"性愛檢定":50}}},
# "攜帶物品": {},
# "劇情演出": [],
# "敏感部位": { "耳朵": {"被吹氣": "感覺耳朵的癢會傳導到下體,開始濕潤"}},
# "基礎信息": {
# "姓名": "",
# "年齡": 0,
# "身份": "",
# "性別": "男性/女性/雙性人",
# "外貌": {
# "身高": 165,
# "髮色": "黑色",
# "髮型": "長直髮/短髮/馬尾/雙馬尾等",
# }
# },
# "性經驗": {
# "性交經驗": "無/生澀/熟練/專家/天才",
# "口交經驗": "無/生澀/熟練/專家/天才",
# "肛交經驗": "無/生澀/熟練/專家/天才",
# "性幻想對象": "鄰居家的大哥哥",
# "性行為次數": 0,
# "性伴侶數量": 0
# },
# "生理狀態": {
# "乳頭狀態": "勃起"
# },
# "心理狀態": { "當前所想": "" },
# "性癖好": { "NTR": "從他人處獲得快感" },
# "性愛人設": "從角色的人設及相關資料分析,寫出85-95字的完整性愛方面的人設",
# "背德經歷": {"初次偷情成功": { "描述": "成功與阿彬做愛,沒讓千鶴知道"}}
# }
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);
}
# ========== / ==========
# "身體特徵": {
# "類型": "female / hermaphrodite",
# "體型": "幼女型/蘿莉型/少女型/御姐型/豐滿型/肉感型/貧乳型/健美型/年長型/誘惑型",
# "三圍": {
# "胸圍": 0,
# "腰圍": 0,
# "臀圍": 0,
# "罩杯": ""
# },
# "乳房": {
# "形狀": "圓潤碗型",
# "乳頭顏色": "粉紅色"
# },
# "私處": {
# "類型": "白虎無毛/肥大陰脣/粉嫩短毛/或ai自行構想(四個字)"
# }
# },
# "性經驗": {
# "處女": "是/否"
# },
# "生理狀態": {
# "陰蒂狀態": "正常",
# "月經週期": 1-28,
# "懷孕狀態": "未懷孕/懷孕初期/懷孕中期/懷孕後期",
# "懷孕天數": 0,
# "授精者": "姓名",
# "懷孕日": 0,
# "流產風險累積": 0,
# "流產待處理": false # false,
# "流產次數": 0,
# "上次流產日": 0
# }
#
# ========== /==========
# "身體特徵": {
# "類型": "male / hermaphrodite",
# "陰莖": {
# "長度": 0,
# "粗度": 0,
# "類型": "包含但不限於:包莖/入珠/巨大/倒鉤/粗大/紋身/短小...等,由AI自行決定",
# }
# },
# "性經驗": {
# "處男": "是/否"
# },
# "生理狀態": {
# "勃起狀態": "未勃起",
# "射精狀態": "未射精"
# }
// ==================== 整數化與上下限修正(獨立函數) ====================
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;
}
: # : '': { '': ''|'', '': '', '': '', '': X, '': '', '': 0, '': {}, '': {}, '': '', '': {}, '': {}, '': {}, '': {}, '': {} }
: ''''
:
: ''
:
: ''''''''''''
: ''
// ==================== 擴充輔助函數 ====================
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;
}
:
:
: 0-0
:
: 0-0
function generateRandomGender() {
return Math.random() < LIVESTOCK_CONFIG.BREEDING_CONFIG.GENDER_RATIO.male ? '' : '';
}
:
: 0-100
: 0-100
function getAffectionStageForSex(level) {
var stage = AFFECTION_SYSTEM.getStage(level);
var map = {
'': '',
'-': '',
'-': '',
'-': '',
'-': '',
'-': '',
'-': '',
'': ''
};
return map[stage.name] || '';
}
: 20滿
:
:
:
:
:
: 0-100
: 0-100
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;
}
: #
{
"背景": "草原中的馬王",
"狀態": "健康",
"騎乘": "true",
"速度加成": 2, #
"屬性": {},
"技能": { "好感(對主角)": { "等級": 1, "當前經驗": 0, "升級所需經驗": 100 } },
"天賦特性": {},
"攜帶物品": {},
"劇情演出": []
}
`{"op":"insert","path":"/寵物/小白","value":{"狀態":"健康","騎乘":true,"速度加成":2,"屬性":{"力量":{"等級":15}},"技能":{"狩獵":{"等級":2}},"天賦特性":{},"劇情演出":[]}}`
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;
}
: #
//
+
0-29%30-59%60-99%100%
harvestActionREMOVE RESET
`{"播種數量": , "品質": "普通/優良/稀有", "種植日期": , "成長週期": , "收成日期": ,"生長階段": "幼苗期/生長期/熟成期/可收割","harvestAction": "REMOVE/RESET"}`
`{"op":"insert","path":"/作物/小麥","value":{"播種數量":10,"品質":"普通","種植日期":5,"成長週期":10,"收成日期":15,"生長階段":"幼苗期","harvestAction":"REMOVE"}}`
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)));
}
: #
"1d2"
{ "年齡": , "飼養數量": { "雄性": , "雌性": }, "生長階段": "幼年期/成長期/成年期/老年期", "產出物": { "名稱": "", "週期": , "下次產出日": , "產量": "" }, "飼養開始日": }
# `{"op":"insert","path":"/畜牧/雞","value":{"年齡":3,"飼養數量":{"雄性":1,"雌性":5},"生長階段":"成年期","產出物":{"名稱":"雞蛋","週期":3,"下次產出日":6,"產量":"1d2"},"飼養開始日":5}}`
# {"op":"replace","path":"/畜牧/雞/產出物/下次產出日","value":7}
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 '';
}
: # : '': {'':'' '': '' '': 100 '':'' '':''}
: /
: ''
:
:
:
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 '';
}
: # : '': { AI }
:
: / / / / /
: /
:
: 50~80100~150200+
:
: : { '': { '': , '': }, ... }
: //"一座簡易的木結構小屋,約三米見方"
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 = ['', '', '', ''];
{ "op": "insert", "path": "/項目/瞭望塔地基", "value": { "位置": "楓葉潭營地", "類別": "建造", "當前進度": 0, "總進度": 120, "所需材料": { "硬木": { "所需": 20, "已消耗": 0 }, "石子": { "所需": 30, "已消耗": 0 }}, "項目產出": "一座簡易的木石結構瞭望塔地基,高約一米。", "描述": "一片經過平整的土地,四周堆放著準備用於建造的硬木和石子。" }}
function isSustainableCrop(cropData) {
return cropData && cropData.harvestAction &&
PROTECTED_CROP_HARVEST_ACTIONS.indexOf(cropData.harvestAction) !== -1;
}
:
- = 100% =
-
- 100%
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;
}
: # : '': { '': { '': {'':0,'':150,'':'...'} } }
:
- ''
- ''
- ''
:
:
: 50~80100~150150+
:
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 + '.', {});
}
if (!_.isPlainObject(get(vars, '.' + n + '..'))) {
set(vars, '.' + n + '..', {});
}
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 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);
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);
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;
{ "op": "insert", "path": "/研究/生存科技", "value": { "進行中": { "基礎陶器燒製技術": { "當前進度": 0, "總進度": 100, "描述": "研究如何控制窯溫和燒製時間,以提高陶器成品率和硬度。" }}}}
/
AI
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;
// ========== 🔴 1. 初始化 effects 和 statuses(每個同伴獨立) ==========
var effects = {};
var statuses = {};
// ========== 🔴 2. 定義 add 函數(在使用之前) ==========
function add(k, name, desc) {
effects[k] = { : name, : desc };
statuses[k] = { name: name };
}
// ========== 3. 確保生效效果路徑存在 ==========
var effPath = base + '..';
var curEff = get(vars, effPath);
if (!_.isPlainObject(curEff)) {
set(vars, effPath, {});
curEff = {};
debugLog(' ' + n + ' ');
}
// ========== 4. 讀取數據 ==========
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;
// ========== 5. 生理週期計算 ==========
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;
var cyclePhase = '';
var phaseDesc = '';
if (isPregnant) {
cyclePhase = '';
phaseDesc = '';
} else {
var cycleDay = menses;
if (cycleDay >= 1 && cycleDay <= 5) {
cyclePhase = '';
phaseDesc = '+20%-2';
} else if (cycleDay >= 10 && cycleDay <= 16) {
cyclePhase = '()';
phaseDesc = '';
} else if (cycleDay >= 6 && cycleDay <= 9) {
cyclePhase = '';
phaseDesc = '';
} else if (cycleDay >= 17 && cycleDay <= 28) {
cyclePhase = '';
phaseDesc = '';
} else {
cyclePhase = '';
phaseDesc = '';
}
}
// ✅ 現在可以安全使用 add 函數
if (cyclePhase !== '') {
add('', cyclePhase, phaseDesc);
}
// 如果懷孕,添加懷孕效果
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 (effects['']) {
delete effects[''];
delete statuses[''];
}
}
// ========== 6. 好感度效果 ==========
var affection = get(vars, base + '..().') || 1;
var affectionStage = '';
var affectionDesc = '';
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);
// ========== 7. 水分 ==========
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('', '', '');
// ========== 8. 飽腹感 ==========
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('', '', '');
// ========== 9. 疲勞 ==========
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');
// ========== 10. 壓力 ==========
if (stress < 10) add('', '', '+5');
else if (stress < 50) add('', '', '');
else if (stress < 80) add('', '', '滿-5');
else if (stress < 100) add('', '', '使-10');
else add('', '', '');
// ========== 11. 睡眠 ==========
if (sleepVal > 90) add('', '', '+5');
else if (sleepVal > 60) add('', '', '');
else if (sleepVal > 30) add('', '', '-5');
else if (sleepVal > 0) add('', '', '-15');
else add('', '', '');
// ========== 12. 清潔 ==========
if (cleanVal > 90) add('', '', '+2');
else if (cleanVal > 40) add('', '', '');
else if (cleanVal > 10) add('', '', '');
else add('', '', '');
// ========== 13. 維生素(受主角影響) ==========
if (playerVit > 80) add('', '', '');
else if (playerVit > 30) add('', '', '');
else if (playerVit > 0) add('', '', '');
else add('', '', '');
// ========== 14. 氮平衡(受主角影響) ==========
if (playerNitro >= 75) add('', '', '');
else if (playerNitro >= 0) add('', '', '');
else if (playerNitro >= -50) add('', '', '');
else add('', '', '');
// ========== 15. 排泄需求(從單一排泄欲轉換) ==========
var pee = excretion;
var poo = excretion;
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);
// ========== 16. 寫入生效效果 ==========
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++;
}
}
// ========== 17. 持續傷害計算 ==========
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);
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._mvuFinalStableV111 = true;
console.log(' (v11.1 )');
console.log('📊 : | | | | | | | | | | ');
})();