Files
factorygame/items/item.c

340 lines
12 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);
}
}
}
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].atlasRects[ORIENT_LEFT] = allocate_32x32(
ItemRegistry[itemRegistryIndex].texture[ORIENT_LEFT], renderer);
ItemRegistry[itemRegistryIndex].atlasRectsOnBelt[ORIENT_LEFT] = allocate_16x16(
ItemRegistry[itemRegistryIndex].textureOnBelt[ORIENT_LEFT], renderer);
ItemRegistry[itemRegistryIndex].type = itemRegistryIndex;
ItemRegistry[itemRegistryIndex].isTile = false;
ItemRegistry[itemRegistryIndex].miscVal = 60;
itemRegistryIndex++;
}
// 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 rect
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, &rect);
SDL_RenderCopy(renderer, atlasTexture, &ItemRegistry[item.type].atlasRectsOnBelt[ORIENT_LEFT], &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) {
for (int i = 0; i < tileTypeIndex; i++) {
TileTypeReg tile = TileRegistry[i];
strcpy(ItemRegistry[itemRegistryIndex].name, tile.name);
memcpy(ItemRegistry[itemRegistryIndex].texture, tile.textures, sizeof(tile.textures));
memcpy(ItemRegistry[itemRegistryIndex].atlasRects, tile.atlasRects, sizeof(tile.atlasRects));
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);
ItemRegistry[itemRegistryIndex].atlasRectsOnBelt[a] = allocate_16x16(
ItemRegistry[itemRegistryIndex].textureOnBelt[a], renderer);
SDL_SetTextureBlendMode(ItemRegistry[itemRegistryIndex].textureOnBelt[a], SDL_BLENDMODE_BLEND);
}
ItemRegistry[itemRegistryIndex].type = itemRegistryIndex;
ItemRegistry[itemRegistryIndex].isTile = true;
itemRegistryIndex++;
}
itemRegistryIndex = ITEMREGISTRY_SIZE / 2;
iterateSortedDir("./assets/items", (DirEntryCallback) registerItem, renderer);
}