15 KiB
Mana Pet World C++ 开放世界原型
这是独立的 C++/raylib 开放世界宠物收集游戏。当前方向是 A+B:在现有原型上继续扩展,同时尽量接入原项目 TMX 地图,做成更完整的开放世界宠物收集游戏。它尽量复用 Source of Mana 现成内容:
- 地图:扫描
assets/maps/**/*.tmx,目前会把能解析的 40 张 TMX 建进世界地图列表。 - 地图池:参考原项目
MapPool.gd,运行时缓存当前地图和可传送到的相邻 TMX,切图时优先复用缓存并清掉不再相邻的旧地图。 - 地图拼接:读取原 Tiled
assets/maps/tonori/Tonori.world中的室外地图全局坐标,玩家越过当前地图边界时会按全局坐标尝试切到相邻 TMX;没有写进.world的其他 TMX 会放进隔离陈列区,仍会显示在世界总览和地图选择里。 - 图块:读取 TMX/TSX 引用的
assets/tilesets/... - 图层:按 TMX 图层语义绘制,
Ground等普通层在实体下方,Fringe/Over等遮挡层在玩家、NPC、怪物上方 - 动态瓦片:读取 TSX 里的
<animation>帧,水波、火把、发光柱等原素材会按持续时间切帧播放 - NPC / 怪物出生点:读取 TMX
Objects图层里的Spawn,NPC 和交互物位置按原 importer 语义使用矩形中心点 - 保底野外宠物:原 TMX 没有
MonsterSpawn 的野外/洞穴/船舱地图会按地图主题补少量可捕捉宠物,城镇、城堡和城墙通道保持无随机宠物 - 触发范围:读取 Spawn 对象的
trigger_radius,没有显式半径时回退到assets/presets/entities/*.tres的_radius,NPC、线索、钥匙等交互半径会尽量按原 Godot 地图配置生效 - 初始状态:读取 TMX Spawn 的
state,支持sit/attack/idle大小写归一,并优先播放原 sprite preset 中的Sit*、Attack*动画 - 脚本交互物:井、线索、钥匙、信件、宝箱等对象即使在 TMX 中标为
Npc,也会优先进入 C++ 的交互/收集系统 - 地图切换:读取 TMX
Warp/Port对象里的dest_map/dest_pos_x/dest_pos_y,并兼容部分旧地图使用的warp+map/dest_x/dest_y;auto_warp=false的传送点会按原 GodotWarpGlobal.gd+Warp.gd流程先显示入口选项,确认后才切图 - 室内门触发:参考原 Tiled importer 生成 Warp Spawn 的方式,旧 Artis 室内门会读取
trigger_x/trigger_y,把 32x32 门点扩成原地图配置的触发区域;Tulimshar 旧Indoor目标不会再按旧坐标落回室外,而是别名到当前项目已有的Tulimshar West ChamberTMX;Artis 的001-2-*室内 TMX 未随当前素材提供时会显示门名和原目标 ID,例如To Library (001-2-4),而不是反复切图失败 - 港口:
Port对象会优先使用sail_pos_x/sail_pos_y作为上船落点,避免进入 Overworld/Ocean 时落到错误位置 - 实体贴图和基础数值:优先从
assets/presets/entities/*.tres解析原项目配置,缺贴图时再读取已有assets/presets/entities/sprites/*.tscn - 实体动画:读取
assets/presets/entities/sprites/*.tscn的hframes/vframes、Idle*、Walk*、Sit*、Attack*帧序列,玩家移动会按原项目的上下左右行走帧切换,野外宠物也会使用原怪物帧表播放 - 交互物显示:宝箱、旧宝箱等会优先使用
assets/presets/entities/*.tres和assets/presets/entities/sprites/*.tscn指向的原实体素材;钥匙等没有直接贴图的对象才回退到原 GUI 图标 - 物品图标与元数据:扫描
assets/icon/items/...、原 GUI 素材和assets/presets/cells/items/**/*.tres,背包会读取原 ItemCell 的图标、slot、usable、stackable、weight、description - NPC 对话:解析
assets/scripts/**/*.gd里的Mes、Chat、Narrate、Choice("...", OnFunc)、简单函数跳转和OnStart里的任务状态分支,选项会按原脚本回调进入对应分支;对话函数中的AddItem/RemoveItem/SetQuest会按原物品名和原ProgressCommons任务状态生效,并用存档记录已执行奖励和原脚本任务状态 - 宠物素材:使用
assets/sprites/monsters/...,缺.tres映射时会按 Spawn 名称和少量别名从现有怪物贴图中兜底查找 - 宠物收集:图鉴目录会扫描所有 TMX Monster Spawn 和运行时兜底野怪;战斗捕捉优先加入 6 只出战队伍,队伍满后继续捕捉会送入宠物仓库,进入战斗会登记图鉴“已见”,捕捉成功会登记“已捕获”,这些记录会写入存档
- UI:优先使用
assets/ui/...里的窗口、按钮、地图、任务、背包等现成贴图;I可打开背包/队伍窗口,道具页按宠物游戏语义分为全部、宠物蛋、捕捉、恢复、技能书,并显示原 ItemCell 图标和数量 - 小地图:参考
sources/gui/Minimap.gd,优先加载assets/minimap/...里的原小地图图片,并使用原orb.png玩家指示点;M世界地图面板会显示选中地图的原缩略图预览,还会把 Tonori.world 室外拼接关系和未放置 TMX 的隔离陈列区画成总览;找不到图片时才回退到 C++ 点阵概览 - 背景音乐:读取 TMX 的
music属性和原assets/db/music.json,运行时播放assets/music/*.ogg中的对应曲目 - 音效:扫描
assets/sounds/**/*.ogg,战斗命中、经验/升级、任务推进、宝箱打开等反馈会播放原项目音效 - 地图氛围:参考原 importer 对
ambient/ambientintensity的处理,读取Cloud、CloudLight、Lighting并在世界渲染后、UI 前绘制保守的光照/云层覆盖 - Ambient 对象:解析 TMX
<polyline>,并把type="Ambient"的 SandstormPolygon 作为半透明沙尘区域绘制到世界层中 - 战斗状态条:参考
assets/presets/gui/HealthBar.tscn,HP 条使用原smallbar.png/smallbarprogress.png九宫格素材,缺素材时才回退纯色条
实现上参考了原 Godot 项目的地图和 UI 结构:
sources/world/World.gd:Warp 后移出旧地图实体,再 Spawn 到新地图。sources/map/Map.gd/MapPool.gd:当前地图加载、邻接地图预加载和超过上限后的旧地图清理。addons/tiled_importer/tiled_map_reader.gd:Warp 对象导入为带触发区、目标地图和auto_warp的 Spawn。assets/maps/tonori/Tonori.world:Tiled 世界文件里的 Tonori 室外地图拼接位置。sources/gui/Minimap.gd:按玩家坐标和地图边界计算小地图位置。sources/system/FileSystem.gd:原项目从assets/minimap/加载对应地图小地图。sources/effects/Lighting.gd与addons/tiled_importer/tiled_map_reader.gd:原项目按地图ambient属性加载氛围节点并应用ambientintensity。assets/presets/gui/HealthBar.tscn:原项目小型生命条使用smallbar.png和smallbarprogress.png。sources/gui/Inventory.gd/CellGrid.gd/CellTile.gd:原项目背包窗口的过滤标签、固定格子、选中框和装备槽布局。assets/scripts/generic/WarpGlobal.gd/Warp.gd/PortGlobal.gd:自动传送直接切图,手动传送转成带目标地图名和取消项的确认交互。assets/scripts/tonori/desertpit/Clue*.gd/ThiefsChest.gd/WaterPond*.gd:蛇坑线索、盗贼宝箱和水池按脚本路径执行轻量奖励逻辑。assets/scripts/tonori/tulimshar/*.gd:NPC 对话按OnStart、OnMainChoice、Choice(..., OnFunc)这类原脚本结构组织。sources/db/Instantiate.gd与ActorCommons.gd:Spawn 的direction/state会成为实体初始朝向和默认状态。assets/presets/entities/sprites/*.tscn:复用原项目Sprite2D的帧表和AnimationPlayer里的 Idle/Walk/Sit/Attack 动画。
构建
依赖:cmake >= 3.20、支持 C++20 的 g++、pkg-config、raylib、pugixml。
Ubuntu/Debian 示例:
sudo apt install cmake g++ pkg-config libraylib-dev libpugixml-dev
进入提交的 Loke 项目根目录后构建:
cmake -S . -B build
cmake --build build
提交版不包含 tests/ 目录;主程序和资源审计都由 mana_pet_world 可执行文件完成。
工程结构
src/app:程序入口。src/assets:资源路径、原 Godot preset、图片、音乐、音效和动画解析。src/battle:战斗资源、布局和战斗场景。src/content:Tonori 道具和内容表。src/core:宠物成长、元素、物种目录和游戏核心数据。src/dialogue:对话脚本、脚本交互和对话效果。src/quest:任务定义和任务状态。src/save:存档读写。src/ui:背包、交互物视觉和 UI 数据模型。src/world:TMX 地图、世界索引、地图池、传送、野怪和地图氛围。assets:运行时所有素材,按maps、tilesets、sprites、ui、icon、music、sounds等类型整理。
运行
从项目根目录运行:
./build/mana_pet_world
也可以直接使用项目根目录的脚本自动构建并运行:
./run.sh
也可以显式传入项目根目录:
./build/mana_pet_world .
如果从别的目录启动,则把 . 换成 Loke 文件夹路径。资源会从项目根目录下的 assets/ 读取,不需要复制到系统目录。
只检查原项目 TMX 世界接入情况,不启动游戏窗口。输出会同时列出手动确认传送和缺失室内传送数量,方便排查门口/室内入口:
./build/mana_pet_world --audit-world .
审计输出也会显示 任务目标地图 和 小地图 覆盖数量,用于确认当前 Tonori 任务目标地图和原 minimap 素材覆盖情况。
操作
WASD:移动E:靠近 NPC 时对话NPC 对话中 1-9:选择原脚本里的对话选项E:靠近宝箱、钥匙、信件、井、线索等交互物时拾取/触发E:站在需要确认的传送点上时打开入口确认框入口确认中 1/Enter:进入目标地图入口确认中 2/Esc:取消传送M:打开/关闭世界地图世界地图中 W/S 或 ↑/↓:选择地图世界地图中 PageUp/PageDown:快速滚动世界地图中 Enter:传送到选中 TMX 地图的默认入口L:打开/关闭任务日志窗口I:打开/关闭背包/宠物队伍窗口背包中 Tab:切换宠物、仓库、道具、制作页面背包道具页 Q/E:切换全部、宠物蛋、捕捉、恢复、技能书分类背包中 WASD 或 ↑/↓/←/→:选择物品格F:朝当前面向方向投掷捕捉符,命中野外宠物后直接尝试捕捉R:前方有野怪时投出首发宠物并进入战斗;没有目标时召唤/收回首发宠物跟随P:打开/关闭宠物图鉴/收藏窗口图鉴中 WASD 或 ↑/↓/←/→:选择已见或已捕获物种F5:保存当前进度到savegame.txtF9:读取savegame.txt- 踩到已有地图 Warp 区域:切换到目标 TMX 地图
- 碰到野外怪物:进入宠物战斗
战斗中 1-4/方向键/鼠标:选择行动;进入道具后先选捕捉符或恢复药,再选择具体道具战斗中 C:直接打开捕捉符选择列表- 战斗中不能使用技能书和宠物蛋,这两类道具只在背包里使用
Esc:关闭对话/世界地图或逃跑
当前实现边界
多地图索引会扫描 assets/maps/**/*.tmx,并用每张地图的 properties.name 建立映射。世界地图面板会列出扫描到的地图、Spawn 数、Warp 数和地图尺寸,原 TMX Warp 可正常踩点传送;Tulimshar 里 8 个旧 Indoor 房门会作为别名处理到当前项目已有的 Tulimshar West Chamber,避免旧目标 (71,72) 把玩家送到 Tulimshar 室外画面。assets/maps/tonori 里没有普通民居室内 TMX,这个房间是 Tonori 现有地图中最接近的小型室内;Artis 的 001-2-* 旧室内 ID 也没有找到对应 TMX,会继续在世界图谱统计里按 legacy_numeric_indoor 分类记录为缺失室内传送;--audit-world 会完整打印这些旧门的对象名和落点坐标,方便后续补人工别名,但不阻塞玩家通过世界地图进入其他 TMX。
C++ 渲染层会按摄像机视口裁剪 TMX 瓦片绘制范围,大地图不会每帧遍历整张地图的所有瓦片。Tonori 室外区域还会读取原 .world 拼接坐标,支持从地图边界自然跨入另一个覆盖该全局位置的 TMX。没有原 .world 坐标的主项目 TMX 不会被丢弃,会在世界总览右侧按隔离网格排布,避免和 Tonori 边界误拼接。
HUD 会显示当前 TMX 地图池大小;任务面板标题为 任务,内容来自原项目 ProgressCommons 任务状态、assets/presets/quests/*.tres 任务文本和 C++ 宠物目标运行时。世界地图会把当前目标地图行标成绿色。右侧小地图会显示当前地图内容:蓝色是传送点,黄色是 NPC,红色是野外宠物,绿色是可交互物。世界地图面板右侧会同时显示选中地图的原小地图,以及完整 TMX 总览:原 Tonori.world 坐标保持原样,其他主项目 TMX 以隔离网格补齐,黄色框是当前地图,蓝色框是选中地图,红点是玩家位置。背包窗口会使用原背包背景、选中框、装备槽和物品 PNG 显示当前收集物,并在右侧显示分类、数量和宠物队伍生命条;队伍标题和 HUD 会同时显示总收藏、图鉴种类和仓库数量。宠物图鉴窗口会复用原 icon/glossary.png、背包背景、选择框和现有怪物 sprite,按目录物种汇总未见、已见和已捕获状态,以及已见次数、捕获次数、出战数量、仓库数量、最高等级和出没地图;未见物种会以更淡颜色显示,已见但未捕获的物种会以淡色显示。
任务系统现在复用原项目 ProgressCommons 任务状态和 assets/presets/quests/*.tres 任务文本。NPC 对话里的 SetQuest 会推进同一套 C++ 任务状态;捕捉、战斗、背包、地图访问和 NPC 对话会更新宠物捕捉目标。新游戏不会直接赠送固定宠物,玩家需要找出生点附近最近的 NPC 选择 Lulea、Piou 或 Fluffy 作为初始宠物。Kael 原脚本里的清怪进度在 C++ 运行时改为捕捉试炼:队伍里有初始宠物之外的野外宠物后,回去汇报才会推进到原 OnTaskComplete 奖励分支。C++ 运行时也会把 TMX 里的部分非 NPC/Monster Spawn 作为交互物处理,例如 Chest、SandstormMineKey、Letter Stash、Well、Snake Pit Clue。这些交互物会使用现有 GUI 物品/钥匙图标显示,并把获得的物品放入背包。Snake Pit 的五个 Clue*.gd 会拼出 Thief's Key,ThiefsChest.gd 会消耗钥匙并给 Scimitar,干净/脏水池会按 WaterPond*.gd 区分处理。
战斗中击败野外宠物会给首发宠物经验,升级后提升最大生命和攻击;捕捉成功会加入出战队伍,队伍满 6 只后会自动送入宠物仓库,也会计入对应物种的清理目标。F5/F9 会保存和恢复当前地图、玩家坐标、出战宠物、仓库宠物、图鉴见过/捕获次数、宠物等级经验、背包物品、已发现地图、已收集交互物、已执行 NPC 对话奖励、原脚本任务状态和任务完成状态。后续可继续把原 Godot 脚本里的任务分支和更细的奖励条件逐步搬到 C++。
当前仍未覆盖 Tiled 的所有高级特性,也没有复刻完整 Godot 联网/账号/服务器系统。