#include #include #include #include "stdio.h" #include "constants.h" #include "game.h" #include "assets.h" #include "player.h" #include "background.h" #include #include "utils.h" Game game; int compare(const void* a, const void* b) { const Entity* eA = *(Entity**)a; const Entity* eB = *(Entity**)b; if (!PhysicsEnabled(eA)) return 1; else if (!PhysicsEnabled(eB)) return -1; return GetPhysicsColliderGlobal(eA).y < GetPhysicsColliderGlobal(eB).y ? -1 : 1; } void AddEntity(Entity* e) { static int nextId = 0; for(int i = 0; i < MAX_ENTITY_COUNT; i++) { if (!EntityAllocated(&game.entities[i])) { e->id = nextId++; e->flags |= ENTITY_ALLOCATED; game.entities[i] = *e; return; } } #ifdef BEATEMUP_DEBUG TraceLog(LOG_WARNING, "Entity pool full, cannot add new entity!"); #endif } void InitGame() { memset(&game, 0, sizeof(Game)); game.camera = (Camera2D) { .offset = (Vector2) {WINDOW_WIDTH / 2.0f, 0.0f}, .rotation = 0.0f, .zoom = 1.0f }; } void UpdateEntity(Entity* e, float deltaTime) { switch (e->type) { case Player_Entity: UpdatePlayer(e, deltaTime); break; default: break; } } void UpdateGame(float deltaTime) { #ifdef BEATEMUP_DEBUG if (IsKeyPressed(KEY_F3)) { game.enableDebugOverlay = !game.enableDebugOverlay; } #endif for (int i = 0; i < MAX_ENTITY_COUNT; i++) { Entity* e = &game.entities[i]; if (EntityAllocated(e)) UpdateEntity(e, deltaTime); } } void DrawEntitySprite(const Entity* e, int spriteIndex) { const Sprite* drawnSprite = &e->sprites[spriteIndex]; Rectangle srcRect; if (!IsAnimated(drawnSprite)) { srcRect = drawnSprite->srcRect; } else { const Animation* currentAnimation = &drawnSprite->animations[drawnSprite->currentAnimation]; srcRect = GetCurrentSourceRectangle(currentAnimation); } Vector2 origin = {0.0f, 0.0f}; float rotation = 0.0f; Rectangle destRect = GetSpriteDrawDestinationRectGlobal(e, spriteIndex); if (drawnSprite->flipX) srcRect.width = -srcRect.width; if (drawnSprite->flipY) srcRect.height = -srcRect.width; DrawTexturePro(drawnSprite->texture, srcRect, destRect, origin, rotation, WHITE); } void DefaultDrawEntity(const Entity* e, DrawLayer toLayer) { for (int i = 0; i < e->numSprites; i++) { if (e->sprites[i].layer == toLayer) DrawEntitySprite(e, i); } } void DrawEntity(const Entity* e, DrawLayer toLayer) { switch (e->type) { default: DefaultDrawEntity(e, toLayer); } } void DrawEntitiesInLayer(Entity* drawOrder[], int layer) { for (int i = 0; i < MAX_ENTITY_COUNT; i++) { Entity* currentEntity = drawOrder[i]; if (ShouldDrawEntity(currentEntity, layer)) DrawEntity(currentEntity, layer); } } void DrawGame() { Entity* drawOrder[MAX_ENTITY_COUNT]; for (int i = 0; i < MAX_ENTITY_COUNT; i++) { drawOrder[i] = &game.entities[i]; } qsort(drawOrder, MAX_ENTITY_COUNT, sizeof(Entity*), compare); BeginDrawing(); ClearBackground(RAYWHITE); BeginMode2D(game.camera); for (DrawLayer layer = Background_Layer; layer <= Foreground_Layer; layer++) DrawEntitiesInLayer(drawOrder, layer); #ifdef BEATEMUP_DEBUG if (game.enableDebugOverlay) { for (int i = 0; i < MAX_ENTITY_COUNT; i++) { DebugHighlights(drawOrder[i]); } } #endif EndMode2D(); #ifdef BEATEMUP_DEBUG if (game.enableDebugOverlay) { const int posX = 10, posY = 10; DrawFPS(posX, posY); } #endif EndDrawing(); } void AddWall(float xpos, float ypos, float width, float height) { Entity wall = {0}; wall.type = Wall_Entity; wall.flags |= ENTITY_PHYSICS_ACTIVE; wall.position = (Vector2) {xpos, ypos}; wall.physicsCollider = (Rectangle) {0, 0, width, height}; #ifdef BEATEMUP_DEBUG wall.physicsColliderColor = BLUE; #endif AddEntity(&wall); } static inline float GetCurrentFrameTime(const Animation* animation) { return animation->frameTimes[animation->currentFrame]; } void UpdateCurrentSpriteAnimation(Sprite* sprite, float dt) { if (!IsAnimated(sprite)) return; Animation* currentAnimation = GetCurrentAnimation(sprite); if (currentAnimation->isComplete) return; currentAnimation->currentFrameTimer += dt; if (currentAnimation->currentFrameTimer >= GetCurrentFrameTime(currentAnimation)) { currentAnimation->currentFrame++; currentAnimation->currentFrameTimer = currentAnimation->currentFrameTimer - GetCurrentFrameTime(currentAnimation); } if (currentAnimation->currentFrame >= currentAnimation->numFrames) { if (currentAnimation->isLooping) { currentAnimation->currentFrame = 0; currentAnimation->currentFrameTimer = 0.0f; } else { currentAnimation->currentFrame = currentAnimation->numFrames - 1; currentAnimation->isComplete = true; } } } Rectangle GetSrcRectFromIndex(Texture texture, int framesX, int framesY, int index) { const float frameWidth = (float)texture.width / framesX; const float frameHeight = (float)texture.height / framesY; return (Rectangle) { .x = (index % framesY) * frameWidth, .y = (index / framesY) * frameHeight, .width = frameWidth, .height = frameHeight }; } Animation AnimationFromIndices(AnimationFromIndicesParams params) { int numFrames = params.numFrames; bool isLooping = params.isLooping; Texture texture = params.texture; int framesX = params.framesX; int framesY = params.framesY; int* indices = params.indices; float* frameTimes = params.frameTimes; Animation animation = (Animation) { .numFrames = numFrames, .isLooping = isLooping, .currentFrame = 0 }; for (int i = 0; i < numFrames; i++) { animation.srcRects[i] = GetSrcRectFromIndex(texture, framesX, framesY, indices[i]); animation.frameTimes[i] = frameTimes[i]; } return animation; }