space-game001/src/Location.h
2026-06-14 22:31:11 +03:00

194 lines
6.7 KiB
C++

#pragma once
#include "render/Renderer.h"
#include "Environment.h"
#include "render/TextureManager.h"
#include "render/TextRenderer.h"
#include "items/GameObjectLoader.h"
#include "items/InteractiveObject.h"
#include "navigation/PathFinder.h"
#include "render/ShadowMap.h"
#include "ScriptEngine.h"
#include "dialogue/DialogueSystem.h"
#include "SparkEmitter.h"
#include "TeleportZone.h"
#include "LocationEditor.h"
#include <functional>
#include <cstdint>
#include <unordered_map>
namespace ZL
{
struct PointLight
{
std::string id;
Eigen::Vector3f position; // world space
Eigen::Vector3f direction; // world space, unit vector the cone points toward
Eigen::Vector3f color; // RGB, values > 1.0 increase intensity
bool autoLight = false; // if true, only activates when nearest light to player
float autoLightDistance = -1.0f; // max distance for autoLight activation; <=0 means unlimited
};
struct TriggerZone
{
std::string id;
Eigen::Vector3f position = Eigen::Vector3f::Zero();
float radius = 1.5f;
float hysteresis = 0.3f; // exit fires at radius + hysteresis to prevent flickering
bool enabled = true;
bool playerInside = false; // runtime tracking state, not serialized
};
struct LocationSetup
{
std::string gameObjectsJsonPath;
std::string interactiveObjectsJsonPath;
std::string npcsJsonPath;
std::string dialoguesJsonPath;
std::vector<std::string> navigationJsonPaths;
std::string scriptPath;
std::string teleportsJsonPath;
std::string triggerZonesJsonPath;
std::string lightsJsonPath;
Eigen::Vector3f playerPosition = Eigen::Vector3f::Zero();
};
class Location
{
public:
Location(Renderer& iRenderer, Inventory& iInventory);
std::unordered_map<std::string, LoadedGameObject> gameObjects;
std::vector<InteractiveObject> interactiveObjects;
std::unique_ptr<Character> player;
std::vector<std::unique_ptr<Character>> npcs;
float cameraAzimuth = -2.35;
float cameraInclination = 1.1036;//M_PI * 30.f / 180.f;
std::vector<PathFinder> navigationMaps;
PathFinder* navigation = nullptr;
int activeNavigationIndex = 0;
std::unique_ptr<ShadowMap> shadowMap;
Eigen::Matrix4f cameraViewMatrix = Eigen::Matrix4f::Identity();
InteractiveObject* targetInteractiveObject = nullptr;
// "Walk to NPC, then fire on_npc_interact" — mirrors targetInteractiveObject
// so a click from outside interactionRadius still leads to a Lua callback
// once the player gets close enough.
Character* targetInteractNpc = nullptr;
int targetInteractNpcIndex = -1;
std::unique_ptr<TextRenderer> npcNameText;
ScriptEngine scriptEngine;
Dialogue::DialogueSystem dialogueSystem;
std::vector<TeleportZone> teleportZones;
TeleportZone* targetTeleportZone = nullptr;
std::function<void(const std::string&, const Eigen::Vector3f&, float)> onTeleport;
std::vector<TriggerZone> triggerZones;
std::vector<PointLight> pointLights;
// Set by Game and kept in sync across location transitions.
// Read by draw functions and raycast — do not write from Location code.
bool isDarklands = false;
bool isNight = false;
bool isDawn = false;
bool tutorialInteractiveObjectsLocked = false;
// Called when the player successfully taps the ground and a floor walk target is set.
// Used by the tutorial system to detect the "tap to walk" gesture.
std::function<void()> onPlayerFloorWalk;
std::function<void()> onPlayerTaxiRequired;
// Set by Game after setup(). Lua and onDeathAnimComplete call this to
// request the transition without coupling Location to Game directly.
std::function<bool()> requestDarklandsTransition;
std::function<void(bool, bool)> requestNightDayTransition;
// Set by Game after setup(). Lua calls this to advance the uni_interior HUD
// from step12 to step13 (trigger-zone encounter hint).
std::function<void()> requestAdvanceDarklandsHud;
std::function<void()> requestClosePhone;
std::function<void()> requestReturnToMainMenu;
std::function<void(int, bool, const std::string&)> requestSetChatUnread;
// Navigation editor — toggle with 'N', save with 'B', right-click to finalize polygon
EditorMode editorMode = EditorMode::None;
LocationEditor editor;
std::vector<std::string> navigationMapPaths;
// Set by Game when the user's primary pointer (left mouse / single touch)
// has crossed the tap-vs-drag threshold and is now rotating the camera.
bool cameraDragging = false;
int lastMouseX = 0;
int lastMouseY = 0;
void setup(const LocationSetup& params, Quest::QuestJournal* journal);
void setupNavigation(const std::vector<std::string>& paths);
bool switchNavigation(int index);
InteractiveObject* raycastInteractiveObjects(const Eigen::Vector3f& rayOrigin, const Eigen::Vector3f& rayDir);
Character* raycastNpcs(const Eigen::Vector3f& rayOrigin, const Eigen::Vector3f& rayDir, float maxDistance = 100.0f);
void drawGame();
void drawShadowDepthPass();
void drawGameWithShadows();
void drawGameDarklands();
void drawGameNight();
void drawNightShadowDepthPass(const PointLight& light);
bool setNavigationAreaAvailable(const std::string& areaName, bool available);
void update(int64_t deltaMs);
void handleDown(int64_t fingerId, int eventX, int eventY, int mx, int my);
void handleUp(int64_t fingerId, int mx, int my);
void handleMotion(int64_t fingerId, int eventX, int eventY, int mx, int my);
bool requestDialogueStart(const std::string& dialogueId);
bool requestCutsceneStart(const std::string& cutsceneId);
void setOnCutsceneFinished(std::function<void(const std::string&)> cb);
void setDialogueFlag(const std::string& flag, int value);
int getDialogueFlag(const std::string& flag) const;
protected:
friend class LocationEditor;
Renderer& renderer;
Inventory& inventory;
private:
void resolveCharacterCollisions();
void updateDynamicReplans(int64_t deltaMs);
void loadTeleportZones(const std::string& jsonPath, const char* zipFile);
void loadTriggerZones(const std::string& jsonPath, const char* zipFile);
void loadPointLights(const std::string& jsonPath, const char* zipFile);
void updateTriggerZones(const Eigen::Vector3f& playerPos);
void nudgeCharacterAside(Character* standing, const Eigen::Vector3f& awayFrom);
int findNpcIndex(const Character* npc) const;
void fireBumpCallbacks(Character* mover, Character* standing);
std::unordered_map<Character*, Eigen::Vector3f> lastCharacterPositions;
std::unordered_map<Character*, int64_t> replanCooldownRemainingMs;
std::unordered_map<Character*, int> stuckFrameCounts;
std::unordered_map<Character*, float> stuckPauseRemainingS;
static constexpr float NPC_BUMP_CALLBACK_COOLDOWN = 5.0f;
std::unordered_map<int, float> npcBumpedByPlayerCooldown;
std::unordered_map<int, float> npcBumpsPlayerCooldown;
};
} // namespace ZL