搜尋

浩劫殺陣車諾比之影CE腳本

返回清單
切換到指定樓層
通知這文章過時或找檔案 發表主題

[電玩遊戲] 《浩劫殺陣:車諾比之影》自製願望實現器攻略 願望實現器安裝教學、作弊選單、腳本修改

[複製連結]
1
o122334234 ( Lv.70 熾天使 ) 發表於 2025-6-7 00:33:22 | 只看該作者 |只看大圖 回覆獎勵 |降序瀏覽 |閱讀模式


各位潛行者們,大家好!隨著《浩劫殺陣》三部曲的強化版(Enhanced Edition)推出,相信許多老玩家都回鍋重溫這款經典。如果你擁有原始的舊版遊戲,就可以在 Steam 上免費安裝強化版。而今天,這篇教學將帶領大家一步步為《浩劫殺陣:車諾比之影》強化版(SoCEE)打造一個功能強大的「願望實現器」(Wish Granter),也就是大家俗稱的作弊選單。

這篇教學屬於技術型文章,會需要修改到遊戲的腳本檔案。不過別擔心,只要跟著步驟一步一步來,即使是新手也能成功!讓我們開始動手,學習如何自己製作遊戲 MOD 吧!

遊戲版本資訊
  • 遊戲名稱:S.T.A.L.K.E.R.: Shadow of Chornobyl - Enhanced Edition (簡稱: SoCEE)
  • 遊戲版本:1.7.2
  • 遊戲執行檔:xrEngine.exe / xrGame.dll
  • 檔案版本:xrEngine.exe -- 1.7.2.13762 (1.7.2+28-3879775)


第一步:解包遊戲資料


三款強化版的遊戲引擎都是基於《浩劫殺陣:普里皮亞季的呼喚》更新後的版本(可能是 Open X-Ray 的分支),所以過去那些將遊戲內容解包到根目錄「gamedata」資料夾的 MOD 修改方式依然適用。要從遊戲的 .db 檔案中提取所有內容,你需要下面這個工具:

db_unpacker.zip 下載連結:
https://pixeldrain.com/u/GX9RtCFU

這個解包工具是為 SoCEE 客製化的,請不要拿它去解包《晴空》或《普里皮亞季的呼喚》的強化版喔!

解包步驟教學

  • 下載上方的壓縮檔,並將其解壓縮到你的遊戲根目錄(例如:D:\SteamLibrary\steamapps\common\STALKER Shadow of Chornobyl - EE)。
  • 啟動遊戲,進入主選單。
  • 按下 F1 鍵。
  • 按下  鍵(波浪號鍵)打開控制台。
  • 再次按下  鍵關閉控制台。
  • 按下 A 鍵開始提取所有資料。

在提取過程中,遊戲會凍結,請耐心等待大約 2-3 分鐘(使用 NVMe M.2 SSD 的情況下)。完成後,你就可以在遊戲主資料夾看到一個名為「unpack」的資料夾。這個過程需要 11.2 GB 的磁碟空間。

解包完成後,為了避免後續製作作弊選單時發生混淆,你可以先刪除「gamedata」資料夾裡的「config」和「scripts」這兩個子資料夾。

我需要這個解包工具,才能將「願望實現器」(也就是作弊選單)整合進遊戲裡。你可以參考下方影片 3:14 處展示的概念:
youtube
外連至此YOUTUBE影片連結



我花了一些時間才搞懂並重新設計了適用於強化版的「願望實現器」。這篇教學會一步步教你如何做到。這並不像把舊版遊戲檔案複製貼上到新版那麼簡單,而且我也想學習如何親手完成它。

簡單來說,我重複利用了「載入遊戲」的對話框和其屬性檔案(.script, .xml),並對其內容進行了修改和添加。過程中,我還需要用像 Adobe Fireworks 這樣的繪圖工具來做視覺規劃,才能確定所有介面元素的位置(X、Y、寬、高)。

為了方便跟隨本教學,建議你先將遊戲的解析度調到最低(例如 1176x664),並將顯示模式設定為「視窗化」。本教學需要一些基本的 Lua 和 XML 知識,你不僅僅是盲目地跟著步驟走,而是需要理解背後的原理。

第二步:前置準備工作


首先,我們需要準備好要修改的基礎檔案。請從剛剛解包出來的「unpack」資料夾中,複製以下 3 個檔案到遊戲根目錄下的「gamedata」對應路徑中。

如果你的遊戲根目錄沒有「gamedata」資料夾,請手動建立一個。

複製來源:

  • game_root\unpack\scripts\ui_main_menu.script
  • game_root\unpack\scripts\ui_load_dialog.script
  • game_root\unpack\config\ui\ui_mm_load_dlg.xml

複製到目標位置:

  • game_root\gamedata\scripts\ui_main_menu.script
  • game_root\gamedata\scripts\ui_load_dialog.script
  • game_root\gamedata\config\ui\ui_mm_load_dlg.xml

(請注意,game_root 是指你的遊戲安裝路徑,每個人的可能都不同。)

xRVthGW.jpg


👉 GM後台版 遊戲 推薦 ⬇️⬇️⬇️ 快速玩各種二次元動漫手遊app



azZZt1w.jpg



rqFtI3O.jpg


為了方便區分,我將 ui_mm_load_dlg.xmlui_load_dialog.script 分別重新命名為 ui_mm_cheat_dlg.xmlui_cheat_dialog.script

完成後,你的 gamedata 資料夾結構應該如下圖所示(請使用你自己的遊戲路徑):
D:\SteamLibrary\steamapps\common\STALKER Shadow of Chornobyl - EE\gamedata\scripts\ui_cheat_dialog.script
D:\SteamLibrary\steamapps\common\STALKER Shadow of Chornobyl - EE\gamedata\scripts\ui_main_menu.script
D:\SteamLibrary\steamapps\common\STALKER Shadow of Chornobyl - EE\gamedata\config\ui\ui_mm_cheat_dlg.xml

第三步:設定觸發方式


為了讓遊戲讀取我們新建的 ui_cheat_dialog.scriptui_mm_cheat_dlg.xml,我們需要修改主選單的腳本 ui_main_menu.script。所有 .script 檔案都是 Lua 腳本,你可以使用 Notepad++ 這類編輯器來開啟它。

  • 打開位於 game_root\gamedata\scripts 資料夾的 ui_main_menu.script
    e7UAKyu.jpg
  • 找到 main_menu:OnKeyboard 這個函式。遊戲原本內建一個生成對話框的功能,但在正式版中被移除了。我們正好可以利用它來載入我們的作弊選單。
    bGvljNe.jpg


  • 將被註解掉的程式碼恢復。移除 --[[ 和 ]]-- 符號,讓它看起來像這樣:
    x6pb3Rj.jpg
  • 接著,把觸發按鍵從 DIK_S 改成 DIK_F1。這樣一來,F1 鍵就會成為我們的觸發熱鍵。
    br8g1dx.jpg
  • 現在 F1 鍵會執行 OnButton_load_spawn 這個函式。從程式碼中可以看出,它會初始化一個名為「ui_spawn_dialog」的對話框。
    hQ9ni49.jpg
  • 現在儲存檔案並執行遊戲。在主選單按下 F1,你會看到一個沒什麼用的空白對話框,但這代表我們成功觸發了它!
    iD3SprD.jpg
  • 滿足好奇心後,讓我們把它改成載入我們的作弊選單。回到 ui_main_menu.script,將 main_menu:OnButton_load_spawn 函式修改成以下內容:
    1. function main_menu:OnButton_load_spawn()
    2. if self.cheat_dlg == nil then
    3. self.cheat_dlg = ui_cheat_dialog.cheat_dialog()
    4. self.cheat_dlg.owner = self
    5. end
    6. self.cheat_dlg:FillList()
    7. self:GetHolder():start_stop_menu(self.cheat_dlg, true)
    8. self:GetHolder():start_stop_menu(self, true) --new
    9. self:Show(false)
    10. end
    複製代碼
  • 儲存 ui_main_menu.script。然後打開 ui_cheat_dialog.script,用 Ctrl+F 尋找所有的 load_dialog 並替換為 cheat_dialog。接著在約 108 行處,將 xml:ParseFile ("ui_mm_load_dlg.xml") 改成 xml:ParseFile ("ui_mm_cheat_dlg.xml")。你也可以刪除 109 和 110 行,我們用不到。修改後如下圖:
    PtSgd5q.jpg
  • 儲存檔案並重新執行遊戲。現在在主選單按下 F1,你將會看到原本的「載入遊戲」選單出現了!這表示我們已經成功地用 F1 鍵載入了我們修改過的腳本檔案,接下來就是把它改造成「願望實現器」了。
    7aC84o4.jpg



第四步:簡化程式碼,重新開始


為了更容易理解和建構,我們將從簡化的程式碼開始,而不是修改現有的複雜內容。

簡化 Lua 腳本
請將 ui_cheat_dialog.script 檔案的內容完全替換為以下程式碼。我已經將暫時用不到的部分註解掉了。
  1. local mode
  2. local s_table = {}

  3. class "cheat_item" (CUIListBoxItem)

  4. function cheat_item:__init(height) super(height)
  5. self.file_name = "filename"
  6. self:SetTextColor(GetARGB(255, 170, 170, 170))
  7. self.fn = self:GetTextItem()
  8. self.fn:SetFont(GetFontLetterica18Russian())
  9. self.fn:SetEllipsis(true)
  10. end

  11. function cheat_item:__finalize()
  12. end

  13. class "cheat_dialog" (CUIScriptWnd)

  14. function cheat_dialog:__init() super()
  15. self:InitControls()
  16. self:InitCallBacks()
  17. end

  18. function cheat_dialog:__finalize()
  19. end

  20. function cheat_dialog:FillList()
  21. self.list_box:RemoveAll()
  22. mode = "item"
  23. local name
  24. for i, v in ipairs(cheat_tables.food_and_drugs) do
  25. name = game.translate_string(system_ini():r_string(v, "inv_name"))
  26. self:AddItemToList(name, v)
  27. end
  28. end

  29. function cheat_dialog:InitControls()
  30. self:Init(0, 0, 1024, 768)
  31. local xml = CScriptXmlInit()
  32. local ctrl
  33. xml:ParseFile("ui_mm_cheat_dlg.xml")

  34. local platform = get_platform_id()
  35. if is_using_4k_movies() then
  36.         xml:InitStatic("back_video_4k", self)
  37. elseif platform == platform_ids.PLATFORM_ORBIS or platform == platform_ids.PLATFORM_PROSPERO or platform == platform_ids.PLATFORM_GDK then
  38.         xml:InitStatic("back_video_orbis", self)
  39. elseif platform == platform_ids.PLATFORM_GDK_1440 then
  40.         xml:InitStatic("back_video_orbis", self)
  41. elseif platform == platform_ids.PLATFORM_GDK_4K then
  42.         xml:InitStatic("back_video_orbis", self)
  43. elseif platform == platform_ids.PLATFORM_NX64 then
  44.         xml:InitStatic("back_video_nx64", self)
  45. else
  46.         xml:InitStatic("back_video", self)
  47.         xml:InitStatic("background", self)
  48.         xml:InitStatic("newspaper_video", self)
  49. end

  50. ctrl = CUIWindow()
  51. xml:InitWindow("file_item:main", 0, ctrl)
  52. self.file_item_main_sz = vector2():set(ctrl:GetWidth(), ctrl:GetHeight())
  53. xml:InitWindow("file_item:fn", 0, ctrl)
  54. self.file_item_fn_sz = vector2():set(ctrl:GetWidth(), ctrl:GetHeight())
  55. xml:InitWindow("file_item:fd", 0, ctrl)
  56. self.file_item_fd_sz = vector2():set(ctrl:GetWidth(), ctrl:GetHeight())
  57. self.form = xml:InitStatic("form", self)
  58. xml:InitStatic("form:caption", self.form)
  59. self.file_caption = xml:InitStatic("form:file_caption", self.form)
  60. self.file_data = xml:InitStatic("form:file_data", self.form)
  61. xml:InitFrame("form:list_frame", self.form)
  62. self.list_box = xml:InitListBox("form:list", self.form)
  63. self.list_box:ShowSelectedItem(true)
  64. self:Register(self.list_box, "list_window")

  65. if not self.mm_is_controller or get_platform_id() == platform_ids.PLATFORM_NX64 then
  66.         self.load_btn = xml:Init3tButton("form:btn_load", self.form)
  67.         self:Register(self.load_btn, "button_load")
  68.         self.delete_btn = xml:Init3tButton("form:btn_delete", self.form)
  69.         self:Register(self.delete_btn, "button_del")
  70.         self.cancel_btn = xml:Init3tButton("form:btn_cancel", self.form)
  71.         self:Register(self.cancel_btn, "button_back")
  72.         if get_platform_id() == platform_ids.PLATFORM_NX64 then
  73.                 self.input_legend = xml:InitInputLegend("input_legend", self)
  74.         end
  75. else
  76.         self.input_legend = xml:InitInputLegend("input_legend", self)
  77. end        

  78. --[[ Omitted Code ]]

  79. self.message_box = CUIMessageBoxEx()
  80. self:Register(self.message_box, "message_box")


  81. end

  82. function cheat_dialog:InitCallBacks()

  83. self:AddCallback("message_box", ui_events.MESSAGE_BOX_YES_CLICKED, self.OnMsgYes, self)
  84. self:AddCallback("message_box", ui_events.MESSAGE_BOX_OK_CLICKED, self.OnMsgYes, self)
  85. self:AddCallback("message_box", ui_events.MESSAGE_BOX_NO_CLICKED, self.OnMsgNo, self)
  86. self:AddCallback("message_box", ui_events.MESSAGE_BOX_CANCEL_CLICKED, self.OnMsgNo, self)

  87. if not self.mm_is_controller or get_platform_id() == platform_ids.PLATFORM_NX64 then
  88.         self:AddCallback("button_load", ui_events.BUTTON_CLICKED, self.OnButton_load_clicked, self)
  89.         self:AddCallback("button_back", ui_events.BUTTON_CLICKED, self.OnButton_back_clicked, self)
  90.         self:AddCallback("button_del", ui_events.BUTTON_CLICKED, self.OnButton_del_clicked, self)
  91.         self:AddCallback("list_window", ui_events.LIST_ITEM_CLICKED, self.OnListItemClicked, self)
  92.         self:AddCallback("list_window", ui_events.WINDOW_LBUTTON_DB_CLICK, self.OnListItemDbClicked, self)

  93.         if get_platform_id() == platform_ids.PLATFORM_NX64 then
  94.                 self:AddCallback("list_window", ui_events.LIST_ITEM_SELECT, self.OnListItemClicked, self)
  95.         end
  96. else
  97.         self:AddCallback("list_window", ui_events.LIST_ITEM_SELECT, self.OnListItemClicked, self)
  98. end

  99. --[[ Omitted Code ]]
  100. IGNORE_WHEN_COPYING_START
  101. content_copy
  102. download
  103. Use code with caution.
  104. IGNORE_WHEN_COPYING_END

  105. end

  106. function cheat_dialog:OnButton_back_clicked()
  107. if self.mm_is_controller then
  108. self.sndDecline:play(nil, 0.0, sound_object.s2d)
  109. end
  110. self:GetHolder():start_stop_menu(self.owner, true)
  111. self:GetHolder():start_stop_menu(self,true)
  112. self.owner:Show(true)
  113. end
複製代碼
接著,打開 ui_main_menu.script,找到 self.cheat_dlg:FillList() 這行(大約在 364 行),在前面加上 -- 將它暫時註解掉。

簡化 XML 介面檔案
同樣地,我們也從一個乾淨的 XML 檔案開始。請將 ui_mm_cheat_dlg.xml 的內容完全替換為以下程式碼:
  1. <?xml version="1.0" encoding="utf-8"?>

  2. <window>

  3. <!-- 根據影像模式載入背景圖片 -->
  4. <back_video_orbis x="0" y="0" width="1024" height="768" stretch="1">
  5.         <texture x="0" y="0" width="1920" height="1080">ui\ui_mm_load_back_new</texture>
  6. </back_video_orbis>

  7. <back_video_4k x="0" y="0" width="1024" height="768" stretch="1">
  8.         <texture x="0" y="0" width="3840" height="2160">ui\ui_mm_load_back_new</texture>
  9. </back_video_4k>

  10. <back_video x="0" y="0" width="1024" height="430">
  11.         <texture x="0" y="0" width="1024" height="430">ui\ui_mm_window_back_crop</texture>
  12. </back_video>

  13. <!-- 重複使用一個影片檔來填補黑色背景空隙 -->
  14. <_back_video x="0" y="0" width="1024" height="512" stretch="1">
  15.         <texture>ui\ui_vid_back_04</texture>
  16. </_back_video>

  17. <!-- 背景圖 -->
  18. <background x="0" y="0" width="1024" height="768">
  19.         <texture ng_ratio="2">ui\ui_static_mm_back_04</texture>
  20. </background>

  21. <!-- 列表項目的名稱和描述欄位的寬高設定 -->
  22. <file_item>
  23.         <main width="392" height="18"/>
  24.         <!-- name -->
  25.         <fn width="284" height="18"/>
  26.         <!-- description -->
  27.         <fd width="88" height="18"/>
  28. </file_item>

  29. <!-- 主視窗本體 -->
  30. <form x="415" y="168" width="560" height="460">
  31.         <!-- 對話框皮膚 -->
  32.         <_texture>ui\ui_options_menu_static</_texture>
  33.         <_texture_offset x="-29" y="-19"/>
  34.         <texture>ui_menu_options_dlg</texture>
  35.         
  36.         <!-- 標題與屬性:位置xy, 大小wh, 標題文字 -->
  37.         <caption x="65" y="10" width="500" height="25" complex_mode="0">
  38.                 <text font="graffiti32">The Wish Granter</text>
  39.         </caption>
  40.         
  41. </form>
  42. IGNORE_WHEN_COPYING_START
  43. content_copy
  44. download
  45. Use code with caution.
  46. IGNORE_WHEN_COPYING_END

  47. </window>
複製代碼

第五步:介面詳解與調整


遊戲介面的預設畫布大小是 1024x768,所有的介面物件都以此為基準進行定位。而我們的主視窗(form)屬性為
  1. <form x="415" y="168" width="560" height="460">
複製代碼
,這代表它是一個位於座標 (415, 168),寬高為 560x460 的矩形。

mzlejfX.jpg


file_item 區塊定義了列表中每一行的格式,包含一個寬 284 像素的名稱欄(fn)和一個寬 88 像素的描述欄(fd)。
X2BO2Ur.jpg


form 區塊則定義了主視窗的外觀和標題。我們將標題文字改為「The Wish Granter」,並使用更大的「graffiti32」字體。

執行遊戲後,你會看到如下畫面。標題位置有點偏,而且沒有按鈕可以返回主選單,你只能強制關閉遊戲。
gRAJ5y0.jpg


現在,讓我們來修正這個問題。在 XML 檔案的 </form> 標籤前,加入以下按鈕的程式碼:
  1. <!-- buttons -->
  2. <btn_load x="65" y="427" width="157" height="48">
  3. <texture>ui_button_main01</texture>
  4. <text font="graffiti22">Apply</text>
  5. </btn_load>

  6. <btn_delete x="221" y="427" width="157" height="48">
  7. <texture>ui_button_main01</texture>
  8. <text font="graffiti22">Stop (Music)</text>
  9. </btn_delete>

  10. <btn_cancel x="377" y="427" width="157" height="48">
  11. <texture>ui_button_main01</texture>
  12. <text font="graffiti22">Cancel</text>
  13. </btn_cancel>
複製代碼
現在介面看起來有模有樣了。點擊「Cancel」按鈕,就可以順利返回主選單。
v2nobGq.jpg


最後,我們微調一下標題的位置,讓它看起來更美觀。修改 <caption> 標籤的 x 和 y 值:
  1. <!-- caption and properties: position xy, size wh, title -->

  2. <caption x="45" y="2" width="500" height="25" complex_mode="0">
  3. <text font="graffiti32">The Wish Granter</text>
  4. </caption>
複製代碼
大功告成!現在你的介面看起來應該像這樣:
vjRymBA.jpg


這篇教學會隨著我的進度持續更新,敬請期待!

參考資料


db_unpacker 來源討論串
https://ap-pro.ru/forums/topic/6 ... ents#comment-337523
db_unpacker 作者原始連結
https://gitlab.com/axet/gist/-/tree/extractor-op2.1

以下廣告滑動後還有帖子內容



《浩劫殺陣:車諾比之影》自製選單常見問題Q&A


Q:為什麼我執行解包步驟時,遊戲會卡住或凍結?
A:這是正常現象。解包過程需要讀取大量遊戲資料,會暫時讓遊戲無回應。請耐心等待 2-3 分鐘,具體時間取決於你的硬碟讀寫速度。

Q:這個解包工具可以用在《晴空》或《普里皮亞季的呼喚》強化版嗎?
A:不行。本教學提供的解包工具是專為《車諾比之影》強化版客製化的,請勿混用。

Q:我的遊戲根目錄下沒有「gamedata」資料夾,該怎麼辦?
A:請手動在遊戲根目錄下建立一個名為「gamedata」的資料夾即可。

Q:我照著步驟做了,但按 F1 遊戲就閃退,是哪裡出錯了?
A:最可能的原因是 .script 或 .xml 檔案的程式碼有誤,例如打錯字、複製貼上時漏掉內容,或是檔案名稱、路徑不正確。請仔細核對教學中的每一步,特別是檔案名稱和程式碼內容。

Q:我可以把觸發熱鍵 F1 改成別的按鍵嗎?
A:可以。在「第三步:設定觸發方式」中,修改 ui_main_menu.script 檔案時,可以將 DIK_F1 改成你想要的按鍵碼,例如 DIK_F2 就是 F2 鍵。


vjRymBA.jpg







大家正在看啥


收藏收藏 分享文章到FB上分享
回覆 使用道具 檢舉
複製專屬你的推廣連結:發至FB與各論壇宣傳:累積點數換GP商品 & 藍鑽
每五點閱率就可以兌換藍鑽積分或遊戲點卡 夢遊推廣文章換GP商品

你需要登入後才可以回覆 登入 | 加入會員

本版積分規則

Copyright (C) 2010-2020 夢遊電玩論壇

廣告合作:請直接聯繫我們,並附上您預刊登位置的預算。  

快速回覆 返回頂端 返回清單