Air-Trap 1.0.0
A multiplayer R-Type clone game engine built with C++23 and ECS architecture
Loading...
Searching...
No Matches
Application.cpp
Go to the documentation of this file.
1/*
2** EPITECH PROJECT, 2025
3** Air-Trap, Client
4** File description:
5**
6 * Application.cpp, main application loop
7*/
8
12
13namespace rtp::client
14{
15
17 // Public API
19
20 Application::Application(const std::string& serverIp, uint16_t serverPort)
21 : _window(sf::VideoMode(
22 {static_cast<unsigned int>(UIConstants::WINDOW_WIDTH),
23 static_cast<unsigned int>(UIConstants::WINDOW_HEIGHT)}),
24 "Air-Trap")
25 , _uiSystemManager(_uiRegistry)
26 , _worldSystemManager(_worldRegistry)
27 , _uiEntityBuilder(_uiRegistry)
28 , _worldEntityBuilder(_worldRegistry)
29 , _clientNetwork(serverIp, serverPort)
30 {
31 _window.setFramerateLimit(60);
32
33 _translations.loadLanguage(_settings.getLanguage());
34
35 setupSettingsCallbacks();
36
37 if (!_colorblindShader.loadFromFile("assets/shaders/colorblind.frag",
38 sf::Shader::Type::Fragment)) {
39 log::warning("Failed to load colorblind shader, running "
40 "without colorblind support");
41 _shaderLoaded = false;
42 } else {
43 _shaderLoaded = true;
44 log::info("Colorblind shader loaded successfully");
45 }
46
47 if (!_renderTexture.resize(
48 {static_cast<unsigned int>(UIConstants::WINDOW_WIDTH),
49 static_cast<unsigned int>(UIConstants::WINDOW_HEIGHT)})) {
50 log::error("Failed to create render texture");
51 }
52
53 // _audioManager.setMasterVolume(_settings.getMasterVolume());
54 // _audioManager.setMusicVolume(_settings.getMusicVolume());
55 // _audioManager.setSfxVolume(_settings.getSfxVolume());
56
57 try {
58 _clientNetwork.start();
59 } catch (const std::exception& e) {
60 throw std::runtime_error(
61 std::string("Failed to start client network: ") + e.what());
62 }
63 initUiSystems();
64 initWorldSystems();
65 initUiECS();
66 initWorldECS();
67 initScenes();
68 changeState(GameState::Login);
69 }
70
72 {
73 sf::Clock clock;
74 while (_window.isOpen()) {
75 sf::Time deltaTime = clock.restart();
77 update(deltaTime);
78 render();
79 }
80 }
81
83 // Private API
85
87 {
93 auto& worldAudioSystem = _worldSystemManager.add<AudioSystem>(_worldRegistry);
94 worldAudioSystem.setMasterVolume(_settings.getMasterVolume());
95 worldAudioSystem.setMusicVolume(_settings.getMusicVolume());
96 worldAudioSystem.setSfxVolume(_settings.getSfxVolume());
97 auto& worldRenderSystem = _worldSystemManager.add<RenderSystem>(_worldRegistry, _window);
99
100 // Update SpriteCustomizer with both render systems for cache clearing
101 auto& uiRenderSystem = _uiSystemManager.getSystem<systems::UIRenderSystem>();
102 SpriteCustomizer::setRenderSystems(&worldRenderSystem, &uiRenderSystem);
103
104 log::info("OK: World systems initialized");
105 }
106
108 {
110 auto& uiAudioSystem = _uiSystemManager.add<AudioSystem>(_uiRegistry);
111 uiAudioSystem.setMasterVolume(_settings.getMasterVolume());
112 uiAudioSystem.setMusicVolume(_settings.getMusicVolume());
113 uiAudioSystem.setSfxVolume(_settings.getSfxVolume());
114 // _uiSystemManager.add<systems::SettingsMenuSystem>(_uiRegistry, _window, _settings);
116
117 // Register UI render system with SpriteCustomizer for cache clearing
118 SpriteCustomizer::setRenderSystems(nullptr, &uiRenderSystem);
119
120 log::info("OK: UI systems initialized");
121 }
122
135
152
154 {
155 auto changeStateCb = [this](GameState s) { this->changeState(s); };
156 try {
158
159 _scenes[GameState::Login] = std::make_unique<scenes::LoginScene>(
161 );
162 _scenes[GameState::Menu] = std::make_unique<scenes::MenuScene>(
164 );
165 _scenes[GameState::Lobby] = std::make_unique<scenes::LobbyScene>(
167 );
168 _scenes[GameState::CreateRoom] = std::make_unique<scenes::CreateRoomScene>(
170 );
171 _scenes[GameState::RoomWaiting] = std::make_unique<scenes::RoomWaitingScene>(
173 );
174 _scenes[GameState::Settings] = std::make_unique<scenes::SettingsScene>(
176 );
177 _scenes[GameState::KeyBindings] = std::make_unique<scenes::KeyBindingScene>(
179 );
180 _scenes[GameState::GamepadSettings] = std::make_unique<scenes::GamepadSettingsScene>(
182 );
183 _scenes[GameState::ModMenu] = std::make_unique<scenes::ModMenuScene>(
185 );
186 _scenes[GameState::Paused] = std::make_unique<scenes::PauseScene>(
188 );
189 _scenes[GameState::GameOver] = std::make_unique<scenes::GameOverScene>(
191 );
192 _scenes[GameState::Playing] = std::make_unique<scenes::PlayingScene>(
194 );
195 } catch (const std::exception& e) {
196 log::error("Failed to get NetworkSyncSystem: {}", e.what());
197 throw;
198 }
199
200 log::info("OK: Scenes initialized");
201 }
202
204 {
205 log::info("Changing state from {} to {}",
206 static_cast<int>(_currentState),
207 static_cast<int>(newState));
208
209 if (newState == _currentState) return;
210
211 if (_activeScene) {
213 }
214
216 auto &worldAudio = _worldSystemManager.getSystem<AudioSystem>();
217 worldAudio.stopAllSounds();
218 }
219
220 if (newState == GameState::Playing) {
221 auto &uiAudio = _uiSystemManager.getSystem<AudioSystem>();
222 uiAudio.stopAllSounds();
224 } else {
225 std::vector<ecs::Entity> audioSourcesToKeep;
226 std::vector<ecs::components::audio::AudioSource> audioSourceData;
227
229 if (audioSources) {
230 auto& sources = audioSources.value().get();
231 for (const auto& entity : sources.entities()) {
232 audioSourcesToKeep.push_back(entity);
233 audioSourceData.push_back(sources[entity]);
234 }
235 }
236
238
239 for (size_t i = 0; i < audioSourcesToKeep.size(); ++i) {
240 _uiRegistry.add<ecs::components::audio::AudioSource>(audioSourcesToKeep[i], audioSourceData[i]);
241 }
242 }
243
244 if (newState != GameState::Playing && newState != GameState::Paused) {
246 }
247
248 // Clear texture caches when leaving ModMenu or entering Playing
249 // This ensures custom sprite changes (including resets) are applied immediately
251 log::info("Clearing texture caches to apply sprite changes");
252 _worldSystemManager.getSystem<RenderSystem>().clearTextureCache();
254 }
255
256 auto it = _scenes.find(newState);
257 if (it != _scenes.end()) {
258 _activeScene = it->second.get();
260 } else {
261 log::warning("No scene found for state {}, cannot change state",
262 static_cast<int>(newState));
263 return;
264 }
265
266 _currentState = newState;
267 }
268
270 {
271 while (auto eventOpt = _window.pollEvent()) {
272 const sf::Event& event = *eventOpt;
273
274 if (event.is<sf::Event::Closed>()) {
275 _window.close();
276 continue;
277 }
278
279 if (_activeScene)
281
282 _uiSystemManager.getSystem<UISystem>().handleEvent(event);
283 }
284 }
285
286 void Application::update(sf::Time delta)
287 {
288 _lastDt = delta.asSeconds();
289 const float dt = _lastDt;
290
291 if (_activeScene) {
292 _activeScene->update(dt);
293 }
294
296
298 }
299
300
302 {
303 _settings.onMasterVolumeChanged([this](float volume) {
304 log::info("Master volume changed to: {:.2f}", volume);
305 _worldSystemManager.getSystem<AudioSystem>().setMasterVolume(volume);
306 _uiSystemManager.getSystem<AudioSystem>().setMasterVolume(volume);
307 });
308
309 _settings.onMusicVolumeChanged([this](float volume) {
310 log::info("Music volume changed to: {:.2f}", volume);
311 _worldSystemManager.getSystem<AudioSystem>().setMusicVolume(volume);
312 _uiSystemManager.getSystem<AudioSystem>().setMusicVolume(volume);
313 });
314
315 _settings.onSfxVolumeChanged([this](float volume) {
316 log::info("SFX volume changed to: {:.2f}", volume);
317 _worldSystemManager.getSystem<AudioSystem>().setSfxVolume(volume);
318 _uiSystemManager.getSystem<AudioSystem>().setSfxVolume(volume);
319 });
320
322 log::info("Language changed to: {}", static_cast<int>(lang));
323
325
326 if (_activeScene) {
329 }
330 });
331 }
332
334 {
335 _window.clear(sf::Color::Black);
336
338
340 sf::RectangleShape overlay({UIConstants::WINDOW_WIDTH, UIConstants::WINDOW_HEIGHT});
341 overlay.setPosition({0.0f, 0.0f});
342
343 switch (_settings.getColorBlindMode()) {
344 case ColorBlindMode::Protanopia: overlay.setFillColor(sf::Color(255, 150, 100, 30)); break;
345 case ColorBlindMode::Deuteranopia: overlay.setFillColor(sf::Color(200, 255, 100, 30)); break;
346 case ColorBlindMode::Tritanopia: overlay.setFillColor(sf::Color(255, 100, 200, 30)); break;
347 default: break;
348 }
349 _window.draw(overlay);
350 }
351
353
354 // Only show gamepad cursor in menus, not in-game (Playing state)
356 _uiSystemManager.getSystem<UISystem>().renderGamepadCursor(_window);
357 }
358
359 _window.display();
360 }
361}
ecs::Registry _worldRegistry
ECS registry for the game world.
bool _shaderLoaded
Flag indicating if the shader was loaded successfully.
float _lastDt
Last delta time value.
ecs::SystemManager _worldSystemManager
ECS system manager for the game world.
void initScenes(void)
Initialize all application scenes.
EntityBuilder _worldEntityBuilder
Entity builder for world entities.
std::unordered_map< GameState, std::unique_ptr< interfaces::IScene > > _scenes
Map of game states to their corresponding scenes.
void update(sf::Time delta)
Update the application state.
ecs::SystemManager _uiSystemManager
ECS system manager for the UI.
void setupSettingsCallbacks()
Setup callbacks for settings changes.
void processInput(void)
Handle input events.
ecs::Registry _uiRegistry
ECS registry for the UI.
ClientNetwork _clientNetwork
Client network manager.
graphics::UiFactory _uiFactory
UI Factory for creating UI components.
void changeState(GameState newState)
Change the current game state and active scene.
void initWorldECS(void)
Initialize the ECS and systems for the game world.
Application(const std::string &serverIp, uint16_t serverPort)
Construct a new Application object.
sf::RenderWindow _window
SFML render window.
void initUiSystems(void)
Initialize the game world systems.
Settings _settings
Application settings manager.
void initUiECS(void)
Initialize the ECS and systems for the UI.
void initWorldSystems(void)
Initialize the UI systems.
void run(void)
Run the main application loop.
void render(void)
Render the current scene.
GameState _currentState
Current game state.
interfaces::IScene * _activeScene
Pointer to the currently active scene.
TranslationManager _translations
Translation manager for localization.
System responsible for handling audio playback (music, SFX, ambient)
void stopAllSounds()
Stop all currently playing sounds.
System to handle network-related operations on the client side.
void onLanguageChanged(LanguageCallback callback)
Definition Settings.hpp:123
float getMusicVolume() const
Definition Settings.hpp:82
ColorBlindMode getColorBlindMode() const
Definition Settings.hpp:128
void onSfxVolumeChanged(VolumeCallback callback)
Definition Settings.hpp:108
float getMasterVolume() const
Definition Settings.hpp:75
void onMusicVolumeChanged(VolumeCallback callback)
Definition Settings.hpp:103
float getSfxVolume() const
Definition Settings.hpp:89
void onMasterVolumeChanged(VolumeCallback callback)
Definition Settings.hpp:98
System responsible for managing shield visual effects.
static void setRenderSystems(void *worldRenderSystem, void *uiRenderSystem)
Set render system pointers for cache clearing Called internally by Application during initialization.
bool loadLanguage(Language language)
Load translations for a specific language.
System responsible for handling menu interactions.
Definition UISystem.hpp:31
virtual void onEnter(void)=0
Called when the scene is entered.
virtual void handleEvent(const sf::Event &)=0
Handle an incoming event.
virtual void onExit(void)=0
Called when the scene is exited.
virtual void update(float dt)=0
Update the scene state.
auto subscribe(this Self &self) -> std::expected< std::reference_wrapper< ConstLike< Self, SparseArray< T > > >, rtp::Error >
void clear(void) noexcept
Definition Registry.cpp:102
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 >
T & add(Args &&...args)
File : Network.hpp License: MIT Author : Elias Josué HAJJAR LLAUQUEN elias-josue.hajjar-llauquen@epit...
constexpr float WINDOW_WIDTH
Standard window width.
constexpr float WINDOW_HEIGHT
Standard window height.
R-Type client namespace.
@ RoomWaiting
Room waiting state.
@ GameOver
Game over state.
@ CreateRoom
Create room state.
@ Login
Login screen state.
@ GamepadSettings
Gamepad settings configuration state.
@ Menu
Main menu state.
@ KeyBindings
Key bindings configuration state.
@ Playing
In-game playing state.
@ ModMenu
Mod menu state.
@ Paused
Game paused state.
@ Settings
Settings menu state.
Language
Supported languages.
Definition Settings.hpp:33
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.
Component representing an animation.
Definition Animation.hpp:17
Component indicating that an entity can be controlled by a player.
Component representing a network identifier for an entity.
Definition NetworkId.hpp:22
Tag component indicating that an entity should loop its position when it exits the screen (infinite s...
Component for visual shield effect displayed around the player.
Component representing a sprite.
Definition Sprite.hpp:21
Component representing position, rotation, and scale of an entity.
Definition Transform.hpp:23
Component representing a 2D velocity.
Definition Velocity.hpp:17
Component for entities that emit continuous sound (music, loops, ambient)
Component for triggering one-time sound effects (SFX)
Component representing a clickable button.
Definition Button.hpp:30
Component for dropdown menu selection.
Definition Dropdown.hpp:21
Component for a draggable slider control.
Definition Slider.hpp:19
Component for displaying a sprite preview in the UI.
Component representing text to render.
Definition Text.hpp:19