// // Created by bruno on 4/24/25. // #include "item.h" #include "../player/player.h" #include "../util/font.h" #include Item ItemRegistry[ITEMREGISTRY_SIZE]; uint16_t itemRegistryIndex = 0; float speed = 0.002f; // fraction of tile per tick void updateItems() { const int dirDx[8] = {-1, -1, -1, 0, 1, 1, 1, 0}; const int dirDy[8] = {1, 0, -1, -1, -1, 0, 1, 1}; const float epsilon = 0.999f; // if we can't move, back it off just below 1 for (int y = 0; y < MAP_HEIGHT; y++) { for (int x = 0; x < MAP_WIDTH; x++) { Tile *t = &tileMap[y][x]; if (t->type != TYPE_BELT) continue; int dir = t->direction; bool horz = (dir == ORIENT_LEFT || dir == ORIENT_RIGHT); bool vert = (dir == ORIENT_UP || dir == ORIENT_DOWN); for (uint8_t lane = 0; lane < 2; lane++) { for (uint8_t slot = 0; slot < 2; slot++) { ItemOnBelt *itm = &t->items[lane][slot]; if (!itm->active) continue; // 1) Advance itm->offset += speed; // 2) Time to hop? if (itm->offset >= 0.5f) { itm->offset -= 1.0f; // target coords int nx = x + dirDx[dir]; int ny = y + dirDy[dir]; // bounds & belt? if (nx < 0 || nx >= MAP_WIDTH || ny < 0 || ny >= MAP_HEIGHT) { itm->active = false; continue; } Tile *next = &tileMap[ny][nx]; if (next->type != TYPE_BELT) { itm->active = false; continue; } // Decide new lane int newLane = lane; int newDir = next->direction; bool nH = (newDir == ORIENT_LEFT || newDir == ORIENT_RIGHT); bool nV = (newDir == ORIENT_UP || newDir == ORIENT_DOWN); if ((horz && nH) || (vert && nV)) { // same axis → keep lane } else if (horz && nV) { // came off a horizontal: lane0=top→vertical.left, lane1=bottom→vertical.right newLane = (dir == ORIENT_RIGHT ? 1 : 0); itm->offset = 0.0f; } else if (vert && nH) { // came off vertical: lane0=left→horizontal.top, lane1=right→horizontal.bottom newLane = (dir == ORIENT_UP ? 0 : 1); itm->offset = 0.0f; } // (diagonals fall back to same-lane) // Find a free slot in newLane int destSlot = -1; if (!next->items[newLane][0].active) destSlot = 0; else if (!next->items[newLane][1].active) destSlot = 1; if (destSlot >= 0) { // MOVE it ItemOnBelt moved = *itm; moved.tileX = nx; moved.tileY = ny; next->items[newLane][destSlot] = moved; next->items[newLane][destSlot].active = true; // clear this one itm->active = false; } else { // both slots full → wait at end itm->offset = epsilon; } } } } } } } void registerItem(char name[20], SDL_Renderer *renderer) { const char *dot = strchr(name, '.'); memcpy(ItemRegistry[itemRegistryIndex].name, name, dot - name); char texturePath[80]; snprintf(texturePath, 80, "./assets/items/%s", name); ItemRegistry[itemRegistryIndex].texture[ORIENT_LEFT] = IMG_LoadTexture(renderer, texturePath); SDL_SetTextureBlendMode(ItemRegistry[itemRegistryIndex].texture[0], SDL_BLENDMODE_BLEND); ItemRegistry[itemRegistryIndex].textureOnBelt[ORIENT_LEFT] = ScaleTexture(renderer, ItemRegistry[itemRegistryIndex].texture[ORIENT_LEFT], TILE_SIZE / 2, TILE_SIZE / 2); SDL_SetTextureBlendMode(ItemRegistry[itemRegistryIndex].textureOnBelt[ORIENT_LEFT], SDL_BLENDMODE_BLEND); ItemRegistry[itemRegistryIndex].type = itemRegistryIndex; itemRegistryIndex++; } // easing function: cosine ease‐in‐out static float ease_in_out(float t) { if (t < -1.0f) t = -1.0f; if (t > 1.0f) t = 1.0f; // Even symmetric easing: reflected across t = 0 return (t < 0.0f) ? -0.5f * (1.0f - cosf(M_PI * -t)) // negative side : 0.5f * (1.0f - cosf(M_PI * t)); // positive side } uint8_t laneTarget = 0; void renderItem(ItemOnBelt item, SDL_Renderer *renderer, int lane) { SDL_Rect rect = {0}; rect.x = item.tileX * TILE_SIZE; rect.y = item.tileY * TILE_SIZE; // get raw direction code int dir = tileMap[item.tileY][item.tileX].direction; //--- 1) build a unit vector (dxf, dyf) for this orientation float dxf = 0.0f, dyf = 0.0f; const float D = M_SQRT1_2; // 1/√2 ≈ 0.7071 switch (dir) { case ORIENT_LEFT_DOWN: dxf = -D; dyf = +D; break; case ORIENT_LEFT: dxf = -1.0f; dyf = 0.0f; break; case ORIENT_LEFT_UP: dxf = -D; dyf = -D; break; case ORIENT_UP: dxf = 0.0f; dyf = -1.0f; break; case ORIENT_RIGHT_UP: dxf = +D; dyf = -D; break; case ORIENT_RIGHT: dxf = +1.0f; dyf = 0.0f; break; case ORIENT_RIGHT_DOWN: dxf = +D; dyf = +D; break; case ORIENT_DOWN: dxf = 0.0f; dyf = +1.0f; break; default: break; } //--- 2) ease the offset and compute forward distance float t = ease_in_out(item.offset); // 0..1 float dist = t * TILE_SIZE; // 0..TILE_SIZE pixels float xOffset = dxf * dist; float yOffset = dyf * dist; switch (dir) { case ORIENT_LEFT_DOWN: xOffset += 0.0f * TILE_SIZE; yOffset += 0.0f * TILE_SIZE; break; case ORIENT_LEFT: xOffset += 0.0f * TILE_SIZE; yOffset += 0.26f * TILE_SIZE; break; case ORIENT_LEFT_UP: xOffset += 0.0f * TILE_SIZE; yOffset += 0.0f * TILE_SIZE; break; case ORIENT_UP: xOffset += 0.22f * TILE_SIZE; //GOTO HEHREHRHE yOffset += 0.0f * TILE_SIZE; break; case ORIENT_RIGHT_UP: xOffset += 0.0f * TILE_SIZE; yOffset += 0.0f * TILE_SIZE; break; case ORIENT_RIGHT: xOffset += 0.0f * TILE_SIZE; yOffset += 0.18f * TILE_SIZE; //FIX THIS break; case ORIENT_RIGHT_DOWN: xOffset += 0.0f * TILE_SIZE; yOffset += 0.0f * TILE_SIZE; break; case ORIENT_DOWN: xOffset += 0.18f * TILE_SIZE; yOffset += 0.0f * TILE_SIZE; break; default: break; } //--- 3) compute perpendicular unit vector (perpX, perpY) = (-dy, dx) float perpX = -dyf; float perpY = dxf; // perp is already unit length because (dxf,dyf) is unit bool horz = (dir == ORIENT_LEFT || dir == ORIENT_RIGHT); bool vert = (dir == ORIENT_UP || dir == ORIENT_DOWN); //--- 4) lane offset // total lane spacing = TILE_SIZE/2, so half of that each side = TILE_SIZE/4 float laneSign = (lane == laneTarget ? +1.0f : -1.0f); float laneOffset = TILE_SIZE * (horz ? 0.12f : 0.14f); // = TILE_SIZE/4 xOffset += perpX * laneSign * laneOffset; yOffset += perpY * laneSign * laneOffset; //--- 5) apply to rect rect.x += (int) roundf(xOffset); rect.y += (int) roundf(yOffset); rect.w = TILE_SIZE / 2; rect.h = TILE_SIZE / 2; adjustRect(&rect); // (Optional debug overlay) // SDL_Rect tileArea = {item.tileX * TILE_SIZE, item.tileY * TILE_SIZE, // TILE_SIZE, TILE_SIZE}; // SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); // SDL_SetRenderDrawColor(renderer, 255, 0, 0, 32); // adjustRect(&tileArea); // SDL_RenderFillRect(renderer, &tileArea); char tempStr[50]; // SDL_Rect rectA = {0}; // rectA.x = item.tileX * TILE_SIZE; // rectA.y = item.tileY * TILE_SIZE; // rectA.w = TILE_SIZE; // rectA.h = TILE_SIZE; // sprintf(tempStr, "L%d\n%f\n%f\n%f", lane, item.offset, xOffset, yOffset); // SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); // SDL_SetRenderDrawColor(renderer, 255, 0, 0, 32); // adjustRect(&rectA); // SDL_RenderFillRect(renderer, &rectA); SDL_RenderCopy(renderer, ItemRegistry[item.type].textureOnBelt[ORIENT_LEFT], NULL, &rect); // renderText(renderer, fonts[3], tempStr, rectA.x, rectA.y); } void renderBeltItems(SDL_Renderer *renderer) { } void putItem(int x, int y, uint16_t itemType, uint8_t lane, uint8_t itemIndex) { tileMap[y][x].items[lane][itemIndex].type = itemType; tileMap[y][x].items[lane][itemIndex].offset = 0; tileMap[y][x].items[lane][itemIndex].active = true; tileMap[y][x].items[lane][itemIndex].tileX = x; tileMap[y][x].items[lane][itemIndex].tileY = y; } void loadItems(SDL_Renderer *renderer) { for(int i = 0; i < tileTypeIndex; i++) { TileType tile = TileRegistry[i]; strcpy(ItemRegistry[itemRegistryIndex].name, tile.name); memcpy(ItemRegistry[itemRegistryIndex].texture, tile.textures, sizeof (tile.textures)); for (int a = 0; a < ORIENT_DIRECTION_COUNT; a++) { SDL_SetTextureBlendMode(ItemRegistry[itemRegistryIndex].texture[a], SDL_BLENDMODE_BLEND); ItemRegistry[itemRegistryIndex].textureOnBelt[a] = ScaleTexture(renderer, tile.textures[a], TILE_SIZE / 2, TILE_SIZE / 2); SDL_SetTextureBlendMode(ItemRegistry[itemRegistryIndex].textureOnBelt[a], SDL_BLENDMODE_BLEND); } ItemRegistry[itemRegistryIndex].type = itemRegistryIndex; itemRegistryIndex++; } DIR *dir = opendir("./assets/items"); if (dir) { struct dirent *entry; while ((entry = readdir(dir)) != NULL) { if (entry->d_name[0] == '.') { continue; } registerItem(entry->d_name, renderer); } } }