Compare commits

..

19 Commits

Author SHA1 Message Date
3af24e812f Added music and jumpscares 2025-09-13 21:17:56 -05:00
27b2ee70d1 acorn is now collectable 2025-09-13 20:16:40 -05:00
8caf00c004 fixed the acorn color 2025-09-13 19:29:36 -05:00
d0eb63ba03 My first Blender model- Acorn 2025-09-13 19:16:33 -05:00
28f5bde1bc Added peripheral boundaries with stone walls 2025-09-13 15:35:31 -05:00
56fa075716 collision mechanics 2025-09-13 14:13:27 -05:00
8417f8855a Added moving fence. 2025-09-13 11:43:03 -05:00
899cf9db8b Adding resources and tweaks 2025-09-13 10:24:08 -05:00
123c374ab4 Tree transparency fix 2025-09-01 15:49:07 -05:00
4a4d198617 Added zombie for testing 2025-08-31 22:55:58 -05:00
d7363921ad Basic flashlight 2025-08-31 22:08:18 -05:00
a1fede92f3 Cleaning up code. 2025-08-31 19:02:18 -05:00
b580a34999 Variable fog of war 2025-08-31 18:00:03 -05:00
bef5ff8916 Merge pull request 'Skybox Implementation' (#1) from feature/skybox into main
Reviewed-on: #1
2025-08-31 21:44:18 +00:00
c04809ff6b Skybox Implementation 2025-08-31 16:36:01 -05:00
ed403627ea Resolving mismatch 2025-08-31 15:22:25 -05:00
db23983847 Seed based rng tree generation.
Removed Chasing PNG
2025-08-31 15:03:06 -05:00
79bfdba0f7 Chatgpt shader implementation for png transparency. 2025-08-30 15:48:20 -05:00
48788a7296 Changing file structure for assets. 2025-08-30 09:54:07 -05:00
323 changed files with 34472 additions and 59 deletions

View File

@@ -0,0 +1,2 @@
My first game project.
Made using Raylib.

689
main.c
View File

@@ -1,7 +1,12 @@
#include "raylib.h"
#include "rcamera.h"
#include "raymath.h"
#include <stdbool.h>
#include "stdbool.h"
#include "stddef.h"
#include "stdlib.h"
#include "stdint.h"
#include "rlgl.h"
#include "float.h"
//----------------------------------------------------------------------------------
// Defines and Macros
@@ -29,15 +34,46 @@
#define NORMALIZE_INPUT 0
#define ARRLEN(a) (sizeof(a) / sizeof((a)[0]))
#define FLOOR_EXTENT 25
#define TILE_SIZE 5.0f
#define PLAYER_RADIUS 0.5f
#define PLAYER_HEIGHT 1.8f
enum game_state {
STATE_MENU, // 0 by default
STATE_GAME, // 1
};
//----------------------------------------------------------------------------------
// RNG
//----------------------------------------------------------------------------------
// One-time world seed (set in main)
static uint32_t gWorldSeed = 123456789u;
// Fast 2D int hash → stable pseudo-random per cell
static inline uint32_t hash2i(int x, int z, uint32_t seed)
{
uint32_t h = (uint32_t)x * 374761393u ^ (uint32_t)z * 668265263u ^ seed * 2654435761u;
h ^= h >> 13; h *= 1274126177u; h ^= h >> 16;
return h;
}
// 1-in-N chance using a hashed value
static inline bool one_in_n(uint32_t h, int n)
{
return (h % (uint32_t)n) == 0u;
}
// Map a few hash bits to [-0.5, +0.5] for jitter
static inline float hash01_to_m05p05(uint32_t h_bits)
{
return ((h_bits & 0xFFu) / 255.0f) - 0.5f;
}
//----------------------------------------------------------------------------------
//Defining program structure
//----------------------------------------------------------------------------------
typedef struct
{
Camera camera; // <- now part of the game state
@@ -61,6 +97,12 @@ typedef struct
enum game_state state;
int shouldExit;
Player player;
int nearAcorn;
int isJumpscared;
float jumpscareTimer;
Sound jumpScare;
Music bgm;
} Master;
//------------------------------------------------------------------------------------
@@ -73,6 +115,20 @@ void UpdateBody(Master* g, float rot, char side, char forward, bool jumpPressed,
static void UpdateCameraAngle(Master* g);
static void DrawLevel(Model mdlTile);
static void CenterMouseAndFlush(void);
int Random(int x);
static void RenderTrees(Master* g, Texture2D tree, Vector3 treepos, float treeSize);
static void ApplyFogShaderToModel(Model *m, Shader fog);
static BoundingBox MakeModelWorldAABB(Model m, Vector3 pos, Vector3 scale, float yawDeg);
static BoundingBox PlayerAABB(Vector3 p);
static void ResolveAgainstBoxXZ(Master* g, BoundingBox box);
static float ModelLengthXUnits(Model m);
static float ModelLengthZUnits(Model m);
static void DrawPerimeter(Model wall, float border, float scale);
static void CollidePerimeter(Master* g, Model wall, float border, float scale); // optional
float square(float num);
//------------------------------------------------------------------------------------
// Program main entry point
@@ -85,6 +141,9 @@ int main(void)
const int screenWidth = 1440*2;
const int screenHeight = 960*2;
// BEFORE InitWindow (optional but nice): enable MSAA for smoother alpha edges
SetConfigFlags(FLAG_MSAA_4X_HINT);
InitWindow(screenWidth, screenHeight, "raylib");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
ToggleFullscreen();
@@ -100,8 +159,10 @@ int main(void)
g.index = 0;
g.state = STATE_MENU;
g.shouldExit = 0;
g.nearAcorn = 0;
g.isJumpscared = 0;
//Define
//Define player values
g.player.sensitivity = (Vector2){ 0.001f, 0.001f };
g.player.lookRotation = (Vector2){ 0 };
g.player.headTimer = 0.0f;
@@ -116,47 +177,165 @@ int main(void)
g.player.position.y + (BOTTOM_HEIGHT + g.player.headLerp),
g.player.position.z,
};
//g.player.camera.target = (Vector3){ 0.0f, 0.0f, 1.0f }; // Camera target
g.player.camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Up vector
g.player.camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };
g.player.camera.fovy = 60.0f;
g.player.camera.projection = CAMERA_PERSPECTIVE;
g.player.cameraMode = CAMERA_FIRST_PERSON;
UpdateCameraAngle(&g);
// Load GLTF model (no animations)
//Model model = LoadModel("bad_sphere.glb");
//Vector3 position = { 0.0f, 0.0f, 0.0f };
//Model wall = LoadModel("resources/3d_models/Tiny_Treats_Homely_House_1.0_FREE/Assets/gltf/fence_straight.gltf");
Model wall = LoadModel("resources/3d_models/RuinsGLB/Walls/WallNormalXL.glb");
Vector3 wall_position = { 10.0f, 0.0f, 0.0f };
float wall_yaw = 0.0f; // rotate around Y
float wall_pitch = 0.0f; // rotate around X
float wall_roll = 0.0f; // rotate around Z
Model acorn = LoadModel("resources/3d_models/my_models/my_acorn.glb");
Vector3 acorn_position = { 0.0f, 0.0f, -9.0f };
float acorn_yaw = 0.0f; // rotate around Y
float acorn_pitch = 0.0f; // rotate around X
float acorn_roll = 0.0f; // rotate around Z
// Load a 2D image as texture for the billboard
//Billboards always point toward the active camera.
Texture2D sign = LoadTexture("diard-charles.jpg");
Texture2D sign = LoadTexture("resources/sprites/husk/ZOMBC1.png");
//Texture2D sign = LoadTexture("resources/sprites/diard-charles.jpg");
GenTextureMipmaps(&sign);
SetTextureFilter(sign, TEXTURE_FILTER_BILINEAR); // optional but looks nicer
Vector3 bbPos = (Vector3){ 1.0f, 2.5f, 1.0f }; // where in 3D world to place it
float bbSize = 5.0f; // billboard height in world units
SetTextureFilter(sign, TEXTURE_FILTER_POINT); // optional but looks nicer
Vector3 bbPos = (Vector3){ -1.0f, 2.0f, -1.0f }; // where in 3D world to place it
float bbSize = 4.0f; // billboard height in world units
// Load a 2D image as texture for the billboard
//Billboards always point toward the active camera.
Texture2D tree = LoadTexture("tree.png");
Texture2D tree = LoadTexture("resources/sprites/tree.png");
GenTextureMipmaps(&tree);
SetTextureFilter(tree, TEXTURE_FILTER_BILINEAR); // optional but looks nicer
Vector3 treepos = (Vector3){ 0.0f, 7.0f, 0.0f }; // where in 3D world to place it
float treeSize = 15.0f; // billboard height in world units
Vector3 treepos = (Vector3){ 0.0f, 0.0f, 0.0f }; // where in 3D world to place it
float treeSize = 15.0f; // billboard height in world units
float cutoffAlpha = 0.7f; //Needs to be aggressive to remove the alpha.
// --- Grass tile setup ---
Texture2D texGrass = LoadTexture("grass_textures/Grass_01.png"); // adjust path
// Grass tile setup
Texture2D texGrass = LoadTexture("resources/textures/grass_textures/Grass_01.png");
GenTextureMipmaps(&texGrass);
SetTextureFilter(texGrass, TEXTURE_FILTER_TRILINEAR); // nicer at angles
SetTextureWrap(texGrass, TEXTURE_WRAP_REPEAT);
// A 3D plane (centered at origin) of size tileSize x tileSize
const float tileSize = 5.0f; // match your DrawLevel tileSize
const float tileSize = TILE_SIZE;
Mesh meshTile = GenMeshPlane(tileSize, tileSize, 1, 1);
Model mdlTile = LoadModelFromMesh(meshTile);
mdlTile.materials[0].maps[MATERIAL_MAP_ALBEDO].texture = texGrass;
// --- Skybox setup ---
// Generate cube mesh
Mesh cube = GenMeshCube(1.0f, 1.0f, 1.0f);
Model skybox = LoadModelFromMesh(cube);
// Load skybox shader
Shader skyboxShader = LoadShader
(
"shaders/skybox.vs",
"shaders/skybox.fs"
);
// Attach shader to skybox
skybox.materials[0].shader = skyboxShader;
Image img = LoadImage("resources/cubemap/cubemap.png");
skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = LoadTextureCubemap(img, CUBEMAP_LAYOUT_AUTO_DETECT);
UnloadImage(img); //no longer need image loaded.
// Tell the shader which material map is the environmentMap
int mapType = MATERIAL_MAP_CUBEMAP;
SetShaderValue(skyboxShader, GetShaderLocation(skyboxShader, "environmentMap"), &mapType, SHADER_UNIFORM_INT);
//Flip/gamma options (set to 0 for PNG cross)
int vflipped = 0;
int doGamma = 0;
SetShaderValue(skyboxShader, GetShaderLocation(skyboxShader, "vflipped"), &vflipped, SHADER_UNIFORM_INT);
SetShaderValue(skyboxShader, GetShaderLocation(skyboxShader, "doGamma"), &doGamma, SHADER_UNIFORM_INT);
// Load the fog shader
Shader fog = LoadShader("shaders/fog.vs", "shaders/fog.fs");
// Grab uniform locations (cache them)
int locPlayer = GetShaderLocation(fog, "u_playerPos");
int locRadius = GetShaderLocation(fog, "u_fogRadius");
int locFeather = GetShaderLocation(fog, "u_fogFeather");
int locFogColor = GetShaderLocation(fog, "u_fogColor");
int locCutoff_fog = GetShaderLocation(fog, "u_alphaCutoff");
int locInner = GetShaderLocation(fog, "u_inner");
int locOuter = GetShaderLocation(fog, "u_outer");
int locMinShadow = GetShaderLocation(fog, "u_minShadow");
// Optional if you added the sharpness curve in the shader
int locSharpness = GetShaderLocation(fog, "u_sharpness");
//Flashlight
int locSpotDir = GetShaderLocation(fog, "u_spotDir");
int locSpotCos = GetShaderLocation(fog, "u_spotCos");
int locSpotSoft = GetShaderLocation(fog, "u_spotSoft");
int locLightRange = GetShaderLocation(fog, "u_lightRange");
int locLightFade = GetShaderLocation(fog, "u_lightFade");
int locIntensity = GetShaderLocation(fog, "u_intensity");
int locBeamSpread = GetShaderLocation(fog, "u_beamSpread");
// Let raylib know which matrices the shader expects
fog.locs[SHADER_LOC_MATRIX_MODEL] = GetShaderLocation(fog, "matModel");
fog.locs[SHADER_LOC_MATRIX_VIEW] = GetShaderLocation(fog, "matView");
fog.locs[SHADER_LOC_MATRIX_PROJECTION] = GetShaderLocation(fog, "matProjection");
// --- Apply shader to your ground tile model ---
mdlTile.materials[0].shader = fog; // same shader also works for opaque
//Apply shader to wall
ApplyFogShaderToModel(&wall, fog);
ApplyFogShaderToModel(&acorn, fog);
// Defaults for opaque geometry (no alpha cut)
float cutoffOpaque = 0.0f;
SetShaderValue(fog, locCutoff_fog, &cutoffOpaque, SHADER_UNIFORM_FLOAT);
// Fog/vignette tuning
float inner = 0.0f; // start radius (center under player)
float outer = 20.0f; // how far until full fog
float minShadow = 0.85f; // baseline darkness at the center (0 = none, 1 = pitch black)
float sharpness = 2.0f; // steeper edge (optional if shader uses it)
Vector4 fogColor = {0.0f, 0.0f, 0.0f, 1.0f};
// Flashligh tuning
float spotAngleDeg = 35.0f; //cone angle
float spotCos = cosf(DEG2RAD * (spotAngleDeg * 0.5f));
float spotSoft = 0.10f;
float lightRange = 150.0f;//100.0f;
float lightFade = 1.0f;
float intensity = 0.25f; // try 0.71.5 to taste
float beamSpread = 0.05f; // try 0.150.35; higher = less “laser” at distance
SetShaderValue(fog, locIntensity, &intensity, SHADER_UNIFORM_FLOAT);
SetShaderValue(fog, locBeamSpread, &beamSpread, SHADER_UNIFORM_FLOAT);
SetShaderValue(fog, locSpotCos, &spotCos, SHADER_UNIFORM_FLOAT);
SetShaderValue(fog, locSpotSoft, &spotSoft, SHADER_UNIFORM_FLOAT);
SetShaderValue(fog, locLightRange, &lightRange, SHADER_UNIFORM_FLOAT);
SetShaderValue(fog, locLightFade, &lightFade, SHADER_UNIFORM_FLOAT);
SetShaderValue(fog, locInner, &inner, SHADER_UNIFORM_FLOAT);
SetShaderValue(fog, locOuter, &outer, SHADER_UNIFORM_FLOAT);
SetShaderValue(fog, locMinShadow, &minShadow, SHADER_UNIFORM_FLOAT);
SetShaderValue(fog, locFogColor, &fogColor, SHADER_UNIFORM_VEC4);
SetShaderValue(fog, locSharpness, &sharpness, SHADER_UNIFORM_FLOAT);
//Music and audio
InitAudioDevice();
g.jumpScare = LoadSound("resources/sound_effects/fnaf.wav");
SetSoundVolume(g.jumpScare, 0.8);
g.bgm = LoadMusicStream("resources/music/background.mp3");
SetMusicVolume(g.bgm, 0.4f);
PlayMusicStream(g.bgm);
//--------------------------------------------------------------------------------------
// Main game loop
@@ -217,6 +396,62 @@ int main(void)
bool crouching = IsKeyDown(KEY_LEFT_CONTROL);
UpdateBody(&g, g.player.lookRotation.x, sideway, forward, IsKeyDown(KEY_SPACE), crouching);
// Build wall collider (scale must match your DrawModelEx scale)
BoundingBox acornBox = MakeModelWorldAABB(acorn, acorn_position, (Vector3){0.25,0.25,0.25}, acorn_yaw);
// Collide player vs wall (XZ slide)
ResolveAgainstBoxXZ(&g, acornBox);
int player_acorn_diff = square(g.player.position.x - acorn_position.x) + square(g.player.position.z - acorn_position.z);
if (player_acorn_diff <= 3)
{
g.nearAcorn = 1;
}
else
{
g.nearAcorn = 0;
}
if (g.nearAcorn == 1 && IsKeyPressed(KEY_E))
{
acorn_position.x = 15;
acorn_position.z = 15;
g.isJumpscared = 1;
g.jumpscareTimer = 5.0f; // show overlay for 1s
PlaySound(g.jumpScare); // <-- trigger ONCE here
}
if (g.isJumpscared == 1)
{
g.jumpscareTimer -= GetFrameTime();
if (g.jumpscareTimer <= 0.0f) g.isJumpscared = 0;
}
// put near your update logic
float B = 122;
float T = 1.0f; // wall half-thickness
float H = 4.0f; // wall height
BoundingBox walls[4] = {
// left (x = -B)
{ (Vector3){-B - T, 0.0f, -B - T}, (Vector3){-B + T, H, B + T} },
// right (x = +B)
{ (Vector3){ B - T, 0.0f, -B - T}, (Vector3){ B + T, H, B + T} },
// bottom (z = -B)
{ (Vector3){-B - T, 0.0f, -B - T}, (Vector3){ B + T, H, -B + T} },
// top (z = +B)
{ (Vector3){-B - T, 0.0f, B - T}, (Vector3){ B + T, H, B + T} },
};
for (int i = 0; i < 4; ++i) ResolveAgainstBoxXZ(&g, walls[i]);
// (Optional) debug draw inside BeginMode3D/EndMode3D:
for (int i = 0; i < 4; ++i) DrawBoundingBox(walls[i], RED);
float delta = GetFrameTime();
g.player.headLerp = Lerp(g.player.headLerp, (crouching ? CROUCH_HEIGHT : STAND_HEIGHT), 20.0f*delta);
g.player.camera.position = (Vector3){
@@ -240,20 +475,37 @@ int main(void)
g.player.lean.x = Lerp(g.player.lean.x, sideway*0.02f, 10.0f*delta);
g.player.lean.y = Lerp(g.player.lean.y, forward*0.015f, 10.0f*delta);
bbPos.x += (g.player.position.x - bbPos.x)*.005;
bbPos.z += (g.player.position.z - bbPos.z)*.005;
// wall editor
if (IsKeyDown(KEY_RIGHT) && IsKeyDown(KEY_LEFT_SHIFT)) acorn_position.x += 0.5f;
else if (IsKeyPressed(KEY_RIGHT)) acorn_position.x += 0.5f;
if (IsKeyDown(KEY_LEFT) && IsKeyDown(KEY_LEFT_SHIFT)) acorn_position.x -= 0.5f;
else if (IsKeyPressed(KEY_LEFT)) acorn_position.x -= 0.5f;
if (IsKeyDown(KEY_UP) && IsKeyDown(KEY_LEFT_SHIFT)) acorn_position.z += 0.5f;
else if (IsKeyPressed(KEY_UP)) acorn_position.z += 0.5f;
if (IsKeyDown(KEY_DOWN) && IsKeyDown(KEY_LEFT_SHIFT)) acorn_position.z -= 0.5f;
else if (IsKeyPressed(KEY_DOWN)) acorn_position.z -= 0.5f;
float rotSpeed = 45.0f * GetFrameTime(); // deg/sec
if (IsKeyDown(KEY_Q)) wall_yaw -= rotSpeed;
if (IsKeyDown(KEY_E)) wall_yaw += rotSpeed;
//
UpdateMusicStream(g.bgm);
UpdateCameraAngle(&g);
int bbdifx = g.player.position.x - bbPos.x;
int bbdifz = g.player.position.z - bbPos.z;
bbPos.x += 0.01 * bbdifx;
bbPos.z += 0.01 * bbdifz;
treepos = (Vector3){ 0.0f, 7.0f, 0.0f };
break;
};
}
@@ -277,38 +529,78 @@ int main(void)
{
ClearBackground(BLACK);
Vector3 camPos = g.player.camera.position;
Vector3 camFwd = Vector3Normalize(Vector3Subtract(g.player.camera.target, camPos));
// Camera forward (normalize target - position)
Vector3 forward = Vector3Normalize(Vector3Subtract(g.player.camera.target, g.player.camera.position));
SetShaderValue(fog, locPlayer, &camPos, SHADER_UNIFORM_VEC3);
SetShaderValue(fog, locSpotDir, &camFwd, SHADER_UNIFORM_VEC3);
BeginMode3D(g.player.camera);
rlDisableBackfaceCulling();
rlDisableDepthMask();
// keep it centered on the camera
skybox.transform = MatrixTranslate(
g.player.camera.position.x,
g.player.camera.position.y,
g.player.camera.position.z
);
DrawModel(skybox, (Vector3){0}, 1.0f, WHITE);
rlEnableDepthMask();
rlEnableBackfaceCulling();
DrawLevel(mdlTile);
DrawBillboard(g.player.camera, sign, bbPos, bbSize, WHITE);
DrawBillboard(g.player.camera, tree, treepos, treeSize, WHITE);
// --- Trees use same fog shader but with alpha-cut enabled ---
treepos.x += 3*5;
treepos.z += 3*5;
DrawBillboard(g.player.camera, tree, treepos, treeSize, WHITE);
SetShaderValue(fog, locCutoff_fog, &cutoffAlpha, SHADER_UNIFORM_FLOAT);
treepos.x += 5*5;
treepos.z += -2*5;
DrawBillboard(g.player.camera, tree, treepos, treeSize, WHITE);
BeginShaderMode(fog);
treepos.x += -4*5;
treepos.z += 3*5;
DrawBillboard(g.player.camera, tree, treepos, treeSize, WHITE);
//DrawModel(wall, wall_position, 5.0, WHITE);
DrawModelEx(acorn, acorn_position, (Vector3){0,1,0}, wall_yaw, (Vector3){0.25,0.25,0.25}, WHITE);
RenderTrees(&g, tree, treepos, treeSize);
//DrawBillboard(g.player.camera, sign, bbPos, bbSize, WHITE); //zombie
treepos.x += 9*5;
treepos.z += 10*5;
DrawBillboard(g.player.camera, tree, treepos, treeSize, WHITE);
DrawPerimeter(wall, 122.0f, 1.5f);
EndShaderMode();
// Restore opaque cutoff for anything else you draw with that material later
SetShaderValue(fog, locCutoff_fog, &cutoffOpaque, SHADER_UNIFORM_FLOAT);
EndMode3D();
// Draw info box
DrawRectangle(5, 5, 330, 75, Fade(SKYBLUE, 0.5f));
DrawRectangleLines(5, 5, 330, 75, BLUE);
if(g.isJumpscared == 1 | g.isJumpscared == 2)
{
DrawTextureEx(sign, (Vector2){700, 300}, /*rotation*/0.0f, /*scale*/50.0f, WHITE);
}
DrawText("Camera controls:", 15, 15, 10, BLACK);
DrawText("- Move keys: W, A, S, D, Space, Left-Ctrl", 15, 30, 10, BLACK);
DrawText("- Look around: arrow keys or mouse", 15, 45, 10, BLACK);
DrawText(TextFormat("- Velocity Len: (%06.3f)", Vector2Length((Vector2){ g.player.velocity.x, g.player.velocity.z })), 15, 60, 10, BLACK);
// Draw info box
DrawRectangle(5, 5, 330*1.5, 75*1.25, Fade(SKYBLUE, 0.5f));
DrawRectangleLines(5, 5, 330*1.5, 75*1.25, BLUE);
DrawText("Camera controls:", 15, 15, 20, BLACK);
DrawText("- Move keys: W, A, S, D, Space, Left-Ctrl", 15, 30, 20, BLACK);
DrawText("- Look around: arrow keys or mouse", 15, 45, 20, BLACK);
DrawText(TextFormat("- Velocity Len: (%06.3f)", Vector2Length((Vector2){ g.player.velocity.x, g.player.velocity.z })), 15, 60, 20, BLACK);
DrawText(
TextFormat("x: (%06.3f), y: (%06.3f), z: (%06.3f)", acorn_position.x, acorn_position.y, acorn_position.z),
15, 75, 20, BLACK
);
if (g.nearAcorn == 1)
{
int acorn_text_width = MeasureText("Press E to Collect the Acorn", 50);
DrawText("Press E to Collect the Acorn", (screenWidth - acorn_text_width)/2, screenHeight * 0.8, 50, PURPLE);
}
break;
}
@@ -316,8 +608,8 @@ int main(void)
EndDrawing();
//----------------------------------------------------------------------------------
};
//--------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------
// De-Initialization
//--------------------------------------------------------------------------------------
// Detach the texture from the model so UnloadModel won't try to free it.
@@ -326,8 +618,15 @@ int main(void)
// Now free resources you loaded:
UnloadModel(mdlTile); // frees the model and its mesh/VBOs
UnloadTexture(texGrass); // frees the grass texture you loaded
UnloadTexture(sign); // frees billboard texture
UnloadTexture(tree); // frees tree texture
UnloadTexture(sign);
UnloadModel(wall);
UnloadModel(acorn);
UnloadSound(g.jumpScare);
StopMusicStream(g.bgm);
UnloadMusicStream(g.bgm);
CloseAudioDevice();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
@@ -501,8 +800,8 @@ static void UpdateCameraAngle(Master* g)
// Draw game level
static void DrawLevel(Model mdlTile)
{
const int floorExtent = 25;
const float tileSize = 5.0f;
const int floorExtent = FLOOR_EXTENT;
const float tileSize = TILE_SIZE;
const Color tileColor1 = (Color){ 150, 200, 200, 255 };
// Floor tiles
@@ -553,7 +852,283 @@ static void DrawLevel(Model mdlTile)
}
//fix mouse
static void CenterMouseAndFlush() {
static void CenterMouseAndFlush()
{
SetMousePosition(GetRenderWidth()/2, GetRenderHeight()/2);
(void)GetMouseDelta();
}
//Random number gen
int Random(int x)
{
int r = (rand() % x) + 1; // 140
return r;
}
static inline void DrawTreeBillboardStable(Camera cam, Texture2D tex, Vector3 groundPos, float heightWorld, Color tint)
{
// Keep sprite aspect ratio
float aspect = (float)tex.width / (float)tex.height;
Vector2 size = (Vector2){ heightWorld * aspect, heightWorld };
// Bottom-center pivot so base sits on groundPos.y
Vector2 origin = (Vector2){ 0.5f, 0.75f };
// Fixed world up (ignore camera.up roll)
Vector3 up = (Vector3){ 0, 1, 0 };
Rectangle src = (Rectangle){ 0, 0, (float)tex.width, (float)tex.height };
DrawBillboardPro(cam, tex, src, groundPos, up, size, origin, 0.0f, tint);
}
static void RenderTrees(Master* g, Texture2D tree, Vector3 baseTreePos, float treeSize)
{
const int floorExtent = FLOOR_EXTENT - 3;
const float tileSize = TILE_SIZE;
// Placement knobs
const int densityA = 10; // 1-in-10 on (odd,odd)
const int densityB = 13; // 1-in-14 on (even,even)
const float jitter = tileSize * 0.35f; // random in-tile offset
const float groundY = baseTreePos.y; // <-- use this as terrain height
for (int z = -floorExtent; z < floorExtent; ++z)
for (int x = -floorExtent; x < floorExtent; ++x)
{
uint32_t h = hash2i(x, z, gWorldSeed);
uint32_t hA = h;
uint32_t hB = h * 0x9E3779B1u + 0x85EBCA6Bu;
// (odd,odd)
if ((z & 1) && (x & 1) && one_in_n(hA, densityA)) {
float ox = hash01_to_m05p05(hA) * jitter;
float oz = hash01_to_m05p05(hA >> 8) * jitter;
Vector3 pos = { x*tileSize + ox, groundY, z*tileSize + oz };
DrawTreeBillboardStable(g->player.camera, tree, pos, /*height*/ treeSize, WHITE);
}
// (even,even)
if (!(z & 1) && !(x & 1) && one_in_n(hB, densityB)) {
float ox = hash01_to_m05p05(hB) * jitter;
float oz = hash01_to_m05p05(hB >> 8) * jitter;
Vector3 pos = { x*tileSize + ox, groundY, z*tileSize + oz };
DrawTreeBillboardStable(g->player.camera, tree, pos, /*height*/ treeSize, WHITE);
}
}
}
static void ApplyFogShaderToModel(Model *m, Shader fog)
{
// 1) Tell raylib which uniform in your shader is the ALBEDO map
fog.locs[SHADER_LOC_MATRIX_MODEL] = GetShaderLocation(fog, "matModel");
fog.locs[SHADER_LOC_MATRIX_VIEW] = GetShaderLocation(fog, "matView");
fog.locs[SHADER_LOC_MATRIX_PROJECTION] = GetShaderLocation(fog, "matProjection");
fog.locs[SHADER_LOC_MAP_ALBEDO] = GetShaderLocation(fog, "texture0"); // your FS: uniform sampler2D texture0;
// (optional but nice)
fog.locs[SHADER_LOC_COLOR_DIFFUSE] = GetShaderLocation(fog, "colDiffuse");
// 2) For each material, make sure the base color texture ends up in the ALBEDO slot
for (int i = 0; i < m->materialCount; ++i) {
Material *mat = &m->materials[i];
// Some importers put the base map in DIFFUSE, copy to ALBEDO if ALBEDO is empty.
if (mat->maps[MATERIAL_MAP_ALBEDO].texture.id == 0 &&
mat->maps[MATERIAL_MAP_DIFFUSE].texture.id != 0) {
mat->maps[MATERIAL_MAP_ALBEDO].texture = mat->maps[MATERIAL_MAP_DIFFUSE].texture;
}
// Ensure non-zero tint
if (mat->maps[MATERIAL_MAP_ALBEDO].color.a == 0) mat->maps[MATERIAL_MAP_ALBEDO].color = WHITE;
// 3) Attach your fog shader to this material
mat->shader = fog;
}
}
// Build player's AABB from its position (XZ radius, fixed height)
static BoundingBox PlayerAABB(Vector3 p)
{
BoundingBox bb;
bb.min = (Vector3){ p.x - PLAYER_RADIUS, p.y, p.z - PLAYER_RADIUS };
bb.max = (Vector3){ p.x + PLAYER_RADIUS, p.y + PLAYER_HEIGHT, p.z + PLAYER_RADIUS };
return bb;
}
// Combine all mesh AABBs of a Model in LOCAL space
static BoundingBox ModelLocalAABB(Model m)
{
BoundingBox out;
out.min = (Vector3){ FLT_MAX, FLT_MAX, FLT_MAX };
out.max = (Vector3){ -FLT_MAX, -FLT_MAX, -FLT_MAX };
for (int i = 0; i < m.meshCount; ++i) {
BoundingBox b = GetMeshBoundingBox(m.meshes[i]); // local-space box of this mesh
if (b.min.x < out.min.x) out.min.x = b.min.x;
if (b.min.y < out.min.y) out.min.y = b.min.y;
if (b.min.z < out.min.z) out.min.z = b.min.z;
if (b.max.x > out.max.x) out.max.x = b.max.x;
if (b.max.y > out.max.y) out.max.y = b.max.y;
if (b.max.z > out.max.z) out.max.z = b.max.z;
}
return out;
}
// Transform a BoundingBox by a matrix and return its world AABB
static BoundingBox TransformAABB(BoundingBox bb, Matrix M)
{
Vector3 c[8] = {
{bb.min.x, bb.min.y, bb.min.z}, {bb.max.x, bb.min.y, bb.min.z},
{bb.min.x, bb.max.y, bb.min.z}, {bb.max.x, bb.max.y, bb.min.z},
{bb.min.x, bb.min.y, bb.max.z}, {bb.max.x, bb.min.y, bb.max.z},
{bb.min.x, bb.max.y, bb.max.z}, {bb.max.x, bb.max.y, bb.max.z},
};
BoundingBox out;
out.min = (Vector3){ FLT_MAX, FLT_MAX, FLT_MAX };
out.max = (Vector3){ -FLT_MAX, -FLT_MAX, -FLT_MAX };
for (int i = 0; i < 8; ++i) {
Vector3 t = Vector3Transform(c[i], M);
if (t.x < out.min.x) out.min.x = t.x;
if (t.y < out.min.y) out.min.y = t.y;
if (t.z < out.min.z) out.min.z = t.z;
if (t.x > out.max.x) out.max.x = t.x;
if (t.y > out.max.y) out.max.y = t.y;
if (t.z > out.max.z) out.max.z = t.z;
}
return out;
}
// Build world AABB for a model using the same T*R*S you render with
static BoundingBox MakeModelWorldAABB(Model m, Vector3 pos, Vector3 scale, float yawDeg)
{
Matrix S = MatrixScale(scale.x, scale.y, scale.z);
Matrix R = MatrixRotateY(DEG2RAD * yawDeg);
Matrix T = MatrixTranslate(pos.x, pos.y, pos.z);
Matrix SRT = MatrixMultiply(MatrixMultiply(S, R), T); // S * R * T
Matrix M = MatrixMultiply(m.transform, SRT); // model.transform * SRT
BoundingBox out = { .min = { FLT_MAX, FLT_MAX, FLT_MAX },
.max = { -FLT_MAX, -FLT_MAX, -FLT_MAX } };
for (int i = 0; i < m.meshCount; ++i) {
BoundingBox local = GetMeshBoundingBox(m.meshes[i]);
BoundingBox world = TransformAABB(local, M);
if (world.min.x < out.min.x) out.min.x = world.min.x;
if (world.min.y < out.min.y) out.min.y = world.min.y;
if (world.min.z < out.min.z) out.min.z = world.min.z;
if (world.max.x > out.max.x) out.max.x = world.max.x;
if (world.max.y > out.max.y) out.max.y = world.max.y;
if (world.max.z > out.max.z) out.max.z = world.max.z;
}
return out;
}
// Push player out of a box on the smallest X/Z overlap (slide) and zero that velocity axis
static void ResolveAgainstBoxXZ(Master* g, BoundingBox box)
{
BoundingBox p = PlayerAABB(g->player.position);
// Quick reject
if (!CheckCollisionBoxes(p, box)) return;
// Overlaps on X and Z
float ox = fminf(p.max.x, box.max.x) - fmaxf(p.min.x, box.min.x);
float oz = fminf(p.max.z, box.max.z) - fmaxf(p.min.z, box.min.z);
if (ox <= 0 || oz <= 0) return;
// Centers to know which way to push
float cxp = 0.5f * (p.min.x + p.max.x);
float czp = 0.5f * (p.min.z + p.max.z);
float cxb = 0.5f * (box.min.x + box.max.x);
float czb = 0.5f * (box.min.z + box.max.z);
// Resolve along the smallest axis
if (ox < oz) {
float push = (cxp < cxb) ? -ox : ox;
g->player.position.x += push;
g->player.velocity.x = 0.0f;
} else {
float push = (czp < czb) ? -oz : oz;
g->player.position.z += push;
g->player.velocity.z = 0.0f;
}
}
// Length of the model along local X (used to tile segments cleanly)
static float ModelLengthXUnits(Model m) {
BoundingBox bb = ModelLocalAABB(m); // you already have this helper
return (bb.max.x - bb.min.x);
}
static float ModelLengthZUnits(Model m) {
BoundingBox bb = ModelLocalAABB(m);
return (bb.max.z - bb.min.z);
}
// Draw repeated wall segments around the perimeter (z=±border, x=±border)
static void DrawPerimeter(Model wall, float border, float scale) {
const float HALF = FLOOR_EXTENT * TILE_SIZE; // e.g. 25*5 = 125
Vector3 scl = { scale, scale, scale };
//float segLen = ModelLengthXUnits(wall) * scale;
float segLen = ModelLengthZUnits(wall) * scale;
if (segLen <= 0.001f) segLen = TILE_SIZE; // safety fallback
int count = (int)ceilf((2.0f * HALF) / segLen) + 1;
scl.x += 0.1;
scl.y += 0.1;
scl.z += 0.1;
// Top & bottom edges: fence runs along +X (yaw 0/180)
for (int i = 0; i < count; ++i) {
float x = -HALF + i * segLen;
DrawModelEx(wall, (Vector3){ x, 0.0f, border }, (Vector3){0,1,0}, 90.0f, scl, WHITE);
DrawModelEx(wall, (Vector3){ x, 0.0f, -border }, (Vector3){0,1,0}, 270.0f, scl, WHITE);
}
// Left & right edges: rotate 90° so fence runs along Z
for (int i = 0; i < count; ++i) {
float z = -HALF + i * segLen;
DrawModelEx(wall, (Vector3){ border, 0.0f, z }, (Vector3){0,1,0}, 0.0f, scl, WHITE);
DrawModelEx(wall, (Vector3){ -border, 0.0f, z }, (Vector3){0,1,0}, 180.0f, scl, WHITE);
}
}
// Optional: collide player with the same perimeter using your AABB helpers
static void CollidePerimeter(Master* g, Model wall, float border, float scale) {
const float HALF = FLOOR_EXTENT * TILE_SIZE;
const Vector3 scl = { scale, scale, scale };
float segLen = ModelLengthXUnits(wall) * scale;
if (segLen <= 0.001f) segLen = TILE_SIZE;
int count = (int)ceilf((2.0f * HALF) / segLen) + 1;
for (int i = 0; i < count; ++i) {
float x = -HALF + i * segLen;
ResolveAgainstBoxXZ(g, MakeModelWorldAABB(wall, (Vector3){ x, 0.0f, border }, scl, 0.0f));
ResolveAgainstBoxXZ(g, MakeModelWorldAABB(wall, (Vector3){ x, 0.0f, -border }, scl, 180.0f));
}
for (int i = 0; i < count; ++i) {
float z = -HALF + i * segLen;
ResolveAgainstBoxXZ(g, MakeModelWorldAABB(wall, (Vector3){ border, 0.0f, z }, scl, 90.0f));
ResolveAgainstBoxXZ(g, MakeModelWorldAABB(wall, (Vector3){ -border, 0.0f, z }, scl, 90.0f));
}
}
float square(float num)
{
float out = num * num;
return out;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Some files were not shown because too many files have changed in this diff Show More