Files
Attacks-for-Bombs-1/source/A4B1/main.cpp
T
2026-09-26 17:29:09 -03:00

1311 lines
26 KiB
C++

#include "raylib.h"
#include <cmath>
#include <vector>
#include <algorithm>
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<Pared>& 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<Pared>& paredes)
{
for (const Pared& pared : paredes)
{
if (CheckCollisionPointRec(
posicion,
pared.rectangulo))
{
return true;
}
}
return false;
}
void MoverBala(
Bala& bala,
float dt,
const std::vector<Pared>& 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<Entidad>& 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<Bala>& 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<Municion>& municiones)
{
for (const Municion& municion : municiones)
{
if (municion.activa)
return true;
}
return false;
}
void CrearMunicion(
std::vector<Municion>& municiones,
const std::vector<Pared>& paredes,
const std::vector<Entidad>& 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<Pared> 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<Entidad> 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<Bala> balas;
std::vector<Municion> 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;
}