diff --git a/.github/assets/readme-features-basecamp-en.webp b/.github/assets/readme-features-basecamp-en.webp new file mode 100644 index 00000000..7f3c3466 Binary files /dev/null and b/.github/assets/readme-features-basecamp-en.webp differ diff --git a/.github/assets/readme-features-basecamp-zh.webp b/.github/assets/readme-features-basecamp-zh.webp new file mode 100644 index 00000000..f1f25d8c Binary files /dev/null and b/.github/assets/readme-features-basecamp-zh.webp differ diff --git a/.github/assets/readme-features-general-en.webp b/.github/assets/readme-features-general-en.webp index a607a006..f5c82bb0 100644 Binary files a/.github/assets/readme-features-general-en.webp and b/.github/assets/readme-features-general-en.webp differ diff --git a/.github/assets/readme-features-general-zh.webp b/.github/assets/readme-features-general-zh.webp index 9f236a58..50a71bd0 100644 Binary files a/.github/assets/readme-features-general-zh.webp and b/.github/assets/readme-features-general-zh.webp differ diff --git a/.github/assets/readme-features-inventory-en.webp b/.github/assets/readme-features-inventory-en.webp index 4cfb888c..7c5e9f7a 100644 Binary files a/.github/assets/readme-features-inventory-en.webp and b/.github/assets/readme-features-inventory-en.webp differ diff --git a/.github/assets/readme-features-inventory-zh.webp b/.github/assets/readme-features-inventory-zh.webp index 19b4f431..647c9daa 100644 Binary files a/.github/assets/readme-features-inventory-zh.webp and b/.github/assets/readme-features-inventory-zh.webp differ diff --git a/.github/assets/readme-features-pal-en.webp b/.github/assets/readme-features-pal-en.webp index ed7d2a5d..06bfe5fc 100644 Binary files a/.github/assets/readme-features-pal-en.webp and b/.github/assets/readme-features-pal-en.webp differ diff --git a/.github/assets/readme-features-pal-zh.webp b/.github/assets/readme-features-pal-zh.webp index 36fed5a7..571b311c 100644 Binary files a/.github/assets/readme-features-pal-zh.webp and b/.github/assets/readme-features-pal-zh.webp differ diff --git a/.github/assets/readme-features-player-en.webp b/.github/assets/readme-features-player-en.webp index 14e33199..3b3552fc 100644 Binary files a/.github/assets/readme-features-player-en.webp and b/.github/assets/readme-features-player-en.webp differ diff --git a/.github/assets/readme-features-player-zh.webp b/.github/assets/readme-features-player-zh.webp index 0d9d9161..843f1b02 100644 Binary files a/.github/assets/readme-features-player-zh.webp and b/.github/assets/readme-features-player-zh.webp differ diff --git a/.github/assets/readme-features-storage-en.webp b/.github/assets/readme-features-storage-en.webp new file mode 100644 index 00000000..90f47c98 Binary files /dev/null and b/.github/assets/readme-features-storage-en.webp differ diff --git a/.github/assets/readme-features-storage-zh.webp b/.github/assets/readme-features-storage-zh.webp new file mode 100644 index 00000000..105e76cf Binary files /dev/null and b/.github/assets/readme-features-storage-zh.webp differ diff --git a/.github/assets/readme-features-templates-en.webp b/.github/assets/readme-features-templates-en.webp index cbdf9602..4e4c4bf6 100644 Binary files a/.github/assets/readme-features-templates-en.webp and b/.github/assets/readme-features-templates-en.webp differ diff --git a/.github/assets/readme-features-templates-zh.webp b/.github/assets/readme-features-templates-zh.webp index 9cbe1a5a..c9384c33 100644 Binary files a/.github/assets/readme-features-templates-zh.webp and b/.github/assets/readme-features-templates-zh.webp differ diff --git a/.github/assets/readme-pal-editor-en.webp b/.github/assets/readme-pal-editor-en.webp index 12c5e55c..44023f62 100644 Binary files a/.github/assets/readme-pal-editor-en.webp and b/.github/assets/readme-pal-editor-en.webp differ diff --git a/.github/assets/readme-pal-editor-zh.webp b/.github/assets/readme-pal-editor-zh.webp index 27efc5c8..17958210 100644 Binary files a/.github/assets/readme-pal-editor-zh.webp and b/.github/assets/readme-pal-editor-zh.webp differ diff --git a/.github/assets/readme-quick-start-en.webp b/.github/assets/readme-quick-start-en.webp index 51d63c2a..9f3d5f55 100644 Binary files a/.github/assets/readme-quick-start-en.webp and b/.github/assets/readme-quick-start-en.webp differ diff --git a/.github/assets/readme-quick-start-zh.webp b/.github/assets/readme-quick-start-zh.webp index f1ee9996..d7d3ef26 100644 Binary files a/.github/assets/readme-quick-start-zh.webp and b/.github/assets/readme-quick-start-zh.webp differ diff --git a/.gitignore b/.gitignore index 9583f690..1dba4f08 100644 --- a/.gitignore +++ b/.gitignore @@ -172,4 +172,6 @@ config.json .worktrees/ *.lock tmp/ -.playwright-cli \ No newline at end of file +.playwright-cli +# PyInstaller writes this next to the build scripts; the AppImage spec is appimage.spec. +/palworld-pal-editor.spec diff --git a/README.cn.md b/README.cn.md index e38cea83..d94064c3 100644 --- a/README.cn.md +++ b/README.cn.md @@ -20,7 +20,7 @@
- 功能丰富的《幻兽帕鲁》存档修改器,支持玩家、帕鲁、科技、背包等内容,并可通过桌面 GUI、WebUI、CLI 或 Docker 运行。 + 功能丰富的《幻兽帕鲁》存档修改器,支持玩家、帕鲁、帕鲁存放、基地、科技、背包等内容,并可通过桌面 GUI、WebUI、CLI 或 Docker 运行。
 @@ -39,6 +39,8 @@ - [功能](#功能) - [通用](#通用) - [帕鲁](#帕鲁) + - [帕鲁存放与转移](#帕鲁存放与转移) + - [基地与公会研究](#基地与公会研究) - [模板与批量操作](#模板与批量操作) - [玩家](#玩家) - [物品栏](#物品栏) @@ -54,7 +56,7 @@ **由 \_connlost 用 ❤️ 开发的《幻兽帕鲁》帕鲁修改器。** -Palworld Pal Editor 是一个功能丰富的存档修改器,支持修改玩家、帕鲁、科技、背包等存档内容,并可通过桌面 GUI、WebUI、CLI 或 Docker 容器运行。 +Palworld Pal Editor 是一个功能丰富的存档修改器,支持修改玩家、帕鲁、帕鲁存放、基地研究、科技、背包等存档内容,并可通过桌面 GUI、WebUI、CLI 或 Docker 容器运行。 > [!NOTE] > Steam 存档根目录:`%LOCALAPPDATA%\Pal\Saved\SaveGames` @@ -92,12 +94,12 @@ Palworld Pal Editor 完整支持 Deutsch、English、Español、Español (Méxic  - [x] 列出玩家及其帕鲁 -- [x] 修改基地帕鲁 - [x] 搜索帕鲁 - [x] 对帕鲁排序 - [x] 筛选帕鲁 - [x] 显示不在常规容器中的帕鲁 - [x] 通过作弊模式启用通常不可用的选项 +- [x] 在作弊模式下显示技能与帕鲁的内部名称 ### 帕鲁 @@ -121,12 +123,32 @@ Palworld Pal Editor 完整支持 Deutsch、English、Español、Español (Méxic - [x] 修改工作适应性 - [x] 修改装备的主动技能 - [x] 修改学会的主动技能 -- [x] 修改被动技能 +- [x] 修改被动技能,并区分帕鲁被动、普通被动与伙伴技能 - [x] 治愈和复活帕鲁 - [x] 治愈所有帕鲁 - [x] 在合法范围内最大化帕鲁属性 - [x] 删除帕鲁 +### 帕鲁存放与转移 + + + +- [x] 浏览存档中的所有容器:队伍、帕鲁终端、基地、观赏笼、帕鲁次元仓库与跨界帕鲁终端 +- [x] 将帕鲁移动到任意玩家、基地或存放处的容器 +- [x] 修改和复制存放在帕鲁次元仓库或跨界帕鲁终端中的帕鲁 +- [x] 处理跨界帕鲁终端中已被占用的槽位:覆盖原有帕鲁,或直接跳转到该帕鲁 +- [x] 在指定的目标容器中直接创建帕鲁 + +### 基地与公会研究 + + + +- [x] 修改基地中工作的帕鲁 +- [x] 修改公会的帕鲁工作研究所 +- [x] 完成单项研究、整个类别,或一次完成全部研究 +- [x] 在包含多个公会的存档中切换公会 +- [x] 研究名称、效果与所需材料均取自游戏数据 + ### 模板与批量操作  @@ -185,6 +207,7 @@ Palworld Pal Editor 完整支持 Deutsch、English、Español、Español (Méxic - [x] 修改帕鲁球模块 - [x] 修改装备的食物 - [x] 修改物品数量 +- [x] 将磨损的物品恢复到满耐久与满弹药 - [x] 清空物品栏槽位 ## 安装与运行 @@ -230,6 +253,8 @@ palworld-pal-editor.exe --mode web --port 58080 --password "请设置强密码" 使用独立部署的前端时,请通过入口页面右上角的服务器菜单选择后端。 +WebUI 是一个渐进式网页应用(PWA):浏览器可以将它安装到桌面或主屏幕,界面与帕鲁图片也会在多次运行之间保留缓存。 + ## 命令行参数 ```text @@ -242,7 +267,7 @@ palworld-pal-editor.exe --mode web --port 58080 --password "请设置强密码" --nocli 在 GUI/WebUI 模式中禁用交互式 CLI ``` -运行 `palworld-pal-editor.exe --help` 可查看当前参数。命令行参数会覆盖 `config.json`;通常不需要手动编辑该文件。 +运行 `palworld-pal-editor.exe --help` 可查看当前参数。命令行参数会覆盖 `config.json`;通常不需要手动编辑该文件。设置、已保存的模板与日志存放在系统的用户数据目录中。 ## 视频教程 diff --git a/README.md b/README.md index 2271b960..97bdbc6f 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@- A feature-rich Palworld save editor for players, Pals, Technology, inventories, and more — available as a desktop GUI, WebUI, CLI, and Docker container. + A feature-rich Palworld save editor for players, Pals, Pal storage, base camps, Technology, inventories, and more — available as a desktop GUI, WebUI, CLI, and Docker container.
 @@ -39,6 +39,8 @@ - [Features](#features) - [General](#general) - [Pals](#pals) + - [Pal storage and transfers](#pal-storage-and-transfers) + - [Base camp and guild research](#base-camp-and-guild-research) - [Templates and bulk operations](#templates-and-bulk-operations) - [Players](#players) - [Inventory](#inventory) @@ -54,7 +56,7 @@ **A Palworld Pal Editor developed by \_connlost with ❤️.** -Palworld Pal Editor is a feature-rich save editor that supports player, Pal, Technology, inventory, and other save modifications. Run it as a desktop GUI, WebUI, CLI, or Docker container. +Palworld Pal Editor is a feature-rich save editor that supports player, Pal, Pal storage, base-camp research, Technology, inventory, and other save modifications. Run it as a desktop GUI, WebUI, CLI, or Docker container. > [!NOTE] > Steam save root: `%LOCALAPPDATA%\Pal\Saved\SaveGames` @@ -92,12 +94,12 @@ For most users, the pre-built desktop application is recommended. It does not re  - [x] List players and their Pals -- [x] Edit base-camp Pals - [x] Search Pals - [x] Sort Pals - [x] Filter Pals - [x] Show Pals outside their normal containers - [x] Enable normally unavailable options through cheat mode +- [x] Show skill and Pal internal names in cheat mode ### Pals @@ -121,12 +123,32 @@ For most users, the pre-built desktop application is recommended. It does not re - [x] Edit work suitability - [x] Edit equipped active skills - [x] Edit learned active skills -- [x] Edit passive skills +- [x] Edit passive skills, listed separately as Pal, regular, and Partner skills - [x] Heal and revive Pals - [x] Heal all Pals - [x] Maximize Pal progression within legal limits - [x] Delete Pals +### Pal storage and transfers + + + +- [x] Browse every container a save has: party, Palbox, base camp, viewing cage, Dimensional Pal Storage, and Global Pal Storage +- [x] Move a Pal to any container of any player, base camp, or storage +- [x] Edit and clone Pals that live in Dimensional Pal Storage or Global Pal Storage +- [x] Resolve a Global Pal Storage slot that is already taken, by overwriting it or by jumping to the Pal holding it +- [x] Create a Pal directly in a chosen destination container + +### Base camp and guild research + + + +- [x] Edit the base camp's worker Pals +- [x] Edit the guild's Pal Labor Research Laboratory +- [x] Complete a single research node, a whole category, or every category at once +- [x] Switch between guilds in a save that holds more than one +- [x] Read research names, effects, and material costs from the game's own data + ### Templates and bulk operations  @@ -185,6 +207,7 @@ For most users, the pre-built desktop application is recommended. It does not re - [x] Edit Pal Sphere modules - [x] Edit equipped food - [x] Edit item quantities +- [x] Restore a worn item to full durability and a full magazine - [x] Clear inventory slots ## Installation and Running @@ -230,6 +253,8 @@ palworld-pal-editor.exe --mode web --port 58080 --password "choose-a-strong-pass When using a separately hosted frontend, choose the backend from the server menu at the top-right of the entry page. +The WebUI is a progressive web app: a browser can install it to the desktop or home screen, and it keeps its interface and Pal images cached between runs. + ## Command-line Options ```text @@ -242,7 +267,7 @@ When using a separately hosted frontend, choose the backend from the server menu --nocli Disable the interactive CLI in GUI/WebUI mode ``` -Run `palworld-pal-editor.exe --help` for the current list. Command-line values override `config.json`; the application manages that file automatically. +Run `palworld-pal-editor.exe --help` for the current list. Command-line values override `config.json`; the application manages that file automatically. Settings, saved templates, and logs are kept in the platform's user data directory. ## Guides diff --git a/palworld-pal-editor.spec b/appimage.spec similarity index 100% rename from palworld-pal-editor.spec rename to appimage.spec diff --git a/build_appimage.sh b/build_appimage.sh index c864931b..341c9018 100755 --- a/build_appimage.sh +++ b/build_appimage.sh @@ -47,12 +47,15 @@ fi echo "🐍 Using $PYTHON_CMD (version $PYTHON_VERSION)" # Set up Python venv and install the Linux GUI backend only for this artifact. +# Nothing is activated: naming the interpreter is what says which environment a +# command runs in, and an activation that fails would leave the rest of this +# script quietly building against system Python. rm -rf venv $PYTHON_CMD -m venv venv -source venv/bin/activate +VENV_PYTHON="./venv/bin/python" -python -m pip install -r requirements.txt -python -m pip install "pywebview[pyside6]==4.4.1" +"$VENV_PYTHON" -m pip install -r requirements.txt +"$VENV_PYTHON" -m pip install "pywebview[pyside6]==4.4.1" # Clean previous build rm -rf "$DISTDIR" @@ -60,7 +63,7 @@ mkdir -p "$DISTDIR" # Build with PyInstaller echo "📦 Building with PyInstaller..." -pyinstaller --clean --noconfirm palworld-pal-editor.spec +"$VENV_PYTHON" -m PyInstaller --clean --noconfirm appimage.spec PYINSTALLER_BINARY="$DISTDIR/$APPNAME" diff --git a/build_executable.ps1 b/build_executable.ps1 index b7d23203..50850961 100644 --- a/build_executable.ps1 +++ b/build_executable.ps1 @@ -50,15 +50,17 @@ if ($majorVersion -lt 3 -or ($majorVersion -eq 3 -and $minorVersion -lt 10)) { Write-Host "Using $($PYTHON_CMD) (version $($majorVersion).$($minorVersion))" -# Create and activate virtual environment +# Create the virtual environment. Nothing is activated: naming the interpreter +# is what says which environment a command runs in, and an activation that fails +# would leave the rest of this script quietly building against system Python. & $PYTHON_CMD -m venv venv -. .\venv\Scripts\Activate.ps1 +$VENV_PYTHON = ".\venv\Scripts\python.exe" # Install dependencies -pip install -r requirements.txt +& $VENV_PYTHON -m pip install -r requirements.txt -pip install pyinstaller +& $VENV_PYTHON -m pip install pyinstaller Remove-Item ".\dist" -Recurse -Force -pyinstaller --onefile -i "./icon.ico" --add-data="src/palworld_pal_editor/assets/data;assets/data" --add-data="src/palworld_pal_editor/assets/icons;assets/icons" --add-data="src/palworld_pal_editor/webui;webui" .\src\palworld_pal_editor\__main__.py --name palworld-pal-editor --log-level=INFO --hidden-import="pkg_resources.extern" +& $VENV_PYTHON -m PyInstaller --onefile -i "./icon.ico" --add-data="src/palworld_pal_editor/assets/data;assets/data" --add-data="src/palworld_pal_editor/assets/icons;assets/icons" --add-data="src/palworld_pal_editor/webui;webui" .\src\palworld_pal_editor\__main__.py --name palworld-pal-editor --log-level=INFO --hidden-import="pkg_resources.extern" diff --git a/build_executable.sh b/build_executable.sh index b849d9db..8c5588af 100755 --- a/build_executable.sh +++ b/build_executable.sh @@ -35,17 +35,18 @@ fi echo "Using ${PYTHON_CMD} (version ${PYTHON_VERSION})" -# Create a virtual environment named 'venv' (skip if already created) +# Create a virtual environment named 'venv' (skip if already created). Nothing +# is activated: naming the interpreter is what says which environment a command +# runs in, and an activation that fails would leave the rest of this script +# quietly building against system Python. ${PYTHON_CMD} -m venv venv - -# Activate the virtual environment -source venv/bin/activate +VENV_PYTHON="./venv/bin/python" # Install packages from requirements.txt -pip install -r requirements.txt +"${VENV_PYTHON}" -m pip install -r requirements.txt -pip install pyinstaller +"${VENV_PYTHON}" -m pip install pyinstaller rm -r ./dist -pyinstaller --onefile -i "./icon.ico" --add-data="src/palworld_pal_editor/assets/data:assets/data" --add-data="src/palworld_pal_editor/assets/icons:assets/icons" --add-data="src/palworld_pal_editor/webui:webui" ./src/palworld_pal_editor/__main__.py --name palworld-pal-editor --hidden-import="pkg_resources.extern" +"${VENV_PYTHON}" -m PyInstaller --onefile -i "./icon.ico" --add-data="src/palworld_pal_editor/assets/data:assets/data" --add-data="src/palworld_pal_editor/assets/icons:assets/icons" --add-data="src/palworld_pal_editor/webui:webui" ./src/palworld_pal_editor/__main__.py --name palworld-pal-editor --hidden-import="pkg_resources.extern" diff --git a/docker/Dockerfile b/docker/Dockerfile index 8dd946e3..482ceb51 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,6 +1,8 @@ FROM nikolaik/python-nodejs:python3.12-nodejs21-slim ENV PATH=/usr/local/nginx/bin:$PATH +# The editor runs out of the source tree copied to /app, not from an install. +ENV PYTHONPATH=/app/src EXPOSE 58888 VOLUME [ "/mnt/gamesave" ] @@ -20,8 +22,7 @@ RUN cd "/app/frontend/palworld-pal-editor-webui" \ && npm run build \ && mv "/app/frontend/palworld-pal-editor-webui/dist" "/app/src/palworld_pal_editor/webui" -RUN pip install --no-cache-dir --force-reinstall --upgrade -r requirements.txt \ - && pip install -e . --no-cache-dir --force-reinstall --upgrade +RUN pip install --no-cache-dir --force-reinstall --upgrade -r requirements.txt RUN chmod +x /app/docker/app.sh diff --git a/frontend/palworld-pal-editor-webui/src/App.vue b/frontend/palworld-pal-editor-webui/src/App.vue index 32f8931a..846b699a 100644 --- a/frontend/palworld-pal-editor-webui/src/App.vue +++ b/frontend/palworld-pal-editor-webui/src/App.vue @@ -16,21 +16,33 @@ import { computed, nextTick, onMounted, ref, watch } from 'vue' import MessageCenter from '@/components/MessageCenter.vue' import SupportDialog from '@/components/SupportDialog.vue' import TopBar from '@/components/TopBar.vue' -import { usePalEditorStore } from '@/stores/paleditor' +import { useBackendStore } from '@/stores/backend' +import { useMessagesStore } from '@/stores/messages' +import { useAppStore } from '@/stores/app' +import { useAppShellStore } from '@/stores/app-shell' +import { useSessionStore } from '@/stores/session' import AuthView from '@/views/AuthView.vue' import BackendErrorView from '@/views/BackendErrorView.vue' import EditorView from '@/views/EditorView.vue' import EntryView from '@/views/EntryView.vue' import uiIconSprite from '@/assets/ui-icons.svg?raw' -const palStore = usePalEditorStore() -const runtimeError = computed(() => palStore.BACKEND_ERROR && palStore.APP_STATE !== 'backend-error') -const applicationDialog = computed(() => !palStore.BACKEND_ERROR && palStore.CURRENT_MESSAGE?.presentation === 'dialog') +const backend = useBackendStore() +const messages = useMessagesStore() +const appStore = useAppStore() +const shell = useAppShellStore() +const sessionStore = useSessionStore() +const runtimeError = computed(() => backend.BACKEND_ERROR && sessionStore.appState !== 'backend-error') +const applicationDialog = computed(() => !backend.BACKEND_ERROR && messages.CURRENT_MESSAGE?.presentation === 'dialog') const supportDialogVisible = computed(() => - palStore.SHOW_DONATE_FLAG && ['entry', 'editor'].includes(palStore.APP_STATE) + shell.donationPromptOpen && ['entry', 'editor'].includes(sessionStore.appState) ) const blockingOverlay = computed(() => runtimeError.value || applicationDialog.value) const modalOverlay = computed(() => blockingOverlay.value || supportDialogVisible.value) +// Spec 8.8: while an operation is running nothing in the app may start a second +// one or edit what the first is about to send. One region, one gate -- so a new +// control is covered by existing here rather than by remembering to disable itself. +const interactionBlocked = computed(() => modalOverlay.value || sessionStore.operationPending) const playersCollapsed = ref(readRosterCollapsed('editor.playersCollapsed')) const palsCollapsed = ref(readRosterCollapsed('editor.palsCollapsed')) const refreshPage = () => window.location.reload() @@ -48,53 +60,53 @@ watch(modalOverlay, async (visible, wasVisible) => { }, { flush: 'sync' }) watch(playersCollapsed, value => persistRosterCollapsed('editor.playersCollapsed', value)) watch(palsCollapsed, value => persistRosterCollapsed('editor.palsCollapsed', value)) -onMounted(palStore.bootstrap) +onMounted(shell.bootstrap)- {{ palStore.getTranslatedText('App_Connecting') }} +
+ {{ appStore.getTranslatedText('App_Connecting') }}