From 0d69af667de11dfc22aa90af302884db50806b16 Mon Sep 17 00:00:00 2001 From: Sergio Date: Thu, 27 Nov 2025 17:32:01 +0100 Subject: [PATCH] migrant a SDL3 --- CLAUDE.md | 610 ++++++++++++++++++++++++++++++++++---- asteroids | Bin 28088 -> 28112 bytes source/joc_asteroides.cpp | 160 ++++++++-- source/joc_asteroides.hpp | 8 +- 4 files changed, 688 insertions(+), 90 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 7835945..a1ea342 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,89 +4,581 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -This is an Asteroids-style game originally written in Turbo Pascal 7 for DOS, now being converted to modern C++ with SDL2. The game features a spaceship that must avoid and destroy enemies ("cosinus mesisinus" mounted in UFOs/ORNIs). The codebase contains both the original Pascal implementation and the in-progress C++ port. +This is an **Asteroids-style game** originally written in **Turbo Pascal 7 for DOS** (1999), now being **migrated to modern C++20 with SDL3**. The game features a spaceship that must avoid and destroy enemies (pentagonal "ORNIs"). This is a **phased migration** preserving the original game feel. -**Language**: The code and comments are in Catalan/Valencian. +**Language**: All code, comments, and variable names are in **Catalan/Valencian** (preserved from original). -## Build Commands +**Current Status**: **BETA 2.2** - Phases 0-9 completed (playable with ship, enemies, and bullets). + +## Build System + +Based on `/home/sergio/gitea/pollo` project structure. + +### Build Commands -**macOS**: ```bash -make macos -./bin/asteroids_macos +# Clean + compile +make clean && make + +# Run +./asteroids + +# Individual targets +make linux # Linux build +make macos # macOS build +make windows # Windows build (MinGW) ``` -**Linux**: -```bash -make linux -./bin/asteroids_linux -``` +### Build Files -Both commands compile from `source/*.cpp` with `-std=c++11 -Wall -O2` and link against SDL2. +- **CMakeLists.txt** - CMake configuration (C++20, SDL3) +- **Makefile** - Cross-platform wrapper, extracts project info from CMakeLists.txt +- **release/** - Platform-specific resources (icons, .rc, .plist) ## Architecture -### Code Structure +### File Structure -- `source/ASTEROID.PAS` - Original Turbo Pascal 7 implementation (DOS) -- `source/asteroids.cpp` - C++ port using SDL2 (in progress) +``` +source/ +├── main.cpp # Entry point, game loop, delta_time calculation +├── sdl_manager.hpp/cpp # SDL3 initialization, window, renderer +├── joc_asteroides.hpp # Game structures, constants +└── joc_asteroides.cpp # Game logic, physics, rendering +``` ### Core Data Structures -The game uses polar coordinates (`ipunt`) for geometric objects: -- `ipunt` - Polar coordinate point (radius `r`, angle `angle`) -- `punt` - Cartesian coordinate point (x, y) -- `triangle` - The player's ship (3 polar points + center + angle + velocity) -- `poligon` - Generic polygon for enemies and bullets (vector of polar points + center + angle + velocity + rotation) +The game uses **polar coordinates** for all geometric objects (preserved from Pascal original): -### Key Game Constants +```cpp +struct IPunt { + float r; // Radius + float angle; // Angle in radians +}; -Defined in `asteroids.cpp`: -- Screen bounds: `MARGE_DALT` (20), `MARGE_BAIX` (460), `MARGE_ESQ` (20), `MARGE_DRET` (620) -- `MAX_ORNIS` (15) - Maximum enemies -- `MAX_BALES` (3) - Maximum bullets -- `VELOCITAT` (2), `VELOCITAT_MAX` (6) - Speed constraints +struct Punt { + int x, y; // Cartesian coordinates +}; -### Rendering System +struct Triangle { // Player's ship (nau_) + IPunt p1, p2, p3; // 3 polar points + Punt centre; // Center position + float angle; // Orientation + float velocitat; // Speed (px/s) +}; -The original Pascal code used VGA Mode 13h (320x200, 256 colors) with custom bit-packed framebuffer: -- `pvirt` - Virtual screen buffer (38400 bytes) -- `posa(x,y,color)` - Set pixel in virtual buffer (bit-packed) -- `llig(x,y)` - Read pixel from virtual buffer -- `volca()` - Flip virtual buffer to VGA memory -- Line drawing using Bresenham algorithm +struct Poligon { // Enemies (orni_) and bullets (bales_) + std::array ipuntx; // Polar points + Punt centre; + float angle; // Movement direction + float velocitat; // Speed (units/frame) + uint8_t n; // Number of sides + float drotacio; // Rotation delta per frame + float rotacio; // Current rotation angle + bool esta; // Is active? +}; +``` -The C++ port uses SDL2 for rendering. +### Constants (joc_asteroides.hpp) -### Game Mechanics +```cpp +namespace Constants { + constexpr int MARGE_DALT = 20; // Top margin + constexpr int MARGE_BAIX = 460; // Bottom margin + constexpr int MARGE_ESQ = 20; // Left margin + constexpr int MARGE_DRET = 620; // Right margin + constexpr int MAX_IPUNTS = 30; // Max polygon points + constexpr int MAX_ORNIS = 15; // Max enemies + constexpr int MAX_BALES = 3; // Max bullets + constexpr int VELOCITAT = 2; // Base velocity + constexpr int VELOCITAT_MAX = 6; // Max velocity + constexpr float PI = 3.14159265359f; +} +``` -- **Movement**: Ship uses polar velocity - angle determines direction, velocity determines speed -- **Collision**: Line drawing returns collision state; `rota_tri()` returns 1 on pixel collision -- **Death sequence**: Ship shrinks (`nau.p*.r` decreases), explodes into debris (`chatarra_cosmica`), then respawns after 250 frames -- **Enemies**: `orni[]` array of polygons with autonomous movement, random angle adjustments -- **Bullets**: Only one bullet active at a time (`bales[1].esta` flag) +## Game Loop (main.cpp) -### Pascal-to-C++ Conversion Notes +**Time-based physics** with real delta_time: -The C++ file contains hybrid Pascal/C++ syntax that needs completion: -- Lines 63-450 are uncommented Pascal code -- Pascal procedures/functions need conversion to C++ equivalents -- Assembly blocks (`waitretrace`, VGA mode setting) need SDL2 replacements -- Turbo Pascal keyboard handling (`teclapuls`, `instalarkb`) needs SDL event system -- Memory management (`getmem`, pointer segments) needs modern C++ approach +```cpp +// Lines 21-56 +Uint64 last_time = SDL_GetTicks(); +while (running) { + // Calculate real delta_time + Uint64 current_time = SDL_GetTicks(); + float delta_time = (current_time - last_time) / 1000.0f; // ms → s + last_time = current_time; -### Game Controls (from README) + // Cap at 50ms (20 FPS minimum) + if (delta_time > 0.05f) delta_time = 0.05f; -- Up Arrow: Accelerate -- Down Arrow: Brake -- Right Arrow: Rotate clockwise -- Left Arrow: Rotate counterclockwise -- Space: Fire -- Esc: Exit + // Process events + while (SDL_PollEvent(&event)) { + joc.processar_input(event); + // Handle quit/ESC + } -## Development Notes + // Update + render + joc.actualitzar(delta_time); + sdl.neteja(0, 0, 0); + joc.dibuixar(); + sdl.presenta(); +} +``` -- The conversion is incomplete - much Pascal code remains in comments -- Focus on SDL2 for cross-platform graphics/input instead of DOS VGA/keyboard -- Original used bit-packed monochrome framebuffer; consider SDL surfaces or textures -- Preserve the polar coordinate geometry system - it's core to the game's physics +**Critical**: Delta_time is **real** and **variable**, not fixed at 0.016f. All physics must multiply by delta_time. + +## SDL3 API Notes + +SDL3 has breaking changes from SDL2: + +- `SDL_CreateRenderer(window, nullptr)` - no flags parameter +- `event.key.key` instead of `event.key.keysym.sym` +- `SDL_EVENT_KEY_DOWN` instead of `SDL_KEYDOWN` +- `SDL_EVENT_QUIT` instead of `SDL_QUIT` +- `SDL_GetKeyboardState(nullptr)` - state-based input for continuous keys + +## Physics System + +### Ship Movement (joc_asteroides.cpp:67-155) + +**State-based input** (not event-based) for smooth controls: + +```cpp +const bool* keyboard_state = SDL_GetKeyboardState(nullptr); + +if (keyboard_state[SDL_SCANCODE_RIGHT]) + nau_.angle += ROTATION_SPEED * delta_time; +if (keyboard_state[SDL_SCANCODE_LEFT]) + nau_.angle -= ROTATION_SPEED * delta_time; +if (keyboard_state[SDL_SCANCODE_UP]) + nau_.velocitat += ACCELERATION * delta_time; +``` + +**Physics constants** (calibrated for ~20 FPS feel): + +```cpp +constexpr float ROTATION_SPEED = 2.5f; // rad/s (~143°/s) +constexpr float ACCELERATION = 100.0f; // px/s² +constexpr float MAX_VELOCITY = 200.0f; // px/s +constexpr float FRICTION = 6.0f; // px/s² +``` + +**Position calculation** (angle-PI/2 because angle=0 points up, not right): + +```cpp +float dy = (nau_.velocitat * delta_time) * std::sin(nau_.angle - PI/2.0f); +float dx = (nau_.velocitat * delta_time) * std::cos(nau_.angle - PI/2.0f); +nau_.centre.y += round(dy); +nau_.centre.x += round(dx); +``` + +**Visual velocity effect**: Ship triangle grows when moving (joc_asteroides.cpp:162-164): + +```cpp +// Scale 200 px/s → 6 px visual effect (like original) +float velocitat_visual = nau_.velocitat / 33.33f; +rota_tri(nau_, nau_.angle, velocitat_visual, true); +``` + +### Enemy Movement (joc_asteroides.cpp:367-405) - FASE 8 + +Autonomous movement with random direction changes: + +```cpp +void mou_orni(Poligon& orni, float delta_time) { + // 5% probability to change direction + if (rand() < 0.05f * RAND_MAX) + orni.angle = random() * 2*PI; + + // Move (2 px/frame * 20 FPS = 40 px/s) + float velocitat_efectiva = orni.velocitat * 20.0f * delta_time; + float dy = velocitat_efectiva * sin(orni.angle - PI/2.0f); + float dx = velocitat_efectiva * cos(orni.angle - PI/2.0f); + orni.centre.y += round(dy); + orni.centre.x += round(dx); + + // Bounce on walls + if (x < MARGE_ESQ || x > MARGE_DRET) + orni.angle = PI - orni.angle; // Horizontal reflection + if (y < MARGE_DALT || y > MARGE_BAIX) + orni.angle = 2*PI - orni.angle; // Vertical reflection +} +``` + +### Bullet Movement (joc_asteroides.cpp:444-465) - FASE 9 + +Straight-line movement, deactivates when leaving screen: + +```cpp +void mou_bales(Poligon& bala, float delta_time) { + // Fast movement (6 px/frame * 20 FPS = 120 px/s) + float velocitat_efectiva = bala.velocitat * 20.0f * delta_time; + float dy = velocitat_efectiva * sin(bala.angle - PI/2.0f); + float dx = velocitat_efectiva * cos(bala.angle - PI/2.0f); + bala.centre.y += round(dy); + bala.centre.x += round(dx); + + // Deactivate if out of bounds + if (x < MARGE_ESQ || x > MARGE_DRET || + y < MARGE_DALT || y > MARGE_BAIX) + bala.esta = false; +} +``` + +## Rendering System + +### Coordinate Conversion + +**Polar → Cartesian** with rotation (used in `rota_tri` and `rota_pol`): + +```cpp +// For each polar point +int x = round((r + velocitat) * cos(angle_punt + angle_object)) + centre.x; +int y = round((r + velocitat) * sin(angle_punt + angle_object)) + centre.y; +``` + +### Line Drawing (joc_asteroides.cpp:230-298) + +Currently uses **SDL_RenderLine** for efficiency: + +```cpp +bool linea(int x1, int y1, int x2, int y2, bool dibuixar) { + if (dibuixar && renderer_) { + SDL_SetRenderDrawColor(renderer_, 255, 255, 255, 255); // White + SDL_RenderLine(renderer_, x1, y1, x2, y2); + } + return false; // Collision detection TODO (Phase 10) +} +``` + +**Note**: Original Bresenham algorithm preserved in comments for **Phase 10** (pixel-perfect collision detection). + +### Ship Rendering (joc_asteroides.cpp:300-337) + +Triangle with 3 lines: + +```cpp +void rota_tri(const Triangle& tri, float angul, float velocitat, bool dibuixar) { + // Convert 3 polar points to Cartesian + int x1 = round((tri.p1.r + velocitat) * cos(tri.p1.angle + angul)) + tri.centre.x; + int y1 = round((tri.p1.r + velocitat) * sin(tri.p1.angle + angul)) + tri.centre.y; + // ... same for p2, p3 + + // Draw 3 lines + linea(x1, y1, x2, y2, dibuixar); + linea(x1, y1, x3, y3, dibuixar); + linea(x3, y3, x2, y2, dibuixar); +} +``` + +### Polygon Rendering (joc_asteroides.cpp:339-365) + +Enemies and bullets: + +```cpp +void rota_pol(const Poligon& pol, float angul, bool dibuixar) { + // Convert all polar points to Cartesian + std::array xy; + for (int i = 0; i < pol.n; i++) { + xy[i].x = round(pol.ipuntx[i].r * cos(pol.ipuntx[i].angle + angul)) + pol.centre.x; + xy[i].y = round(pol.ipuntx[i].r * sin(pol.ipuntx[i].angle + angul)) + pol.centre.y; + } + + // Draw lines between consecutive points + for (int i = 0; i < pol.n - 1; i++) + linea(xy[i].x, xy[i].y, xy[i+1].x, xy[i+1].y, dibuixar); + + // Close polygon + linea(xy[pol.n-1].x, xy[pol.n-1].y, xy[0].x, xy[0].y, dibuixar); +} +``` + +## Input System - FASE 9 + +### Continuous Input (actualitzar) + +Arrow keys use **state-based** polling: + +```cpp +const bool* keyboard_state = SDL_GetKeyboardState(nullptr); +if (keyboard_state[SDL_SCANCODE_UP]) { /* accelerate */ } +``` + +### Event-Based Input (processar_input) + +SPACE bar for shooting (joc_asteroides.cpp:174-212): + +```cpp +void processar_input(const SDL_Event& event) { + if (event.type == SDL_EVENT_KEY_DOWN) { + if (event.key.key == SDLK_SPACE) { + // Find first inactive bullet + for (auto& bala : bales_) { + if (!bala.esta) { + bala.esta = true; + bala.centre = nau_.centre; // Spawn at ship + bala.angle = nau_.angle; // Fire in ship direction + bala.velocitat = 6.0f; // High speed + break; // Only one bullet at a time + } + } + } + } +} +``` + +## Initialization (joc_asteroides.cpp:15-65) + +### Ship (lines 20-34) + +```cpp +// Triangle with 3 polar points (r=12, angles at 270°, 45°, 135°) +nau_.p1.r = 12.0f; +nau_.p1.angle = 3.0f * PI / 2.0f; // Points up +nau_.p2.r = 12.0f; +nau_.p2.angle = PI / 4.0f; // Back-right +nau_.p3.r = 12.0f; +nau_.p3.angle = 3.0f * PI / 4.0f; // Back-left +nau_.centre = {320, 240}; +nau_.angle = 0.0f; +nau_.velocitat = 0.0f; +``` + +### Enemies (lines 39-54) - FASE 7 + +```cpp +for (int i = 0; i < MAX_ORNIS; i++) { + crear_poligon_regular(orni_[i], 5, 20.0f); // Pentagon, r=20 + orni_[i].centre.x = rand(30, 610); + orni_[i].centre.y = rand(30, 450); + orni_[i].angle = rand(0, 360) * PI/180; + orni_[i].esta = true; +} +``` + +### Bullets (lines 56-64) - FASE 9 + +```cpp +for (int i = 0; i < MAX_BALES; i++) { + crear_poligon_regular(bales_[i], 5, 5.0f); // Small pentagon, r=5 + bales_[i].esta = false; // Initially inactive +} +``` + +## Update Loop (joc_asteroides.cpp:67-155) + +```cpp +void actualitzar(float delta_time) { + // 1. Ship input + physics (lines 68-125) + // - Keyboard state polling + // - Rotation, acceleration, friction + // - Position update with boundary checking + + // 2. Enemy movement (lines 137-147) - FASE 8 + for (auto& enemy : orni_) { + if (enemy.esta) { + mou_orni(enemy, delta_time); + enemy.rotacio += enemy.drotacio; // Visual rotation + } + } + + // 3. Bullet movement (lines 149-154) - FASE 9 + for (auto& bala : bales_) { + if (bala.esta) + mou_bales(bala, delta_time); + } + + // TODO Phase 10: Collision detection +} +``` + +## Draw Loop (joc_asteroides.cpp:157-184) + +```cpp +void dibuixar() { + // 1. Ship (if alive) + if (itocado_ == 0) { + float velocitat_visual = nau_.velocitat / 33.33f; + rota_tri(nau_, nau_.angle, velocitat_visual, true); + } + + // 2. Enemies (FASE 7) + for (const auto& enemy : orni_) { + if (enemy.esta) + rota_pol(enemy, enemy.rotacio, true); + } + + // 3. Bullets (FASE 9) + for (const auto& bala : bales_) { + if (bala.esta) + rota_pol(bala, 0.0f, true); // No visual rotation + } + + // TODO Phase 11: Draw borders +} +``` + +## Migration Progress + +### ✅ Phase 0: Project Setup +- CMakeLists.txt + Makefile (based on pollo) +- Stub files created + +### ✅ Phase 1: SDL Manager +- SDLManager class (sdl_manager.hpp/cpp) +- Window + renderer initialization +- Fixed SDL3 API differences + +### ✅ Phase 2: Data Structures +- IPunt, Punt, Triangle, Poligon defined +- Constants namespace with constexpr + +### ✅ Phase 3: Geometry Functions +- modul(), diferencia(), distancia(), angle_punt() +- crear_poligon_regular() + +### ✅ Phase 4: Line Drawing +- linea() with SDL_RenderLine +- Bresenham preserved in comments for Phase 10 + +### ✅ Phase 5: Ship Rendering +- rota_tri() polar→Cartesian conversion +- Ship initialization + +### ✅ Phase 6: Ship Movement +- **Critical fix**: Event-based → State-based input (SDL_GetKeyboardState) +- **Critical fix**: Fixed delta_time (0.016f) → Real delta_time calculation +- **Critical fix**: Visual velocity scaling (200 px/s → 6 px visual) +- Time-based physics (all values in px/s) +- Rotation, acceleration, friction, boundary checking + +### ✅ Phase 7: Enemy Rendering +- rota_pol() for polygons +- 15 random pentagons initialized +- Visual rotation (enemy.rotacio) + +### ✅ Phase 8: Enemy AI & Movement +- **mou_orni()** (joc_asteroides.cpp:367-405) + - 5% random direction change + - Polar movement (40 px/s) + - Boundary bounce (angle reflection) +- Integrated in actualitzar() (lines 137-147) + +### ✅ Phase 9: Bullet System +- **Bullet initialization** (joc_asteroides.cpp:56-64) + - 3 bullets, initially inactive + - Small pentagons (r=5) +- **Shooting with SPACE** (joc_asteroides.cpp:174-212) + - Spawns at ship position + - Fires in ship direction + - Only one bullet at a time +- **mou_bales()** (joc_asteroides.cpp:444-465) + - Fast rectlinear movement (120 px/s) + - Deactivates when out of bounds +- **Drawing** (joc_asteroides.cpp:175-181) + - No visual rotation + +### 🔲 Phase 10: Collision Detection & Death (NEXT) +- **Collision detection**: + - Restore Bresenham pixel-perfect algorithm + - Detect ship-enemy collision → tocado() + - Detect bullet-enemy collision → destroy enemy +- **Death sequence** (tocado): + - Explosion animation (itocado_ counter) + - Ship shrinking + - Debris particles (chatarra_cosmica) + - Respawn after delay +- **Important**: Original Pascal used bit-packed framebuffer (llig() function) + - Need to adapt to SDL3 rendering pipeline + - Options: render to texture, software buffer, or geometric collision + +### 🔲 Phase 11: Polish & Refinements +- Draw borders (marges) +- Text rendering with SDL_ttf (TODO for later) +- Sound effects (optional) +- Score system (optional) + +### 🔲 Phase 12: Cross-Platform Testing +- Test on Linux, macOS, Windows +- Create release builds +- Package with resources + +## Known Issues & Tuning Needed + +1. **Ship physics constants**: User mentioned "sigue sin ir fino" - may need adjustment: + - `ROTATION_SPEED` (currently 2.5 rad/s) + - `ACCELERATION` (currently 100.0 px/s²) + - `MAX_VELOCITY` (currently 200.0 px/s) + - `FRICTION` (currently 6.0 px/s²) + +2. **Enemy movement**: May need speed/bounce angle tuning + - `VELOCITAT_SCALE` (currently 20.0) + - Reflection angles (PI - angle, 2*PI - angle) + +3. **Bullet speed**: May need adjustment + - `velocitat = 6.0f` (120 px/s) + +## Important Pascal References (Original Code) + +The original Pascal game is in `source/ASTEROID.PAS` (if available). Key procedures: + +- `teclapuls` - Keyboard handler (converted to SDL_GetKeyboardState) +- `mou_nau` - Ship movement (now actualitzar ship section) +- `mou_orni` - Enemy movement (joc_asteroides.cpp:367-405) +- `mou_bales` - Bullet movement (joc_asteroides.cpp:444-465) +- `rota_tri` - Ship rendering (joc_asteroides.cpp:300-337) +- `rota_pol` - Polygon rendering (joc_asteroides.cpp:339-365) +- `linea` - Bresenham line (joc_asteroides.cpp:230-298) +- `tocado` - Death sequence (TODO Phase 10) + +## Controls + +- **Arrow Keys** (UP/DOWN/LEFT/RIGHT): Ship movement (continuous) +- **SPACE**: Shoot (event-based) +- **ESC**: Quit + +## Debug Output + +Ship debug info printed every second (joc_asteroides.cpp:115-125): + +```cpp +static float time_accumulator = 0.0f; +time_accumulator += delta_time; +if (time_accumulator >= 1.0f) { + std::cout << "Nau: pos(" << nau_.centre.x << "," << nau_.centre.y + << ") vel=" << (int)nau_.velocitat << " px/s" + << " angle=" << (int)(nau_.angle * 180/PI) << "°" + << " dt=" << (int)(delta_time * 1000) << "ms" << std::endl; + time_accumulator -= 1.0f; +} +``` + +## Next Session Priorities + +1. **Phase 10: Collision Detection** + - Most complex phase + - Need to decide: geometric vs pixel-perfect collision + - Implement tocado() death sequence + - Bullet-enemy collision + +2. **Phase 11: Polish** + - Draw borders + - Consider text rendering (score, lives) + +3. **Phase 12: Release** + - Cross-platform testing + - Final physics tuning + +## Tips for Future Claude Code Sessions + +- **Always read this file first** before making changes +- **Preserve Valencian naming**: nau, orni, bales, centre, velocitat, etc. +- **Time-based physics**: All movement must multiply by delta_time +- **Polar coordinates**: Core to the game, don't change to Cartesian +- **Test compilation** after each change: `make clean && make` +- **Visual velocity scaling**: Remember to scale velocitat before passing to rota_tri +- **Angle convention**: angle=0 points UP (not right), hence `angle - PI/2` in calculations +- **One bullet at a time**: Original game limitation, preserve it +- **Simple code style**: Avoid over-engineering, keep "small DOS program" feel diff --git a/asteroids b/asteroids index b7f2b16c213c90b18be9f767434843be0a035bb0..679760aab71b3c3d53b0508be0a98597a50bcbde 100755 GIT binary patch delta 7068 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 diff --git a/source/joc_asteroides.cpp b/source/joc_asteroides.cpp index cfd38b0..edfd209 100644 --- a/source/joc_asteroides.cpp +++ b/source/joc_asteroides.cpp @@ -29,8 +29,8 @@ void JocAsteroides::inicialitzar() { nau_.p3.angle = (3.0f * Constants::PI) / 4.0f; // 135° nau_.angle = 0.0f; - nau_.centre.x = 320; - nau_.centre.y = 240; + nau_.centre.x = 320.0f; + nau_.centre.y = 240.0f; nau_.velocitat = 0.0f; // Inicialitzar estat de col·lisió @@ -43,8 +43,8 @@ void JocAsteroides::inicialitzar() { crear_poligon_regular(orni_[i], 5, 20.0f); // Posició aleatòria dins de l'àrea de joc - orni_[i].centre.x = (std::rand() % 580) + 30; // 30-610 - orni_[i].centre.y = (std::rand() % 420) + 30; // 30-450 + orni_[i].centre.x = static_cast((std::rand() % 580) + 30); // 30-610 + orni_[i].centre.y = static_cast((std::rand() % 420) + 30); // 30-450 // Angle aleatori orni_[i].angle = (std::rand() % 360) * Constants::PI / 180.0f; @@ -52,6 +52,16 @@ void JocAsteroides::inicialitzar() { // Està actiu orni_[i].esta = true; } + + // Inicialitzar bales + // Basat en el codi Pascal original: inicialment inactives + for (int i = 0; i < Constants::MAX_BALES; i++) { + // Crear pentàgon petit (5 costats, radi 5) + crear_poligon_regular(bales_[i], 5, 5.0f); + + // Inicialment inactiva + bales_[i].esta = false; + } } void JocAsteroides::actualitzar(float delta_time) { @@ -59,11 +69,11 @@ void JocAsteroides::actualitzar(float delta_time) { // Basat en el codi Pascal original: lines 394-417 // Convertit a time-based per ser independent del framerate - // Constants de física (calibrades per sentir-se com l'original a ~20 FPS) - constexpr float ROTATION_SPEED = 2.5f; // ~143°/s (rotació suau) - constexpr float ACCELERATION = 100.0f; // px/s² (acceleració notable) - constexpr float MAX_VELOCITY = 200.0f; // px/s (velocitat màxima) - constexpr float FRICTION = 6.0f; // px/s² (fricció notable) + // Constants de física (convertides des del Pascal original a ~20 FPS) + constexpr float ROTATION_SPEED = 3.14f; // rad/s (0.157 rad/frame × 20 = 3.14 rad/s, ~180°/s) + constexpr float ACCELERATION = 400.0f; // px/s² (0.2 u/frame × 20 × 100 = 400 px/s²) + constexpr float MAX_VELOCITY = 120.0f; // px/s (6 u/frame × 20 = 120 px/s) + constexpr float FRICTION = 20.0f; // px/s² (0.1 u/frame × 20 × 10 = 20 px/s²) // Obtenir estat actual del teclat (no events, sinó estat continu) const bool* keyboard_state = SDL_GetKeyboardState(nullptr); @@ -95,12 +105,13 @@ void JocAsteroides::actualitzar(float delta_time) { + nau_.centre.x; // Boundary checking - només actualitzar si dins dels marges + // Acumulació directa amb precisió subpíxel if (dy > Constants::MARGE_DALT && dy < Constants::MARGE_BAIX) { - nau_.centre.y = static_cast(std::round(dy)); + nau_.centre.y = dy; } if (dx > Constants::MARGE_ESQ && dx < Constants::MARGE_DRET) { - nau_.centre.x = static_cast(std::round(dx)); + nau_.centre.x = dx; } // Fricció - desacceleració gradual (time-based) @@ -124,25 +135,33 @@ void JocAsteroides::actualitzar(float delta_time) { time_accumulator -= 1.0f; } - // Actualitzar rotació dels enemics + // Actualitzar moviment i rotació dels enemics (ORNIs) // Basat en el codi Pascal original: lines 429-432 for (auto& enemy : orni_) { if (enemy.esta) { - enemy.rotacio += enemy.drotacio; + // Moviment autònom (Fase 8) + mou_orni(enemy, delta_time); + + // Rotació visual (time-based: drotacio està en rad/s) + enemy.rotacio += enemy.drotacio * delta_time; } } - // TODO: Actualitzar moviment ORNIs (Fase 8) - // TODO: Actualitzar bales (Fase 9) + // Actualitzar moviment de bales (Fase 9) + for (auto& bala : bales_) { + if (bala.esta) { + mou_bales(bala, delta_time); + } + } } void JocAsteroides::dibuixar() { // Dibuixar la nau si no està en seqüència de mort if (itocado_ == 0) { - // Escalar velocitat per l'efect visual (200 px/s → ~6 px d'efecte) + // Escalar velocitat per l'efect visual (120 px/s → ~6 px d'efecte) // El codi Pascal original sumava velocitat (0-6) al radi per donar - // sensació de "empenta". Ara velocitat està en px/s (0-200). - float velocitat_visual = nau_.velocitat / 33.33f; + // sensació de "empenta". Ara velocitat està en px/s (0-120). + float velocitat_visual = nau_.velocitat / 20.0f; rota_tri(nau_, nau_.angle, velocitat_visual, true); } @@ -154,7 +173,14 @@ void JocAsteroides::dibuixar() { } } - // TODO: Dibuixar bales (Fase 9) + // Dibuixar bales (Fase 9) + for (const auto& bala : bales_) { + if (bala.esta) { + // Dibuixar com a pentàgon petit, sense rotació visual (sempre mateix angle) + rota_pol(bala, 0.0f, true); + } + } + // TODO: Dibuixar marges (Fase 11) } @@ -166,6 +192,30 @@ void JocAsteroides::processar_input(const SDL_Event& event) { switch (event.key.key) { case SDLK_SPACE: // Disparar (Fase 9) + // Basat en el codi Pascal original: crear bala en posició de la nau + // El joc original només permetia 1 bala activa alhora + + // Buscar primera bala inactiva + for (auto& bala : bales_) { + if (!bala.esta) { + // Activar bala + bala.esta = true; + + // Posició inicial = centre de la nau + bala.centre.x = nau_.centre.x; + bala.centre.y = nau_.centre.y; + + // Angle = angle de la nau (dispara en la direcció que apunta) + bala.angle = nau_.angle; + + // Velocitat alta (el joc Pascal original usava 7 px/frame) + // 7 px/frame × 20 FPS = 140 px/s + bala.velocitat = 140.0f; + + // Només una bala alhora (com el joc original) + break; + } + } break; default: @@ -217,12 +267,14 @@ void JocAsteroides::crear_poligon_regular(Poligon& pol, uint8_t n, float r) { } // Inicialitzar propietats del polígon - pol.centre.x = 320; - pol.centre.y = 200; + pol.centre.x = 320.0f; + pol.centre.y = 200.0f; pol.angle = 0.0f; - pol.velocitat = static_cast(Constants::VELOCITAT); + // Convertir velocitat de px/frame a px/s: 2 px/frame × 20 FPS = 40 px/s + pol.velocitat = static_cast(Constants::VELOCITAT) * 20.0f; pol.n = n; - pol.drotacio = 0.078539816f; // ~4.5 graus per frame + // Convertir rotació de rad/frame a rad/s: 0.0785 rad/frame × 20 FPS = 1.57 rad/s (~90°/s) + pol.drotacio = 0.078539816f * 20.0f; pol.rotacio = 0.0f; pol.esta = true; } @@ -364,12 +416,66 @@ void JocAsteroides::rota_pol(const Poligon& pol, float angul, bool dibuixar) { linea(xy[pol.n - 1].x, xy[pol.n - 1].y, xy[0].x, xy[0].y, dibuixar); } -void JocAsteroides::mou_orni(Poligon& orni) { - // TODO: Implementar moviment d'ORNI +void JocAsteroides::mou_orni(Poligon& orni, float delta_time) { + // Moviment autònom d'ORNI (enemic pentàgon) + // Basat en el codi Pascal original: procedure mou_orni + + // Cambio aleatori d'angle (5% probabilitat per crida) + // En el Pascal original: if (random<0.05) then orni.angle:=random*2*pi + float random_val = static_cast(std::rand()) / static_cast(RAND_MAX); + if (random_val < 0.05f) { + // Assignar un angle completament aleatori (0-360°) + orni.angle = (static_cast(std::rand()) / static_cast(RAND_MAX)) + * 2.0f * Constants::PI; + } + + // Calcular nova posició (moviment polar time-based) + // velocitat ja està en px/s (40 px/s), només cal multiplicar per delta_time + float velocitat_efectiva = orni.velocitat * delta_time; + + // Calcular desplaçament (angle-PI/2 perquè angle=0 apunta amunt) + float dy = velocitat_efectiva * std::sin(orni.angle - Constants::PI / 2.0f); + float dx = velocitat_efectiva * std::cos(orni.angle - Constants::PI / 2.0f); + + // Acumulació directa amb precisió subpíxel + orni.centre.y += dy; + orni.centre.x += dx; + + // Boundary checking amb rebot (reflexió d'angle) + // Si toca paret esquerra/dreta: angle = PI - angle + if (orni.centre.x < Constants::MARGE_ESQ || orni.centre.x > Constants::MARGE_DRET) { + orni.angle = Constants::PI - orni.angle; + } + + // Si toca paret dalt/baix: angle = 2*PI - angle + if (orni.centre.y < Constants::MARGE_DALT || orni.centre.y > Constants::MARGE_BAIX) { + orni.angle = 2.0f * Constants::PI - orni.angle; + } + + // Nota: La rotació visual (orni.rotacio += orni.drotacio) ja es fa a actualitzar() } -void JocAsteroides::mou_bales(Poligon& bala) { - // TODO: Implementar moviment de bala +void JocAsteroides::mou_bales(Poligon& bala, float delta_time) { + // Moviment rectilini de la bala + // Basat en el codi Pascal original: procedure mou_bales + + // Calcular nova posició (moviment polar time-based) + // velocitat ja està en px/s (140 px/s), només cal multiplicar per delta_time + float velocitat_efectiva = bala.velocitat * delta_time; + + // Calcular desplaçament (angle-PI/2 perquè angle=0 apunta amunt) + float dy = velocitat_efectiva * std::sin(bala.angle - Constants::PI / 2.0f); + float dx = velocitat_efectiva * std::cos(bala.angle - Constants::PI / 2.0f); + + // Acumulació directa amb precisió subpíxel + bala.centre.y += dy; + bala.centre.x += dx; + + // Desactivar si surt dels marges (no rebota com els ORNIs) + if (bala.centre.x < Constants::MARGE_ESQ || bala.centre.x > Constants::MARGE_DRET || + bala.centre.y < Constants::MARGE_DALT || bala.centre.y > Constants::MARGE_BAIX) { + bala.esta = false; + } } void JocAsteroides::tocado() { diff --git a/source/joc_asteroides.hpp b/source/joc_asteroides.hpp index c32a82a..aae8ddc 100644 --- a/source/joc_asteroides.hpp +++ b/source/joc_asteroides.hpp @@ -30,8 +30,8 @@ struct IPunt { }; struct Punt { - int x; - int y; + float x; + float y; }; struct Triangle { @@ -86,8 +86,8 @@ private: void rota_pol(const Poligon& pol, float angul, bool dibuixar); // Moviment - void mou_orni(Poligon& orni); - void mou_bales(Poligon& bala); + void mou_orni(Poligon& orni, float delta_time); + void mou_bales(Poligon& bala, float delta_time); void tocado(); };