R-Type
Distributed multiplayer game engine in C++
Loading...
Searching...
No Matches
EntityRenderer.cpp
Go to the documentation of this file.
1/*
2** EPITECH PROJECT, 2025
3** rtype
4** File description:
5** EntityRenderer.cpp - Implementation of entity rendering system
6*/
7
8#include "EntityRenderer.hpp"
9#include <chrono>
10#include <cmath>
11#include "../common/Logger/Logger.hpp"
12
14 LOG_DEBUG("EntityRenderer created");
15}
16
18 int health, const std::string &currentAnimation, int srcX, int srcY,
19 int srcW, int srcH, float velocityX, float velocityY, uint32_t serverTick) {
20 uint64_t currentTime = getCurrentTimeMs();
21
22 auto it = _entities.find(id);
23 if (it != _entities.end()) {
24 bool isLocalPlayer = (id == _myEntityId);
25
26 // Always update type and health first (critical data)
27 it->second.type = type;
28 it->second.health = health;
29
30 // Check if entity is a projectile (bullets should NOT be interpolated)
31 bool isProjectile = (type == RType::Messages::Shared::EntityType::PlayerBullet ||
33
34 if (isLocalPlayer && _clientSidePredictionEnabled) {
35 // CLIENT-SIDE PREDICTION for local player
36 float errorX = x - it->second.x;
37 float errorY = y - it->second.y;
38 float errorDistance = std::sqrt(errorX * errorX + errorY * errorY);
39
40 // ADAPTIVE MICRO-JITTER FILTERING
41 // When player IS MOVING: Apply strict filtering to avoid jitter during active movement
42 // When player IS STOPPED: Accept small corrections to prevent drift accumulation
43 float jitterThreshold = _localPlayerIsMoving ? 2.0f : 0.5f;
44
45 if (errorDistance < jitterThreshold) {
46 // When stopped, we still want to sync positions, just not create visible jumps
47 // So we'll do a very gentle correction instead of ignoring it completely
48 if (!_localPlayerIsMoving && errorDistance > 0.1f) {
49 // Gentle correction: slowly drift towards server position
50 it->second.prevX = it->second.x;
51 it->second.prevY = it->second.y;
52 it->second.targetX = x;
53 it->second.targetY = y;
54 it->second.interpolationFactor = 0.5f; // Start halfway for very smooth transition
55 }
56 return; // Don't apply large corrections for micro-jitter
57 }
58
59 // Only reconcile when error exceeds threshold
60 if (errorDistance > _reconciliationThreshold) {
61 // Significant desync detected - smooth correction needed
62 it->second.prevX = it->second.x;
63 it->second.prevY = it->second.y;
64 it->second.targetX = x;
65 it->second.targetY = y;
66 it->second.interpolationFactor = 0.0f;
67 }
68 // Otherwise keep predicted position - client knows best!
69 } else if (_interpolationEnabled && !isProjectile) {
70 // TIME-BASED INTERPOLATION for other entities (but NOT projectiles)
71 // Projectiles move too fast (300 units/sec) for smooth interpolation
72 // Add new snapshot to buffer
74 snapshot.x = x;
75 snapshot.y = y;
76 snapshot.velocityX = velocityX;
77 snapshot.velocityY = velocityY;
78 snapshot.timestamp = currentTime;
79 snapshot.serverTick = serverTick;
80
81 it->second.snapshots.push_back(snapshot);
82
83 // Keep only last 3 snapshots
84 while (it->second.snapshots.size() > 3) {
85 it->second.snapshots.pop_front();
86 }
87
88 // Also update legacy fields for smooth transition
89 it->second.prevX = it->second.x;
90 it->second.prevY = it->second.y;
91 it->second.targetX = x;
92 it->second.targetY = y;
93 it->second.interpolationFactor = 0.0f;
94 } else {
95 // No interpolation - snap directly (for projectiles and when disabled)
96 it->second.x = x;
97 it->second.y = y;
98 }
99
100 // Always update sprite coords and animation
101 it->second.currentAnimation = currentAnimation;
102 it->second.startPixelX = srcX;
103 it->second.startPixelY = srcY;
104 it->second.spriteSizeX = srcW;
105 it->second.spriteSizeY = srcH;
106 } else {
107 // Create new entity
108 RenderableEntity newEntity;
109 newEntity.entityId = id;
110 newEntity.type = type;
111 newEntity.x = x;
112 newEntity.y = y;
113 newEntity.health = health;
114 newEntity.interpolationDelay = 100; // 100ms interpolation delay
115 newEntity.extrapolationEnabled = true;
116 newEntity.prevX = x;
117 newEntity.prevY = y;
118 newEntity.targetX = x;
119 newEntity.targetY = y;
120 newEntity.interpolationFactor = 1.0f;
121 newEntity.startPixelX = srcX;
122 newEntity.startPixelY = srcY;
123 newEntity.spriteSizeX = srcW;
124 newEntity.spriteSizeY = srcH;
125 newEntity.offsetX = 0;
126 newEntity.offsetY = 0;
127 newEntity.scale = 3.0f;
128 newEntity.currentAnimation = currentAnimation;
129 newEntity.currentFrame = 0;
130
131 // Add initial snapshot
133 snapshot.x = x;
134 snapshot.y = y;
135 snapshot.velocityX = velocityX;
136 snapshot.velocityY = velocityY;
137 snapshot.timestamp = currentTime;
138 snapshot.serverTick = serverTick;
139 newEntity.snapshots.push_back(snapshot);
140
141 _entities[id] = newEntity;
142 LOG_DEBUG("Entity created: ID=", id, " Type=", static_cast<int>(type), " at (", x, ",", y, ")");
143 }
144}
145
147 auto it = _entities.find(id);
148 if (it != _entities.end()) {
149 LOG_DEBUG("Entity removed: ID=", id);
150 _entities.erase(it);
151 }
152}
153
155 LOG_INFO("Clearing all entities (", _entities.size(), " total)");
156 _entities.clear();
157}
158
159void EntityRenderer::setBackground(const std::string &mainBackground, const std::string &parallaxBackground,
160 float scrollSpeed, float parallaxSpeedFactor) {
161 // Clear previous backgrounds
163
164 // Always activate background (even with just black)
165 _backgroundActive = true;
166
167 // Configure main background (if provided)
168 if (!mainBackground.empty()) {
169 _mainBackground.texturePath = mainBackground;
170 _mainBackground.textureName = "bg_main";
171 _mainBackground.scrollSpeed = scrollSpeed;
173 _mainBackground.loaded = false;
174
175 // Load texture
176 std::string fullPath = "assets/" + mainBackground;
177 if (_graphics.LoadTexture(_mainBackground.textureName.c_str(), fullPath.c_str()) == 0) {
180 _mainBackground.loaded = true;
181 LOG_INFO("Loaded main background: ", fullPath, " (", _mainBackground.textureWidth, "x",
183 } else {
184 LOG_WARNING("Failed to load main background: ", fullPath, " - using black background");
185 }
186 } else {
187 LOG_INFO("No main background defined - using black background");
188 }
189
190 // Configure parallax background (rendered on top, scrolls slower) - only if provided
191 if (!parallaxBackground.empty()) {
192 _parallaxBackground.texturePath = parallaxBackground;
193 _parallaxBackground.textureName = "bg_parallax";
194 _parallaxBackground.scrollSpeed = scrollSpeed * parallaxSpeedFactor;
197
198 // Load texture
199 std::string fullPath = "assets/" + parallaxBackground;
200 if (_graphics.LoadTexture(_parallaxBackground.textureName.c_str(), fullPath.c_str()) == 0) {
204 LOG_INFO("Loaded parallax background: ", fullPath, " (", _parallaxBackground.textureWidth, "x",
205 _parallaxBackground.textureHeight, ") speed factor: ", parallaxSpeedFactor);
206 } else {
207 LOG_WARNING("Failed to load parallax background: ", fullPath, " - no parallax layer");
208 }
209 }
210 // If no parallaxBackground provided, simply don't render any parallax layer (transparent)
211
212 LOG_INFO("Background system activated (main: ", _mainBackground.loaded ? "loaded" : "black",
213 ", parallax: ", _parallaxBackground.loaded ? "loaded" : "none", ")");
214}
215
228
229void EntityRenderer::updateBackground(float deltaTime) {
230 if (!_backgroundActive) {
231 return;
232 }
233
234 // Update scroll offsets (scrolling left = negative offset increases)
237 // Wrap around for seamless tiling
241 }
242 }
243 }
244
247 // Wrap around for seamless tiling
251 }
252 }
253 }
254}
255
257 int screenWidth = _graphics.GetWindowWidth();
258 int screenHeight = _graphics.GetWindowHeight();
259
260 // Always draw a black background first as base
261 _graphics.DrawRectFilled(0, 0, screenWidth, screenHeight, 0xFF000000);
262
263 if (!_backgroundActive) {
264 return;
265 }
266
267 // Render main background (bottom layer)
268 // Scale texture to fit screen height, calculate scaled width for tiling
270 // Calculate the scaled dimensions to fit screen height
271 float scale = static_cast<float>(screenHeight) / static_cast<float>(_mainBackground.textureHeight);
272 float scaledWidth = static_cast<float>(_mainBackground.textureWidth) * scale;
273
274 // Calculate how many tiles needed to cover screen + 1 extra for seamless scroll
275 int tilesNeeded = static_cast<int>(std::ceil(static_cast<float>(screenWidth) / scaledWidth)) + 2;
276
277 // Wrap scroll offset to prevent overflow
278 float wrappedOffset = std::fmod(_mainBackground.scrollOffset * scale, scaledWidth);
279 if (wrappedOffset < 0) {
280 wrappedOffset += scaledWidth;
281 }
282
283 for (int i = 0; i < tilesNeeded; i++) {
284 float drawX = (static_cast<float>(i) * scaledWidth) - wrappedOffset;
285
286 // Draw texture stretched to fit screen height, tiled horizontally
288 _mainBackground.textureHeight, drawX, 0.0f, scaledWidth,
289 static_cast<float>(screenHeight), 0xFFFFFFFF);
290 }
291 }
292
293 // Render parallax background on top (overlay layer - only if loaded)
294 // This layer should have transparency in the texture (e.g., stars with transparent background)
297 float scale =
298 static_cast<float>(screenHeight) / static_cast<float>(_parallaxBackground.textureHeight);
299 float scaledWidth = static_cast<float>(_parallaxBackground.textureWidth) * scale;
300
301 int tilesNeeded = static_cast<int>(std::ceil(static_cast<float>(screenWidth) / scaledWidth)) + 2;
302
303 float wrappedOffset = std::fmod(_parallaxBackground.scrollOffset * scale, scaledWidth);
304 if (wrappedOffset < 0) {
305 wrappedOffset += scaledWidth;
306 }
307
308 for (int i = 0; i < tilesNeeded; i++) {
309 float drawX = (static_cast<float>(i) * scaledWidth) - wrappedOffset;
310
313 drawX, 0.0f, scaledWidth, static_cast<float>(screenHeight), 0xFFFFFFFF);
314 }
315 }
316}
317
319 _myEntityId = id;
320 LOG_INFO("Local player entity ID set to: ", id);
321 LOG_DEBUG("_myEntityId is now: ", _myEntityId);
322}
323
325 // Always render background first (even if no entities)
327
328 if (_entities.empty()) {
329 return;
330 }
331
332 // Debug: count entities by type once per second
333 static int frameCount = 0;
334 if (++frameCount % 60 == 0) {
335 int wallCount = 0, playerCount = 0, enemyCount = 0;
336 for (const auto &[id, entity] : _entities) {
338 wallCount++;
339 else if (entity.type == RType::Messages::Shared::EntityType::Player)
340 playerCount++;
342 enemyCount++;
343 }
344 LOG_INFO("EntityRenderer: Rendering ", _entities.size(), " entities - Players:", playerCount,
345 " Enemies:", enemyCount, " Walls:", wallCount);
346 }
347
348 // Note: Interpolation is updated separately via updateInterpolation()
349 // which should be called from GameLoop before render()
350
351 for (const auto &[id, entity] : _entities) {
352 switch (entity.type) {
354 renderPlayer(entity, id == _myEntityId);
355 break;
356
358 renderEnemy(entity);
359 break;
360
363 renderProjectile(entity);
364 break;
365
367 renderWall(entity);
368 break;
369
371 renderOrbitalModule(entity);
372 break;
373
374 default:
375 LOG_WARNING("Unknown entity type: ", static_cast<int>(entity.type));
376 break;
377 }
378 if (_showDebugInfo) {
379 renderDebugInfo(entity);
380 }
381 }
382}
383
384void EntityRenderer::renderPlayer(const RenderableEntity &entity, bool isLocalPlayer) {
385 // Draw player sprite with animation
386 // R-Type player ship sprites are in the top-left area of r-typesheet1
387
388 // Source rectangle on the sprite sheet (frame from animation)
389 int srcX = entity.startPixelX;
390 int srcY = entity.startPixelY;
391 int srcWidth = entity.spriteSizeX > 0 ? entity.spriteSizeX : 33;
392 int srcHeight = entity.spriteSizeY > 0 ? entity.spriteSizeY : 17;
393
394 // Scale up 3x for better visibility
395 float scale = entity.scale > 0.0f ? entity.scale : 3.0f;
396
397 // Visual differentiation: tint green for local player
398 uint32_t tint = isLocalPlayer ? 0xFF008000 : 0xFFFFFFFF;
399
400 _graphics.DrawTextureEx("PlayerShips.gif", srcX, srcY, srcWidth, srcHeight,
401 entity.x - (srcWidth * scale / 2), entity.y - (srcHeight * scale / 2), 0.0f,
402 scale, tint);
403
404 // Render health bar if entity has health
405 if (entity.health > 0) {
406 renderHealthBar(entity.x, entity.y - 30.0f, entity.health, 100);
407 }
408
409 if (isLocalPlayer) {
410 // Show local player indicator
411 _graphics.DrawText(-1, "YOU", static_cast<int>(entity.x - 15), static_cast<int>(entity.y - 50), 14,
412 0x9DFF73AA);
413
414 // TODO: Add charge indicator
415 // When weapon is charging, show a progress bar or glow effect around the ship
416 // Example: Draw a circular progress bar based on charge level (0.0 to 1.0)
417 // if (entity.weaponChargeLevel > 0.0f) {
418 // renderChargeIndicator(entity.x, entity.y, entity.weaponChargeLevel);
419 // }
420 }
421}
422
424 // Red enemy visualization
425 // TODO: Load enemy sprite based on type (EnemyType1, EnemyType2, etc.)
426
427 // Placeholder: Red rectangle
428 uint32_t color = 0xFF0000FF; // Red
429 float halfSize = 12.0f;
430
431 _graphics.DrawRectFilled(static_cast<int>(entity.x - halfSize), static_cast<int>(entity.y - halfSize), 24,
432 24, color);
433
434 // Render health bar for enemies
435 if (entity.health > 0) {
436 renderHealthBar(entity.x, entity.y - 20.0f, entity.health, 50);
437 }
438}
439
441 // Draw projectile sprite with animation from sprite sheet
442
443 // Source rectangle on the sprite sheet (frame from animation)
444 int srcX = entity.startPixelX > 0 ? entity.startPixelX : 267;
445 int srcY = entity.startPixelY > 0 ? entity.startPixelY : 84;
446 int srcWidth = entity.spriteSizeX > 0 ? entity.spriteSizeX : 17;
447 int srcHeight = entity.spriteSizeY > 0 ? entity.spriteSizeY : 13;
448
449 // Debug log (only for first few frames)
450 static int debugCount = 0;
451 if (debugCount < 10) {
452 LOG_DEBUG("Projectile ", entity.entityId, ": sprite(", srcX, ",", srcY, ",", srcWidth, ",", srcHeight,
453 ") anim=", entity.currentAnimation);
454 debugCount++;
455 }
456
457 // Use scale from entity (server sends 2.0f for normal, 2.5f for charged)
458 float scale = entity.scale > 0.0f ? entity.scale : 2.0f;
459
460 // Different tint for enemy bullets
461 uint32_t tint = 0xFFFFFFFF;
463 tint = 0xFF5555FF; // Reddish tint for enemy bullets
464 }
465
466 _graphics.DrawTextureEx("Projectiles", srcX, srcY, srcWidth, srcHeight, entity.x - (srcWidth * scale / 2),
467 entity.y - (srcHeight * scale / 2), 0.0f, scale, tint);
468}
469
470void EntityRenderer::renderHealthBar(float x, float y, int health, int maxHealth) {
471 // Sanity check
472 if (maxHealth <= 0 || health < 0) {
473 return;
474 }
475
476 // Bar dimensions
477 float barWidth = 30.0f;
478 float barHeight = 4.0f;
479 float healthRatio = static_cast<float>(health) / static_cast<float>(maxHealth);
480
481 // Clamp health ratio to [0, 1]
482 if (healthRatio > 1.0f)
483 healthRatio = 1.0f;
484 if (healthRatio < 0.0f)
485 healthRatio = 0.0f;
486
487 // Calculate bar position (centered above entity)
488 float barX = x - barWidth / 2.0f;
489
490 // Background bar (damage indicator - red)
491 _graphics.DrawRectFilled(static_cast<int>(barX), static_cast<int>(y), static_cast<int>(barWidth),
492 static_cast<int>(barHeight), 0xFF0000FF);
493
494 // Foreground bar (remaining health - green)
495 _graphics.DrawRectFilled(static_cast<int>(barX), static_cast<int>(y),
496 static_cast<int>(barWidth * healthRatio), static_cast<int>(barHeight),
497 0x00FF00FF);
498
499 // Optional: Border around health bar for visibility
500 // _graphics.DrawRectangleBorder(barX, y, barWidth, barHeight, 1.0f, 0xFFFFFFFF);
501}
502
504 // Wall rendering with Wall.png texture
505 float width = entity.spriteSizeX > 0 ? static_cast<float>(entity.spriteSizeX) : 50.0f;
506 float height = entity.spriteSizeY > 0 ? static_cast<float>(entity.spriteSizeY) : 50.0f;
507
508 // Calculate position (top-left corner for drawing)
509 float x = entity.x - width / 2.0f;
510 float y = entity.y - height / 2.0f;
511
512 // Color tint: ABGR format
513 uint32_t tint = 0xFFFFFFFF; // White (no tint) by default
514 if (entity.health > 0) {
515 // Destructible wall - tint red when damaged
516 float healthRatio = entity.health / 100.0f;
517 uint8_t red = 255;
518 uint8_t green = static_cast<uint8_t>(255 * healthRatio);
519 uint8_t blue = static_cast<uint8_t>(255 * healthRatio);
520 // ABGR format: 0xAABBGGRR
521 tint = 0xFF000000 | (blue << 16) | (green << 8) | red;
522 }
523
524 // Draw Wall.png texture stretched to fit the wall dimensions
525 // For proper stretching, we need to use DrawTexturePro or tile it
526 // Let's just fill with solid color for now, more efficient for large walls
527 uint32_t wallColor = 0xFF13458B; // Brown color in ARGB (0xAARRGGBB)
528 if (entity.health > 0) {
529 wallColor = tint; // Use health-based tint for destructible walls
530 }
531
532 _graphics.DrawRectFilled(static_cast<int>(x), static_cast<int>(y), static_cast<int>(width),
533 static_cast<int>(height), wallColor);
534
535 // Draw border for visibility
536 _graphics.DrawRectangleLines(static_cast<int>(x), static_cast<int>(y), static_cast<int>(width),
537 static_cast<int>(height), 0xFF000000);
538
539 // If destructible, show health bar
540 if (entity.health > 0) {
541 renderHealthBar(entity.x, y - 10.0f, entity.health, 100);
542 }
543}
544
546 // Draw orbital module sprite with animation
547
548 // Source rectangle on the sprite sheet (frame from animation)
549 int srcX = entity.startPixelX;
550 int srcY = entity.startPixelY;
551 int srcWidth = entity.spriteSizeX > 0 ? entity.spriteSizeX : 17;
552 int srcHeight = entity.spriteSizeY > 0 ? entity.spriteSizeY : 18;
553
554 // Use scale from entity (server sends 2.0f)
555 float scale = entity.scale > 0.0f ? entity.scale : 2.0f;
556
557 // White tint (no color modification)
558 uint32_t tint = 0xFFFFFFFF;
559
560 _graphics.DrawTextureEx("OrbitalModule", srcX, srcY, srcWidth, srcHeight,
561 entity.x - (srcWidth * scale / 2), entity.y - (srcHeight * scale / 2), 0.0f,
562 scale, tint);
563
564 // Optional: Render health bar if it has health
565 if (entity.health > 0) {
566 renderHealthBar(entity.x, entity.y - 15.0f, entity.health, 50);
567 }
568}
569
571 // Display entity ID above the entity
572 std::string idText = "ID:" + std::to_string(entity.entityId);
573 float textX = entity.x - 15.0f;
574 float textY = entity.y - 45.0f;
575
576 _graphics.DrawText(-1, idText.c_str(), static_cast<int>(textX), static_cast<int>(textY), 10, 0xFFFFFFFF);
577
578 // Display health if applicable
579 if (entity.health > 0) {
580 std::string hpText = "HP:" + std::to_string(entity.health);
581 _graphics.DrawText(-1, hpText.c_str(), static_cast<int>(textX), static_cast<int>(textY - 12), 10,
582 0xFFFFFFFF);
583 }
584
585 // Display entity type (for debugging)
586 std::string typeText = "Type:" + std::to_string(static_cast<int>(entity.type));
587 _graphics.DrawText(-1, typeText.c_str(), static_cast<int>(textX), static_cast<int>(textY - 24), 10,
588 0xAAAAAAAA);
589}
590
593 return;
594 }
595
596 for (auto &[id, entity] : _entities) {
597 // Skip projectiles - they move too fast for interpolation (300 units/sec)
598 // Interpolation would cause visual "sliding" instead of smooth linear movement
601 continue;
602 }
603
604 // Skip if already at target
605 if (entity.interpolationFactor >= 1.0f) {
606 continue;
607 }
608
609 // Advance interpolation factor based on deltaTime and speed
610 entity.interpolationFactor += deltaTime * _interpolationSpeed;
611 entity.interpolationFactor = clamp(entity.interpolationFactor, 0.0f, 1.0f);
612
613 // Calculate interpolated position using linear interpolation
614 entity.x = lerp(entity.prevX, entity.targetX, entity.interpolationFactor);
615 entity.y = lerp(entity.prevY, entity.targetY, entity.interpolationFactor);
616 }
617}
618
619void EntityRenderer::moveEntityLocally(uint32_t entityId, float deltaX, float deltaY) {
620 auto it = _entities.find(entityId);
621 if (it == _entities.end()) {
622 return; // Entity doesn't exist
623 }
624
625 // Apply movement immediately to current position (prediction)
626 it->second.x += deltaX;
627 it->second.y += deltaY;
628
629 it->second.targetX += deltaX;
630 it->second.targetY += deltaY;
631 it->second.prevX += deltaX;
632 it->second.prevY += deltaY;
633}
634
635float EntityRenderer::lerp(float start, float end, float t) const {
636 return start + ((end - start) * t);
637}
638
639float EntityRenderer::clamp(float value, float min, float max) const {
640 if (value < min) {
641 return min;
642 }
643 if (value > max) {
644 return max;
645 }
646 return value;
647}
648
650 auto now = std::chrono::steady_clock::now();
651 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch());
652 return static_cast<uint64_t>(duration.count());
653}
#define LOG_INFO(...)
Definition Logger.hpp:181
#define LOG_DEBUG(...)
Definition Logger.hpp:180
#define LOG_WARNING(...)
Definition Logger.hpp:182
void moveEntityLocally(uint32_t entityId, float deltaX, float deltaY)
Move an entity locally (client-side prediction)
std::unordered_map< uint32_t, RenderableEntity > _entities
Entity cache: maps entity ID to its renderable state.
void renderHealthBar(float x, float y, int health, int maxHealth)
Render a health bar above an entity.
void setMyEntityId(uint32_t id)
Set the local player's entity ID for visual differentiation.
void renderOrbitalModule(const RenderableEntity &entity)
Render an orbital module (drone)
void renderBackground()
Render scrolling background layers.
BackgroundConfig _mainBackground
Main background layer (scrolls at map's scroll speed)
void removeEntity(uint32_t id)
Remove an entity from the rendering cache.
Graphics::RaylibGraphics & _graphics
Reference to graphics subsystem for drawing operations.
void renderProjectile(const RenderableEntity &entity)
Render a projectile (player or enemy bullet)
void renderWall(const RenderableEntity &entity)
Render a wall/obstacle.
void renderDebugInfo(const RenderableEntity &entity)
Render debug information for an entity.
uint64_t getCurrentTimeMs() const
Get current time in milliseconds.
EntityRenderer(Graphics::RaylibGraphics &graphics)
Constructor.
void clearBackground()
Clear background configuration.
bool _localPlayerIsMoving
Track whether local player is currently moving (used for reconciliation logic)
void updateBackground(float deltaTime)
Update background scroll positions.
void setBackground(const std::string &mainBackground, const std::string &parallaxBackground, float scrollSpeed, float parallaxSpeedFactor)
Set up background layers for parallax scrolling.
uint32_t _myEntityId
Local player's entity ID (for visual differentiation)
bool _clientSidePredictionEnabled
Client-side prediction enabled flag (for local player only)
void renderPlayer(const RenderableEntity &entity, bool isLocalPlayer)
Render a player entity.
void updateEntity(uint32_t id, RType::Messages::Shared::EntityType type, float x, float y, int health, const std::string &currentAnimation, int srcX, int srcY, int srcW, int srcH, float velocityX=0.0f, float velocityY=0.0f, uint32_t serverTick=0)
Update or create an entity in the local cache.
float lerp(float start, float end, float t) const
Linear interpolation between two values.
float clamp(float value, float min, float max) const
Clamp a value between min and max.
bool _interpolationEnabled
Interpolation enabled flag.
void updateInterpolation(float deltaTime)
Update interpolation for all entities.
void clearAllEntities()
Clear all entities from the cache.
bool _backgroundActive
Whether backgrounds are configured and active.
void render()
Render all cached entities.
void renderEnemy(const RenderableEntity &entity)
Render an enemy entity.
bool _showDebugInfo
Debug mode: show entity IDs and health bars (toggle with F3)
BackgroundConfig _parallaxBackground
Parallax background layer (rendered on top, scrolls slower for depth effect)
Raylib implementation of the IGraphics interface.
void DrawRectFilled(int x, int y, int width, int height, unsigned int color) override
Draw a filled rectangle.
void DrawTextureEx(const char *textureName, int srcX, int srcY, int srcW, int srcH, float destX, float destY, float rotation, float scale, unsigned int tint) override
Draw a texture with advanced parameters (rotation, scale, source rectangle)
bool GetTextureSize(const char *textureName, int &width, int &height) const override
Get the dimensions of a loaded texture.
void UnloadTexture(const char *textureName) override
Unload a previously loaded texture.
void DrawTexturePro(const char *textureName, int srcX, int srcY, int srcW, int srcH, float destX, float destY, float destW, float destH, unsigned int tint) override
Draw a texture with separate width/height scaling (for non-uniform scaling)
void DrawRectangleLines(int x, int y, int width, int height, unsigned int color) override
Draw a rectangle outline (alias for DrawRect)
int GetWindowHeight() const override
Get current window height in pixels.
void DrawText(int fontHandle, const char *text, int x, int y, int fontSize, unsigned int color) override
Draw text using a loaded font.
int GetWindowWidth() const override
Get current window width in pixels.
int LoadTexture(const char *name, const char *filepath) override
Load a texture from an image file.
EntityType
Entity type enum - matches Cap'n Proto enum.
Configuration for scrolling parallax backgrounds.
int textureHeight
Texture height for tiling.
float scrollOffset
Current scroll offset (updates each frame)
bool loaded
Whether texture is loaded.
std::string textureName
Texture name (loaded texture)
int textureWidth
Texture width for tiling.
std::string texturePath
Asset path for texture loading.
float scrollSpeed
Scroll speed in pixels/second.
A single state snapshot with timestamp for time-based interpolation.
uint64_t timestamp
Local timestamp when received (milliseconds)
float velocityX
Velocity X (for extrapolation)
float velocityY
Velocity Y (for extrapolation)
Cached entity state for rendering.
float interpolationFactor
Progress from 0.0 (prev) to 1.0 (target) (DEPRECATED)
int spriteSizeY
Sprite sheet size Y.
uint32_t entityId
Unique entity identifier.
int startPixelX
Sprite sheet start pixel X.
int offsetY
Sprite offset Y for rendering.
float prevY
Previous position Y (DEPRECATED)
float scale
Sprite scale multiplier.
float targetX
Target position X (DEPRECATED)
float targetY
Target position Y (DEPRECATED)
int offsetX
Sprite offset X for rendering.
float prevX
Previous position X (DEPRECATED)
float x
Current rendered position X.
std::string currentAnimation
Current animation name from server.
bool extrapolationEnabled
Allow extrapolation beyond last snapshot.
int health
Current health (-1 for entities without health)
int spriteSizeX
Sprite sheet size X.
std::deque< Snapshot > snapshots
Recent snapshots (max 3)
RType::Messages::Shared::EntityType type
Entity type (Player, Enemy, Bullet, etc.)
int currentFrame
Current animation frame.
uint64_t interpolationDelay
Time to look back for interpolation (ms)
int startPixelY
Sprite sheet start pixel Y.
float y
Current rendered position Y.