diff --git a/src/network/LocalClient.cpp b/src/network/LocalClient.cpp index b6fc7f6..e0a8d36 100644 --- a/src/network/LocalClient.cpp +++ b/src/network/LocalClient.cpp @@ -10,13 +10,14 @@ namespace ZL { void LocalClient::Connect(const std::string& host, uint16_t port) { generateBoxes(); + initializeNPCs(); lastUpdateMs = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()).count(); } void LocalClient::generateBoxes() { serverBoxes.clear(); - + std::random_device rd; std::mt19937 gen(rd()); @@ -64,20 +65,101 @@ namespace ZL { std::cout << "LocalClient: Generated " << serverBoxes.size() << " boxes\n"; } + Eigen::Vector3f LocalClient::generateRandomPosition() { + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_real_distribution<> distrib(-50.0, 50.0); + + return Eigen::Vector3f( + (float)distrib(gen), + (float)distrib(gen), + (float)distrib(gen) + 45000.0f + ); + } + + void LocalClient::initializeNPCs() { + npcs.clear(); + + for (int i = 0; i < 3; ++i) { + LocalNPC npc; + npc.id = 100 + i; + npc.speed = 20.0f + (i * 5.0f); + npc.currentState.id = npc.id; + npc.currentState.position = generateRandomPosition(); + npc.currentState.rotation = Eigen::Matrix3f::Identity(); + npc.currentState.velocity = npc.speed; + npc.targetPosition = generateRandomPosition(); + npc.lastStateUpdateMs = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count(); + + npc.stateHistory.add_state(npc.currentState); + npcs.push_back(npc); + + std::cout << "LocalClient: Created NPC with id=" << npc.id + << " at pos (" << npc.currentState.position.x() << ", " + << npc.currentState.position.y() << ", " + << npc.currentState.position.z() << ")" << std::endl; + } + } + + void LocalClient::updateNPCs() { + auto now_ms = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count(); + + for (auto& npc : npcs) { + uint64_t deltaMs = now_ms - npc.lastStateUpdateMs; + float dt = deltaMs / 1000.0f; + npc.lastStateUpdateMs = now_ms; + + Eigen::Vector3f direction = npc.targetPosition - npc.currentState.position; + float distance = direction.norm(); + + if (distance < 5.0f) { + npc.targetPosition = generateRandomPosition(); + direction = npc.targetPosition - npc.currentState.position; + distance = direction.norm(); + } + + if (distance > 0.001f) { + direction.normalize(); + npc.currentState.position += direction * npc.speed * dt; + npc.currentState.velocity = npc.speed; + + Eigen::Vector3f localForward(0.0f, 0.0f, -1.0f); + Eigen::Vector3f worldForward = direction; + + Eigen::Vector3f cross = localForward.cross(worldForward); + float dot = localForward.dot(worldForward); + + if (cross.norm() > 0.001f) { + float angle = std::atan2(cross.norm(), dot); + cross.normalize(); + Eigen::AngleAxisf aa(angle * 0.05f, cross); + npc.currentState.rotation = npc.currentState.rotation * aa.toRotationMatrix(); + } + } + + npc.currentState.lastUpdateServerTime = std::chrono::system_clock::time_point( + std::chrono::milliseconds(now_ms)); + npc.stateHistory.add_state(npc.currentState); + } + } + void LocalClient::Poll() { updatePhysics(); + updateNPCs(); checkCollisions(); } void LocalClient::updatePhysics() { auto now_ms = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()).count(); - + if (lastUpdateMs == 0) { lastUpdateMs = now_ms; return; } - + uint64_t deltaMs = now_ms - lastUpdateMs; float dt = deltaMs / 1000.0f; lastUpdateMs = now_ms; @@ -147,7 +229,7 @@ namespace ZL { std::cout << "LocalClient: Box " << boxIdx << " destroyed by projectile from player " << projectiles[projIdx].shooterId << std::endl; - if (std::find(projIndicesToRemove.begin(), projIndicesToRemove.end(), (int)projIdx) + if (std::find(projIndicesToRemove.begin(), projIndicesToRemove.end(), (int)projIdx) == projIndicesToRemove.end()) { projIndicesToRemove.push_back(static_cast(projIdx)); } @@ -198,7 +280,7 @@ namespace ZL { tokens.push_back(token); } return tokens; - }(message, ':'); + }(message, ':'); if (parts.empty()) return; @@ -252,8 +334,8 @@ namespace ZL { pinfo.velocity = velocity; pendingProjectiles.push_back(pinfo); - std::cout << "LocalClient: Created projectile at pos (" << shotPos.x() << ", " - << shotPos.y() << ", " << shotPos.z() << ") vel (" << pr.vel.x() << ", " + std::cout << "LocalClient: Created projectile at pos (" << shotPos.x() << ", " + << shotPos.y() << ", " << shotPos.z() << ") vel (" << pr.vel.x() << ", " << pr.vel.y() << ", " << pr.vel.z() << ")" << std::endl; } } @@ -265,6 +347,14 @@ namespace ZL { return result; } + std::unordered_map LocalClient::getRemotePlayers() { + std::unordered_map result; + for (const auto& npc : npcs) { + result[npc.id] = npc.stateHistory; + } + return result; + } + std::vector> LocalClient::getServerBoxes() { std::vector> result; for (const auto& box : serverBoxes) { diff --git a/src/network/LocalClient.h b/src/network/LocalClient.h index c795e49..d94a82d 100644 --- a/src/network/LocalClient.h +++ b/src/network/LocalClient.h @@ -9,67 +9,79 @@ namespace ZL { - struct LocalServerBox { - Eigen::Vector3f position; - Eigen::Matrix3f rotation; - float collisionRadius = 2.0f; - bool destroyed = false; - }; + struct LocalServerBox { + Eigen::Vector3f position; + Eigen::Matrix3f rotation; + float collisionRadius = 2.0f; + bool destroyed = false; + }; - struct LocalProjectile { - int shooterId = -1; - uint64_t spawnMs = 0; - Eigen::Vector3f pos; - Eigen::Vector3f vel; - float lifeMs = 5000.0f; - }; + struct LocalProjectile { + int shooterId = -1; + uint64_t spawnMs = 0; + Eigen::Vector3f pos; + Eigen::Vector3f vel; + float lifeMs = 5000.0f; + }; - class LocalClient : public INetworkClient { - private: - std::queue messageQueue; - std::vector serverBoxes; - std::vector projectiles; - std::vector pendingProjectiles; - std::vector pendingDeaths; - std::vector pendingBoxDestructions; - std::vector pendingRespawns; + struct LocalNPC { + int id = -1; + ClientState currentState; + ClientStateInterval stateHistory; + Eigen::Vector3f targetPosition; + float speed = 30.0f; + uint64_t lastStateUpdateMs = 0; + }; - uint64_t lastUpdateMs = 0; - ClientState localPlayerState; - bool hasLocalPlayerState = false; + class LocalClient : public INetworkClient { + private: + std::queue messageQueue; + std::vector serverBoxes; + std::vector projectiles; + std::vector pendingProjectiles; + std::vector pendingDeaths; + std::vector pendingBoxDestructions; + std::vector pendingRespawns; + + uint64_t lastUpdateMs = 0; + ClientState localPlayerState; + bool hasLocalPlayerState = false; + + std::vector npcs; + + void updatePhysics(); + void checkCollisions(); + void generateBoxes(); + void initializeNPCs(); + void updateNPCs(); + Eigen::Vector3f generateRandomPosition(); + + public: + void Connect(const std::string& host, uint16_t port) override; - void updatePhysics(); - void checkCollisions(); - void generateBoxes(); + void Poll() override; - public: - void Connect(const std::string& host, uint16_t port) override; + void Send(const std::string& message) override; - void Poll() override; + bool IsConnected() const override { return true; } + int GetClientId() const override { return 1; } + std::vector getPendingProjectiles() override; - void Send(const std::string& message) override; + std::unordered_map getRemotePlayers() override; - bool IsConnected() const override { return true; } - int GetClientId() const override { return 1; } - std::vector getPendingProjectiles() override; + std::vector> getServerBoxes() override; - std::unordered_map getRemotePlayers() override { - return std::unordered_map(); - } + std::vector getPendingDeaths() override; - std::vector> getServerBoxes() override; + std::vector getPendingRespawns() override { + return {}; + } - std::vector getPendingDeaths() override; + std::vector getPendingBoxDestructions() override; - std::vector getPendingRespawns() override { - return {}; - } - - std::vector getPendingBoxDestructions() override; - - void setLocalPlayerState(const ClientState& state) { - localPlayerState = state; - hasLocalPlayerState = true; - } - }; + void setLocalPlayerState(const ClientState& state) { + localPlayerState = state; + hasLocalPlayerState = true; + } + }; } \ No newline at end of file