diff color_game/main.c @ 76:35b1abc37969

Updating my website to use seobeo library.
author June Park <parkjune1995@gmail.com>
date Wed, 31 Dec 2025 14:11:21 -0800
parents fff1b048dda6
children 8d17f6e6e290
line wrap: on
line diff
--- a/color_game/main.c	Wed Dec 31 11:20:08 2025 -0800
+++ b/color_game/main.c	Wed Dec 31 14:11:21 2025 -0800
@@ -1,913 +1,17 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <math.h>
 #include "third_party/raylib/include/raylib.h"
-
+#include "dowa/dowa.h"
 
 #define INIT_SCREEN_WIDTH 1200
 #define INIT_SCREEN_HEIGHT 700
-#define PLAYER_SPEED 200.0f
-#define PLAYER_RADIUS 20.0f
-#define PLAYER_MAX_HEALTH 100.0f
-
-#define MONSTER_SPEED 100.0f
-#define MONSTER_RADIUS 15.0f
-
-#define BULLET_SPEED 300.0f
-#define BULLET_RADIUS 5.0f
-#define BULLET_LIFETIME 3.0f
-#define SHOOT_INTERVAL 0.5f
-#define HIT_PROBABILITY 0.7f  // 70% aim accuracy (0.0 = random, 1.0 = perfect)
-                              
-#define MAX_PASSIVE_NODES 12
-#define PASSIVE_TREE_RADIUS 250.0f
-#define PASSIVE_NODE_RADIUS 20.0f
-
-#define MONSTER_WEIGHT 10.0f
-#define MONSTER_CONTACT_DAMAGE 5.0f
-
-#define BASE_DAMAGE 10.0f
-#define BOSS_HEALTH_MULTIPLIER 5.0f
-#define BOSS_MONSTER_WEIGHT 10.0f
-#define BOSS_SPAWN_TIME 10.0f
-
-#define COLOR_UNLOCK_TIME 5.0f
-#define MAX_DAMAGE_NUMBERS 100
-#define DAMAGE_NUMBER_LIFETIME 1.0f
-#define EXP_PER_MONSTER 10.0f
-#define EXP_PER_BOSS 50.0f
-#define EXP_TO_LEVEL 100.0f
-
-typedef struct Player {
-  Vector2 position;
-  float health;
-  float maxHealth;
-  float invulnerabilityTimer;
-  int unlockedBulletTypes[MAX_PASSIVE_NODES];
-  int unlockedCount;
-  int passivePoints;
-  int level;
-  float experience;
-  float expToNextLevel;
-  Color color;
-} Player;
-
-typedef struct DamageNumber {
-  Vector2 position;
-  float damage;
-  float lifetime;
-  bool active;
-  Color color;
-} DamageNumber;
-
-typedef struct Map {
-  float width;
-  float height;
-} Map;
-
-typedef struct Monster {
-  Vector2 position;
-  float hue;
-  float saturation;
-  float health;
-  float maxHealth;
-  float weight;
-  bool alive;
-  bool hasCollision;
-  bool isBoss;
-} Monster;
-
-typedef struct Bullet {
-  Vector2 position;
-  Vector2 velocity;
-  float lifetime;
-  float hue;
-  float damage;
-  bool active;
-} Bullet;
-
-typedef struct PassiveNode {
-  float angle;
-  float hue;
-  char description[64];
-  float damageBonus;
-  bool unlocked;
-} PassiveNode;
-
-float RandomFloat(float min, float max)
-{
-  return min + ((float)rand() / (float)RAND_MAX) * (max - min);
-}
-
-float CalculateColorDamage(float bulletHue, float monsterHue, float baseDamage)
-{
-  // Calculate hue difference (0-180 degrees)
-  float diff = fabs(bulletHue - monsterHue);
-  if (diff > 180.0f) diff = 360.0f - diff;
-
-  // Opposite colors (180 degrees) = 2x damage
-  // Same color (0 degrees) = 0.5x damage
-  // Linear scale between them
-  float damageMultiplier = 0.5f + (diff / 180.0f) * 1.5f;
-
-  return baseDamage * damageMultiplier;
-}
-
-void SpawnDamageNumber(DamageNumber* damageNumbers, int maxCount, Vector2 position, float damage, Color color)
-{
-  for (int i = 0; i < maxCount; i++)
-  {
-    if (!damageNumbers[i].active)
-    {
-      damageNumbers[i].position = position;
-      damageNumbers[i].damage = damage;
-      damageNumbers[i].lifetime = DAMAGE_NUMBER_LIFETIME;
-      damageNumbers[i].active = true;
-      damageNumbers[i].color = color;
-      break;
-    }
-  }
-}
-
-void UpdateDamageNumbers(DamageNumber* damageNumbers, int count, float deltaTime)
-{
-  for (int i = 0; i < count; i++)
-  {
-    if (!damageNumbers[i].active) continue;
-
-    damageNumbers[i].lifetime -= deltaTime;
-    damageNumbers[i].position.y -= 30.0f * deltaTime; // Float upward
-
-    if (damageNumbers[i].lifetime <= 0)
-    {
-      damageNumbers[i].active = false;
-    }
-  }
-}
-
-void AddExperience(Player* player, float exp)
-{
-  player->experience += exp;
-
-  // Check for level up
-  while (player->experience >= player->expToNextLevel)
-  {
-    player->experience -= player->expToNextLevel;
-    player->level++;
-    player->passivePoints++;
-    // Increase exp requirement for next level
-    player->expToNextLevel = EXP_TO_LEVEL * player->level;
-  }
-}
-
-void InitializePassiveTree(PassiveNode* nodes)
-{
-  const char* nodeDescriptions[MAX_PASSIVE_NODES] = {
-    "Red Bullet",      // 0°
-    "Orange Bullet",   // 30°
-    "Yellow Bullet",   // 60°
-    "Chartreuse",      // 90°
-    "Green Bullet",    // 120°
-    "Spring Green",    // 150°
-    "Cyan Bullet",     // 180°
-    "Azure Bullet",    // 210°
-    "Blue Bullet",     // 240°
-    "Violet Bullet",   // 270°
-    "Magenta Bullet",  // 300°
-    "Rose Bullet"      // 330°
-  };
-
-  for (int i = 0; i < MAX_PASSIVE_NODES; i++)
-  {
-    nodes[i].angle = (360.0f / MAX_PASSIVE_NODES) * i;
-    nodes[i].hue = nodes[i].angle;
-    snprintf(nodes[i].description, sizeof(nodes[i].description), "%s", nodeDescriptions[i]);
-    nodes[i].damageBonus = 5.0f + (i * 2.0f); // Scaling damage bonus
-    nodes[i].unlocked = false;
-  }
-}
-
-void HandlePlayerMovement(Player* player, Map* map, float deltaTime)
-{
-  Vector2 movement = {0};
-
-  if (IsKeyDown(KEY_W) || IsKeyDown(KEY_UP))    movement.y -= 1.0f;
-  if (IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN))  movement.y += 1.0f;
-  if (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT))  movement.x -= 1.0f;
-  if (IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT)) movement.x += 1.0f;
-
-  // Normalize diagonal movement
-  float length = sqrtf(movement.x * movement.x + movement.y * movement.y);
-  if (length > 0)
-  {
-    movement.x /= length;
-    movement.y /= length;
-  }
-
-  // Apply movement
-  player->position.x += movement.x * PLAYER_SPEED * deltaTime;
-  player->position.y += movement.y * PLAYER_SPEED * deltaTime;
-
-  // Keep player within map bounds
-  if (player->position.x < PLAYER_RADIUS) player->position.x = PLAYER_RADIUS;
-  if (player->position.x > map->width - PLAYER_RADIUS)
-    player->position.x = map->width - PLAYER_RADIUS;
-  if (player->position.y < PLAYER_RADIUS) player->position.y = PLAYER_RADIUS;
-  if (player->position.y > map->height - PLAYER_RADIUS)
-    player->position.y = map->height - PLAYER_RADIUS;
-}
-
-void UpdateMonsters(Monster* monsters, int monsterCount, Vector2 playerPos, float deltaTime)
-{
-  for (int i = 0; i < monsterCount; i++)
-  {
-    if (!monsters[i].alive) continue;
-
-    // Calculate direction to player
-    Vector2 direction = {
-      playerPos.x - monsters[i].position.x,
-      playerPos.y - monsters[i].position.y
-    };
-
-    // Normalize direction
-    float length = sqrtf(direction.x * direction.x + direction.y * direction.y);
-    if (length > 0)
-    {
-      direction.x /= length;
-      direction.y /= length;
-    }
-
-    // Move toward player
-    Vector2 newPos = {
-      monsters[i].position.x + direction.x * MONSTER_SPEED * deltaTime,
-      monsters[i].position.y + direction.y * MONSTER_SPEED * deltaTime
-    };
-
-    // Check collision with other monsters if this monster has collision enabled
-    if (monsters[i].hasCollision)
-    {
-      bool canMove = true;
-      for (int j = 0; j < monsterCount; j++)
-      {
-        if (i == j || !monsters[j].alive || !monsters[j].hasCollision) continue;
-
-        float dx = newPos.x - monsters[j].position.x;
-        float dy = newPos.y - monsters[j].position.y;
-        float distance = sqrtf(dx * dx + dy * dy);
-
-        if (distance < MONSTER_RADIUS * 2)
-        {
-          // If this monster is heavier, push the other monster away
-          if (monsters[i].weight > monsters[j].weight)
-          {
-            // Calculate push direction (away from current monster)
-            Vector2 pushDir = {dx, dy};
-            float pushLength = sqrtf(pushDir.x * pushDir.x + pushDir.y * pushDir.y);
-            if (pushLength > 0)
-            {
-              pushDir.x /= pushLength;
-              pushDir.y /= pushLength;
-            }
-
-            // Push the lighter monster away
-            float pushForce = (monsters[i].weight - monsters[j].weight) * MONSTER_SPEED * deltaTime * 0.5f;
-            monsters[j].position.x += pushDir.x * pushForce;
-            monsters[j].position.y += pushDir.y * pushForce;
-          }
-          else
-          {
-            // This monster is lighter or equal weight, can't move through
-            canMove = false;
-            break;
-          }
-        }
-      }
-
-      if (canMove)
-      {
-        monsters[i].position = newPos;
-      }
-    }
-    else
-    {
-      monsters[i].position = newPos;
-    }
-  }
-}
-
-void CheckPlayerMonsterCollision(Player* player, Monster* monsters, int monsterCount, float deltaTime)
-{
-  if (player->invulnerabilityTimer > 0)
-  {
-    player->invulnerabilityTimer -= deltaTime;
-    return;
-  }
-
-  for (int i = 0; i < monsterCount; i++)
-  {
-    if (!monsters[i].alive) continue;
-
-    float dx = player->position.x - monsters[i].position.x;
-    float dy = player->position.y - monsters[i].position.y;
-    float distance = sqrtf(dx * dx + dy * dy);
-
-    if (distance < PLAYER_RADIUS + MONSTER_RADIUS)
-    {
-      player->health -= MONSTER_CONTACT_DAMAGE;
-      player->invulnerabilityTimer = 0.5f; // 0.5 second invulnerability
-      if (player->health < 0) player->health = 0;
-      break;
-    }
-  }
-}
-
-void UpdateBullets(Bullet* bullets, int bulletCount, float deltaTime)
-{
-  for (int i = 0; i < bulletCount; i++)
-  {
-    if (!bullets[i].active) continue;
-
-    // Update position
-    bullets[i].position.x += bullets[i].velocity.x * deltaTime;
-    bullets[i].position.y += bullets[i].velocity.y * deltaTime;
-
-    // Update lifetime
-    bullets[i].lifetime -= deltaTime;
-    if (bullets[i].lifetime <= 0)
-    {
-      bullets[i].active = false;
-    }
-  }
-}
-
-int CheckCollisions(Bullet* bullets, int bulletCount, Monster* monsters, int monsterCount, Player* player, DamageNumber* damageNumbers)
-{
-  int bossesKilled = 0;
-
-  for (int i = 0; i < bulletCount; i++)
-  {
-    if (!bullets[i].active) continue;
-
-    for (int j = 0; j < monsterCount; j++)
-    {
-      if (!monsters[j].alive) continue;
-
-      // Check distance between bullet and monster
-      float dx = bullets[i].position.x - monsters[j].position.x;
-      float dy = bullets[i].position.y - monsters[j].position.y;
-      float distance = sqrtf(dx * dx + dy * dy);
-
-      if (distance < BULLET_RADIUS + MONSTER_RADIUS)
-      {
-        // Calculate damage based on color difference
-        float damage = CalculateColorDamage(bullets[i].hue, monsters[j].hue, bullets[i].damage);
-        monsters[j].health -= damage;
-
-        // Spawn damage number
-        Color damageColor = damage > bullets[i].damage * 1.5f ? ORANGE : WHITE;
-        SpawnDamageNumber(damageNumbers, MAX_DAMAGE_NUMBERS, monsters[j].position, damage, damageColor);
-
-        // Check if monster died
-        if (monsters[j].health <= 0)
-        {
-          monsters[j].alive = false;
-
-          // Award experience
-          float expGain = monsters[j].isBoss ? EXP_PER_BOSS : EXP_PER_MONSTER;
-          AddExperience(player, expGain);
-
-          // Award passive point if boss was killed
-          if (monsters[j].isBoss)
-          {
-            player->passivePoints++;
-            bossesKilled++;
-          }
-        }
-
-        bullets[i].active = false;
-        break;
-      }
-    }
-  }
-
-  return bossesKilled;
-}
-
-Vector2 FindNearestMonster(Monster* monsters, int monsterCount, Vector2 playerPos)
-{
-  float minDistance = INFINITY;
-  Vector2 nearestPos = playerPos;
-  bool found = false;
-
-  for (int i = 0; i < monsterCount; i++)
-  {
-    if (!monsters[i].alive) continue;
-
-    float dx = monsters[i].position.x - playerPos.x;
-    float dy = monsters[i].position.y - playerPos.y;
-    float distance = sqrtf(dx * dx + dy * dy);
-
-    if (distance < minDistance)
-    {
-      minDistance = distance;
-      nearestPos = monsters[i].position;
-      found = true;
-    }
-
-    if (found)
-      break;
-  }
-
-  if (!found)
-  {
-    // No monsters, return random direction
-    float angle = RandomFloat(0.0f, 2.0f * PI);
-    return (Vector2){
-      playerPos.x + cosf(angle) * 100.0f,
-      playerPos.y + sinf(angle) * 100.0f
-    };
-  }
-
-  return nearestPos;
-}
 
 int main()
 {
-  InitWindow(INIT_SCREEN_WIDTH, INIT_SCREEN_HEIGHT, "color game");
-  SetTargetFPS(60);
-  srand(time(NULL));
-
-  // --- All Global Variables ---
-  Map map = {
-    .width = INIT_SCREEN_WIDTH * 3,
-    .height = INIT_SCREEN_HEIGHT * 3
-  };
-
-  Player player = {
-    .position = {map.width / 2, map.height / 2},
-    .color = BLUE,
-    .health = PLAYER_MAX_HEALTH,
-    .maxHealth = PLAYER_MAX_HEALTH,
-    .unlockedCount = 0,
-    .passivePoints = 3,
-    .invulnerabilityTimer = 0.0f,
-    .level = 1,
-    .experience = 0.0f,
-    .expToNextLevel = EXP_TO_LEVEL
-  };
-
-  PassiveNode passiveTree[MAX_PASSIVE_NODES];
-  InitializePassiveTree(passiveTree);
-  passiveTree[0].unlocked = true;
-  player.unlockedBulletTypes[player.unlockedCount++] = 0;
-
-  // Initialize damage numbers array
-  DamageNumber damageNumbers[MAX_DAMAGE_NUMBERS];
-  for (int i = 0; i < MAX_DAMAGE_NUMBERS; i++)
-  {
-    damageNumbers[i].active = false;
-  }
-
-  Camera2D camera = {0};
-  camera.target = player.position;
-  camera.offset = (Vector2){INIT_SCREEN_WIDTH / 2.0f, INIT_SCREEN_HEIGHT / 2.0f};
-  camera.rotation = 0.0f;
-  camera.zoom = 1.0f;
-
-  bool menuOpen = false;
-  bool showMonsterColors = false;
-
-  Vector2 menuCenter = {
-    .x = INIT_SCREEN_WIDTH / 2,
-    .y = INIT_SCREEN_HEIGHT / 2
-  };
-
-  float gameTime = 0.0f;
-  float currentMonsterHue = 0.0f;
-  float currentMonsterSaturation = 0.0f; // Start grayscale
-  int bossesDefeated = 0;
-  float nextBossTime = BOSS_SPAWN_TIME;
-  bool colorUnlocked = false;
-
-  // Initialize monsters array
-  #define MAX_MONSTERS 1000
-  Monster live_monsters[MAX_MONSTERS];
-  int monsterCount = 0;
-
-  // Spawn initial monsters (grayscale)
-  for (int i = 0; i < 30; i++)
-  {
-    live_monsters[monsterCount++] = (Monster){
-      .position = {RandomFloat(0, map.width), RandomFloat(0, map.height)},
-      .hue = currentMonsterHue,
-      .saturation = currentMonsterSaturation,
-      .weight = MONSTER_WEIGHT,
-      .health = 50.0f,
-      .maxHealth = 50.0f,
-      .alive = true,
-      .hasCollision = true,
-      .isBoss = false
-    };
-  }
-
-  // Initialize bullets array
-  #define MAX_BULLETS 200
-  Bullet bullets[MAX_BULLETS];
-  int bulletCount = 0;
-  for (int i = 0; i < MAX_BULLETS; i++)
-  {
-    bullets[i].active = false;
-  }
-
-  // Shooting timer
-  float shootTimer = 0.0f;
-  float hitProbability = HIT_PROBABILITY;
-
-  while (!WindowShouldClose())
+  Vector2 pos = {.x=INIT_SCREEN_WIDTH/2, .y=INIT_SCREEN_HEIGHT/2};
+  InitWindow(INIT_SCREEN_WIDTH, INIT_SCREEN_HEIGHT, "Color Game");
+  while(!WindowShouldClose())
   {
-    float deltaTime = GetFrameTime();
-
-    if (IsKeyPressed(KEY_P))
-    {
-      menuOpen = !menuOpen;
-    }
-
-    if (IsKeyPressed(KEY_R))
-    {
-      showMonsterColors = !showMonsterColors;
-    }
-
-    // Handle passive tree menu interactions
-    if (menuOpen)
-    {
-      if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
-      {
-        Vector2 mousePos = GetMousePosition();
-
-        // Check if clicked on a passive node
-        for (int i = 0; i < MAX_PASSIVE_NODES; i++)
-        {
-          float angleRad = passiveTree[i].angle * DEG2RAD;
-          Vector2 nodePos = {
-            menuCenter.x + cosf(angleRad) * PASSIVE_TREE_RADIUS,
-            menuCenter.y + sinf(angleRad) * PASSIVE_TREE_RADIUS
-          };
-
-          float dx = mousePos.x - nodePos.x;
-          float dy = mousePos.y - nodePos.y;
-          float distance = sqrtf(dx * dx + dy * dy);
-
-          if (distance < PASSIVE_NODE_RADIUS && !passiveTree[i].unlocked && player.passivePoints > 0)
-          {
-            // Unlock the node
-            passiveTree[i].unlocked = true;
-            player.unlockedBulletTypes[player.unlockedCount++] = i;
-            player.passivePoints--;
-            break;
-          }
-        }
-      }
-    }
-
-    // Only handle game logic when menu is closed
-    if (!menuOpen)
-    {
-      // Update game time and progression
-      gameTime += deltaTime;
-
-      // Unlock color after COLOR_UNLOCK_TIME
-      if (!colorUnlocked && gameTime >= COLOR_UNLOCK_TIME)
-      {
-        colorUnlocked = true;
-        currentMonsterSaturation = 1.0f;
-      }
-
-      // Spawn boss and unlock new color
-      if (gameTime >= nextBossTime && monsterCount < MAX_MONSTERS)
-      {
-        // Generate new color for this boss cycle
-        currentMonsterHue = RandomFloat(0, 360);
-        nextBossTime += BOSS_SPAWN_TIME;
-
-        // Spawn boss monster
-        live_monsters[monsterCount++] = (Monster){
-          .position = {RandomFloat(0, map.width), RandomFloat(0, map.height)},
-          .hue = currentMonsterHue,
-          .saturation = currentMonsterSaturation,
-          .health = 50.0f * BOSS_HEALTH_MULTIPLIER,
-          .maxHealth = 50.0f * BOSS_HEALTH_MULTIPLIER,
-          .weight = BOSS_MONSTER_WEIGHT,
-          .alive = true,
-          .hasCollision = true,
-          .isBoss = true
-        };
-      }
-
-      // Spawn regular monsters periodically
-      static float monsterSpawnTimer = 0.0f;
-      monsterSpawnTimer += deltaTime;
-      if (monsterSpawnTimer >= 3.0f && monsterCount < MAX_MONSTERS)
-      {
-        monsterSpawnTimer = 0.0f;
-        live_monsters[monsterCount++] = (Monster){
-          .position = {RandomFloat(0, map.width), RandomFloat(0, map.height)},
-          .hue = currentMonsterHue,
-          .saturation = currentMonsterSaturation,
-          .health = 50.0f,
-          .maxHealth = 50.0f,
-          .alive = true,
-          .hasCollision = true,
-          .isBoss = false
-        };
-      }
-
-      HandlePlayerMovement(&player, &map, deltaTime);
-
-      // Check player-monster collision
-      CheckPlayerMonsterCollision(&player, live_monsters, monsterCount, deltaTime);
-
-      // Update shoot timer and shoot bullets
-      shootTimer += deltaTime;
-      if (shootTimer >= SHOOT_INTERVAL && player.unlockedCount > 0)
-      {
-        shootTimer = 0.0f;
-
-        // Find inactive bullet slot
-        for (int i = 0; i < MAX_BULLETS; i++)
-        {
-          if (!bullets[i].active)
-          {
-            // Choose random bullet type from unlocked types
-            int randomIndex = (int)RandomFloat(0, player.unlockedCount);
-            int nodeIndex = player.unlockedBulletTypes[randomIndex];
-            PassiveNode* selectedNode = &passiveTree[nodeIndex];
-
-            // Find nearest monster to aim at
-            Vector2 targetPos = FindNearestMonster(live_monsters, monsterCount, player.position);
-
-            // Calculate direction to target
-            Vector2 direction = {
-              targetPos.x - player.position.x,
-              targetPos.y - player.position.y
-            };
-
-            // Normalize
-            float length = sqrtf(direction.x * direction.x + direction.y * direction.y);
-            if (length > 0)
-            {
-              direction.x /= length;
-              direction.y /= length;
-            }
-
-            // Calculate base angle
-            float baseAngle = atan2f(direction.y, direction.x);
-
-            // Add random deviation based on accuracy
-            float maxDeviation = (1.0f - hitProbability) * PI;
-            float deviation = RandomFloat(-maxDeviation, maxDeviation);
-            float finalAngle = baseAngle + deviation;
-
-            bullets[i] = (Bullet){
-              .position = player.position,
-              .velocity = {
-                cosf(finalAngle) * BULLET_SPEED,
-                sinf(finalAngle) * BULLET_SPEED
-              },
-              .lifetime = BULLET_LIFETIME,
-              .hue = selectedNode->hue,
-              .damage = BASE_DAMAGE + selectedNode->damageBonus,
-              .active = true
-            };
-            break;
-          }
-        }
-      }
-
-      // Update monsters
-      UpdateMonsters(live_monsters, monsterCount, player.position, deltaTime);
-
-      // Update bullets
-      UpdateBullets(bullets, MAX_BULLETS, deltaTime);
-
-      // Update damage numbers
-      UpdateDamageNumbers(damageNumbers, MAX_DAMAGE_NUMBERS, deltaTime);
-
-      // Check collisions
-      int bossKills = CheckCollisions(bullets, MAX_BULLETS, live_monsters, monsterCount, &player, damageNumbers);
-      bossesDefeated += bossKills;
-
-      // Update camera to follow player
-      camera.target = player.position;
-
-      // Clamp camera to map edges
-      float minX = INIT_SCREEN_WIDTH / 2.0f;
-      float maxX = map.width - INIT_SCREEN_WIDTH / 2.0f;
-      float minY = INIT_SCREEN_HEIGHT / 2.0f;
-      float maxY = map.height - INIT_SCREEN_HEIGHT / 2.0f;
-
-      if (camera.target.x < minX) camera.target.x = minX;
-      if (camera.target.x > maxX) camera.target.x = maxX;
-      if (camera.target.y < minY) camera.target.y = minY;
-      if (camera.target.y > maxY) camera.target.y = maxY;
-    }
-
-    // --- Drawings ---
     BeginDrawing();
-      ClearBackground(RAYWHITE);
-
-      if (menuOpen)
-      {
-        // Draw color wheel background
-        for (int i = 0; i < 360; i++)
-        {
-          Color color = ColorFromHSV((float)i, 1.0f, 1.0f);
-          DrawCircleSector(
-            menuCenter,
-            PASSIVE_TREE_RADIUS + 30.0f,
-            (float)i,
-            (float)(i + 1),
-            1,
-            color
-          );
-        }
-
-        // Draw passive tree nodes
-        for (int i = 0; i < MAX_PASSIVE_NODES; i++)
-        {
-          float angleRad = passiveTree[i].angle * DEG2RAD;
-          Vector2 nodePos = {
-            menuCenter.x + cosf(angleRad) * PASSIVE_TREE_RADIUS,
-            menuCenter.y + sinf(angleRad) * PASSIVE_TREE_RADIUS
-          };
-
-          Color nodeColor = ColorFromHSV(passiveTree[i].hue, 1.0f, 1.0f);
-
-          if (passiveTree[i].unlocked)
-          {
-            // Draw unlocked node
-            DrawCircleV(nodePos, PASSIVE_NODE_RADIUS, nodeColor);
-            DrawCircleV(nodePos, PASSIVE_NODE_RADIUS - 3, WHITE);
-            DrawCircleV(nodePos, PASSIVE_NODE_RADIUS - 6, nodeColor);
-          }
-          else
-          {
-            // Draw locked node
-            DrawCircleV(nodePos, PASSIVE_NODE_RADIUS, DARKGRAY);
-            DrawCircleV(nodePos, PASSIVE_NODE_RADIUS - 3, GRAY);
-          }
-
-          // Draw node info on hover
-          Vector2 mousePos = GetMousePosition();
-          float dx = mousePos.x - nodePos.x;
-          float dy = mousePos.y - nodePos.y;
-          float distance = sqrtf(dx * dx + dy * dy);
-
-          if (distance < PASSIVE_NODE_RADIUS)
-          {
-            DrawText(passiveTree[i].description, nodePos.x - 50, nodePos.y - 40, 12, BLACK);
-            DrawText(TextFormat("+%.0f dmg", passiveTree[i].damageBonus), nodePos.x - 30, nodePos.y - 28, 10, DARKGRAY);
-          }
-        }
-
-        // Draw menu instructions
-        DrawText("PASSIVE TREE MENU", menuCenter.x - 100, menuCenter.y - 350, 20, BLACK);
-        DrawText("Click nodes to unlock bullet types", menuCenter.x - 120, menuCenter.y - 330, 16, DARKGRAY);
-        DrawText(TextFormat("Passive Points Available: %d", player.passivePoints), menuCenter.x - 120, menuCenter.y - 310, 16, PURPLE);
-        DrawText("Defeat bosses to earn passive points!", menuCenter.x - 120, menuCenter.y - 290, 14, DARKGRAY);
-        DrawText("Press P to close", menuCenter.x - 70, menuCenter.y + 320, 18, BLACK);
-      }
-      else
-      {
-        // Draw game world (with camera)
-        BeginMode2D(camera);
-
-          // Draw map background
-          DrawRectangle(0, 0, map.width, map.height, (Color){240, 240, 240, 255});
-
-          // Draw map border
-          DrawRectangleLinesEx((Rectangle){0, 0, map.width, map.height}, 5.0f, DARKGRAY);
-
-          // Draw grid to show map scale
-          for (int i = 0; i <= map.width; i += 100)
-          {
-            DrawLine(i, 0, i, map.height, LIGHTGRAY);
-          }
-          for (int i = 0; i <= map.height; i += 100)
-          {
-            DrawLine(0, i, map.width, i, LIGHTGRAY);
-          }
-
-          // Draw monsters
-          for (int i = 0; i < monsterCount; i++)
-          {
-            if (live_monsters[i].alive)
-            {
-              Color monsterColor = ColorFromHSV(live_monsters[i].hue, live_monsters[i].saturation, 1.0f);
-              float radius = live_monsters[i].isBoss ? MONSTER_RADIUS * 2 : MONSTER_RADIUS;
-
-              DrawCircleV(live_monsters[i].position, radius, monsterColor);
-
-              // Draw boss crown indicator
-              if (live_monsters[i].isBoss)
-              {
-                DrawCircleV(live_monsters[i].position, radius - 5, YELLOW);
-                DrawCircleV(live_monsters[i].position, radius - 10, monsterColor);
-              }
-
-              // Draw health bar
-              float healthPercent = live_monsters[i].health / live_monsters[i].maxHealth;
-              Vector2 barPos = {live_monsters[i].position.x - 15, live_monsters[i].position.y - radius - 10};
-              DrawRectangle(barPos.x, barPos.y, 30, 4, DARKGRAY);
-              DrawRectangle(barPos.x, barPos.y, 30 * healthPercent, 4, GREEN);
-
-              // Draw color label if toggle is on
-              if (showMonsterColors)
-              {
-                char colorText[32];
-                snprintf(colorText, sizeof(colorText), "H:%.0f S:%.1f", live_monsters[i].hue, live_monsters[i].saturation);
-                DrawText(colorText, live_monsters[i].position.x - 25, live_monsters[i].position.y + radius + 5, 10, BLACK);
-              }
-            }
-          }
-
-          // Draw bullets
-          for (int i = 0; i < MAX_BULLETS; i++)
-          {
-            if (bullets[i].active)
-            {
-              Color bulletColor = ColorFromHSV(bullets[i].hue, 1.0f, 1.0f);
-              DrawCircleV(bullets[i].position, BULLET_RADIUS, bulletColor);
-            }
-          }
-
-          // Draw damage numbers
-          for (int i = 0; i < MAX_DAMAGE_NUMBERS; i++)
-          {
-            if (damageNumbers[i].active)
-            {
-              printf("True\n");
-              float alpha = damageNumbers[i].lifetime / DAMAGE_NUMBER_LIFETIME;
-              Color color = damageNumbers[i].color;
-              color.a = (unsigned char)(255 * alpha);
-              DrawText(TextFormat("%.0f", damageNumbers[i].damage),
-                       damageNumbers[i].position.x - 10,
-                       damageNumbers[i].position.y,
-                       20,
-                       color);
-            }
-          }
-
-          // Draw player (flash when invulnerable)
-          if (player.invulnerabilityTimer <= 0 || ((int)(player.invulnerabilityTimer * 10) % 2 == 0))
-          {
-            DrawCircleV(player.position, PLAYER_RADIUS, player.color);
-          }
-
-        EndMode2D();
-
-        // Draw UI (screen space)
-        // Timer at top center
-        int minutes = (int)(gameTime / 60);
-        int seconds = (int)gameTime % 60;
-        DrawText(TextFormat("Time: %02d:%02d", minutes, seconds), INIT_SCREEN_WIDTH / 2 - 60, 10, 30, BLACK);
-
-        // Player health bar
-        float healthPercent = player.health / player.maxHealth;
-        DrawRectangle(10, 10, 200, 20, DARKGRAY);
-        DrawRectangle(10, 10, 200 * healthPercent, 20, RED);
-        DrawText(TextFormat("HP: %.0f/%.0f", player.health, player.maxHealth), 15, 12, 16, WHITE);
-
-        // Experience bar
-        float expPercent = player.experience / player.expToNextLevel;
-        DrawRectangle(10, 35, 200, 15, DARKGRAY);
-        DrawRectangle(10, 35, 200 * expPercent, 15, SKYBLUE);
-        DrawText(TextFormat("Lvl %d", player.level), 15, 36, 12, WHITE);
-        DrawText(TextFormat("%.0f/%.0f XP", player.experience, player.expToNextLevel), 70, 36, 12, WHITE);
-
-        // Passive points
-        DrawText(TextFormat("Passive Points: %d", player.passivePoints), 10, 55, 18, PURPLE);
-
-        // Controls
-        DrawText("WASD: Move | P: Menu | R: Toggle Colors", 10, 80, 16, DARKGRAY);
-
-        // Count alive monsters and bosses
-        int aliveCount = 0;
-        int bossCount = 0;
-        for (int i = 0; i < monsterCount; i++)
-        {
-          if (live_monsters[i].alive)
-          {
-            aliveCount++;
-            if (live_monsters[i].isBoss) bossCount++;
-          }
-        }
-        DrawText(TextFormat("Monsters: %d | Bosses: %d", aliveCount, bossCount), 10, 105, 18, DARKGRAY);
-        DrawText(TextFormat("Unlocked bullets: %d/%d", player.unlockedCount, MAX_PASSIVE_NODES), 10, 130, 16, DARKGRAY);
-        DrawText(TextFormat("Bosses defeated: %d", bossesDefeated), 10, 155, 16, DARKGRAY);
-      }
-
+      DrawPixelV(pos, RED);
     EndDrawing();
   }
-  return 0;
 }