Files
factorygame/items/item.c
2025-06-08 17:22:30 +02:00

395 lines
13 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// Created by bruno on 4/24/25.
//
#include "item.h"
#include "../player/player.h"
#include "../util/font.h"
#include "../tiles/furnace.h"
#include "../tiles/tilecallbacks.h"
#include "../util/atlas.h"
#include <dirent.h>
Item ItemRegistry[ITEMREGISTRY_SIZE];
uint16_t itemRegistryIndex = 0;
double speed = 1.0f / TILE_SIZE; // fraction of tile per tick
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
void updateItems() {
for (int i = 0; i < neededUpdates.activeCount; i++) {
int x = neededUpdates.tiles[i].x;
int y = neededUpdates.tiles[i].y;
Tile *t = &tileMap[y][x];
if (t->type == TYPE_AIR) continue;
TileTypeReg tt = TileRegistry[t->type];
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 < ItemSlotCount; lane++) {
if (!tt.outputLane[lane]) continue;
ItemOnBelt *itm = &t->items[lane];
if (itm->type == 0) continue;
if (tt.itemMoves) {
itm->offset += speed;
// 1) Advance
}
// 2) Time to hop?
if (itm->offset >= 0.5f || !tt.itemMoves) {
if (tt.itemMoves) {
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) {
if (tt.itemMoves) {
itm->offset += 1.0f - speed;
}
continue;
}
Tile *next = &tileMap[ny][nx];
TileTypeReg ntt = TileRegistry[next->type];
int newLane = lane;
switch (next->type) {
case TYPE_BELT:
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 ^ newDir == ORIENT_UP ? 0 : 1);
itm->offset = 0.0f;
} else if (vert && nH) {
// came off vertical: lane0=left→horizontal.top, lane1=right→horizontal.bottom
newLane = (dir == ORIENT_UP ^ newDir == ORIENT_RIGHT ? 1 : 0);
itm->offset = 0.0f;
}
// (diagonals fall back to same-lane)
// Find a free slot in
break;
default:
itm->offset += 1.0f - speed;
}
if (next->items[newLane].type == 0 && ntt.allowedInItems[newLane][itm->type]) {
// MOVE it
ItemOnBelt moved = *itm;
moved.tileX = nx;
moved.tileY = ny;
if (!ntt.itemMoves) {
moved.offset = 0.5f;
}
next->items[newLane] = moved;
// clear this one
itm->type = 0;
} else {
// both slots full → wait at end
itm->offset = epsilon;
}
} else {
}
}
const UpdateTileCallback cb = ItemTileCallbacks[t->type];
if (cb) {
cb(t);
}
}
}
// easing function: cosine easeinout
//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
//}
static float ease_in_out(float t) {
// Clamp t to [-1.0, 1.0]
if (t < -1.0f) t = -1.0f;
if (t > 1.0f) t = 1.0f;
return t; // Linear: no easing
}
uint8_t laneTarget = 0;
void renderItem(ItemOnBelt item, SDL_Renderer *renderer, int lane, SDL_Rect playerRect) {
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 + (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 + (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 - (TILE_SIZE / 2);
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 + (TILE_SIZE / 8);
yOffset += 0.0f * TILE_SIZE - (TILE_SIZE / 2);
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 tileRect
rect.x += (int) roundf(xOffset);
rect.y += (int) roundf(yOffset);
rect.w = TILE_SIZE / 2;
rect.h = TILE_SIZE / 2;
adjustRect(&rect, playerRect);
// (Optional debug overlay)
char tempStr[50];
SDL_Rect rectA = {0};
if (debugMode) {
SDL_Rect tileArea = {item.tileX * TILE_SIZE, item.tileY * TILE_SIZE,
TILE_SIZE, TILE_SIZE};
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 32);
adjustRect(&tileArea, playerRect);
SDL_RenderFillRect(renderer, &tileArea);
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_SetRenderDrawColor(renderer, 255, 0, 0, 32);
adjustRect(&rectA, playerRect);
SDL_RenderFillRect(renderer, &rectA);
}
//SDL_RenderCopyx(renderer, ItemRegistry[item.type].textureOnBelt[ORIENT_LEFT], NULL, &tileRect);
SDL_RenderCopy(renderer, atlasTexture,
&ItemRegistry[item.type].beltAnimation.atlasRects[ORIENT_LEFT][
(animationStep / ItemRegistry[item.type].beltAnimation.divisor) %
ItemRegistry[item.type].beltAnimation.frameCount],
&rect);
if (debugMode) {
renderText(renderer, fonts[3], tempStr, rectA.x, rectA.y);
}
}
void putItem(int x, int y, ItemType itemType, uint8_t lane) {
tileMap[y][x].items[lane].type = itemType;
tileMap[y][x].items[lane].offset = 0;
tileMap[y][x].items[lane].tileX = x;
tileMap[y][x].items[lane].tileY = y;
}
void loadItems(SDL_Renderer *renderer) {
// Load tile-based items
for (int i = 0; i < tileTypeIndex; i++) {
TileTypeReg tile = TileRegistry[i];
Item *item = &ItemRegistry[itemRegistryIndex];
strcpy(item->name, tile.name);
memcpy(&item->animation, &tile.animation, sizeof(tile.animation));
for (int frame = 0; frame < item->animation.frameCount; frame++) {
for (int a = 0; a < ORIENT_DIRECTION_COUNT; a++) {
SDL_SetTextureBlendMode(item->animation.textures[a][frame], SDL_BLENDMODE_BLEND);
item->beltAnimation.textures[a][frame] = ScaleTexture(renderer, tile.animation.textures[a][frame],
TILE_SIZE / 2, TILE_SIZE / 2);
item->beltAnimation.atlasRects[a][frame] = allocate_16x16(item->beltAnimation.textures[a][frame],
renderer);
if (frame + 1 > item->beltAnimation.frameCount) {
item->beltAnimation.frameCount = frame + 1;
}
SDL_SetTextureBlendMode(item->beltAnimation.textures[a][frame], SDL_BLENDMODE_BLEND);
}
}
item->type = itemRegistryIndex;
item->isTile = true;
item->animation.divisor = 1;
item->beltAnimation.divisor = 1;
itemRegistryIndex++;
}
// Skip ahead to avoid overlap (tile items use lower indices)
itemRegistryIndex = ITEMREGISTRY_SIZE / 2;
// Load sprite-based items with animations
DIR *dir = opendir("./assets/items");
if (!dir) {
perror("Failed to open item asset folder");
return;
}
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
if (!strstr(entry->d_name, ".png")) continue;
int frame, indexItem;
char name[64];
if (sscanf(entry->d_name, "%d-%20[^-]-%d.png", &indexItem, name, &frame) == 3) {
// Success: you now have index, fname, and frame
} else {
fprintf(stderr, "Invalid format: %s\n", entry->d_name);
}
indexItem += ITEMREGISTRY_SIZE / 2;
Item *item;
item = &ItemRegistry[indexItem];
memset(item, 0, sizeof(Item));
strcpy(item->name, name);
item->type = indexItem;
item->isTile = false;
item->miscVal = 60;
item->animation.divisor = 1;
item->beltAnimation.divisor = 1;
// Load the texture
char path[128];
snprintf(path, sizeof(path), "./assets/items/%s", entry->d_name);
SDL_Texture *tex = IMG_LoadTexture(renderer, path);
if (!tex) {
fprintf(stderr, "Failed to load texture %s: %s\n", path, IMG_GetError());
continue;
}
SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND);
SDL_Texture *beltTex = ScaleTexture(renderer, tex, TILE_SIZE / 2, TILE_SIZE / 2);
SDL_SetTextureBlendMode(beltTex, SDL_BLENDMODE_BLEND);
SDL_Rect mainRect = allocate_32x32(tex, renderer);
SDL_Rect beltRect = allocate_16x16(beltTex, renderer);
// Assign to all orientations
for (int o = 0; o < ORIENT_DIRECTION_COUNT; o++) {
item->animation.textures[o][frame] = tex;
item->beltAnimation.textures[o][frame] = beltTex;
item->animation.atlasRects[o][frame] = mainRect;
item->beltAnimation.atlasRects[o][frame] = beltRect;
if (frame + 1 > item->animation.frameCount) {
item->animation.frameCount = frame + 1;
item->beltAnimation.frameCount = frame + 1;
}
}
item->animation.divisor = 1;
if (indexItem + 1 > itemRegistryIndex) {
itemRegistryIndex = indexItem + 1;
}
}
closedir(dir);
}