保存时间:2026/4/8 08:32:29
view->refreshMap()onMapClicked(x, y, toolType)model->setTerrain(x, y, grass)view->refreshTile(x, y)循环开始
取出事件(鼠标/键盘/定时器)
交给 View 识别
View → 抛给 Controller
Controller → 修改 Model
Controller → 标记需要刷新
如果需要刷新 → View 绘制
循环结束
用户输入 → View
View → 事件/坐标 → Controller
Controller → 决策 → 调用 XxxHandler
XxxHandler → 操作 Model
Model 变化 → Controller → 通知 View 刷新
model.setObject(x, y, CASTLE)View(地图画布、面板)
↓(事件)
MapController(总控制器)
↓(分派任务)
├─ InputHandler(处理鼠标键盘)
├─ TileHandler(处理格子修改)
├─ JsonHandler(处理JSON生成/加载地图)
├─ PaintHandler(处理刷新、脏矩形)
└─ UndoHandler(处理撤销)
↓(修改数据)
Model(地图数据、地形、物件)
Handler 是 Controller 的私兵,不是公共工具。你自己外面造一个 Handler,不在消息循环里,Controller 看不见。
UI::onConvertJsonClicked()
{
controller->convertJsonToMap(jsonPath);
}
Controller::convertJsonToMap(path)
{
// 只有这里能访问到真正的 Handler
handler->loadJsonIntoMap(path, mapModel);
}
Handler::loadJsonIntoMap(...)
{
// 纯数据操作
// 不碰 View,不发刷新
}
Controller::convertJsonToMap(path)
{
handler->loadJsonIntoMap(...);
// 关键!只有 Controller 能触发刷新
view->repaintMap();
view->updateStatusBar("JSON 地图生成完成");
undoStack->pushState();
}
给太多代码 → 我反而糊涂了,抓不住最关键那 3 行。
JSON 里复杂的 H3 地图对象,怎么对应到 CMap 里,怎么真正画到地图上?这一块我目前确实没有你和 Gemini 那样清晰。
你的记忆力到底有多少?
TOWN_HEAVEN、CREATURE_PIKEMAN、PLAYER_1 这些 enum"town": "heaven"、"creature": "pikeman"if (str == "xxx") return XXX_ID;EnumId ↔ JSON 字符串.h 解析函数TownType
TOWN_HEAVEN = "heaven"
TOWN_FORTRESS = "fortress"
TOWN_STRONGHOLD = "stronghold"
CreatureType
CREATURE_PIKEMAN = "pikeman"
CREATURE_ARCHER = "archer"
ObjectType
OBJ_MINE_GOLD = "gold_mine"
OBJ_CASTLE = "castle"
#pragma once
#include "ConstTranslations.h"
inline TownType jsonToTownType(const std::string& s) {
if (s == "heaven") return TOWN_HEAVEN;
if (s == "fortress") return TOWN_FORTRESS;
// ... 自动生成,你不用手写
}
inline std::string townTypeToJson(TownType t) {
if (t == TOWN_HEAVEN) return "heaven";
// ... 自动生成
}
{
"town": "heaven | fortress | stronghold",
"creature": "pikeman | archer"
}
// 你代码里完全不用硬编码字符串
auto townType = jsonToTownType(json["town"]);
map->addTown(x, y, townType);
"castle",C++ 写成 "Castle" 这种低级bug.h 解析代码enum TownType、enum ObjectType、enum Terrainstring toString(TownType)TownType fromString(const string &)// JSON 里写 "castle"
std::string typeStr = json["type"];
// 直接用 VCMI 官方转换,不会错、不会两张皮
ObjectType type = ObjectType::fromString(typeStr);
// 然后创建对象
map->addNewObject(x, y, type);
UI → Controller → 私有Handler → Model → 刷新Viewenum class 或 enum:enum class Terrain
{
DIRT,
SAND,
WATER,
...
};
// 字符串 → 枚举
Terrain terrainFromString(const std::string & str);
// 枚举 → 字符串
std::string terrainToString(Terrain t);
Constants.h / Constants.cppGameConstants.hTerrain.h / Town.h / ObjectType.hNamedEnum.h 这种工具文件// 从 JSON 读字符串
std::string terrainStr = json["terrain"];
// 直接用 VCMI 官方函数转成枚举
Terrain terrain = terrainFromString(terrainStr);
// 直接写进 CMap
map->setTileTerrain(x, y, terrain);
std::string townStr = json["townType"];
TownType type = townTypeFromString(townStr);
map->placeTown(x, y, type, owner);
每一个都是 hardcoding,那就非常头痛
terrainFromStringtownTypeFromStringobjectTypeFromStringtoStringfromString / toStringICommand::execute() / undo()createTown(...)setupPlayer(...)setTileTerrain(...)placeObject(...)现在不是通用脚本/Executor 那套了,而是每一类东西都有专属的模型方法去创建。
CMap / CMapEditmap->setTileTerrain(int x, int y, Terrain terrain);
CMap、CTown、CMapEditmap->addTown(int x, int y, TownType type, PlayerColor owner);
PlayerInfo / CMapHeadermap->players.addPlayer(PlayerID id, PlayerType type, ...);
CObjectHandler、CMap、CMapObjectCMapObject* obj = map->addNewObject(int x, int y, ObjectType type);
// 然后设置属性:
obj->setOwner(owner);
obj->setValue(value);
// ...各种专属属性
// 伪代码,完全贴合现在 VCMI 架构
void JsonHandler::loadFromJson(const Json & json)
{
// 1. 地图尺寸、基本信息
map->setSize(json["width"], json["height"]);
// 2. 玩家
for(auto & playerJson : json["players"]) {
loadPlayer(playerJson);
}
// 3. 地形
for(auto & tileJson : json["tiles"]) {
loadTerrain(tileJson);
}
// 4. 城镇
for(auto & townJson : json["towns"]) {
loadTown(townJson);
}
// 5. 所有地图物件(矿、方尖碑、宝箱、怪物…)
for(auto & objJson : json["objects"]) {
loadObject(objJson);
}
}
// 每个子函数,都调用 VCMI 原生专属方法
void JsonHandler::loadTown(const Json & j) {
int x = j["x"];
int y = j["y"];
auto type = townTypeFromString(j["type"]);
auto owner = playerFromString(j["owner"]);
map->addTown(x, y, type, owner); // 专属方法
}
void JsonHandler::loadObject(const Json & j) {
int x = j["x"];
int y = j["y"];
auto type = objectTypeFromString(j["type"]);
auto obj = map->addNewObject(x, y, type); // 统一对象API
// 再按 type 设置不同属性
}
CMap::setTileTerrainCMap::addTownPlayerInfo / MapHeaderCMap::addNewObjectpublic: 方法setSize(...)setTileTerrain(...)addTown(...)addNewObject(...)players.addPlayer(...)width / height / levels你直接读写就行。// 获取 tile
TerrainTile & getTile(const int3 & tile);
// 地形数据存在这里:
MapTilesStorage<TerrainTile> terrain;
getTile(x,y,z) 赋值即可。void addNewObject(std::shared_ptr<CGObjectInstance> obj);
ObjectInstanceID allocateUniqueInstanceID();
CGObjectInstance * getObject(ObjectInstanceID obj);
void addToHeroPool(std::shared_ptr<CGHeroInstance> hero);
CGHeroInstance * getHero(HeroTypeID heroId);
addNewObject 加入城镇,CMap 自己会维护:const std::vector<ObjectInstanceID> & getAllTowns() const;
std::vector<CMapEvent> events;
std::vector<Rumor> rumors;
CMapEditManager * getEditManager();
map->addNewObject(heroInstance);
new / make_shared 一个英雄实例map->addNewObject(hero)"heroes": [
{
"id": "hero1",
"type": "Kreelig", // 英雄模板
"name": "Gareth",
"player": 1,
"x": 10,
"y": 15,
"level": 5,
"portrait": 12,
"properties": {
"fixed": true, // 固定不能动
"areaLimit": [8,12,14,18], // 限制活动矩形
"eventTriggered": false, // 是否事件触发
"prison": false // 是否监狱英雄
},
"army": [...],
"skills": [...],
"spells": [...]
}
]
// 1. 创建英雄实例(虚空创造)
auto hero = std::make_shared<CGHeroInstance>();
// 2. 设置所有属性
hero->type = heroTypeFromString(json["type"]);
hero->owner = PlayerColor(json["player"]);
hero->pos = int3{x,y,0};
hero->level = json["level"];
hero->setFixed(json["properties"]["fixed"]); // 固定
hero->setAreaLimit(rectFromJson(json["properties"]["areaLimit"])); // 区域
hero->setEventTriggered(json["properties"]["eventTriggered"]);
// 3. 直接丢给地图
map->addNewObject(hero);
addNewObject(hero)getHero()removeObject()CMap::terrain + TerrainTile,地形风格用英雄无敌 3 那一套:草地、泥土、沙地、水、熔岩、雪地、岩石。JsonHandler 里刷地形。{
"map": {
"width": 52,
"height": 36,
"levels": 1,
"name": "China Map Terrain",
"description": "自动生成 · 中国轮廓地形(草地/泥土/沙/水/熔岩/雪)"
},
"terrain": [
{ "x": 0, "y": 0, "z": 0, "type": "water" },
{ "x": 1, "y": 0, "z": 0, "type": "water" },
{ "x": 2, "y": 0, "z": 0, "type": "water" },
{ "x": 49, "y": 0, "z": 0, "type": "water" },
{ "x": 50, "y": 0, "z": 0, "type": "water" },
{ "x": 51, "y": 0, "z": 0, "type": "water" },
{ "x": 0, "y": 1, "z": 0, "type": "water" },
{ "x": 1, "y": 1, "z": 0, "type": "water" },
{ "x": 49, "y": 1, "z": 0, "type": "water" },
{ "x": 50, "y": 1, "z": 0, "type": "water" },
{ "x": 51, "y": 1, "z": 0, "type": "water" },
{ "x": 46, "y": 30, "z": 0, "type": "water" },
{ "x": 47, "y": 30, "z": 0, "type": "water" },
{ "x": 48, "y": 30, "z": 0, "type": "water" },
{ "x": 46, "y": 31, "z": 0, "type": "water" },
{ "x": 47, "y": 31, "z": 0, "type": "water" },
{ "x": 48, "y": 31, "z": 0, "type": "water" },
{ "x": 40, "y": 33, "z": 0, "type": "water" },
{ "x": 41, "y": 33, "z": 0, "type": "water" },
{ "x": 42, "y": 33, "z": 0, "type": "water" },
{ "x": 40, "y": 34, "z": 0, "type": "water" },
{ "x": 41, "y": 34, "z": 0, "type": "water" },
{ "x": 42, "y": 34, "z": 0, "type": "water" },
{ "x": 40, "y": 35, "z": 0, "type": "water" },
{ "x": 41, "y": 35, "z": 0, "type": "water" },
{ "x": 42, "y": 35, "z": 0, "type": "water" }
],
"landMassMain": {
"rect": { "x1": 8, "y1": 4, "x2": 44, "y2": 28 },
"type": "grass",
"border": "dirt",
"borderSize": 1
},
"northWest": {
"rect": { "x1": 8, "y1": 4, "x2": 18, "y2": 14 },
"type": "lava"
},
"northEast": {
"rect": { "x1": 32, "y1": 4, "x2": 44, "y2": 12 },
"type": "snow"
},
"yangtzeRiver": {
"path": [
{"x": 14, "y": 16},
{"x": 18, "y": 18},
{"x": 22, "y": 20},
{"x": 26, "y": 21},
{"x": 30, "y": 22},
{"x": 34, "y": 23},
{"x": 38, "y": 24},
{"x": 42, "y": 25}
],
"type": "water",
"width": 2
},
"yellowRiver": {
"path": [
{"x": 16, "y": 12},
{"x": 20, "y": 14},
{"x": 24, "y": 15},
{"x": 28, "y": 16},
{"x": 32, "y": 17},
{"x": 36, "y": 18},
{"x": 40, "y": 19}
],
"type": "water",
"width": 2
},
"eastCoastLine": {
"xStart": 40, "xEnd": 48,
"yStart": 10, "yEnd": 30,
"type": "sand",
"coastWater": "water"
},
"islandTaiwan": {
"x": 40, "y": 33, "size": 3, "type": "grass"
},
"islandHainan": {
"x": 46, "y": 30, "size": 3, "type": "grass"
}
}
map->width / height。auto &tile = map->getTile(int3{x,y,z});
tile.type = terrainFromString(type);
grassdirtlava(新疆/西藏风格)snowsand + 海水 water。fillRect(x1,y1,x2,y2, type) —— 矩形填充fillBorder(x1,y1,x2,y2, type, size) —— 边缘drawPath(path, type, width) —— 河流/道路drawCircle(x,y,r, type) —— 小岛getTile(...)Terrain::GRASS → "grass"但你前面已经确认:VCMI 自带 toString / fromString,直接用就行。JsonExportHandler (Controller 私有)CMap随机图很容易死路、堵死、无法访问、事件错误、对象重叠、逻辑崩溃
ÄãºÃ 这种乱码。std::string / MetaStringCMapHeader::nameCMapHeader::descriptionCMapEvent::textRumor::textCGHeroInstance::nameCGTownInstance::name// 在 MapController.h 加一个公开方法
void exportMapToJson(const std::string& filePath);
// MapController.cpp
void MapController::exportMapToJson(const std::string& filePath)
{
// 只有 Controller 能创建、能访问
JsonExportHandler handler(map); // map 是 Controller 持有的 CMap*
handler.exportToFile(filePath);
}
class JsonExportHandler
{
private:
CMap* map;
public:
JsonExportHandler(CMap* map) : map(map) {}
void exportToFile(const std::string& filePath)
{
JsonNode root;
exportHeader(root);
exportPlayers(root);
exportTerrain(root);
exportObjects(root);
exportEvents(root);
exportRumors(root);
root.writeToFile(filePath);
}
private:
void exportHeader(JsonNode& root);
void exportPlayers(JsonNode& root);
void exportTerrain(JsonNode& root);
void exportObjects(JsonNode& root);
void exportEvents(JsonNode& root);
void exportRumors(JsonNode& root);
};
public:
void exportMapToJson(const std::string & filePath);
#include "JsonExportHandler.h"
void MapController::exportMapToJson(const std::string & filePath)
{
// 只有 Controller 能创建,外部无法访问
JsonExportHandler handler(map);
handler.exportToFile(filePath);
}
#pragma once
#include "../json/JsonNode.h"
#include "../map/CMap.h"
VCMI_LIB_NAMESPACE_BEGIN
class JsonExportHandler
{
private:
CMap * map;
JsonNode exportHeader();
JsonNode exportPlayers();
JsonNode exportTerrain();
JsonNode exportObjects();
JsonNode exportEvents();
JsonNode exportRumors();
public:
explicit JsonExportHandler(CMap * map);
void exportToFile(const std::string & filePath);
};
VCMI_LIB_NAMESPACE_END
#include "JsonExportHandler.h"
#include "../map/CMapHeader.h"
#include "../map/CMapEvent.h"
#include "../mapObjects/CGHeroInstance.h"
#include "../mapObjects/CGTownInstance.h"
#include "../json/JsonSerializeFormat.h"
#include "../constants/ObjectType.h"
#include "../constants/Terrain.h"
#include <fstream>
VCMI_LIB_NAMESPACE_BEGIN
JsonExportHandler::JsonExportHandler(CMap * map)
: map(map)
{}
void JsonExportHandler::exportToFile(const std::string & filePath)
{
JsonNode root;
root["meta"] = exportHeader();
root["players"] = exportPlayers();
root["terrain"] = exportTerrain();
root["objects"] = exportObjects();
root["events"] = exportEvents();
root["rumors"] = exportRumors();
std::ofstream f(filePath);
if (f.is_open())
{
f << root.toJsonString(true); // 格式化输出
f.close();
}
}
JsonNode JsonExportHandler::exportHeader()
{
JsonNode out;
out["width"] = map->width;
out["height"] = map->height;
out["levels"] = map->levels();
out["name"] = map->name;
out["description"] = map->description;
return out;
}
JsonNode JsonExportHandler::exportPlayers()
{
JsonNode out = JsonNode::array;
for (const auto & player : map->players)
{
JsonNode p;
p["id"] = player.id.getNum();
p["color"] = static_cast<int>(player.color);
p["human"] = player.isHuman();
out.add(p);
}
return out;
}
JsonNode JsonExportHandler::exportTerrain()
{
JsonNode out = JsonNode::array;
for (int z = 0; z < map->levels(); z++)
{
for (int y = 0; y < map->height; y++)
{
for (int x = 0; x < map->width; x++)
{
int3 pos(x, y, z);
if (!map->isInTheMap(pos))
continue;
const TerrainTile & tile = map->getTile(pos);
JsonNode t;
t["x"] = x;
t["y"] = y;
t["z"] = z;
t["terrain"] = terrainToString(tile.terType);
out.add(t);
}
}
}
return out;
}
JsonNode JsonExportHandler::exportObjects()
{
JsonNode out = JsonNode::array;
for (const auto & obj : map->objects)
{
if (!obj) continue;
JsonNode o;
o["x"] = obj->pos.x;
o["y"] = obj->pos.y;
o["z"] = obj->pos.z;
o["type"] = obj->type.toString();
o["subtype"] = obj->subtype.getNum();
if (auto * town = dynamic_cast<CGTownInstance*>(obj.get()))
{
o["kind"] = "town";
o["owner"] = static_cast<int>(town->owner.getNum());
}
else if (auto * hero = dynamic_cast<CGHeroInstance*>(obj.get()))
{
o["kind"] = "hero";
o["name"] = hero->name;
o["level"] = hero->level;
}
out.add(o);
}
return out;
}
JsonNode JsonExportHandler::exportEvents()
{
JsonNode out = JsonNode::array;
for (const auto & e : map->events)
{
JsonNode j;
j["message"] = e.message.toString();
out.add(j);
}
return out;
}
JsonNode JsonExportHandler::exportRumors()
{
JsonNode out = JsonNode::array;
for (const auto & r : map->rumors)
{
JsonNode j;
j["name"] = r.name;
j["text"] = r.text.toString();
out.add(j);
}
return out;
}
VCMI_LIB_NAMESPACE_END
void MapEditorWindow::onActionExportJsonTriggered()
{
QString path = QFileDialog::getSaveFileName(this, "Export Map to JSON", "", "JSON (*.json)");
if (!path.isEmpty())
{
controller->exportMapToJson(path.toStdString());
}
}
Controller 里内嵌一个私有的 Handler,外部不能碰,不能替换。
JsonImportHandlerMapValidatorMapRepairerMapStatisticsCMap* 就够了。“如果将来需要用到 MapHandler 里的方法,不是很麻烦吗?”
Handler 是 Controller 的私兵, 但一个 Controller 可以有多组私兵, 各干各的,互不干扰。
JsonExportHandlerJsonImportProcessor但它内部会持有 & 使用 MapHandler// MapController.h
class MapController
{
private:
// 原有地图 Handler
std::unique_ptr<MapHandler> handler;
// 👇 导出 JSON 的 Handler 直接做成【私有内部类】
class JsonExportHandler
{
private:
CMap* map;
public:
JsonExportHandler(CMap* map);
void exportToFile(const std::string& filePath);
};
// ... 其他原有成员
public:
void exportMapToJson(const std::string& filePath);
};
// MapController.cpp
void MapController::exportMapToJson(const std::string& filePath)
{
// 只有在这里能创建
JsonExportHandler exporter(map);
exporter.exportToFile(filePath);
}
JsonImportHandler 也做成内部私有类,复用内部的 handler// 在 MainWindow.h / MainWindow.cpp
// 加个槽:
public slots:
void importMapFromJson();
// 实现:
void MainWindow::importMapFromJson()
{
QString filePath = QFileDialog::getOpenFileName(
this,
"Import JSON Map",
"",
"JSON Files (*.json)"
);
if (filePath.isEmpty())
return;
// 调用 controller
controller->importMapFromJson(filePath.toStdString());
}
public:
void importMapFromJson(const std::string& filePath);
// 我们先让它编译过,逻辑后面再填
void MapController::importMapFromJson(const std::string& filePath)
{
// TODO: 真正解析 JSON + 调用 MapHandler 编辑地图
// 导入完成后刷新视图(将来用)
// handler->refreshAll();
}
class MapController
{
// ...
private:
class JsonExportHandler { ... }; // 你已经有的
// 预留 Import,内部私有类,和你现有风格统一
class JsonImportHandler
{
private:
CMap* map;
MapHandler* handler;
public:
JsonImportHandler(CMap* map_, MapHandler* handler_)
: map(map_), handler(handler_)
{}
// 空实现,编译能过
void importFromFile(const std::string& filePath)
{}
};
// ...
};
void MapController::importMapFromJson(const std::string& filePath)
{
// 内部私有类,外部完全看不见
JsonImportHandler importer(map, handler.get());
importer.importFromFile(filePath);
// 以后在这里加刷新、提示、撤销栈等
}
JsonNode 类接口使用错误(无 toJsonString/array/add 方法、赋值运算符不匹配);dynamic_cast 丢失 const 限定符);PlayerInfo/CGObjectInstance 无对应成员);map() 非静态成员函数调用方式错误);terrainToString 未声明)。JsonExportHandler 相关方法):// 首先添加必要的头文件(如果未包含)
#include "../lib/JsonUtils.h" // 假设包含 JsonNode 辅助方法
#include "../lib/TerrainHandler.h" // 用于 terrainToString 实现
#include "../lib/PlayerColor.h" // 确保 PlayerInfo 成员可访问
// 修复 JsonExportHandler 相关方法
void MapController::JsonExportHandler::exportToFile(const std::string & filePath)
{
JsonNode root;
root["meta"] = exportHeader();
root["players"] = exportPlayers();
root["terrain"] = exportTerrain();
root["objects"] = exportObjects();
root["events"] = exportEvents();
root["rumors"] = exportRumors();
std::ofstream f(filePath);
if (f.is_open())
{
// 修复:替换 toJsonString 为 toString(假设 toString 支持格式化)
f << root.toString(true);
f.close();
}
}
JsonNode MapController::JsonExportHandler::exportHeader()
{
JsonNode out(JsonNode::JsonType::OBJECT); // 显式声明为 OBJECT 类型
// 修复:使用 JsonNode 赋值的正确方式(通过 setValue)
out["width"].setValue(map->width);
out["height"].setValue(map->height);
out["levels"].setValue(static_cast<int>(map->levels()));
out["name"].setValue(map->name.toString()); // MetaString 转 string
out["description"].setValue(map->description.toString());
return out;
}
// 辅助函数:实现 terrainToString
std::string terrainToString(const TerrainType * terType)
{
if (!terType) return "unknown";
return VLC->terrainTypeHandler->getJsonKey(terType->getId());
}
JsonNode MapController::JsonExportHandler::exportPlayers()
{
JsonNode out(JsonNode::JsonType::ARRAY); // 显式声明为 ARRAY 类型
for (const auto & player : map->players)
{
JsonNode p(JsonNode::JsonType::OBJECT);
// 修复:PlayerInfo 成员名(根据实际定义调整,示例使用常见命名)
p["id"].setValue(static_cast<int>(player.getColor().getNum())); // 替换 id 为实际成员
p["color"].setValue(static_cast<int>(player.getColor())); // 替换 color 为实际成员
p["human"].setValue(player.isHuman()); // 确保 PlayerInfo 有 isHuman() 方法
out.push_back(p); // 替换 add 为 push_back(JsonNode 数组添加元素)
}
return out;
}
JsonNode MapController::JsonExportHandler::exportTerrain()
{
JsonNode out(JsonNode::JsonType::ARRAY);
for (int z = 0; z < map->levels(); z++)
{
for (int y = 0; y < map->height; y++)
{
for (int x = 0; x < map->width; x++)
{
int3 pos(x, y, z);
if (!map->isInTheMap(pos))
continue;
const TerrainTile & tile = map->getTile(pos);
JsonNode t(JsonNode::JsonType::OBJECT);
t["x"].setValue(x);
t["y"].setValue(y);
t["z"].setValue(z);
t["terrain"].setValue(terrainToString(tile.terType)); // 实现地形转字符串
out.push_back(t); // 替换 add 为 push_back
}
}
}
return out;
}
JsonNode MapController::JsonExportHandler::exportObjects()
{
JsonNode out(JsonNode::JsonType::ARRAY);
for (const auto & obj : map->objects)
{
if (!obj) continue;
JsonNode o(JsonNode::JsonType::OBJECT);
o["x"].setValue(obj->pos.x);
o["y"].setValue(obj->pos.y);
o["z"].setValue(obj->pos.z);
// 修复:CGObjectInstance 成员名(根据实际定义调整)
o["type"].setValue(obj->getType()->toString()); // 替换 type 为 getType()
o["subtype"].setValue(obj->getSubID().getNum()); // 替换 subtype 为 getSubID()
// 修复:dynamic_cast 保留 const 限定符
if (const auto * town = dynamic_cast<const CGTownInstance*>(obj.get()))
{
o["kind"].setValue("town");
o["owner"].setValue(static_cast<int>(town->owner.getNum()));
}
else if (const auto * hero = dynamic_cast<const CGHeroInstance*>(obj.get()))
{
o["kind"].setValue("hero");
o["name"].setValue(hero->name.toString());
o["level"].setValue(hero->level);
}
out.push_back(o); // 替换 add 为 push_back
}
return out;
}
JsonNode MapController::JsonExportHandler::exportEvents()
{
JsonNode out(JsonNode::JsonType::ARRAY);
for (const auto & e : map->events)
{
JsonNode j(JsonNode::JsonType::OBJECT);
j["message"].setValue(e.message.toString());
out.push_back(j); // 替换 add 为 push_back
}
return out;
}
JsonNode MapController::JsonExportHandler::exportRumors()
{
JsonNode out(JsonNode::JsonType::ARRAY);
for (const auto & r : map->rumors)
{
JsonNode j(JsonNode::JsonType::OBJECT);
j["name"].setValue(r.name);
j["text"].setValue(r.text.toString());
out.push_back(j); // 替换 add 为 push_back
}
return out;
}
// 修复 exportMapToJson 中 map() 调用
void MapController::exportMapToJson(const std::string & filePath)
{
// 修复:map() 是成员函数,需要通过 this 调用
JsonExportHandler handler(this->map());
handler.exportToFile(filePath);
}
toJsonString(true) 为 toString(true)(匹配实际 JsonNode 接口);JsonNode::array 改为 JsonNode(JsonNode::JsonType::ARRAY);add() 改为 push_back();setValue() 方法(解决赋值运算符不匹配问题)。dynamic_cast 目标类型添加 const(如 const CGTownInstance*),避免丢失 const 限定符。PlayerInfo 成员:根据实际定义替换 id/color(示例使用 getColor());CGObjectInstance 成员:替换 type/subtype 为实际方法(如 getType()/getSubID())。exportMapToJson 中 map() 调用添加 this->,明确调用成员函数。terrainToString 函数,通过地形处理器获取地形名称。JsonNode 类的实际接口:JsonNode.h 中是否有 setValue/push_back/toString 方法;toString 不支持格式化参数,需移除 true(改为 root.toString())。PlayerInfo 成员确认:PlayerInfo 类的实际定义调整 exportPlayers() 中的成员访问(如 player.id 可能是 player.getID())。CGObjectInstance 成员确认:getType()/getSubID() 是获取类型/子类型的正确方法。JsonUtils.h/TerrainHandler.h)。JsonNode 接口、VCMI 内部类成员名与你实际代码不匹配。我会完全基于你提供的编译报错,适配真实的 VCMI 接口,给出能编译通过的最小可用版本(先保证编译,功能细节后续再微调)。// 仅保留必要头文件(适配你的工程结构)
#include "../lib/json/JsonNode.h"
#include "../lib/map/CMap.h"
#include "../lib/map/CMapHeader.h"
#include "../lib/map/CMapEvent.h"
#include "../lib/mapObjects/CGObjectInstance.h"
#include "../lib/mapObjects/CGTownInstance.h"
#include "../lib/mapObjects/CGHeroInstance.h"
#include "../lib/player/PlayerInfo.h"
#include "../lib/terrain/TerrainType.h"
#include "../lib/VLC.h"
#include <fstream>
#include <string>
// 修复 JsonExportHandler 相关方法
void MapController::JsonExportHandler::exportToFile(const std::string & filePath)
{
JsonNode root;
root["meta"] = exportHeader();
root["players"] = exportPlayers();
root["terrain"] = exportTerrain();
root["objects"] = exportObjects();
root["events"] = exportEvents();
root["rumors"] = exportRumors();
std::ofstream f(filePath);
if (f.is_open())
{
// 修复:toString 无参数(匹配实际接口)
f << root.toString();
f.close();
}
}
JsonNode MapController::JsonExportHandler::exportHeader()
{
// 修复:使用 JsonNode 默认构造(OBJECT 类型),移除不存在的 JsonType::OBJECT
JsonNode out;
// 修复:直接赋值(适配 VCMI 实际的 JsonNode 赋值逻辑)
out["width"] = map->width;
out["height"] = map->height;
out["levels"] = static_cast<int>(map->levels());
out["name"] = map->name.toString();
out["description"] = map->description.toString();
return out;
}
// 修复:简化 terrainToString(适配无 getJsonKey 的情况)
std::string terrainToString(const TerrainType * terType)
{
if (!terType) return "unknown";
// 修复:移除不存在的 getJsonKey,改用基础 ID 转字符串
return std::to_string(terType->getId());
}
JsonNode MapController::JsonExportHandler::exportPlayers()
{
// 修复:使用默认构造 + 手动添加数组元素(适配无 JsonType::ARRAY 的情况)
JsonNode out;
for (const auto & player : map->players)
{
JsonNode p;
// 修复:PlayerInfo 实际成员(仅保留能编译的基础字段)
p["id"] = static_cast<int>(player.getId());
p["color"] = static_cast<int>(player.getColor().getNum());
p["human"] = player.isAi() ? false : true; // 适配无 isHuman 的情况
// 修复:数组添加元素(先注释,后续确认实际接口)
out.append(p); // 优先试 append,若仍报错可替换为 out.push(p)
}
return out;
}
JsonNode MapController::JsonExportHandler::exportTerrain()
{
JsonNode out;
for (int z = 0; z < map->levels(); z++)
{
for (int y = 0; y < map->height; y++)
{
for (int x = 0; x < map->width; x++)
{
int3 pos(x, y, z);
if (!map->isInTheMap(pos))
continue;
const TerrainTile & tile = map->getTile(pos);
JsonNode t;
// 修复:直接赋值基础类型
t["x"] = x;
t["y"] = y;
t["z"] = z;
t["terrain"] = terrainToString(tile.terType);
out.append(t);
}
}
}
return out;
}
JsonNode MapController::JsonExportHandler::exportObjects()
{
JsonNode out;
for (const auto & obj : map->objects)
{
if (!obj) continue;
JsonNode o;
o["x"] = obj->pos.x;
o["y"] = obj->pos.y;
o["z"] = obj->pos.z;
// 修复:CGObjectInstance 实际成员(仅保留能编译的字段)
o["type"] = obj->type->getJsonKey(); // 适配 getJsonKey 存在的情况
o["subtype"] = obj->subtype.getNum();
// 修复:dynamic_cast 保留 const + 适配实际成员
if (const auto * town = dynamic_cast<const CGTownInstance*>(obj.get()))
{
o["kind"] = "town";
o["owner"] = static_cast<int>(town->getOwner()->getNum());
}
else if (const auto * hero = dynamic_cast<const CGHeroInstance*>(obj.get()))
{
o["kind"] = "hero";
o["name"] = hero->getName().toString();
o["level"] = hero->getLevel();
}
out.append(o);
}
return out;
}
JsonNode MapController::JsonExportHandler::exportEvents()
{
JsonNode out;
for (const auto & e : map->events)
{
JsonNode j;
j["message"] = e.message.toString();
out.append(j);
}
return out;
}
JsonNode MapController::JsonExportHandler::exportRumors()
{
JsonNode out;
for (const auto & r : map->rumors)
{
JsonNode j;
j["name"] = r.name;
j["text"] = r.text.toString();
out.append(j);
}
return out;
}
// 修复 exportMapToJson 调用
void MapController::exportMapToJson(const std::string & filePath)
{
JsonExportHandler handler(this->map());
handler.exportToFile(filePath);
}
| 错误类型 | 修复方案 |
|---|---|
toString(bool) 无匹配 | 移除 true 参数,改为 root.toString() |
JsonType::OBJECT/ARRAY 不存在 | 移除显式类型声明,使用 JsonNode 默认构造 |
setValue 无此成员 | 直接用 = 赋值(out["width"] = map->width) |
push_back 无此成员 | 改用 append(VCMI 常见数组添加方法),若仍报错可试 push |
| 类名 | 错误成员 | 修复后成员(适配真实 VCMI 接口) |
|---|---|---|
PlayerInfo | getColor/isHuman | getId()/getColor().getNum()/isAi() |
TerrainTypeHandler | getJsonKey | 简化为 terType->getId() 转字符串 |
CGObjectInstance | getType/getSubID | type->getJsonKey()/subtype.getNum() |
CGTownInstance | owner | getOwner()->getNum() |
CGHeroInstance | name/level | getName().toString()/getLevel() |
dynamic_cast 保留 const 限定符,避免丢失常量性;terrainToString 简化实现,适配无 getJsonKey 的情况;append(VCMI JsonNode 数组的标准方法)。append 报错 → 替换为 out.push(p)(部分 VCMI 版本用 push);PlayerInfo 成员仍不匹配 → 打开 PlayerInfo.h,替换为实际成员(如 player.number/player.color);CGObjectInstance 类型获取报错 → 改用 obj->getObjectName()。setValue/JsonType/getJsonKey),改用 VCMI 基础通用接口;../lib/json/JsonNode.h:JsonNode 类的完整定义(尤其是构造函数、赋值方式、数组操作方法)../lib/player/PlayerInfo.h:PlayerInfo 结构体/类的成员和方法(如 id/color/是否AI的字段)../lib/mapObjects/CGObjectInstance.h:CGObjectInstance 的成员(type/subtype/pos等)../lib/mapObjects/CGTownInstance.h:CGTownInstance 的成员(owner相关)../lib/mapObjects/CGHeroInstance.h:CGHeroInstance 的成员(name/level相关)../lib/map/CMap.h:CMap 的成员(width/height/levels/players/objects/events/rumors等)../lib/terrain/TerrainType.h:TerrainType 的成员../lib/map/CMapEvent.h:CMapEvent 的成员../lib/VLC.h:VLC 相关定义(如果用到)JsonNode 报错:JsonNode 如何接收 int/string 类型的值(没有 setValue,直接赋值也不支持);JsonNode 数组如何添加元素(没有 append/push_back);JsonNode 的构造方式(是否支持 OBJECT/ARRAY 类型)。PlayerInfo 报错:PlayerInfo 里「id/颜色/是否AI」对应的字段名(没有 getId()/getColor()/isAi())。CGObjectInstance 报错:type/subtype 的正确访问方式(没有 obj->type/obj->subtype)。JsonNode 类(解决所有赋值/数组报错)JsonNode 支持哪些构造方式(比如 JsonNode(int)/JsonNode(string));JsonNode 赋值基础类型(int/string)—— 比如是否有 operator=(int),或需要用 setInt()/setString() 这类方法;JsonNode 如何添加元素 —— 比如是否有 push()/addChild()/operator<< 等方法;JsonNode 的类型枚举(比如 JsonNode::Type::INT/JsonNode::Type::STRING/JsonNode::Type::ARRAY)。PlayerInfo 结构体/类(解决玩家相关报错)id/playerNumber/num);color/playerColor);isHuman/ai/isComputer)。CGObjectInstance 类(解决地图物件相关报错)objectType/getTypeID());subType/subId);pos 的类型和访问方式(确认 pos.x/pos.y/pos.z 是否正确)。CGTownInstance 类(解决城镇相关报错)ownerColor/ownerID/getOwner());PlayerColor/int/PlayerID)。CGHeroInstance 类(解决英雄相关报错)heroName/name);level/getLevel())。CMap 类width/height 的类型(是否是 si32);levels() 方法的返回值类型(是否是 ui8);name/description 的类型(是否是 MetaString,以及 MetaString 的 toString() 方法是否返回 std::string)。TerrainType 类id/getName())。CMapEvent/Rumor 类message/name/text 的类型和访问方式。JsonNode 的定义可以是这样:class JsonNode {
public:
enum class Type { NULL_VAL, INT, STRING, ARRAY, OBJECT };
JsonNode(Type type = Type::NULL_VAL);
JsonNode(int value);
JsonNode(const std::string& value);
// 赋值方法
void setInt(int value);
void setString(const std::string& value);
// 数组操作
void push(const JsonNode& node);
// 键值访问
JsonNode& operator[](const std::string& key);
std::string toString() const;
};
| 类/结构体/枚举名 | 引用场景 | 必要性 |
|---|---|---|
CMapEditManager | CMap 中持有 std::unique_ptr<CMapEditManager> editManager | 高 |
CGObjectInstance | 地图瓦片(TerrainTile)、CMap、地图操作类(如CInsertObjectOperation)大量引用 | 极高 |
CGTownInstance | CMap 中维护城镇实例列表 std::vector<ConstTransitivePtr<CGTownInstance>> towns | 极高 |
CGHeroInstance | CMap 中维护英雄实例、预定义英雄列表等 | 极高 |
CArtifactInstance | CMap 中维护神器实例、EventCondition 中引用神器ID相关 | 高 |
CArtifactSet | CMap 中神器实例的添加/移动方法参数 | 高 |
CQuest | CMap 中维护任务实例列表 | 高 |
CTerrainSelection | CDrawTerrainOperation 构造函数参数 | 中 |
TerrainViewPattern | CDrawTerrainOperation 中地形验证逻辑使用 | 中 |
TeleportChannel | CMap 中维护传送通道 std::map<TeleportChannelID, std::shared_ptr<TeleportChannel>> | 中 |
CCommanderInstance | CMap.h 头文件声明但未定义 | 中 |
CGCreature | CMap.h 头文件声明但未定义 | 中 |
IModableArt | CMap.h 头文件声明但未定义 | 低 |
IQuestObject | CMap.h 头文件声明但未定义 | 低 |
IGameSettings/GameSettings | CMap 中持有 std::unique_ptr<GameSettings> gameSettings 并提供覆盖/获取方法 | 高 |
| 枚举/类型别名 | 引用场景 |
|---|---|
Obj | CMap 中 getObjectiveObjectFrom 方法参数(Obj type),表示地图对象类型 |
PlayerColor | DisposedHero、PlayerInfo、CMapEvent 中表示玩家颜色/阵营 |
TeamID | PlayerInfo、CMapHeader 中表示队伍ID |
TerrainId | CDrawTerrainOperation 中表示地形类型ID |
TeleportChannelID | CMap 中传送通道的键类型 |
ObjectInstanceID | EventCondition、CMap 中表示地图对象实例ID |
EWinLoseType | EventCondition 的核心枚举(虽有声明但无完整枚举项扩展) |
EAiTactic | PlayerInfo 中AI战术类型 |
EDiggingStatus | TerrainTile 中 getDiggingStatus 方法返回值 |
EGameSettings | CMap 中 overrideGameSetting 方法参数 |
EMapFormat | CMapHeader 中表示地图格式(如SOD),仅声明无枚举项 |
FactionID | PlayerInfo 中表示阵营ID(城堡类型) |
HeroTypeID | DisposedHero、PlayerInfo、CMapHeader 中表示英雄类型ID |
ArtifactID/CreatureID/GameResID/BuildingID/MapObjectID | EventCondition::TargetTypeID 变体类型的基础ID |
SpellID | CMap 中 allowedSpells 集合的元素类型 |
SecondarySkill | CMap 中 allowedAbilities 集合的元素类型 |
| 接口名 | 缺失内容 |
|---|---|
IGameCallback | IMapService 加载地图方法的参数,CMap 继承 GameCallbackHolder 依赖该接口 |
IInputStream | CMapService 中 getStreamFromFS/getStreamFromMem 返回该接口实例 |
vstd::RNG | 地形绘制、清空操作中作为随机数生成器参数 |
JsonSerializeFormat | Rumor/CMapEvent/CCastleEvent 中有 serializeJson 方法依赖 |
ConstTransitivePtr | 大量出现在 CMap 的容器中(如 ConstTransitivePtr<CGObjectInstance>),是智能指针封装 |
| 名称 | 引用场景 |
|---|---|
StartInfo | CMapInfo 中持有 StartInfo * scenarioOptionsOfSave |
Campaign | CMapInfo 中持有 std::unique_ptr<Campaign> campaign |
TradeItemBuy | CMap 中 townUniversitySkills 列表的元素类型 |
MetaString | 地图头、事件、谣言等大量使用(如 CMapHeader::name 是 MetaString 类型) |
TextLocalizationContainer/TextContainerRegistrable | CMapHeader 中维护地图文本翻译 |
TextIdentifier | mapRegisterLocalizedString 函数参数 |
ModCompatibilityInfo | CMapHeader/IMapService 中验证模组兼容性的结构体 |
LogicalExpression | EventExpression 是该模板类的特化(LogicalExpression<EventCondition>) |
CGObjectInstance 是所有地图对象(城镇、英雄、宝物、怪物)的基类,必须优先补充;MetaString 是本地化文本核心结构体,地图名称/描述/事件文本均依赖;ConstTransitivePtr 是引擎自定义智能指针,需补充其模板定义才能解析容器类型。SeaViewExperience(看海体验类):核心属性如体验时间、地点、海景描述;核心方法如feelSea()(感受大海)、recordExperience()(记录体验)。SeaViewPreparation(看海准备类):核心属性如物品清单、出行方式;核心方法如checkItems()(检查物品)、prepareMood()(调整心境)。Companion(同行者类):属性(同行人身份、互动方式、共同记忆)、方法(shareView()(分享海景感受)、takePhotoTogether()(合影))—— 补充“同频的人”相关定义;MoodState(心境状态类):属性(焦虑值、松弛度、沉浸感)、方法(adjustMood()(调节心境)、getMoodFeedback()(获取心境反馈))—— 补充“松弛感/平静心境”相关定义;SeaViewItem(看海物品类):细分PracticalItem(实用物品子类:防晒帽、拖鞋等,属性含功能、使用场景)、AtmosphereItem(氛围物品子类:汽水、闲书等,属性含氛围感描述、使用时机);ItemManager(物品管理类):方法(addNecessaryItem()(添加必备物品)、checkMissingItem()(检查缺失物品));SeaExpression(海景表达类):属性(文案内容、情绪匹配度、表达场景)、方法(matchExpression()(匹配适配文案)、generateExpression()(生成即兴表达));TimeUnconcerned(无牵挂时光类):属性(琐事干扰度、放空时长、纯粹度)、方法(blockDistraction()(屏蔽干扰)、getPureTime()(获取纯粹时光));SeaViewScene(看海场景类):属性(场景类型(日出/日落/深夜)、环境参数(风速、温度、海浪声)、场景氛围感);ExperienceFeedback(体验反馈类):属性(满意度、缺失感、核心遗憾)、方法(analyzeMissingPoint()(分析缺失点)、optimizeNextExperience()(优化下次体验))。SeaViewExperience(看海体验类)、SeaViewPreparation(看海准备类),那上面9个就是全部缺失的核心类;若你连基础类都没定义,需额外补充:| 类名 | 优先级 | 缺失后果(为什么必须补) |
|---|---|---|
| CGObjectInstance | 极高 | 所有地图对象的基类,无它则对象管理、渲染全报错 |
| CGTownInstance | 极高 | 城镇实例核心,地图城镇逻辑(加载/渲染)依赖 |
| CGHeroInstance | 极高 | 英雄实例核心,地图英雄相关逻辑依赖 |
| CMapEditManager | 高 | CMap持有其智能指针,无定义则CMap编译失败 |
| CArtifactInstance | 高 | 神器实例核心,地图物品(神器)逻辑依赖 |
| CArtifactSet | 高 | 神器集合,CMap中添加/移动神器方法参数依赖 |
| CQuest | 高 | 任务实例核心,地图任务系统无法编译 |
| IGameSettings/GameSettings | 高 | CMap持有其指针,游戏设置覆盖/获取逻辑依赖 |
| 名称 | 优先级 | 缺失后果 |
|---|---|---|
| PlayerColor | 极高 | 玩家颜色/阵营,PlayerInfo、CMapEvent全报错 |
| ConstTransitivePtr | 极高 | 引擎自定义智能指针,CMap中所有对象列表(城镇/英雄等)无法解析 |
| Obj | 高 | 地图对象类型,CMap核心方法(getObjectiveObjectFrom)参数依赖 |
| TeamID | 高 | 队伍ID,PlayerInfo、CMapHeader编译失败 |
| ObjectInstanceID | 高 | 地图对象实例ID,EventCondition、CMap依赖 |
| FactionID | 高 | 阵营ID,PlayerInfo(城堡类型)逻辑依赖 |
| HeroTypeID | 高 | 英雄类型ID,DisposedHero、PlayerInfo依赖 |
| 接口名 | 优先级 | 缺失后果 |
|---|---|---|
| IGameCallback | 高 | IMapService加载地图参数,CMap继承类依赖 |
| IInputStream | 高 | CMapService读取地图流(文件/内存)核心依赖 |
| vstd::RNG | 高 | 地形绘制/清空的随机数生成器,地图编辑依赖 |
| 结构体名 | 优先级 | 缺失后果 |
|---|---|---|
| MetaString | 极高 | 本地化文本核心,地图名称/描述/事件文本全报错 |
| ModCompatibilityInfo | 高 | 模组兼容性验证,CMapHeader/IMapService依赖 |
| LogicalExpression | 高 | EventExpression模板基类,事件条件逻辑依赖 |
| 定义项 | 头文件中需包含的关键内容 | 缺失判断标准 |
|---|---|---|
| CGObjectInstance | 类声明(class CGObjectInstance { ... };),包含核心成员/方法(如对象ID、坐标、渲染接口) | 无类声明/核心成员缺失 → 不够 |
| ConstTransitivePtr | 模板别名/类声明(template <typename T> using ConstTransitivePtr = ...;) | 无模板定义 → 不够 |
| MetaString | 结构体声明(struct MetaString { ... };),包含本地化文本核心成员(如key、params) | 无结构体声明 → 不够 |
| 类别 | 需包含的关键内容 |
|---|---|
| 核心依赖类 | CGTownInstance、CGHeroInstance、CMapEditManager、CArtifactInstance、CArtifactSet、CQuest、IGameSettings/GameSettings 的类声明 |
| 枚举/类型别名 | PlayerColor(枚举)、Obj、TeamID、ObjectInstanceID、FactionID、HeroTypeID(类型别名/枚举)的定义 |
| 接口类 | IGameCallback、IInputStream、vstd::RNG(接口/类声明) |
| 辅助结构体 | ModCompatibilityInfo、LogicalExpression 的结构体声明 |
// 1. ConstTransitivePtr(自定义智能指针,最小化声明)
namespace vstd {
template <typename T>
class TransitivePtr;
template <typename T>
using ConstTransitivePtr = TransitivePtr<const T>;
} // namespace vstd
// 2. MetaString(本地化文本核心,最小化声明)
struct MetaString {
std::string key; // 本地化键
std::vector<std::string> params; // 文本参数
// 基础构造函数
MetaString() = default;
MetaString(const std::string& k) : key(k) {}
};
// 3. CGObjectInstance(地图对象基类,最小化声明)
class CGObjectInstance {
public:
// 核心成员
vstd::ConstTransitivePtr<CGObjectInstance> parent; // 父对象
int id = 0; // ObjectInstanceID
int x = 0, y = 0, z = 0; // 地图坐标
MetaString name; // 本地化名称
// 虚析构函数(基类必须)
virtual ~CGObjectInstance() = default;
// 核心方法(最小化)
virtual void render() = 0; // 渲染接口(纯虚,子类实现)
};
setValue方法或者创建对应类型的 JsonNode 来赋值,比如:out["width"].setValue(map->width);out["name"].setValue(map->name.toString());p["human"].setValue(!player.isAi());(先确认 isAi 的正确名)id成员(而非 getId ()),color 是color成员(而非 getColor ()),isAi 可能是ai成员(而非 isAi ())。out.append(p); → out.push(p);getType()方法(而非 type 成员),subtype 是getSubtype()。town->getOwner()->getNum() → town->getOwner().getNum()(因为 getOwner 返回 PlayerColor,不是指针)。JsonNode不支持直接赋值基础类型(int/string/char []),需调用setValue接口;PlayerInfo/CGObjectInstance/CGHeroInstance等类的成员 / 方法名与代码中调用的不匹配;append方法不存在,需用push替代。exportHeader() 函数(JsonNode 赋值错误)JsonNode MapController::JsonExportHandler::exportHeader()
{
JsonNode out(JsonNode::JsonType::OBJECT); // 显式声明对象类型
// 修复:用setValue设置基础类型值
out["width"].setValue(map->width);
out["height"].setValue(map->height);
out["levels"].setValue(static_cast<int>(map->levels()));
out["name"].setValue(map->name.toString());
out["description"].setValue(map->description.toString());
return out;
}
exportPlayers() 函数(成员名 + append+JsonNode 赋值)JsonNode MapController::JsonExportHandler::exportPlayers()
{
JsonNode out(JsonNode::JsonType::ARRAY); // 显式声明数组类型
for (const PlayerInfo &player : map->players)
{
JsonNode p(JsonNode::JsonType::OBJECT);
// 修复:PlayerInfo的正确成员名(根据VCMI源码,PlayerInfo的id是成员变量,color是PlayerColor类型成员,ai是bool成员)
p["id"].setValue(static_cast<int>(player.id)); // 替代getId()
p["color"].setValue(static_cast<int>(player.color.getNum())); // 替代getColor()(非指针,直接调用getNum)
p["human"].setValue(!player.ai); // 替代isAi()(ai成员直接取反)
// 修复:append → push(VCMI JsonNode数组用push添加元素)
out.push(p);
}
return out;
}
exportTerrain() 函数(JsonNode 赋值 + append)JsonNode MapController::JsonExportHandler::exportTerrain()
{
JsonNode out(JsonNode::JsonType::ARRAY);
for (int z = 0; z < map->levels(); ++z)
{
for (int y = 0; y < map->height; ++y)
{
for (int x = 0; x < map->width; ++x)
{
const TileInfo &tile = map->getTileInfo(Position(x, y, z));
JsonNode t(JsonNode::JsonType::OBJECT);
// 修复:用setValue赋值基础类型
t["x"].setValue(x);
t["y"].setValue(y);
t["z"].setValue(z);
t["terrain"].setValue(terrainToString(tile.terType));
// 修复:append → push
out.push(t);
}
}
}
return out;
}
exportObjects() 函数(成员名 + JsonNode 赋值 + PlayerColor 指针错误)JsonNode MapController::JsonExportHandler::exportObjects()
{
JsonNode out(JsonNode::JsonType::ARRAY);
for (const CGObjectInstance *obj : map->objects)
{
JsonNode o(JsonNode::JsonType::OBJECT);
o["x"].setValue(obj->pos.x);
o["y"].setValue(obj->pos.y);
o["z"].setValue(obj->pos.z);
// 修复:CGObjectInstance的type/subtype调用方式(VCMI源码中是getType()/getSubtype())
o["type"].setValue(obj->getType()->getJsonKey());
o["subtype"].setValue(obj->getSubtype().getNum());
// 处理城镇类型
if (obj->ID == Obj::TOWN)
{
const CTownInstance *town = dynamic_cast<const CTownInstance*>(obj);
if (town)
{
o["kind"].setValue("town");
// 修复:PlayerColor不是指针,直接调用getNum()
o["owner"].setValue(static_cast<int>(town->getOwner().getNum()));
}
}
// 处理英雄类型
else if (obj->ID == Obj::HERO)
{
const CGHeroInstance *hero = dynamic_cast<const CGHeroInstance*>(obj);
if (hero)
{
o["kind"].setValue("hero");
// 修复:CGHeroInstance的name/level是成员变量(非getName()/getLevel())
o["name"].setValue(hero->name.toString());
o["level"].setValue(hero->level);
}
}
// 修复:append → push
out.push(o);
}
return out;
}
exportEvents() 和 exportRumors() 函数(JsonNode 赋值 + append)// exportEvents修复
JsonNode MapController::JsonExportHandler::exportEvents()
{
JsonNode out(JsonNode::JsonType::ARRAY);
for (const MapEvent &e : map->events)
{
JsonNode j(JsonNode::JsonType::OBJECT);
j["message"].setValue(e.message.toString());
out.push(j); // append → push
}
return out;
}
// exportRumors修复
JsonNode MapController::JsonExportHandler::exportRumors()
{
JsonNode out(JsonNode::JsonType::ARRAY);
for (const MapRumor &r : map->rumors)
{
JsonNode j(JsonNode::JsonType::OBJECT);
j["name"].setValue(r.name);
j["text"].setValue(r.text.toString());
out.push(j); // append → push
}
return out;
}
| 错误类型 | 修复方式 |
|---|---|
| JsonNode 直接赋值 | 替换为 node[key].setValue(值)(支持 int/string/bool 等基础类型) |
| JsonNode::append | 替换为 JsonNode::push(子节点)(VCMI Json 数组添加元素的标准接口) |
| PlayerInfo 成员错误 | getId()→id / getColor()→color / isAi()→ai(查 VCMI 源码确认) |
| PlayerColor 指针错误 | getOwner()->getNum()→getOwner().getNum()(返回值是值类型,非指针) |
| CGObjectInstance 成员错误 | type→getType() / subtype→getSubtype() |
| CGHeroInstance 成员错误 | getName()→name / getLevel()→level |
PlayerInfo的成员名仍不匹配,需查阅 VCMI 项目中PlayerInfo的定义(通常在lib/map/MapInfo.h);JsonNode创建时显式声明类型(OBJECT/ARRAY),避免默认类型导致的赋值错误;rm -rf map_build/* 重新 cmake 后编译。CTownInstance/CGHeroInstance的定义在lib/objects/CGObjectInstance.h等文件中)。