#include "raylib.h" #include #include #include struct Bala { Vector2 posicion; Vector2 direccion; float velocidad; int equipo; int rebotes; bool activa; }; struct Municion { Vector2 posicion; bool activa; }; struct Entidad { Vector2 posicion; Vector2 velocidad; Vector2 direccion; float radio; float vida; float vidaMaxima; int equipo; bool jugador; bool viva; int municion; float tiempoDisparo; }; struct Pared { Rectangle rectangulo; }; const float MUNDO_ANCHO = 1000.0f; const float MUNDO_ALTO = 650.0f; const float BORDE = 35.0f; const float RADIO_JUGADOR = 18.0f; const float RADIO_ENEMIGO = 20.0f; const float VELOCIDAD_JUGADOR = 300.0f; const float VELOCIDAD_IA = 175.0f; const float VELOCIDAD_BALA = 720.0f; const float DAÑO_BALA = 25.0f; const float RADIO_VISION = 235.0f; float Limitar(float valor, float minimo, float maximo) { if (valor < minimo) return minimo; if (valor > maximo) return maximo; return valor; } Vector2 Normalizar(Vector2 v) { float largo = sqrtf(v.x * v.x + v.y * v.y); if (largo <= 0.0001f) return {0.0f, 0.0f}; return { v.x / largo, v.y / largo }; } float Distancia(Vector2 a, Vector2 b) { float x = a.x - b.x; float y = a.y - b.y; return sqrtf(x * x + y * y); } bool CirculoCirculo( Vector2 a, float radioA, Vector2 b, float radioB) { return Distancia(a, b) < radioA + radioB; } bool CirculoRectangulo( Vector2 centro, float radio, Rectangle rect) { float puntoX = Limitar( centro.x, rect.x, rect.x + rect.width ); float puntoY = Limitar( centro.y, rect.y, rect.y + rect.height ); float dx = centro.x - puntoX; float dy = centro.y - puntoY; return dx * dx + dy * dy < radio * radio; } void RebotarBorde(Entidad& entidad) { float izquierda = BORDE + entidad.radio; float derecha = MUNDO_ANCHO - BORDE - entidad.radio; float arriba = BORDE + entidad.radio; float abajo = MUNDO_ALTO - BORDE - entidad.radio; if (entidad.posicion.x < izquierda) { entidad.posicion.x = izquierda; entidad.velocidad.x = fabsf(entidad.velocidad.x); } if (entidad.posicion.x > derecha) { entidad.posicion.x = derecha; entidad.velocidad.x = -fabsf(entidad.velocidad.x); } if (entidad.posicion.y < arriba) { entidad.posicion.y = arriba; entidad.velocidad.y = fabsf(entidad.velocidad.y); } if (entidad.posicion.y > abajo) { entidad.posicion.y = abajo; entidad.velocidad.y = -fabsf(entidad.velocidad.y); } } void RebotarPared( Entidad& entidad, const std::vector& paredes) { for (const Pared& pared : paredes) { if (!CirculoRectangulo( entidad.posicion, entidad.radio, pared.rectangulo)) { continue; } float izquierda = pared.rectangulo.x; float derecha = pared.rectangulo.x + pared.rectangulo.width; float arriba = pared.rectangulo.y; float abajo = pared.rectangulo.y + pared.rectangulo.height; float dIzquierda = fabsf(entidad.posicion.x - izquierda); float dDerecha = fabsf(entidad.posicion.x - derecha); float dArriba = fabsf(entidad.posicion.y - arriba); float dAbajo = fabsf(entidad.posicion.y - abajo); float menor = std::min( std::min(dIzquierda, dDerecha), std::min(dArriba, dAbajo) ); if (menor == dIzquierda) { entidad.posicion.x = izquierda - entidad.radio; entidad.velocidad.x = -fabsf(entidad.velocidad.x); } else if (menor == dDerecha) { entidad.posicion.x = derecha + entidad.radio; entidad.velocidad.x = fabsf(entidad.velocidad.x); } else if (menor == dArriba) { entidad.posicion.y = arriba - entidad.radio; entidad.velocidad.y = -fabsf(entidad.velocidad.y); } else { entidad.posicion.y = abajo + entidad.radio; entidad.velocidad.y = fabsf(entidad.velocidad.y); } } } bool TocarPared( Vector2 posicion, const std::vector& paredes) { for (const Pared& pared : paredes) { if (CheckCollisionPointRec( posicion, pared.rectangulo)) { return true; } } return false; } void MoverBala( Bala& bala, float dt, const std::vector& paredes) { if (!bala.activa) return; Vector2 siguiente = { bala.posicion.x + bala.direccion.x * bala.velocidad * dt, bala.posicion.y + bala.direccion.y * bala.velocidad * dt }; bool golpeo = false; if (siguiente.x < BORDE) { siguiente.x = BORDE; if (bala.rebotes == 0) { bala.direccion.x *= -1.0f; bala.rebotes++; } else { bala.activa = false; } golpeo = true; } if (siguiente.x > MUNDO_ANCHO - BORDE) { siguiente.x = MUNDO_ANCHO - BORDE; if (bala.rebotes == 0) { bala.direccion.x *= -1.0f; bala.rebotes++; } else { bala.activa = false; } golpeo = true; } if (siguiente.y < BORDE) { siguiente.y = BORDE; if (bala.rebotes == 0) { bala.direccion.y *= -1.0f; bala.rebotes++; } else { bala.activa = false; } golpeo = true; } if (siguiente.y > MUNDO_ALTO - BORDE) { siguiente.y = MUNDO_ALTO - BORDE; if (bala.rebotes == 0) { bala.direccion.y *= -1.0f; bala.rebotes++; } else { bala.activa = false; } golpeo = true; } if (!bala.activa) return; if (!golpeo) { for (const Pared& pared : paredes) { if (!CheckCollisionPointRec( siguiente, pared.rectangulo)) { continue; } if (bala.rebotes == 0) { float izquierda = pared.rectangulo.x; float derecha = pared.rectangulo.x + pared.rectangulo.width; float arriba = pared.rectangulo.y; float abajo = pared.rectangulo.y + pared.rectangulo.height; float dxIzquierda = fabsf(siguiente.x - izquierda); float dxDerecha = fabsf(siguiente.x - derecha); float dyArriba = fabsf(siguiente.y - arriba); float dyAbajo = fabsf(siguiente.y - abajo); float menorX = std::min(dxIzquierda, dxDerecha); float menorY = std::min(dyArriba, dyAbajo); if (menorX < menorY) bala.direccion.x *= -1.0f; else bala.direccion.y *= -1.0f; bala.rebotes++; siguiente.x += bala.direccion.x * 3.0f; siguiente.y += bala.direccion.y * 3.0f; } else { bala.activa = false; } break; } } if (bala.activa) bala.posicion = siguiente; } int BuscarObjetivo( int indice, const std::vector& entidades) { int objetivo = -1; float menorDistancia = 999999.0f; for (int i = 0; i < (int)entidades.size(); i++) { if (i == indice) continue; if (!entidades[i].viva) continue; if (entidades[i].equipo == entidades[indice].equipo) { continue; } float distancia = Distancia( entidades[indice].posicion, entidades[i].posicion ); if (distancia < menorDistancia) { menorDistancia = distancia; objetivo = i; } } return objetivo; } void Disparar( Entidad& entidad, std::vector& balas) { if (!entidad.viva) return; if (entidad.municion <= 0) return; Bala bala; bala.posicion = { entidad.posicion.x + entidad.direccion.x * 27.0f, entidad.posicion.y + entidad.direccion.y * 27.0f }; bala.direccion = Normalizar(entidad.direccion); bala.velocidad = VELOCIDAD_BALA; bala.equipo = entidad.equipo; bala.rebotes = 0; bala.activa = true; balas.push_back(bala); entidad.municion--; } bool HayMunicion( const std::vector& municiones) { for (const Municion& municion : municiones) { if (municion.activa) return true; } return false; } void CrearMunicion( std::vector& municiones, const std::vector& paredes, const std::vector& entidades) { if ((int)municiones.size() >= 2) return; Vector2 posicion; bool valida = false; for (int intento = 0; intento < 100 && !valida; intento++) { posicion = { (float)GetRandomValue( (int)BORDE + 25, (int)MUNDO_ANCHO - (int)BORDE - 25 ), (float)GetRandomValue( (int)BORDE + 25, (int)MUNDO_ALTO - (int)BORDE - 25 ) }; valida = !TocarPared( posicion, paredes ); if (!valida) continue; valida = false; } if (!valida) return; for (const Entidad& entidad : entidades) { if (!entidad.viva) continue; if (Distancia( posicion, entidad.posicion) < 100.0f) { return; } } municiones.push_back({ posicion, true }); } void DibujarEntidad( const Entidad& entidad, float escalaX, float escalaY, float escala) { if (!entidad.viva) return; Vector2 posicion = { entidad.posicion.x * escalaX, entidad.posicion.y * escalaY }; Color color = entidad.equipo == 0 ? BLUE : RED; DrawCircleV( posicion, entidad.radio * escala, color ); Vector2 direccion = { posicion.x + entidad.direccion.x * 25.0f * escala, posicion.y + entidad.direccion.y * 25.0f * escala }; DrawLineEx( posicion, direccion, 3.0f * escala, WHITE ); float vidaAncho = 42.0f * escala; float vidaAlto = 5.0f * escala; float vidaX = posicion.x - vidaAncho * 0.5f; float vidaY = posicion.y - entidad.radio * escala - 13.0f * escala; DrawRectangle( (int)vidaX, (int)vidaY, (int)vidaAncho, (int)vidaAlto, DARKGRAY ); DrawRectangle( (int)vidaX, (int)vidaY, (int)( vidaAncho * (entidad.vida / entidad.vidaMaxima) ), (int)vidaAlto, GREEN ); } int main() { SetConfigFlags( FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT ); InitWindow( 1100, 750, "A4B1 0.1" ); SetTargetFPS(120); SetRandomSeed( (unsigned int)GetTime() ); std::vector paredes = { {{280.0f, 150.0f, 180.0f, 14.0f}}, {{540.0f, 150.0f, 180.0f, 14.0f}}, {{280.0f, 486.0f, 180.0f, 14.0f}}, {{540.0f, 486.0f, 180.0f, 14.0f}}, {{240.0f, 230.0f, 14.0f, 150.0f}}, {{746.0f, 230.0f, 14.0f, 150.0f}}, {{450.0f, 245.0f, 100.0f, 14.0f}}, {{450.0f, 391.0f, 100.0f, 14.0f}} }; std::vector entidades = { { {220.0f, 325.0f}, {0.0f, 0.0f}, {1.0f, 0.0f}, RADIO_JUGADOR, 100.0f, 100.0f, 0, true, true, 5, 0.0f }, { {180.0f, 500.0f}, {0.0f, 0.0f}, {1.0f, 0.0f}, RADIO_ENEMIGO, 100.0f, 100.0f, 0, false, true, 5, 0.0f }, { {780.0f, 180.0f}, {0.0f, 0.0f}, {-1.0f, 0.0f}, RADIO_ENEMIGO, 100.0f, 100.0f, 1, false, true, 5, 0.0f }, { {780.0f, 470.0f}, {0.0f, 0.0f}, {-1.0f, 0.0f}, RADIO_ENEMIGO, 100.0f, 100.0f, 1, false, true, 5, 0.0f } }; std::vector balas; std::vector municiones; float tiempoAparicionMunicion = 0.0f; int impactos = 0; while (!WindowShouldClose()) { float dt = GetFrameTime(); if (IsKeyPressed(KEY_F11)) ToggleFullscreen(); int anchoPantalla = GetScreenWidth(); int altoPantalla = GetScreenHeight(); float escalaX = anchoPantalla / MUNDO_ANCHO; float escalaY = altoPantalla / MUNDO_ALTO; float escala = std::min( escalaX, escalaY ); Entidad& jugador = entidades[0]; Vector2 movimiento = { 0.0f, 0.0f }; if (IsKeyDown(KEY_W)) movimiento.y -= 1.0f; if (IsKeyDown(KEY_S)) movimiento.y += 1.0f; if (IsKeyDown(KEY_A)) movimiento.x -= 1.0f; if (IsKeyDown(KEY_D)) movimiento.x += 1.0f; movimiento = Normalizar(movimiento); if (movimiento.x != 0.0f || movimiento.y != 0.0f) { jugador.direccion = movimiento; } jugador.velocidad = { movimiento.x * VELOCIDAD_JUGADOR, movimiento.y * VELOCIDAD_JUGADOR }; if (jugador.viva) { jugador.posicion.x += jugador.velocidad.x * dt; jugador.posicion.y += jugador.velocidad.y * dt; RebotarBorde(jugador); RebotarPared( jugador, paredes ); } jugador.tiempoDisparo -= dt; if (jugador.viva && IsKeyPressed(KEY_SPACE) && jugador.tiempoDisparo <= 0.0f) { if (jugador.municion > 0) { Disparar( jugador, balas ); jugador.tiempoDisparo = 0.18f; } } for (int i = 1; i < (int)entidades.size(); i++) { Entidad& entidad = entidades[i]; if (!entidad.viva) continue; int objetivo = BuscarObjetivo( i, entidades ); if (objetivo >= 0) { Vector2 direccion = Normalizar({ entidades[objetivo].posicion.x - entidad.posicion.x, entidades[objetivo].posicion.y - entidad.posicion.y }); entidad.direccion = direccion; float distancia = Distancia( entidad.posicion, entidades[objetivo].posicion ); Vector2 movimientoIA = direccion; if (distancia < 250.0f) { movimientoIA = { -direccion.y, direccion.x }; } entidad.velocidad = { movimientoIA.x * VELOCIDAD_IA, movimientoIA.y * VELOCIDAD_IA }; entidad.tiempoDisparo -= dt; if (entidad.tiempoDisparo <= 0.0f && entidad.municion > 0) { Disparar( entidad, balas ); entidad.tiempoDisparo = 0.75f; } } entidad.posicion.x += entidad.velocidad.x * dt; entidad.posicion.y += entidad.velocidad.y * dt; RebotarBorde( entidad ); RebotarPared( entidad, paredes ); } for (Bala& bala : balas) { if (!bala.activa) continue; MoverBala( bala, dt, paredes ); if (!bala.activa) continue; for (int i = 0; i < (int)entidades.size(); i++) { Entidad& entidad = entidades[i]; if (!entidad.viva) continue; if (entidad.equipo == bala.equipo) continue; if (!CirculoCirculo( bala.posicion, 6.0f, entidad.posicion, entidad.radio)) { continue; } entidad.vida -= DAÑO_BALA; impactos++; Vector2 empuje = bala.direccion; entidad.posicion.x += empuje.x * 10.0f; entidad.posicion.y += empuje.y * 10.0f; if (entidad.vida <= 0.0f) { entidad.vida = 0.0f; entidad.viva = false; entidad.velocidad = { 0.0f, 0.0f }; } bala.activa = false; break; } } balas.erase( std::remove_if( balas.begin(), balas.end(), [](const Bala& bala) { return !bala.activa; } ), balas.end() ); tiempoAparicionMunicion -= dt; if (tiempoAparicionMunicion <= 0.0f) { CrearMunicion( municiones, paredes, entidades ); tiempoAparicionMunicion = 4.0f; } for (Municion& municion : municiones) { if (!municion.activa) continue; if (!jugador.viva) continue; if (Distancia( jugador.posicion, municion.posicion ) < 28.0f) { jugador.municion += 5; municion.activa = false; } } municiones.erase( std::remove_if( municiones.begin(), municiones.end(), [](const Municion& municion) { return !municion.activa; } ), municiones.end() ); BeginDrawing(); ClearBackground({ 0, 0, 0, 255 }); Vector2 jugadorPantalla = { jugador.posicion.x * escalaX, jugador.posicion.y * escalaY }; for (const Pared& pared : paredes) { Vector2 centro = { pared.rectangulo.x + pared.rectangulo.width * 0.5f, pared.rectangulo.y + pared.rectangulo.height * 0.5f }; if (Distancia( centro, jugador.posicion ) > RADIO_VISION + 180.0f) { continue; } Rectangle r = { pared.rectangulo.x * escalaX, pared.rectangulo.y * escalaY, pared.rectangulo.width * escalaX, pared.rectangulo.height * escalaY }; DrawRectangleRounded( r, 0.3f, 8, { 45, 45, 52, 255 } ); DrawRectangleRoundedLines( r, 0.3f, 8, { 120, 120, 130, 255 } ); } Rectangle borde = { BORDE * escalaX, BORDE * escalaY, (MUNDO_ANCHO - BORDE * 2.0f) * escalaX, (MUNDO_ALTO - BORDE * 2.0f) * escalaY }; if (Distancia( jugador.posicion, { MUNDO_ANCHO * 0.5f, MUNDO_ALTO * 0.5f } ) < RADIO_VISION + 500.0f) { DrawRectangleRoundedLines( borde, 0.08f, 16, WHITE ); } for (const Municion& municion : municiones) { if (!municion.activa) continue; if (Distancia( jugador.posicion, municion.posicion ) > RADIO_VISION) continue; Vector2 posicion = { municion.posicion.x * escalaX, municion.posicion.y * escalaY }; DrawRectangle( (int)posicion.x - 9, (int)posicion.y - 9, 18, 18, YELLOW ); DrawText( "+5", (int)posicion.x - 8, (int)posicion.y - 7, 14, BLACK ); } for (const Bala& bala : balas) { if (!bala.activa) continue; if (Distancia( jugador.posicion, bala.posicion ) > RADIO_VISION) continue; Vector2 posicion = { bala.posicion.x * escalaX, bala.posicion.y * escalaY }; Color color = bala.equipo == 0 ? BLUE : RED; DrawCircleV( posicion, 5.0f * escala, color ); } for (const Entidad& entidad : entidades) { if (!entidad.viva) continue; if (Distancia( jugador.posicion, entidad.posicion ) > RADIO_VISION) continue; DibujarEntidad( entidad, escalaX, escalaY, escala ); } DrawCircle( (int)jugadorPantalla.x, (int)jugadorPantalla.y, RADIO_VISION * escala, Fade(BLACK, 0.0f) ); DrawRectangle( 15, 15, 270, 145, Fade(BLACK, 0.75f) ); DrawText( TextFormat( "VIDA: %d", (int)jugador.vida ), 28, 25, 22, WHITE ); DrawText( TextFormat( "BALAS: %d", jugador.municion ), 28, 53, 22, YELLOW ); DrawText( TextFormat( "IMPACTOS: %d", impactos ), 28, 81, 19, WHITE ); DrawText( TextFormat( "FPS: %d", GetFPS() ), 28, 107, 18, GREEN ); DrawText( "AZUL 2 VS ROJO 2", 28, 132, 16, LIGHTGRAY ); EndDrawing(); } CloseWindow(); return 0; }