跳至主要內容

EternalTD 關卡與地圖

EternalTD 中的「關卡」是位於 plugins/EternalTD/levels/ 的 YAML 設定,搭配 plugins/EternalTD/worlds/ 中的範本世界資料夾。當玩家加入時,EternalTD 會將範本世界複製到伺服器的世界容器中,並在該複製副本中執行工作階段。

關卡設定欄位

欄位類型預設值備註
isEnabledbooltrue已停用的關卡在載入時會被跳過
levelNamestringnull顯示在訊息、計分板和 NPC 選單中的顯示名稱
levelDescriptionstring list[]顯示在 NPC 選單中的行。支援 $highscoreWave$highscorePlayer 佔位符
worldNamestringnullplugins/EternalTD/worlds/ 下的範本資料夾名稱
startLocationstring listnull怪物生成的序列化位置列表
endLocationstring listnull怪物行走的目標位置列表(「紅色」格子)
levelLocationsstring listnull關卡中所有可行走的格子方塊;在目前的建置中,這些必須來自內容套件,或手動寫入 YAML
wavesConfigFilestring必填連結波次設定的檔案名稱(waves/<name>.yml
highscoreWaveint0此關卡達到的最佳波次
highscorePlayerNamestring"no one"設定該分數的玩家顯示名稱
environmentenumNORMAL載入複製世界時使用的世界環境

整體使用的網格大小為每個邏輯格 3 個方塊(程式碼中的常數 GRID_SIZE)。

waveCount 已不再存在。仍帶有該鍵的舊關卡檔案會直接被忽略——外掛既不會讀取它,也不會把它寫回去。

位置字串格式

EternalTD 中的位置序列化為以逗號分隔的字串,格式如下:

worldName,x,y,z,yaw,pitch

下載的內容套件通常會提供這些值。對於自製地圖,目前的選取與註冊指令並不會儲存 levelLocations,因此你必須手動把產生的格線位置寫入關卡 YAML。

世界生命週期

當玩家加入關卡時:

  1. EternalTD 會檢查這位玩家是否已經有一份複製正在進行中。若第一份仍在複製時再次執行 /etd join,會被以「Your level is already being prepared.」拒絕。
  2. 它會根據 worldNameplugins/EternalTD/worlds/ 中查詢範本資料夾;若世界資料夾不存在、連結的波次檔案載入失敗,或世界名稱不是安全的資料夾名稱,就會拒絕這次加入。
  3. 它會保留下一個可用的數字後綴(<worldName>_0<worldName>_1、...)。保留動作是同步化的,並會記住已保留但尚未寫入磁碟的名稱,因此兩位玩家同時加入時不會撞到同一個實例名稱。
  4. 範本會在主執行緒之外被複製到伺服器的世界容器中,因此伺服器不會因為大型地圖而卡住。
  5. 複製的世界透過 MagmaCore 的 TemporaryWorldManager 作為臨時 void 世界載入,因此 Paper 26.1+ 遷移被隔離,缺失的區塊會回傳為虛空而非新產生的地形。
  6. 玩家被傳送到新世界;內部的 InstanceProtector 套用 EternalTD 的保護規則。

範本需求

複製動作會在寫入任何東西之前先經過驗證,若範本符合下列任一情況就會被拒絕:

  • 它不是一個目錄,或者它是一個符號連結
  • 它的根目錄沒有 level.dat
  • 它內部任何位置含有符號連結
  • 它含有名為 .eternaltd-instance 的檔案(該名稱為保留名稱,見下文)

複製失敗會被回滾,並告知玩家關卡未能開始。

實例歸屬標記

每個複製副本的根目錄都會寫入一個 .eternaltd-instance 檔案,記錄範本名稱與實例名稱。EternalTD 只會刪除標記與它所認定實例相符的世界資料夾。這道防護可避免過時的名稱衝突、手動建立的資料夾,或不相符的工作階段刪掉不是 EternalTD 建立的世界——若標記缺失或不相符,該資料夾會被保留,並改為記錄一則警告。

因當機而殘留的過時實例會在載入關卡時被清理,而且即使如此,也只會處理帶有有效標記且目前未載入的資料夾。

工作階段啟動驗證

只有在下列條件全部成立時,工作階段才會真正可玩。任何一項失敗都會中止該次遊玩、還原玩家(物品欄、遊戲模式、飛行、計分板)並刪除臨時世界:

  • levelLocationsstartLocationendLocation 皆存在且非空——否則會顯示「This level doesn't have a complete play area, start, and end configuration!」
  • 遊玩區域本身能解析為有效格線——否則會顯示「This level's play area is invalid!」
  • 每個起點格子都同時具備通往某個終點格子的地面路徑與空中路徑——否則會顯示「This level does not have complete land and air paths from every start to an end!」

當工作階段結束時:

  • 複製世界中的任何剩餘玩家都會被傳送回 config.yml 中的重生點位置,或者如果沒有設定重生點則被踢出。
  • 世界的保護規則會被移除,所有殘留的塔會被拆除(拆除時拋出例外的塔會被記錄並略過,而不會讓其餘的塔卡住)。
  • 為遊玩區域取得的區塊票證會被釋放(請參閱路徑驗證)。
  • 複製的世界會從磁碟卸載並刪除(TemporaryWorldManager.permanentlyDeleteWorld),舊有版面配置與 Paper 26.1+ 遷移後的版面配置皆會處理。
  • 結束是冪等的——第二次 end()(例如 /etd quit 之後立刻 /etd hub)不會有任何作用。

實例保護規則

當關卡活躍時,複製的世界會套用這些規則:

  • 停用爆炸
  • 停用液體流動
  • 停用鞘翅
  • 防止切換飛行
  • 防止友軍傷害
  • 防止原版怪物生成

地圖創作工作流程

目前的地圖創作流程使用遊戲內工具:

  1. 將範本世界資料夾放在 plugins/EternalTD/worlds/<worldName>/ 下。
  2. plugins/EternalTD/levels/ 中建立或下載相符的關卡 YAML。
  3. 執行 /etd reload 並手動加入關卡世界(或在單人模式中開啟以進行設定)。
  4. 使用 /etd selectfloor 並右鍵/左鍵點擊兩個角來標記遊戲區域,或使用 /etd selectfloorcoordinates <x1> <y1> <z1> <x2> <y2> <z2> 直接提供它們。
  5. 執行 /etd showselection <level> 以確認選擇看起來正確。
  6. 執行 /etd register <level> 以清除選擇。請注意,在目前建置中,registershowselection 實際上都不會持久化地板區域 — 可儲存 levelLocations 的輔助方法(LevelsConfigFields#addLevelLocations)雖已定義,但從未被任何指令呼叫。如果 levelLocations 尚未由下載的套件填入,你目前必須手動將其寫入關卡 YAML。
  7. 站在起始生成格子上並執行 /etd register <level> start。對每個起始格子重複此操作(此指令確實會持久化到 startLocation)。
  8. 站在終點格子上並執行 /etd register <level> end。對每個終點格子重複此操作(此指令確實會持久化到 endLocation)。
  9. 再次重新載入,並透過 NPC 選單或 /etd join <level> 加入以測試關卡。

選擇指令使用以下公式產生網格方塊:

size = abs(corner1 - corner2 + 1) / 3

當某個格子的地板方塊可通行時,或者當地板正上方的方塊可通行時,該格子會被跳過。換句話說,地板必須是實心的,而其上方的空間必須淨空,該格子才會被註冊為可遊玩——請選取地板,而不是它上方的空氣。

路徑驗證

EternalTD 會為每個起點格子快取一條通往任一終點格子的最省 A* 路徑——一條地面路徑與一條空中路徑。當有多個終點格子時,它會比較已完成路徑的成本並保留最短的那條,因此一張有多個出口的地圖會把敵人導向最近的可達出口,而不是最先找到的那個。

地面快取會在工作階段開始時、每次放置塔時,以及每次販售塔時重建。空中快取只會計算一次,就在第一次重建時:空中路徑完全忽略塔,因此玩家蓋的任何東西都無法使其失效。

空中敵人使用另外的空中路徑,該路徑完全忽略塔的阻擋,改為沿著空中偏移(設定路徑上方 4 格)行進。

載入遊玩區域時也會強制載入每一個含有格子方塊的區塊,因此長時間的遊玩不會卡在未載入的區塊上。這些區塊票證會在工作階段結束時、實例世界被刪除之前釋放。

該快取同時也是判斷關卡是否可玩的關卡:若任何起點格子缺少地面路徑或空中路徑,該工作階段會在加入時就被拒絕,而不會開始一場敵人無法完成的遊玩。放置塔時也會執行同樣的檢查,因此會封死某個起點格子的塔會被拒絕且不會花費金幣。

起點或終點位置若未位於其 3x3 格線方格的中心,會被捨棄並在主控台發出警告,這通常就是關卡回報路徑集不完整的原因。

每個解析出來的起點與終點格子也都會被標記為不可建造,因此玩家無法藉由在其上建造來封死生成點或出口。這些格子在放置高亮下會顯示為淡藍色。

NPC 和關卡選單

plugins/EternalTD/npcs/ 中的 NPC 設定將村民 NPC 連結到一個或多個關卡。右鍵點擊 NPC 會開啟一個 9 格物品欄,將每個關卡列為標有關卡名稱和描述的綠色染色玻璃板。

欄位類型預設值備註
isEnabledbooltrue已停用的 NPC 會被跳過
levelIDsstring list必填此 NPC 提供的關卡檔案名稱
locationstringnull標準 worldName,x,y,z,yaw,pitch 格式的生成位置
namestring"Default Name"NPC 顯示名稱
difficultystring"Difficulty: Not Set"顯示在 NPC 上方的難度標籤
disguisestringnullLibsDisguises 描述符(例如 custom:etd_tutorial_npc
customDisguiseDatastringnull額外的 LibsDisguises 指令資料 — 通常是那串很長的玩家外觀字串

村民會以無敵、停用 AI、持久化並以 EternalTD 的 NPC 命名空間鍵標記的方式生成。如果安裝了 LibsDisguises 且 disguisecustomDisguiseData 都已設定,村民會在生成時被偽裝。

具有 difficulty 標籤的漂浮盔甲架會在 NPC 上方 2.3 格處生成。

重生點行為

DefaultConfig 控制玩家在大廳世界中的管理方式:

  • setupDone — 追蹤是否已完成首次設定指引的旗標(預設 false)。
  • spawnLocations — 預設為 etd_spawn,0,65,0,0,0,且一律會被寫入設定檔。只有在 etd_spawn 世界存在時才會被解析並使用。
  • manageSpawn — 預設為 true。啟用時,加入的玩家會在登入後 1 刻被傳送到重生點位置。
  • playerGuide — 遊戲內指南書文字。
  • nightbreak.autoDownloadPluginUpdates — 共享的 MagmaCore 設定(預設 false)。啟用時,外掛和內容更新會在啟動時自動下載。

manageSpawn 為 true 且重生世界已載入時,每個加入伺服器的玩家都會被傳送到 spawnLocations。在 EternalTD 仍在初始化期間就已連線的玩家,會在初始化完成後一併被送往同一個重生點,除非他們已經身處大廳世界。