Air-Trap 1.0.0
A multiplayer R-Type clone game engine built with C++23 and ECS architecture
Loading...
Searching...
No Matches
LevelSystem.cpp
Go to the documentation of this file.
1/*
2** EPITECH PROJECT, 2025
3** Air-Trap
4** File description:
5** LevelSystem
6*/
7
9#include "RType/Logger.hpp"
14
15#include <cmath>
16
17namespace rtp::server {
18
20 // Public API
22
24 EntitySystem& entitySystem,
25 RoomSystem& roomSystem,
26 NetworkSyncSystem& networkSync)
27 : _registry(registry)
28 , _entitySystem(entitySystem)
29 , _roomSystem(roomSystem)
30 , _networkSync(networkSync)
31 {
32 }
33
34 void LevelSystem::registerLevelPath(uint32_t levelId, const std::string& path)
35 {
36 _levelPaths[levelId] = path;
37 }
38
39 std::optional<LevelData> LevelSystem::loadLevel(uint32_t levelId) const
40 {
41 auto it = _levelPaths.find(levelId);
42 if (it == _levelPaths.end()) {
43 log::warning("No level path registered for level {}", levelId);
44 return std::nullopt;
45 }
46
47 std::string error;
48 auto level = loadLevelFromFile(it->second, error);
49 if (!level) {
50 log::error("Failed to load level {}: {}", levelId, error);
51 return std::nullopt;
52 }
53
54 return level;
55 }
56
57 void LevelSystem::startLevelForRoom(uint32_t roomId, uint32_t levelId)
58 {
59 auto level = loadLevel(levelId);
60 if (!level) {
61 return;
62 }
63
64 ActiveLevel active{};
65 active.data = std::move(level.value());
66 _activeLevels[roomId] = std::move(active);
67 log::info("Loaded level {} for room {}", levelId, roomId);
68 }
69
70 void LevelSystem::stopLevelForRoom(uint32_t roomId)
71 {
72 _activeLevels.erase(roomId);
73 }
74
75 void LevelSystem::update(float dt)
76 {
77 constexpr float scrollSpeed = 120.0f;
78
79 for (auto& [roomId, active] : _activeLevels) {
80 auto room = _roomSystem.getRoom(roomId);
81 if (!room || room->getState() != Room::State::InGame) {
82 continue;
83 }
84
85 active.elapsed += dt;
86
87 auto getFrontPlayerX = [&]() -> float {
88 float maxX = 0.0f;
89 auto view = _registry.zipView<
93 >();
94 for (auto &&[tf, type, room] : view) {
95 if (room.id != roomId || type.type != net::EntityType::Player) {
96 continue;
97 }
98 if (tf.position.x > maxX) {
99 maxX = tf.position.x;
100 }
101 }
102 return maxX;
103 };
104
105 while (active.nextSpawn < active.data.spawns.size() &&
106 active.data.spawns[active.nextSpawn].atTime <= active.elapsed) {
107 const auto& spawn = active.data.spawns[active.nextSpawn];
108 Vec2f startPos = spawn.startPosition;
109 const int patternIndex = static_cast<int>(active.nextSpawn % 5);
110 const float xOffsets[5] = {0.0f, 100.0f, -40.0f, 80.0f, -80.0f};
111 startPos.x += xOffsets[patternIndex];
112 const float frontX = getFrontPlayerX();
113 const float minAhead = (spawn.type == net::EntityType::Boss || spawn.type == net::EntityType::Boss2)
114 ? 450.0f // boss plus éloigné
115 : 200.0f; // distance standard pour scouts/tanks/etc.
116 if (startPos.x < frontX + minAhead) {
117 startPos.x = frontX + minAhead;
118 }
119 auto entity = _entitySystem.createEnemyEntity(
120 roomId, startPos, spawn.pattern,
121 spawn.speed, spawn.amplitude, spawn.frequency, spawn.type);
122 spawnEntityForRoom(roomId, entity);
123
124 // If spawning a Boss, also spawn 4 BossShields in a semi-circle in front of it
125 if (spawn.type == net::EntityType::Boss) {
126 const float shieldRadius = 250.0f; // Distance from boss center (more separation)
127 // Semi-circle in front (left side, towards player): angles from 90° to 270°
128 const float angles[4] = {120.0f, 150.0f, 210.0f, 240.0f}; // 4 shields spread in front (left)
129
130 for (int i = 0; i < 4; i++) {
131 float angleRad = angles[i] * 3.14159f / 180.0f;
132 Vec2f shieldPos = {
133 startPos.x + shieldRadius * std::cos(angleRad),
134 startPos.y + shieldRadius * std::sin(angleRad)
135 };
136
137 auto shieldEntity = _entitySystem.createEnemyEntity(
138 roomId, shieldPos, spawn.pattern,
139 spawn.speed, spawn.amplitude, spawn.frequency,
141 spawnEntityForRoom(roomId, shieldEntity);
142 }
143 }
144
145 active.nextSpawn++;
146 }
147
148 while (active.nextPowerup < active.data.powerups.size() &&
149 active.data.powerups[active.nextPowerup].atTime <= active.elapsed) {
150 const auto& powerup = active.data.powerups[active.nextPowerup];
152 roomId, powerup.position, powerup.type, powerup.value, powerup.duration);
154 entity, ecs::components::Velocity{ {-scrollSpeed, 0.0f}, 0.0f }
155 );
156 spawnEntityForRoom(roomId, entity);
157 active.nextPowerup++;
158 }
159
160 while (active.nextObstacle < active.data.obstacles.size() &&
161 active.data.obstacles[active.nextObstacle].atTime <= active.elapsed) {
162 const auto& obstacle = active.data.obstacles[active.nextObstacle];
164 roomId, obstacle.position, obstacle.size, obstacle.health, obstacle.type);
166 entity, ecs::components::Velocity{ {-scrollSpeed, 0.0f}, 0.0f }
167 );
168 spawnEntityForRoom(roomId, entity);
169 active.nextObstacle++;
170 }
171 }
172 }
173
175 // Private API
177
178 void LevelSystem::spawnEntityForRoom(uint32_t roomId, const ecs::Entity& entity)
179 {
180 auto room = _roomSystem.getRoom(roomId);
181 if (!room) {
182 return;
183 }
184
185 const auto players = room->getPlayers();
186 if (players.empty()) {
187 return;
188 }
189
190 std::vector<uint32_t> sessions;
191 sessions.reserve(players.size());
192 for (const auto& player : players) {
193 sessions.push_back(player->getId());
194 }
195
196 auto transformRes = _registry.get<ecs::components::Transform>();
197 auto typeRes = _registry.get<ecs::components::EntityType>();
200 if (!transformRes || !typeRes || !netRes) {
201 log::error("Missing component array for level spawn");
202 return;
203 }
204
205 auto &transforms = transformRes->get();
206 auto &types = typeRes->get();
207 auto &nets = netRes->get();
208 auto *boxes = boxRes ? &boxRes->get() : nullptr;
209 if (!transforms.has(entity) || !types.has(entity) || !nets.has(entity)) {
210 log::error("Level spawned entity {} missing Transform/EntityType/NetworkId", entity.index());
211 return;
212 }
213
214 const auto &transform = transforms[entity];
215 const auto &type = types[entity];
216 const auto &net = nets[entity];
217 float sizeX = 0.0f;
218 float sizeY = 0.0f;
219 if (boxes && boxes->has(entity)) {
220 const auto &box = (*boxes)[entity];
221 sizeX = box.width;
222 sizeY = box.height;
223 }
224
226 net::EntitySpawnPayload payload = {
227 net.id,
228 static_cast<uint8_t>(type.type),
229 transform.position.x,
230 transform.position.y,
231 sizeX,
232 sizeY
233 };
234 packet << payload;
236 }
237
238 const LevelData* LevelSystem::getLevelData(uint32_t roomId) const
239 {
240 auto it = _activeLevels.find(roomId);
241 if (it == _activeLevels.end()) {
242 return nullptr;
243 }
244 return &it->second.data;
245 }
246
247} // namespace rtp::server
Logger declaration with support for multiple log levels.
Represents an entity in the ECS (Entity-Component-System) architecture.
Definition Entity.hpp:63
constexpr std::uint32_t index(void) const
auto add(Entity entity, Args &&...args) -> std::expected< std::reference_wrapper< T >, rtp::Error >
auto get(this const Self &self) -> std::expected< std::reference_wrapper< ConstLike< Self, SparseArray< T > > >, rtp::Error >
auto zipView(this Self &self)
Network packet with header and serializable body.
Definition Packet.hpp:471
Base class for systems that operate on entities in the ECS.
ecs::Entity createObstacleEntity(uint32_t roomId, const Vec2f &pos, const Vec2f &size, int health, net::EntityType type=net::EntityType::Obstacle)
Create a new obstacle entity in the ECS.
ecs::Entity createEnemyEntity(uint32_t roomId, const Vec2f &pos, ecs::components::Patterns pattern, float speed, float amplitude, float frequency, net::EntityType type=net::EntityType::Scout)
Create a new enemy entity in the ECS.
ecs::Entity createPowerupEntity(uint32_t roomId, const Vec2f &pos, ecs::components::PowerupType type, float value, float duration)
Create a new powerup entity in the ECS.
void stopLevelForRoom(uint32_t roomId)
Stop the level for a specific room.
const LevelData * getLevelData(uint32_t roomId) const
Get the level data for a specific room.
LevelSystem(ecs::Registry &registry, EntitySystem &entitySystem, RoomSystem &roomSystem, NetworkSyncSystem &networkSync)
Constructor for LevelSystem.
void spawnEntityForRoom(uint32_t roomId, const ecs::Entity &entity)
Spawn an entity for a specific room.
RoomSystem & _roomSystem
Reference to the RoomSystem.
std::unordered_map< uint32_t, std::string > _levelPaths
Level file paths mapped by level ID.
void startLevelForRoom(uint32_t roomId, uint32_t levelId)
Start a level for a specific room.
std::optional< LevelData > loadLevel(uint32_t levelId) const
Load level data from a file.
NetworkSyncSystem & _networkSync
Reference to the NetworkSyncSystem.
ecs::Registry & _registry
Reference to the entity registry.
void registerLevelPath(uint32_t levelId, const std::string &path)
Register a level file path with a level ID.
EntitySystem & _entitySystem
Reference to the EntitySystem.
void update(float dt)
Update system logic for one frame.
std::unordered_map< uint32_t, ActiveLevel > _activeLevels
Active levels mapped by room ID.
System to handle network-related operations on the server side.
void sendPacketToSessions(const std::vector< uint32_t > &sessions, const net::Packet &packet, net::NetworkMode mode)
Send a packet to multiple sessions.
System to handle room-related operations on the server side.
std::shared_ptr< Room > getRoom(uint32_t roomId)
Get a room by its ID.
@ InGame
Game in progress.
Definition Room.hpp:43
File : Network.hpp License: MIT Author : Elias Josué HAJJAR LLAUQUEN elias-josue.hajjar-llauquen@epit...
void error(LogFmt< std::type_identity_t< Args >... > fmt, Args &&...args) noexcept
Log an error message.
void warning(LogFmt< std::type_identity_t< Args >... > fmt, Args &&...args) noexcept
Log a warning message.
void info(LogFmt< std::type_identity_t< Args >... > fmt, Args &&...args) noexcept
Log an informational message.
@ EntitySpawn
Entity spawn notification.
File : GameManager.hpp License: MIT Author : Elias Josué HAJJAR LLAUQUEN elias-josue....
std::optional< LevelData > loadLevelFromFile(const std::string &path, std::string &error)
Component representing a network identifier for an entity.
Definition NetworkId.hpp:22
Component representing a network identifier for an entity.
Definition RoomId.hpp:22
Component representing position, rotation, and scale of an entity.
Definition Transform.hpp:23
Component representing a 2D velocity.
Definition Velocity.hpp:17
Entity spawn notification data.
Definition Packet.hpp:348
Struct to hold active level data for a room.