Sync all projects
This commit is contained in:
+3918
-473
@@ -1,506 +1,3951 @@
|
||||
---
|
||||
# 變量詳細參考文檔 (Variable Reference)
|
||||
(async function() {
|
||||
if (window._mvuFinalStableV110) return;
|
||||
await waitGlobalInitialized('Mvu');
|
||||
console.log('✅ MVU ZOD 最終穩定後處理 v11.1 已啓用');
|
||||
|
||||
生理: # 反映角色身體內部具體生理需求的數值。
|
||||
當前生命值: 代表綜合身體機能的抽象數值。降至0則角色死亡。受傷、疾病、飢餓、脫水等會降低此值。
|
||||
生命值上限: 由腳本自動計算,AI不需任何改動。
|
||||
碳水: 通過進食含有碳水的食物補充。在進行劇烈活動,處於寒冷環境或其他會消耗比基礎代謝更多的熱量的時候。先於脂肪和蛋白質消耗。當碳水超過2000的時候,多餘碳水的75%將會轉換爲脂肪。
|
||||
脂肪: 通過進食富含脂肪的食物補充。超過2000的碳水的75%也會轉換成脂肪。
|
||||
氮平衡: 身體每天消耗和合成蛋白質的平衡,食用富含蛋白質的食物時增加。75以上正氮平衡會獲得1.5倍力量經驗增長,更快從疲勞恢復。
|
||||
蛋白質: 每小時會增加或減少氮平衡值的五分之一。低於2200會導致力量下降,免疫下降,傷口難以癒合,更難恢復耐力或其他缺乏蛋白質的副作用,低於越多副作用越大。
|
||||
維生素: 通過進食水果、蔬菜等補充。低於50會影響免疫力並可能導致疾病。
|
||||
水分: 任何進食行為水分都直接恢復到80。在進行體力活動、處於高溫環境等出汗狀態加速消耗時,進行額外的扣除。低於50會開始影響耐力並造成生命值損傷。0時死亡。喝水或使用富含水分的食物補充,不可超過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),由自然環境模擬引擎自動維護。引擎基於季節、氣團、地理環境、天氣等因素平滑推演氣溫變化。
|
||||
|
||||
系統: # 遊戲世界的核心數據和任務信息。
|
||||
經過天數: 遊戲經過的天數
|
||||
前次天數: 紀錄上一次的天數
|
||||
當前時間: 24小時制時間,格式爲HH:MM。隨行動和時間流逝而更新。
|
||||
前次時間: 腳本自動計算
|
||||
求生點數: 玩家擁有的求生點數,用來抽取抽卡卡池,每次消耗100點
|
||||
當前存糧天數: 當前剩餘糧食可供消耗的天數(單位:天),由AI根據食物消耗和補充情況更新
|
||||
性愛結果: # 由劇情AI輸出,腳本自動讀取並清除
|
||||
角色: 同伴名稱
|
||||
完成: true/false # true=射精完成,false=中斷
|
||||
內射: true/false
|
||||
授精者: 角色名稱 # 新增:內射者的名稱,用於懷孕判定時記錄父親
|
||||
避孕措施: 無/保險套/體外射精/避孕藥/安全期/肛交
|
||||
情境: 情感推進/調教誘騙/武力強迫/被動捲入
|
||||
腳本修正狀態: 記錄腳本修正的狀態
|
||||
|
||||
商店: # 每日跨日(00:00後)刷新販售物品,每天每個類別僅有一種。
|
||||
# 類型:包含材料、工具、奇物、夥伴-技術與後勤、夥伴-戰鬥與探索、夥伴-反派與妖魅、夥伴-純愛與保護控、藥物、盲盒九個類別
|
||||
# 每個類別都是單一物件(不是陣列),格式為:{ "名稱": "文字","品階":"N/R/SR/SSR/UR", "描述": "文字", "需求點數": 數字 }
|
||||
|
||||
材料: 材料的名稱、詳細描述(包含數量與計量單位),如汽(柴)油、礦物、合金、布匹、纖維絲線等。
|
||||
工具: 非一次性使用的工具,如鐵斧、狩獵弓、便攜式瓦斯爐。
|
||||
奇物: 人類無法製造的特殊物品。描述必須列出正式出處(神話、漫畫、動漫、小說、電影等)、功能、特殊效果。
|
||||
藥物: 治療(含恢復健康、體力、解毒、淨化)、提升抗性。強化(臨時大幅強化特定效果、永久性小幅度強化屬性或能力)、毒藥、促進作物生長、特殊功效(春藥、失憶、洗腦、催眠、致幻、誠實)。
|
||||
盲盒: 可能是材料、工具、奇物、夥伴、藥物或點數盲盒,品階均為'?'。
|
||||
|
||||
夥伴-技術與後勤:
|
||||
描述: 擅長維修、建造、醫療、戰術策劃、資源管理等非戰鬥支援型角色。描述中必須明確列出其正式出處(不可用"某荒野求生漫畫"等不明確作品名稱)、求生優勢、性愛優勢,這些優勢將直接決定該角色的技能、天賦特性及費用。
|
||||
品階: 按照其稀缺性、特殊性、知名度等,綜合評定夥伴的品階高低
|
||||
分類判定標準(滿足任一即可):
|
||||
- 職業或身份為:科學家、工程師、醫生、護士、機械師、建築師、程式設計師、會計、廚師、裁縫、教師、圖書館員、策劃師等
|
||||
- 核心能力為:維修、建造、醫療、戰術策劃、資源管理、情報分析、通訊、研發、生產
|
||||
- 性格特質為:理性、謹慎、善於規劃、喜歡待在安全的後方
|
||||
- 戰鬥能力為:較弱或非主要強項(即使有戰鬥能力,也以支援為主)
|
||||
求生優勢評分側重: 專業技能的不可替代性、資源管理效率、降低團隊消耗的能力
|
||||
|
||||
夥伴-戰鬥與探索:
|
||||
描述: 擁有卓越戰鬥技巧、偵查、潛行、野地生存等前線作戰能力的角色。描述中必須明確列出其正式出處(不可用"某荒野求生漫畫"等不明確作品名稱)、求生優勢、性愛優勢,這些優勢將直接決定該角色的技能、天賦特性及費用。
|
||||
品階: 按照其稀缺性、特殊性、知名度等,綜合評定夥伴的品階高低
|
||||
分類判定標準(滿足任一即可):
|
||||
- 職業或身份為:士兵、傭兵、獵人、偵查兵、遊俠、忍者、騎士、警衛、保鏢、格鬥家、運動員等
|
||||
- 核心能力為:戰鬥技巧、武器精通、偵查潛行、野地生存、追蹤獵殺、護衛、警戒
|
||||
- 性格特質為:果斷、警覺、勇猛、行動派、習慣於危險環境
|
||||
- 戰鬥能力為:明顯擅長一對一戰鬥或小規模衝突
|
||||
求生優勢評分側重: 個人戰力強度、野外適應力、偵查與護衛效能
|
||||
|
||||
夥伴-反派與妖魅:
|
||||
描述: 具有危險氣質、邪惡陣營、或帶有神秘/非人(如妖、魔)魅力的角色。忠誠度可能需要特殊手段維繫。描述中必須明確列出其正式出處(不可用"某荒野求生漫畫"等不明確作品名稱)、求生優勢、性愛優勢,這些優勢將直接決定該角色的技能、天賦特性及費用。
|
||||
品階: 按照其稀缺性、特殊性、知名度等,綜合評定夥伴的品階高低
|
||||
分類判定標準(滿足任一即可):
|
||||
- 陣營或立場為:反派、惡役、黑道、犯罪者、叛徒、墮落者、被詛咒者
|
||||
- 種族或身份為:魔族、妖怪、鬼魂、吸血鬼、狼人、惡魔、邪神眷屬、非人存在
|
||||
- 核心能力為:詛咒、下毒、操控、欺騙、脅迫、黑市知識、禁忌力量
|
||||
- 性格特質為:邪氣、危險、神秘、傲慢、算計、充滿誘惑力、令人又愛又怕
|
||||
- 與主角的關係為:敵對立場、相互利用、或充滿張力的曖昧危險關係
|
||||
求生優勢評分側重: 非常規手段的獨特價值(如用黑市管道取得物資、用恐懼操控他人)、神秘力量的特定情境優勢
|
||||
性愛優勢評分側重: 危險誘惑、邪魅氣質、非人美感、支配/被支配的魅力
|
||||
|
||||
夥伴-純愛與保護控:
|
||||
描述: 對主角(或特定對象)有極強忠誠、保護慾、或純粹愛慕之情的角色,其驅動力來自情感連結。描述中必須明確列出其正式出處(不可用"某荒野求生漫畫"等不明確作品名稱)、求生優勢、性愛優勢,這些優勢將直接決定該角色的技能、天賦特性及費用。
|
||||
品階: 按照其稀缺性、特殊性、知名度等,綜合評定夥伴的品階高低
|
||||
分類判定標準(滿足任一即可):
|
||||
- 與主角的關係為:戀人、青梅竹馬、未婚夫妻、暗戀者、前伴侶、命中注定之人
|
||||
- 核心驅動力為:對主角的愛、保護慾、佔有慾、忠誠、奉獻
|
||||
- 性格特質為:溫柔、專情、執著、體貼、或病嬌、過度保護、跟蹤狂(極端版)
|
||||
- 行為模式為:以主角為中心行動、將主角安全置於一切之上
|
||||
求生優勢評分側重: 忠誠帶來的可靠性、保護主角的決心(危急時爆發)、情感羈絆帶來的穩定後勤支援
|
||||
性愛優勢評分側重: 純粹愛意、溫柔呵護、或強烈佔有慾帶來的情感張力與獨特魅力
|
||||
|
||||
夥伴總則(四個夥伴類別通用):
|
||||
1. 查看當前同伴列表,不得抽取同名角色
|
||||
2. 本次抽取不得與歷史紀錄中任何夥伴類別的角色出處相同,操作方式:遍歷'/系統/商店歷史/商品',出處相同即須重新抽取
|
||||
3. 角色的基礎能力應符合當前遊戲進度
|
||||
4. 如果抽到強度過高的角色,請自動降低其能力(過強角色在荒野世界會有一定弱化)
|
||||
5. 必定是明確作品中的角色,不要自創素人角色。
|
||||
6. 總需求點數 = 求生優勢 + 性愛優勢 + 知名度,三項各為100~500,知名度可為0
|
||||
|
||||
分類衝突時的歸屬優先級(若角色同時符合多個類別):
|
||||
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).模糊地帶; }
|
||||
};
|
||||
|
||||
營地: # 物件,以營地名稱為鍵,記錄角色建立的營地信息。
|
||||
格式:{ "位置": "", "樣式": "", "物品": {}, "食材": {}, "醫藥": {}, "材料": {}, "牲畜": {}, "設施": {}, "奇物": {}, "武器": {} }
|
||||
說明:
|
||||
- 位置:營地的地點及地形
|
||||
- 樣式:營地包含的主要建築及型式
|
||||
- 物品/食材/醫藥/材料/牲畜/設施/奇物/武器:存放在營地的各類物品
|
||||
操作:insert(新增營地),remove + 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%
|
||||
harvestAction:REMOVE(收成後移除)或 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~80,中等100~150,大型200+。
|
||||
描述: 用自然語言描述項目的當前外觀和進展。每次更新進度時同步更新描述。
|
||||
所需材料: 記錄材料名、需投入的總數量和已投入數量。格式: { '材料名': { '所需': 數量, '已消耗': 數量 }, ... }。材料必須從庫存實際扣除後才能更新已投入數量。
|
||||
項目產出: 記錄項目完成後將生成的產物,用自然語言描述產物名稱和基本特徵。生成產物時遵循已有的物品/設施/技能生成規則。如"一座簡易的木結構小屋,約三米見方"。
|
||||
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~80,中等100~150,高深研究150+。
|
||||
描述: 用自然語言描述當前的研究狀態、發現和瓶頸。每次推進時更新。當研究有突破性進展或深化時,可以在此描述中累積記錄,而不是新建條目。
|
||||
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('📊 功能模塊: 好感系統 | 性愛系統 | 季節系統 | 作物系統 | 畜牧系統 | 懷孕系統 | 幼兒系統 | 排泄系統 | 任務系統 | 入眠系統 | 狀態效果');
|
||||
})();
|
||||
Reference in New Issue
Block a user