Compare commits

2 Commits
Author SHA1 Message Date
Clinv 92daf86379 Nuevo funcionamiento TALANES 2026-09-27 00:29:48 -03:00
Clinv 95a1231c63 Nivel TALAN 2026-09-27 00:12:17 -03:00
7 changed files with 1901 additions and 3204 deletions

No files matched your search

BIN
View File
Binary file not shown.
+6 -34
View File
@@ -1,36 +1,8 @@
# Disable raylib examples/games and include FetchContent
set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(BUILD_GAMES OFF CACHE BOOL "" FORCE)
include(FetchContent)
find_package(PkgConfig REQUIRED)
# Declare and make raylib available
FetchContent_Declare(
raylib
GIT_REPOSITORY "https://github.com/raysan5/raylib.git"
GIT_TAG "master"
GIT_PROGRESS TRUE
pkg_check_modules(RAYLIB REQUIRED IMPORTED_TARGET raylib)
add_exe_module(
A4B1
DEPENDS PkgConfig::RAYLIB
)
# 2. Descargar Pugixml (Lector de XML/SVG)
FetchContent_Declare(
pugixml
GIT_REPOSITORY "https://github.com/zeux/pugixml.git"
GIT_TAG "v1.14" # Versión estable
GIT_PROGRESS TRUE
)
# 1. Save the local PLATFORM variable
set(ORIGINAL_PLATFORM "${PLATFORM}")
# 2. Override the local variable to "Desktop" (This shadows the cache and hides "linux" from raylib)
set(PLATFORM "Desktop")
# 3. Configure raylib
FetchContent_MakeAvailable(raylib pugixml)
# 4. Restore your project's local PLATFORM variable back to "linux"
set(PLATFORM "${ORIGINAL_PLATFORM}")
add_exe_module(pacman OUTPUT_NAME "Pacman")
target_link_libraries(pacman PRIVATE raylib pugixml)
+975
View File
@@ -0,0 +1,975 @@
#include "raylib.h"
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
#include <limits>
extern Vector3 playerPosition;
extern bool IsSolidAt(Vector3 position, float radius);
const int NPC_COUNT = 20;
Vector3 enemyPosition[NPC_COUNT] =
{
{ -72.0f, 1.0f, -72.0f },
{ 72.0f, 1.0f, -72.0f },
{ -72.0f, 1.0f, 72.0f },
{ 72.0f, 1.0f, 72.0f },
{ -48.0f, 1.0f, -36.0f },
{ 48.0f, 1.0f, -36.0f },
{ -48.0f, 1.0f, 36.0f },
{ 48.0f, 1.0f, 36.0f },
{ -28.0f, 1.0f, -68.0f },
{ 28.0f, 1.0f, -68.0f },
{ -28.0f, 1.0f, 68.0f },
{ 28.0f, 1.0f, 68.0f },
{ -60.0f, 1.0f, -52.0f },
{ 60.0f, 1.0f, -52.0f },
{ -60.0f, 1.0f, 52.0f },
{ 60.0f, 1.0f, 52.0f },
{ -16.0f, 1.0f, -48.0f },
{ 16.0f, 1.0f, -48.0f },
{ -16.0f, 1.0f, 48.0f },
{ 16.0f, 1.0f, 48.0f }
};
float enemyWalkTime[NPC_COUNT] = {};
float enemyStunTime[NPC_COUNT] = {};
float enemySpin[NPC_COUNT] = {};
bool enemyMoving[NPC_COUNT] = {};
const float enemySpeed = 5.5f;
const float enemyStunDuration = 2.5f;
static bool showEnemyPaths = false;
static const int GRID_SIZE = 41;
static const float CELL_SIZE = 4.0f;
static const float GRID_OFFSET = 20.0f;
struct GridNode
{
int x;
int z;
bool operator==(const GridNode& other) const
{
return x == other.x && z == other.z;
}
};
static std::vector<GridNode> enemyPath[NPC_COUNT];
static int WorldToGridX(float x)
{
return (int)std::round(x / CELL_SIZE + GRID_OFFSET);
}
static int WorldToGridZ(float z)
{
return (int)std::round(z / CELL_SIZE + GRID_OFFSET);
}
static Vector3 GridToWorld(int x, int z)
{
return
{
(x - GRID_OFFSET) * CELL_SIZE,
1.0f,
(z - GRID_OFFSET) * CELL_SIZE
};
}
static bool IsInsideGrid(int x, int z)
{
return
x >= 0 &&
x < GRID_SIZE &&
z >= 0 &&
z < GRID_SIZE;
}
static bool IsWalkableCell(int x, int z)
{
if (!IsInsideGrid(x, z))
return false;
Vector3 position = GridToWorld(x, z);
return !IsSolidAt(position, 0.42f);
}
static float GridDistance(
int x1,
int z1,
int x2,
int z2
)
{
float dx = (float)(x1 - x2);
float dz = (float)(z1 - z2);
return std::sqrt(dx * dx + dz * dz);
}
static std::vector<GridNode> FindPath(
Vector3 startWorld,
Vector3 targetWorld
)
{
GridNode start =
{
WorldToGridX(startWorld.x),
WorldToGridZ(startWorld.z)
};
GridNode target =
{
WorldToGridX(targetWorld.x),
WorldToGridZ(targetWorld.z)
};
if (!IsInsideGrid(start.x, start.z))
return {};
if (!IsInsideGrid(target.x, target.z))
return {};
// Si el destino está ocupado por un mueble,
// buscamos la celda caminable más cercana.
if (!IsWalkableCell(target.x, target.z))
{
bool found = false;
for (int radius = 1; radius <= 5 && !found; radius++)
{
for (int z = target.z - radius;
z <= target.z + radius && !found;
z++)
{
for (int x = target.x - radius;
x <= target.x + radius;
x++)
{
if (IsWalkableCell(x, z))
{
target = {x, z};
found = true;
break;
}
}
}
}
if (!found)
return {};
}
if (!IsWalkableCell(start.x, start.z))
return {};
struct Node
{
int x;
int z;
float g;
float f;
};
std::vector<Node> open;
std::vector<bool> closed(
GRID_SIZE * GRID_SIZE,
false
);
std::vector<GridNode> cameFrom(
GRID_SIZE * GRID_SIZE,
{-1, -1}
);
std::vector<float> gScore(
GRID_SIZE * GRID_SIZE,
std::numeric_limits<float>::infinity()
);
auto indexOf =
[](int x, int z)
{
return z * GRID_SIZE + x;
};
gScore[indexOf(start.x, start.z)] = 0.0f;
open.push_back(
{
start.x,
start.z,
0.0f,
GridDistance(
start.x,
start.z,
target.x,
target.z
)
}
);
const int directions[4][2] =
{
{ 1, 0 },
{-1, 0 },
{ 0, 1 },
{ 0,-1 }
};
while (!open.empty())
{
int best = 0;
for (int i = 1; i < (int)open.size(); i++)
{
if (open[i].f < open[best].f)
best = i;
}
Node current = open[best];
open.erase(open.begin() + best);
if (current.x == target.x &&
current.z == target.z)
{
std::vector<GridNode> path;
GridNode currentNode =
{
target.x,
target.z
};
while (!(currentNode == start))
{
path.push_back(currentNode);
GridNode previous =
cameFrom[
indexOf(
currentNode.x,
currentNode.z
)
];
if (previous.x < 0)
{
path.clear();
return path;
}
currentNode = previous;
}
path.push_back(start);
std::reverse(
path.begin(),
path.end()
);
return path;
}
int currentIndex =
indexOf(current.x, current.z);
if (closed[currentIndex])
continue;
closed[currentIndex] = true;
for (const auto& direction : directions)
{
int nx = current.x + direction[0];
int nz = current.z + direction[1];
if (!IsInsideGrid(nx, nz))
continue;
if (!IsWalkableCell(nx, nz))
continue;
int nextIndex = indexOf(nx, nz);
if (closed[nextIndex])
continue;
float newG = current.g + 1.0f;
if (newG < gScore[nextIndex])
{
gScore[nextIndex] = newG;
cameFrom[nextIndex] =
{
current.x,
current.z
};
float h =
GridDistance(
nx,
nz,
target.x,
target.z
);
open.push_back(
{
nx,
nz,
newG,
newG + h
}
);
}
}
}
return {};
}
static bool PositionIsFreeForEnemy(
Vector3 position,
int ignoreIndex
)
{
if (IsSolidAt(position, 0.45f))
return false;
for (int i = 0; i < NPC_COUNT; i++)
{
if (i == ignoreIndex)
continue;
float dx =
enemyPosition[i].x -
position.x;
float dz =
enemyPosition[i].z -
position.z;
if (dx * dx + dz * dz < 0.75f * 0.75f)
return false;
}
return true;
}
void MoveEnemyWithCollision(
int index,
float moveX,
float moveZ
)
{
Vector3 nextX =
enemyPosition[index];
nextX.x += moveX;
if (PositionIsFreeForEnemy(nextX, index))
enemyPosition[index].x = nextX.x;
Vector3 nextZ =
enemyPosition[index];
nextZ.z += moveZ;
if (PositionIsFreeForEnemy(nextZ, index))
enemyPosition[index].z = nextZ.z;
}
static void RecalculatePath(int index)
{
enemyPath[index] =
FindPath(
enemyPosition[index],
playerPosition
);
}
static void MoveEnemyAlongPath(
int index,
float deltaTime
)
{
if (enemyPath[index].size() < 2)
{
enemyMoving[index] = false;
return;
}
Vector3 target =
GridToWorld(
enemyPath[index][1].x,
enemyPath[index][1].z
);
float dx =
target.x -
enemyPosition[index].x;
float dz =
target.z -
enemyPosition[index].z;
float distance =
std::sqrt(dx * dx + dz * dz);
if (distance < 0.18f)
{
enemyPath[index].erase(
enemyPath[index].begin()
);
return;
}
dx /= distance;
dz /= distance;
MoveEnemyWithCollision(
index,
dx * enemySpeed * deltaTime,
dz * enemySpeed * deltaTime
);
enemyMoving[index] = true;
enemyWalkTime[index] += deltaTime * 9.0f;
}
static int FindClosestEnemy()
{
int best = 0;
float bestDistance =
std::numeric_limits<float>::infinity();
for (int i = 0; i < NPC_COUNT; i++)
{
float dx =
enemyPosition[i].x -
playerPosition.x;
float dz =
enemyPosition[i].z -
playerPosition.z;
float distance =
dx * dx + dz * dz;
if (distance < bestDistance)
{
bestDistance = distance;
best = i;
}
}
return best;
}
static bool HasNearbyTalan()
{
for (int i = 0; i < NPC_COUNT; i++)
{
float dx =
enemyPosition[i].x -
playerPosition.x;
float dz =
enemyPosition[i].z -
playerPosition.z;
float distance =
std::sqrt(dx * dx + dz * dz);
if (distance < 28.0f)
return true;
}
return false;
}
static void TeleportOneTalanToPlayerArea()
{
int index = FindClosestEnemy();
float bestDistance = 18.0f;
for (int attempt = 0; attempt < 40; attempt++)
{
float angle =
(float)attempt *
(PI * 2.0f / 40.0f);
float distance =
bestDistance +
(attempt % 5) * 1.5f;
Vector3 position =
{
playerPosition.x +
std::cos(angle) * distance,
1.0f,
playerPosition.z +
std::sin(angle) * distance
};
if (!PositionIsFreeForEnemy(
position,
index))
{
continue;
}
std::vector<GridNode> testPath =
FindPath(
position,
playerPosition
);
if (!testPath.empty())
{
enemyPosition[index] =
position;
enemyWalkTime[index] = 0.0f;
enemyStunTime[index] = 0.0f;
enemySpin[index] = 0.0f;
enemyPath[index] =
testPath;
return;
}
}
}
void UpdateEnemy(float deltaTime)
{
// P muestra/oculta las rutas.
if (IsKeyPressed(KEY_P))
{
showEnemyPaths =
!showEnemyPaths;
}
// Siempre debe haber un talán relativamente
// cerca del jugador.
if (!HasNearbyTalan())
{
TeleportOneTalanToPlayerArea();
}
for (int i = 0; i < NPC_COUNT; i++)
{
float dx =
playerPosition.x -
enemyPosition[i].x;
float dz =
playerPosition.z -
enemyPosition[i].z;
float distance =
std::sqrt(dx * dx + dz * dz);
// =========================================
// MAREADO
// =========================================
if (enemyStunTime[i] > 0.0f)
{
enemyStunTime[i] -= deltaTime;
enemySpin[i] += deltaTime * 18.0f;
enemyMoving[i] = false;
if (enemyStunTime[i] < 0.0f)
enemyStunTime[i] = 0.0f;
continue;
}
// =========================================
// EMPUJAR JUGADOR
// =========================================
if (distance > 0.001f &&
distance < 1.5f)
{
float pushX =
(dx / distance) *
10.0f *
deltaTime;
float pushZ =
(dz / distance) *
10.0f *
deltaTime;
Vector3 next = playerPosition;
next.x += pushX;
next.z += pushZ;
if (!IsSolidAt(next, 0.45f))
playerPosition = next;
}
// =========================================
// RUTA INTELIGENTE
// =========================================
// Recalcular frecuentemente para que los talanes
// reaccionen si el jugador cambia de dirección.
if (enemyPath[i].empty() ||
((int)(GetTime() * 4.0) + i) % 5 == 0)
{
RecalculatePath(i);
}
if (distance > 1.8f)
{
MoveEnemyAlongPath(
i,
deltaTime
);
}
else
{
enemyMoving[i] = false;
enemyWalkTime[i] = 0.0f;
}
}
// =========================================
// CLICK DERECHO
// =========================================
if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT))
{
for (int i = 0; i < NPC_COUNT; i++)
{
float dx =
enemyPosition[i].x -
playerPosition.x;
float dz =
enemyPosition[i].z -
playerPosition.z;
float distance =
std::sqrt(
dx * dx +
dz * dz
);
if (distance < 4.0f &&
distance > 0.001f)
{
dx /= distance;
dz /= distance;
MoveEnemyWithCollision(
i,
dx * 3.5f,
dz * 3.5f
);
enemyStunTime[i] =
enemyStunDuration;
enemySpin[i] = 0.0f;
enemyWalkTime[i] = 0.0f;
}
}
}
}
void TeleportEnemyNearPlayer(
int index,
float yaw
)
{
if (index < 0 ||
index >= NPC_COUNT)
return;
float dx =
enemyPosition[index].x -
playerPosition.x;
float dz =
enemyPosition[index].z -
playerPosition.z;
float oldDistance =
std::sqrt(
dx * dx +
dz * dz
);
if (oldDistance < 3.0f)
oldDistance = 3.0f;
float baseAngle =
std::atan2(
dz,
dx
);
for (int attempt = 0; attempt < 16; attempt++)
{
float angle =
baseAngle +
PI +
attempt * 0.4f;
Vector3 newPosition =
{
playerPosition.x +
std::cos(angle) * oldDistance,
1.0f,
playerPosition.z +
std::sin(angle) * oldDistance
};
if (!PositionIsFreeForEnemy(
newPosition,
index))
{
continue;
}
enemyPosition[index] =
newPosition;
enemyStunTime[index] = 0.0f;
enemySpin[index] = 0.0f;
enemyWalkTime[index] = 0.0f;
RecalculatePath(index);
return;
}
}
int CheckEnemyHit(Vector3 bulletPosition)
{
for (int i = 0; i < NPC_COUNT; i++)
{
float hitX =
bulletPosition.x -
enemyPosition[i].x;
float hitY =
bulletPosition.y -
1.2f;
float hitZ =
bulletPosition.z -
enemyPosition[i].z;
float distance =
std::sqrt(
hitX * hitX +
hitY * hitY +
hitZ * hitZ
);
if (distance < 0.8f)
return i;
}
return -1;
}
// =============================================
// RUTAS VISIBLES CON P
// =============================================
void DrawEnemyPaths()
{
if (!showEnemyPaths)
return;
for (int i = 0; i < NPC_COUNT; i++)
{
if (enemyPath[i].size() < 2)
continue;
Vector3 previous =
enemyPosition[i];
previous.y = 0.12f;
for (size_t p = 1;
p < enemyPath[i].size();
p++)
{
Vector3 current =
GridToWorld(
enemyPath[i][p].x,
enemyPath[i][p].z
);
current.y = 0.12f;
DrawLine3D(
previous,
current,
LIME
);
previous = current;
}
}
}
// =============================================
// TALÁN ORIGINAL
// NO TOCAR
// =============================================
void DrawEnemy()
{
for (int i = 0; i < NPC_COUNT; i++)
{
float legMove = 0.0f;
float armMove = 0.0f;
if (enemyMoving[i])
{
legMove =
std::sin(enemyWalkTime[i]) *
0.18f;
armMove =
std::sin(enemyWalkTime[i]) *
0.25f;
}
float spinX = 0.0f;
float spinZ = 0.0f;
if (enemyStunTime[i] > 0.0f)
{
spinX =
std::sin(enemySpin[i]) *
0.25f;
spinZ =
std::cos(enemySpin[i]) *
0.25f;
}
DrawCube(
{
enemyPosition[i].x -
0.22f +
spinX,
0.45f,
enemyPosition[i].z +
legMove +
spinZ
},
0.25f,
0.9f,
0.25f,
DARKGRAY
);
DrawCube(
{
enemyPosition[i].x +
0.22f -
spinX,
0.45f,
enemyPosition[i].z -
legMove -
spinZ
},
0.25f,
0.9f,
0.25f,
DARKGRAY
);
DrawCube(
{
enemyPosition[i].x,
1.25f,
enemyPosition[i].z
},
0.8f,
1.0f,
0.45f,
BLUE
);
DrawSphere(
{
enemyPosition[i].x + spinX,
2.05f,
enemyPosition[i].z + spinZ
},
0.38f,
BEIGE
);
DrawCube(
{
enemyPosition[i].x -
0.55f +
spinX,
1.25f + armMove,
enemyPosition[i].z +
spinZ
},
0.18f,
0.75f,
0.18f,
BLUE
);
DrawCube(
{
enemyPosition[i].x +
0.55f -
spinX,
1.25f - armMove,
enemyPosition[i].z -
spinZ
},
0.18f,
0.75f,
0.18f,
BLUE
);
}
}
+457
View File
@@ -0,0 +1,457 @@
#include "raylib.h"
#include <cmath>
#include <vector>
#include <random>
struct Wall
{
Vector3 position;
Vector3 size;
};
struct Furniture
{
Vector3 position;
Vector3 size;
int type;
};
static const int MAZE_W = 41;
static const int MAZE_H = 41;
static const float CELL = 4.0f;
static bool maze[MAZE_H][MAZE_W];
static std::vector<Wall> walls;
static std::vector<Furniture> furniture;
extern Vector3 playerPosition;
extern Camera3D GetPlayerCamera();
extern void UpdatePlayer(float deltaTime);
extern void UpdateEnemy(float deltaTime);
extern void UpdateWeapons(float deltaTime);
extern void DrawEnemy();
extern void DrawBullets();
extern void DrawPlayerWeapon();
bool IsSolidAt(Vector3 position, float radius)
{
for (const Wall& wall : walls)
{
if (position.x >= wall.position.x - wall.size.x / 2.0f - radius &&
position.x <= wall.position.x + wall.size.x / 2.0f + radius &&
position.z >= wall.position.z - wall.size.z / 2.0f - radius &&
position.z <= wall.position.z + wall.size.z / 2.0f + radius)
{
return true;
}
}
for (const Furniture& object : furniture)
{
if (position.x >= object.position.x - object.size.x / 2.0f - radius &&
position.x <= object.position.x + object.size.x / 2.0f + radius &&
position.z >= object.position.z - object.size.z / 2.0f - radius &&
position.z <= object.position.z + object.size.z / 2.0f + radius)
{
return true;
}
}
return false;
}
static Vector3 CellToWorld(int x, int z)
{
return {
(x - MAZE_W / 2) * CELL,
0.0f,
(z - MAZE_H / 2) * CELL
};
}
static void GenerateMaze()
{
for (int z = 0; z < MAZE_H; z++)
{
for (int x = 0; x < MAZE_W; x++)
{
maze[z][x] = true;
}
}
std::vector<Vector2> stack;
stack.push_back({1, 1});
maze[1][1] = false;
std::mt19937 rng(12345);
const int dirs[4][2] =
{
{ 2, 0 },
{-2, 0 },
{ 0, 2 },
{ 0,-2 }
};
while (!stack.empty())
{
Vector2 current = stack.back();
std::vector<int> available;
for (int i = 0; i < 4; i++)
{
int nx = (int)current.x + dirs[i][0];
int nz = (int)current.y + dirs[i][1];
if (nx > 0 && nx < MAZE_W - 1 &&
nz > 0 && nz < MAZE_H - 1 &&
maze[nz][nx])
{
available.push_back(i);
}
}
if (available.empty())
{
stack.pop_back();
continue;
}
int d = available[rng() % available.size()];
int nx = (int)current.x + dirs[d][0];
int nz = (int)current.y + dirs[d][1];
maze[((int)current.y + nz) / 2]
[((int)current.x + nx) / 2] = false;
maze[nz][nx] = false;
stack.push_back({(float)nx, (float)nz});
}
for (int z = 3; z < MAZE_H - 3; z += 6)
{
for (int x = 3; x < MAZE_W - 3; x += 6)
{
maze[z][x] = false;
if (x + 1 < MAZE_W - 1)
maze[z][x + 1] = false;
if (x > 1)
maze[z][x - 1] = false;
if (z + 1 < MAZE_H - 1)
maze[z + 1][x] = false;
if (z > 1)
maze[z - 1][x] = false;
}
}
}
static void BuildWalls()
{
walls.clear();
for (int z = 0; z < MAZE_H; z++)
{
for (int x = 0; x < MAZE_W; x++)
{
if (!maze[z][x])
continue;
Vector3 p = CellToWorld(x, z);
walls.push_back({
{p.x, 1.5f, p.z},
{CELL, 3.0f, CELL}
});
}
}
}
static void GenerateFurniture()
{
furniture.clear();
for (int z = 2; z < MAZE_H - 2; z++)
{
for (int x = 2; x < MAZE_W - 2; x++)
{
if (maze[z][x])
continue;
if ((x * 13 + z * 7) % 17 != 0)
continue;
if (x <= 4 && z <= 4)
continue;
Vector3 p = CellToWorld(x, z);
Furniture object;
object.type = (x * 3 + z * 5) % 5;
switch (object.type)
{
case 0:
object.position = {p.x, 0.8f, p.z};
object.size = {2.4f, 1.6f, 2.4f};
break;
case 1:
object.position = {p.x, 0.75f, p.z};
object.size = {1.5f, 1.5f, 1.5f};
break;
case 2:
object.position = {p.x, 1.35f, p.z};
object.size = {1.8f, 2.7f, 1.0f};
break;
case 3:
object.position = {p.x, 0.4f, p.z};
object.size = {2.5f, 0.8f, 0.8f};
break;
default:
object.position = {p.x, 0.6f, p.z};
object.size = {2.0f, 1.2f, 1.2f};
break;
}
if (!IsSolidAt(object.position, 0.2f))
furniture.push_back(object);
}
}
}
static void DrawFloor()
{
for (int z = 0; z < MAZE_H; z++)
{
for (int x = 0; x < MAZE_W; x++)
{
if (maze[z][x])
continue;
Vector3 p = CellToWorld(x, z);
int shade = 108 + ((x * 17 + z * 11) % 9);
Color color = {
(unsigned char)shade,
(unsigned char)shade,
(unsigned char)(shade + 1),
255
};
DrawCube(
{p.x, -0.06f, p.z},
CELL + 0.16f,
0.12f,
CELL + 0.16f,
color
);
}
}
}
static void DrawWalls()
{
for (const Wall& wall : walls)
{
DrawCube(
wall.position,
wall.size.x,
wall.size.y,
wall.size.z,
{52, 82, 116, 255}
);
}
}
static void DrawCeiling()
{
for (int z = 0; z < MAZE_H; z++)
{
for (int x = 0; x < MAZE_W; x++)
{
if (maze[z][x])
continue;
Vector3 p = CellToWorld(x, z);
bool lightZone =
((x * 3 + z * 7) % 11 == 0);
Color ceilingColor =
lightZone
? Color{155, 155, 155, 255}
: Color{70, 70, 70, 255};
DrawCube(
{p.x, 3.12f, p.z},
CELL + 0.16f,
0.16f,
CELL + 0.16f,
ceilingColor
);
if (lightZone)
{
DrawCube(
{p.x, 3.01f, p.z},
1.3f,
0.04f,
0.7f,
RAYWHITE
);
}
}
}
}
static void DrawFurniture()
{
for (const Furniture& object : furniture)
{
Color color;
switch (object.type)
{
case 0:
color = {75, 75, 75, 255};
break;
case 1:
color = {100, 82, 65, 255};
break;
case 2:
color = {62, 62, 68, 255};
break;
case 3:
color = {84, 70, 55, 255};
break;
default:
color = {90, 90, 90, 255};
break;
}
DrawCube(
object.position,
object.size.x,
object.size.y,
object.size.z,
color
);
}
}
static void DrawHUD()
{
DrawRectangle(
0,
0,
300,
70,
{0, 0, 0, 100}
);
DrawText(
"NIVEL: TALAN-TALAN",
15,
12,
20,
RAYWHITE
);
DrawText(
"WASD MOVER",
15,
39,
15,
LIGHTGRAY
);
int centerX = GetScreenWidth() / 2;
int centerY = GetScreenHeight() / 2;
DrawLine(
centerX - 8,
centerY,
centerX + 8,
centerY,
RAYWHITE
);
DrawLine(
centerX,
centerY - 8,
centerX,
centerY + 8,
RAYWHITE
);
}
void RunGame()
{
InitWindow(
1366,
768,
"A4B1 - Nivel Talan-Talan"
);
SetTargetFPS(60);
DisableCursor();
GenerateMaze();
BuildWalls();
GenerateFurniture();
while (!WindowShouldClose())
{
float deltaTime = GetFrameTime();
if (IsKeyPressed(KEY_F11))
{
ToggleFullscreen();
}
UpdatePlayer(deltaTime);
UpdateEnemy(deltaTime);
UpdateWeapons(deltaTime);
BeginDrawing();
ClearBackground({25, 25, 28, 255});
BeginMode3D(GetPlayerCamera());
DrawFloor();
DrawWalls();
DrawCeiling();
DrawFurniture();
DrawEnemy();
DrawBullets();
EndMode3D();
DrawPlayerWeapon();
DrawHUD();
EndDrawing();
}
EnableCursor();
CloseWindow();
}
+4 -3170
View File
File diff suppressed because it is too large. Load diff
+234
View File
@@ -0,0 +1,234 @@
#include "raylib.h"
#include <cmath>
extern bool IsSolidAt(Vector3 position, float radius);
Vector3 playerPosition = {-76.0f, 1.0f, -76.0f};
float verticalVelocity = 0.0f;
float playerYaw = 0.0f;
float playerPitch = 0.0f;
float weaponBobTime = 0.0f;
float weaponShootTime = 0.0f;
const float moveSpeed = 11.0f;
const float jumpForce = 9.0f;
const float gravity = 20.0f;
static const float PLAYER_RADIUS = 0.45f;
static const float PLAYER_GROUND_Y = 1.0f;
static const float PLAYER_HEAD_Y = 2.0f;
static const float CEILING_Y = 3.0f;
Camera3D GetPlayerCamera()
{
Camera3D camera = {};
camera.position = playerPosition;
Vector3 forward =
{
cosf(playerPitch) * cosf(playerYaw),
sinf(playerPitch),
cosf(playerPitch) * sinf(playerYaw)
};
camera.target =
{
camera.position.x + forward.x,
camera.position.y + forward.y,
camera.position.z + forward.z
};
camera.up = {0.0f, 1.0f, 0.0f};
camera.fovy = 75.0f;
camera.projection = CAMERA_PERSPECTIVE;
return camera;
}
static void MovePlayer(Vector3 movement)
{
Vector3 nextX = playerPosition;
nextX.x += movement.x;
if (!IsSolidAt(nextX, PLAYER_RADIUS))
playerPosition.x = nextX.x;
Vector3 nextZ = playerPosition;
nextZ.z += movement.z;
if (!IsSolidAt(nextZ, PLAYER_RADIUS))
playerPosition.z = nextZ.z;
}
void UpdatePlayer(float deltaTime)
{
Vector2 mouse = GetMouseDelta();
playerYaw += mouse.x * 0.003f;
playerPitch -= mouse.y * 0.003f;
if (playerPitch > 1.45f)
playerPitch = 1.45f;
if (playerPitch < -1.45f)
playerPitch = -1.45f;
Vector3 movement = {0.0f, 0.0f, 0.0f};
Vector3 forward =
{
cosf(playerYaw),
0.0f,
sinf(playerYaw)
};
Vector3 right =
{
-sinf(playerYaw),
0.0f,
cosf(playerYaw)
};
if (IsKeyDown(KEY_W))
{
movement.x += forward.x;
movement.z += forward.z;
}
if (IsKeyDown(KEY_S))
{
movement.x -= forward.x;
movement.z -= forward.z;
}
if (IsKeyDown(KEY_D))
{
movement.x += right.x;
movement.z += right.z;
}
if (IsKeyDown(KEY_A))
{
movement.x -= right.x;
movement.z -= right.z;
}
float movementLength =
sqrtf(
movement.x * movement.x +
movement.z * movement.z
);
if (movementLength > 0.0f)
{
movement.x /= movementLength;
movement.z /= movementLength;
MovePlayer({
movement.x * moveSpeed * deltaTime,
0.0f,
movement.z * moveSpeed * deltaTime
});
weaponBobTime += deltaTime * 10.0f;
}
bool grounded =
playerPosition.y <= PLAYER_GROUND_Y + 0.001f;
if (grounded)
{
playerPosition.y = PLAYER_GROUND_Y;
verticalVelocity = 0.0f;
if (IsKeyPressed(KEY_SPACE))
verticalVelocity = jumpForce;
}
verticalVelocity -= gravity * deltaTime;
playerPosition.y +=
verticalVelocity * deltaTime;
if (playerPosition.y < PLAYER_GROUND_Y)
{
playerPosition.y = PLAYER_GROUND_Y;
verticalVelocity = 0.0f;
}
if (playerPosition.y + PLAYER_HEAD_Y > CEILING_Y)
{
playerPosition.y =
CEILING_Y - PLAYER_HEAD_Y;
if (verticalVelocity > 0.0f)
verticalVelocity = 0.0f;
}
if (weaponShootTime > 0.0f)
weaponShootTime -= deltaTime;
}
void DrawPlayerWeapon()
{
float bobX = 0.0f;
float bobY = 0.0f;
bobX = sinf(weaponBobTime) * 5.0f;
bobY = fabsf(cosf(weaponBobTime)) * 4.0f;
if (weaponShootTime > 0.0f)
{
bobX += GetRandomValue(-4, 4);
bobY += GetRandomValue(-5, 5);
}
int w = GetScreenWidth();
int h = GetScreenHeight();
float x = w * 0.76f + bobX;
float y = h * 0.68f + bobY;
DrawRectangle(
(int)x,
(int)y,
190,
95,
DARKGRAY
);
DrawRectangle(
(int)x + 55,
(int)y - 35,
80,
55,
GRAY
);
DrawRectangle(
(int)x + 72,
(int)y - 62,
45,
40,
BLACK
);
DrawRectangle(
(int)x + 20,
(int)y + 30,
45,
65,
BLACK
);
DrawRectangle(
(int)x + 120,
(int)y + 15,
65,
35,
BLACK
);
}
+225
View File
@@ -0,0 +1,225 @@
#include "raylib.h"
#include <cmath>
#include <vector>
extern Camera3D GetPlayerCamera();
extern Vector3 playerPosition;
extern float playerYaw;
extern float weaponShootTime;
extern int CheckEnemyHit(Vector3 bulletPosition);
extern void TeleportEnemyNearPlayer(int index, float yaw);
extern bool IsSolidAt(Vector3 position, float radius);
struct Bullet
{
Vector3 position;
Vector3 direction;
float speed;
bool active;
};
static const int MAX_BULLETS = 100;
static Bullet bullets[MAX_BULLETS];
static Vector3 GetForward()
{
Vector3 forward =
{
cosf(playerYaw),
0.0f,
sinf(playerYaw)
};
return forward;
}
static void SpawnBullet(
Vector3 start,
Vector3 direction
)
{
for (int i = 0; i < MAX_BULLETS; i++)
{
if (!bullets[i].active)
{
bullets[i].position = start;
bullets[i].direction = direction;
bullets[i].speed = 35.0f;
bullets[i].active = true;
return;
}
}
}
void UpdateWeapons(float deltaTime)
{
if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
{
Camera3D camera = GetPlayerCamera();
Vector3 forward =
{
camera.target.x - camera.position.x,
camera.target.y - camera.position.y,
camera.target.z - camera.position.z
};
float length =
sqrtf(
forward.x * forward.x +
forward.y * forward.y +
forward.z * forward.z
);
if (length > 0.001f)
{
forward.x /= length;
forward.y /= length;
forward.z /= length;
}
Vector3 right =
{
-sinf(playerYaw),
0.0f,
cosf(playerYaw)
};
Vector3 start =
{
camera.position.x + forward.x * 0.7f,
camera.position.y + forward.y * 0.7f,
camera.position.z + forward.z * 0.7f
};
const float spread = 0.07f;
SpawnBullet(
start,
{
forward.x,
forward.y,
forward.z
}
);
SpawnBullet(
start,
{
forward.x + right.x * spread,
forward.y,
forward.z + right.z * spread
}
);
SpawnBullet(
start,
{
forward.x - right.x * spread,
forward.y,
forward.z - right.z * spread
}
);
SpawnBullet(
start,
{
forward.x + right.x * spread * 2.0f,
forward.y,
forward.z + right.z * spread * 2.0f
}
);
SpawnBullet(
start,
{
forward.x - right.x * spread * 2.0f,
forward.y,
forward.z - right.z * spread * 2.0f
}
);
weaponShootTime = 0.12f;
}
for (int i = 0; i < MAX_BULLETS; i++)
{
if (!bullets[i].active)
continue;
Vector3 movement =
{
bullets[i].direction.x *
bullets[i].speed * deltaTime,
bullets[i].direction.y *
bullets[i].speed * deltaTime,
bullets[i].direction.z *
bullets[i].speed * deltaTime
};
Vector3 nextPosition =
{
bullets[i].position.x + movement.x,
bullets[i].position.y + movement.y,
bullets[i].position.z + movement.z
};
// Las balas chocan con paredes y muebles.
if (IsSolidAt(nextPosition, 0.08f))
{
bullets[i].active = false;
continue;
}
bullets[i].position = nextPosition;
int hitNPC =
CheckEnemyHit(bullets[i].position);
if (hitNPC >= 0)
{
TeleportEnemyNearPlayer(
hitNPC,
playerYaw
);
bullets[i].active = false;
continue;
}
float dx =
bullets[i].position.x -
playerPosition.x;
float dz =
bullets[i].position.z -
playerPosition.z;
float distance =
sqrtf(dx * dx + dz * dz);
if (distance > 200.0f)
{
bullets[i].active = false;
}
}
}
void DrawBullets()
{
for (int i = 0; i < MAX_BULLETS; i++)
{
if (!bullets[i].active)
continue;
DrawSphere(
bullets[i].position,
0.10f,
YELLOW
);
}
}