Added startup scripts
This commit is contained in:
1 parent
a479fa4ac1
commit
13ef92160d
26 files changed
+117164
-150
No files matched your search
+120
-22
@@ -137,6 +137,7 @@ namespace ZL
|
||||
}
|
||||
|
||||
loadTeleportZones(params.teleportsJsonPath, CONST_ZIP_FILE);
|
||||
loadTriggerZones(params.triggerZonesJsonPath, CONST_ZIP_FILE);
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
// Create shadow map (2048x2048, ortho size 40, near 0.1, far 100)
|
||||
@@ -145,7 +146,7 @@ namespace ZL
|
||||
std::cout << "Shadow map initialized" << std::endl;
|
||||
#endif
|
||||
|
||||
setupNavigation(params.navigationJsonPath);
|
||||
setupNavigation(params.navigationJsonPaths);
|
||||
|
||||
dialogueSystem.init(renderer, CONST_ZIP_FILE);
|
||||
dialogueSystem.loadDatabase(params.dialoguesJsonPath);
|
||||
@@ -165,7 +166,7 @@ namespace ZL
|
||||
false
|
||||
});*/
|
||||
|
||||
navigationEditorConfigPath = params.navigationJsonPath;
|
||||
// navigationMapPaths is already populated by setupNavigation
|
||||
|
||||
scriptEngine.init(this, &inventory, params.scriptPath);
|
||||
|
||||
@@ -231,13 +232,84 @@ namespace ZL
|
||||
std::cout << "[TELEPORT] Loaded " << teleportZones.size() << " teleport(s) from " << jsonPath << std::endl;
|
||||
}
|
||||
|
||||
void Location::setupNavigation(const std::string& navigationJsonPath)
|
||||
void Location::loadTriggerZones(const std::string& jsonPath, const char* zipFile)
|
||||
{
|
||||
// Static navigation blockers are defined in the navigation JSON as polygons.
|
||||
// NPC + player are handled as dynamic obstacles, so they are intentionally NOT in JSON.
|
||||
navigation.build({}, navigationJsonPath, CONST_ZIP_FILE);
|
||||
if (jsonPath.empty()) return;
|
||||
|
||||
if (navigationEditorMode) {
|
||||
std::string content;
|
||||
try {
|
||||
if (!zipFile || zipFile[0] == '\0') {
|
||||
content = readTextFile(jsonPath);
|
||||
} else {
|
||||
auto buf = readFileFromZIP(jsonPath, zipFile);
|
||||
if (buf.empty()) {
|
||||
std::cerr << "[TRIGGER] Failed to read " << jsonPath << " from zip" << std::endl;
|
||||
return;
|
||||
}
|
||||
content.assign(buf.begin(), buf.end());
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "[TRIGGER] Failed to open " << jsonPath << ": " << e.what() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
using json = nlohmann::json;
|
||||
json j;
|
||||
try {
|
||||
j = json::parse(content);
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "[TRIGGER] JSON parse error in " << jsonPath << ": " << e.what() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!j.contains("trigger_zones") || !j["trigger_zones"].is_array()) return;
|
||||
|
||||
for (const auto& item : j["trigger_zones"]) {
|
||||
TriggerZone tz;
|
||||
tz.id = item.value("id", "");
|
||||
tz.enabled = item.value("enabled", true);
|
||||
tz.radius = item.value("radius", 1.5f);
|
||||
tz.hysteresis = item.value("hysteresis", 0.3f);
|
||||
tz.position = Eigen::Vector3f(
|
||||
item.value("positionX", 0.0f),
|
||||
item.value("positionY", 0.0f),
|
||||
item.value("positionZ", 0.0f)
|
||||
);
|
||||
triggerZones.push_back(std::move(tz));
|
||||
}
|
||||
|
||||
std::cout << "[TRIGGER] Loaded " << triggerZones.size() << " trigger zone(s) from " << jsonPath << std::endl;
|
||||
}
|
||||
|
||||
void Location::updateTriggerZones(const Eigen::Vector3f& playerPos)
|
||||
{
|
||||
for (auto& tz : triggerZones) {
|
||||
if (!tz.enabled) continue;
|
||||
const float dist = (playerPos - tz.position).norm();
|
||||
if (!tz.playerInside && dist <= tz.radius) {
|
||||
tz.playerInside = true;
|
||||
scriptEngine.callTriggerEnterCallback(tz.id);
|
||||
} else if (tz.playerInside && dist > tz.radius + tz.hysteresis) {
|
||||
tz.playerInside = false;
|
||||
scriptEngine.callTriggerExitCallback(tz.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Location::setupNavigation(const std::vector<std::string>& paths)
|
||||
{
|
||||
navigationMapPaths = paths;
|
||||
navigationMaps.clear();
|
||||
navigationMaps.resize(paths.size());
|
||||
|
||||
for (size_t i = 0; i < paths.size(); ++i) {
|
||||
navigationMaps[i].build({}, paths[i], CONST_ZIP_FILE);
|
||||
}
|
||||
|
||||
activeNavigationIndex = 0;
|
||||
navigation = navigationMaps.empty() ? nullptr : &navigationMaps[0];
|
||||
|
||||
if (navigationEditorMode && navigation) {
|
||||
navigationEditorBuildNavMeshes();
|
||||
}
|
||||
|
||||
@@ -245,6 +317,7 @@ namespace ZL
|
||||
|
||||
auto makePlanner = [this](const Character* self) {
|
||||
return [this, self](const Eigen::Vector3f& start, const Eigen::Vector3f& end) {
|
||||
if (!navigation) return std::vector<Eigen::Vector3f>{};
|
||||
std::vector<PathFinder::DynamicObstacle> dynamicObstacles;
|
||||
dynamicObstacles.reserve(npcs.size() + 1);
|
||||
|
||||
@@ -257,7 +330,7 @@ namespace ZL
|
||||
}
|
||||
|
||||
PathFinder::DynamicObstacle obs;
|
||||
obs.position = Eigen::Vector3f(other->position.x(), navigation.getFloorY(), other->position.z());
|
||||
obs.position = Eigen::Vector3f(other->position.x(), navigation->getFloorY(), other->position.z());
|
||||
obs.radius = (std::max)(0.0f, other->collisionRadius);
|
||||
dynamicObstacles.push_back(obs);
|
||||
};
|
||||
@@ -267,7 +340,7 @@ namespace ZL
|
||||
addCharacter(npc.get());
|
||||
}
|
||||
|
||||
return navigation.findPath(start, end, dynamicObstacles);
|
||||
return navigation->findPath(start, end, dynamicObstacles);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -282,12 +355,35 @@ namespace ZL
|
||||
}
|
||||
}
|
||||
|
||||
bool Location::switchNavigation(int index)
|
||||
{
|
||||
if (index < 0 || index >= static_cast<int>(navigationMaps.size())) {
|
||||
std::cerr << "[NAV] switchNavigation: index " << index << " out of range\n";
|
||||
return false;
|
||||
}
|
||||
activeNavigationIndex = index;
|
||||
navigation = &navigationMaps[index];
|
||||
|
||||
// Force all characters to replan their paths against the new nav map.
|
||||
if (player) player->forceReplan();
|
||||
for (auto& npc : npcs) {
|
||||
if (npc) npc->forceReplan();
|
||||
}
|
||||
|
||||
if (navigationEditorMode) {
|
||||
navigationEditorBuildNavMeshes();
|
||||
}
|
||||
std::cout << "[NAV] Switched to navigation map " << index << "\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
void Location::navigationEditorBuildNavMeshes()
|
||||
{
|
||||
navigationEditorNavMeshes.clear();
|
||||
const float y = navigation.getFloorY() + 0.02f;
|
||||
if (!navigation) return;
|
||||
const float y = navigation->getFloorY() + 0.02f;
|
||||
const Eigen::Vector3f red(1.0f, 0.0f, 0.0f);
|
||||
for (const auto& obs : navigation.getObstaclePolygons()) {
|
||||
for (const auto& obs : navigation->getObstaclePolygons()) {
|
||||
if (obs.polygon.size() < 3) continue;
|
||||
VertexRenderStruct mesh;
|
||||
mesh.data = CreatePolygonFloor(obs.polygon, y, red);
|
||||
@@ -311,7 +407,7 @@ namespace ZL
|
||||
{
|
||||
VertexDataStruct data;
|
||||
const Eigen::Vector3f yellow(1.0f, 1.0f, 0.0f);
|
||||
const float y = navigation.getFloorY() + 0.05f;
|
||||
const float y = navigation ? navigation->getFloorY() + 0.05f : 0.05f;
|
||||
const float s = 0.2f;
|
||||
|
||||
for (const auto& pt : navigationEditorPoints) {
|
||||
@@ -381,13 +477,13 @@ namespace ZL
|
||||
poly.polygon.emplace_back(pt.x(), pt.z());
|
||||
}
|
||||
|
||||
navigation.addObstaclePolygon(poly);
|
||||
if (navigation) navigation->addObstaclePolygon(poly);
|
||||
std::cout << "[NAV_EDITOR] Added obstacle '" << poly.name << "' with "
|
||||
<< poly.polygon.size() << " vertices\n";
|
||||
|
||||
// Add a red mesh for the new obstacle polygon so it's visible immediately.
|
||||
{
|
||||
const float y = navigation.getFloorY() + 0.02f;
|
||||
const float y = navigation ? navigation->getFloorY() + 0.02f : 0.02f;
|
||||
const Eigen::Vector3f red(1.0f, 0.0f, 0.0f);
|
||||
VertexRenderStruct mesh;
|
||||
mesh.data = CreatePolygonFloor(poly.polygon, y, red);
|
||||
@@ -409,7 +505,7 @@ namespace ZL
|
||||
if (!std::filesystem::exists(filename)) break;
|
||||
}
|
||||
|
||||
if (navigation.saveConfig(filename)) {
|
||||
if (navigation && navigation->saveConfig(filename)) {
|
||||
std::cout << "[NAV_EDITOR] Saved navigation config to: " << filename << "\n";
|
||||
} else {
|
||||
std::cerr << "[NAV_EDITOR] Failed to save to: " << filename << "\n";
|
||||
@@ -418,8 +514,8 @@ namespace ZL
|
||||
|
||||
void Location::navigationEditorReload()
|
||||
{
|
||||
setupNavigation(navigationEditorConfigPath);
|
||||
std::cout << "[NAV_EDITOR] Reloaded navigation from: " << navigationEditorConfigPath << "\n";
|
||||
setupNavigation(navigationMapPaths);
|
||||
std::cout << "[NAV_EDITOR] Reloaded navigation maps (" << navigationMapPaths.size() << " maps)\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -821,7 +917,8 @@ namespace ZL
|
||||
|
||||
bool Location::setNavigationAreaAvailable(const std::string& areaName, bool available)
|
||||
{
|
||||
return navigation.setAreaAvailable(areaName, available);
|
||||
if (!navigation) return false;
|
||||
return navigation->setAreaAvailable(areaName, available);
|
||||
}
|
||||
|
||||
void Location::resolveCharacterCollisions()
|
||||
@@ -882,9 +979,9 @@ namespace ZL
|
||||
newB.z() += normal.y() * push;
|
||||
newB.y() = 0.f;
|
||||
|
||||
if (navigation.isReady()) {
|
||||
const bool aOk = navigation.isWalkable(newA);
|
||||
const bool bOk = navigation.isWalkable(newB);
|
||||
if (navigation && navigation->isReady()) {
|
||||
const bool aOk = navigation->isWalkable(newA);
|
||||
const bool bOk = navigation->isWalkable(newB);
|
||||
|
||||
if (aOk && bOk) {
|
||||
a->position = newA;
|
||||
@@ -978,7 +1075,8 @@ namespace ZL
|
||||
{
|
||||
if (player) {
|
||||
player->update(delta);
|
||||
dialogueSystem.update(static_cast<int>(delta), player->position);
|
||||
dialogueSystem.update(static_cast<int>(delta));
|
||||
updateTriggerZones(player->position);
|
||||
}
|
||||
for (auto& npc : npcs)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user