- No se, molts canvis i coses

This commit is contained in:
2025-11-17 14:24:03 +01:00
parent 3c56cd59f8
commit 3ed64a471a
17 changed files with 295 additions and 1427 deletions

View File

@@ -1,4 +1,4 @@
libs = -lSDL3 -lGL libs = -lSDL3
cppflags = -g cppflags = -g
executable = dilemmaker executable = dilemmaker
sourcepath = source source/japi sourcepath = source source/japi

View File

@@ -1,137 +0,0 @@
#include "enemies.h"
#include <filesystem>
#include <iostream>
#include <fstream>
#include <algorithm>
#include <unordered_map>
namespace fs = std::filesystem;
namespace enemies
{
std::unordered_map<std::string, enemy_t> enemies;
inline void trim(std::string& s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); }));
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), s.end());
}
void processKeyValue(const std::string& enemy_name, const std::string& section, const std::string& key, const std::string& value)
{
enemy_t& enemy = enemies[enemy_name];
if (section == "global") {
if (key == "tileSetFile") {
enemy.tileSetFile = images::getImage("enemies/"+value);
} else if (key == "frame_width") {
enemy.frame_width = std::stoi(value);
} else if (key == "frame_height") {
enemy.frame_height = std::stoi(value);
}
} else if (section == "animation") {
if (key == "name") {
enemy.animations.back().name = value;
printf("animacio '%s' frames: ", value.c_str());
} else if (key == "loop") {
enemy.animations.back().loop = std::stoi(value);
} else if (key == "speed") {
enemy.animations.back().speed = std::stof(value);
} else if (key == "frames") {
std::stringstream ss(value);
std::string frame;
while (std::getline(ss, frame, ',')) {
if (!frame.empty()) {
enemy.animations.back().frames.emplace_back(std::stoi(frame));
printf("%s,", frame.c_str());
}
}
printf("\n");
}
}
}
void loadEnemy(fs::path filename)
{
std::string enemy_name = filename.stem().string();
std::ifstream file(filename);
if (!file) {
std::cerr << "Failed to open file.\n";
return;
}
std::string current_section = "global";
std::string line;
while (std::getline(file, line))
{
//std::cout << "'" << line << "'\n";
// Trim and clean line, ignore empty lines and comments
trim(line);
if (line.empty() || line[0] == '#') continue;
// Remove inline comments
/*std::size_t comment_pos = line.find('#');
if (comment_pos != std::string::npos) {
line = line.substr(0, comment_pos);
trim(line);
}*/
// Check if entering or exiting a section
if (line[0] == '[')
{
std::size_t closing_pos = line.find(']');
if (closing_pos != std::string::npos && closing_pos > 1)
{
std::string section = line.substr(1,closing_pos-1);
if (section[0] == '/') {
current_section = "global";
} else {
current_section = section;
if (section == "animation") enemies[enemy_name].animations.emplace_back();
}
}
}
// Get and process key/value
std::size_t eq_pos = line.find('=');
if (eq_pos != std::string::npos) {
std::string key = line.substr(0, eq_pos);
std::string value = line.substr(eq_pos + 1);
trim(key);
trim(value);
processKeyValue(enemy_name, current_section, key, value);
}
}
}
void load()
{
// Get all enemies
fs::path target_dir = "./data/enemies";
std::vector<fs::path> enemy_files;
try {
for (const auto& entry : fs::directory_iterator(target_dir)) {
if (entry.is_regular_file() && entry.path().extension() == ".ani") {
enemy_files.push_back(entry.path());
}
}
for (const auto& path : enemy_files) {
std::cout << "Processing " << path.filename() << '\n';
loadEnemy(path);
}
} catch (const fs::filesystem_error& e) {
std::cerr << "Filesystem error: " << e.what() << '\n';
}
}
enemy_t &get(std::string name)
{
return enemies[name];
}
}

View File

@@ -1,26 +0,0 @@
#pragma once
#include "images.h"
#include <string>
#include <vector>
namespace enemies
{
struct animation_t
{
std::string name {""};
float speed {0.0f};
uint8_t loop {0};
std::vector<uint8_t> frames;
};
struct enemy_t
{
draw::surface *tileSetFile {nullptr};
uint8_t frame_width {0};
uint8_t frame_height {0};
std::vector<animation_t> animations;
};
void load();
enemy_t &get(std::string name);
}

View File

@@ -1,59 +0,0 @@
#include "images.h"
#include <map>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
namespace images
{
std::map<std::string, draw::surface*> images;
draw::surface *getImage(std::string filename)
{
auto item = images.find(filename);
if (item != images.end()) return item->second;
draw::surface *image = draw::loadSurface(filename.c_str());
images[filename] = image;
return image;
}
int loadPalette(std::string filename)
{
uint32_t palette[16];
FILE *file = fopen(filename.c_str(), "r");
if (!file) return -1;
char header[32];
int version, count;
// Read and validate header
if (!fgets(header, sizeof(header), file) || strncmp(header, "JASC-PAL", 8) != 0) {
fclose(file);
return -2;
}
if (fscanf(file, "%d\n%d\n", &version, &count) != 2 || count != 16) {
fclose(file);
return -3;
}
// Read 16 RGB triplets
for (int i = 0; i < 16; ++i) {
int r, g, b;
if (fscanf(file, "%d %d %d\n", &r, &g, &b) != 3) {
fclose(file);
return -4;
}
palette[i] = (r << 16) | (g << 8) | b;
}
fclose(file);
draw::setPalette(palette, 16);
return 0;
}
}

View File

@@ -1,9 +0,0 @@
#pragma once
#include "japi/draw.h"
#include <string>
namespace images
{
draw::surface *getImage(std::string filename);
int loadPalette(std::string filename);
}

View File

@@ -2,88 +2,16 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include "gif.h" #include "gif.h"
#include "file.h" #include "file.h"
//#include "shader.h"
namespace draw namespace draw
{ {
// La idea de esta unitat es usar "superficies", que no son mes que arrays de bytes, per a anar pintant.
// El resultat final s'ha de pintar en algun moment a la superficie "screen" (o siga, especificar nullptr en setDestination)
// Aleshores, en "render" el contingut de screen se volca a la textura SDL que crearem,
// i eixa textura se pintarà a pantalla com se sol fer amb SDL. Ho anirem veient en el codi.
SDL_Window *sdl_window {nullptr}; // La finestra de SDL SDL_Window *sdl_window {nullptr}; // La finestra de SDL
SDL_Renderer *sdl_renderer {nullptr}; // El renderer de SDL SDL_Renderer *sdl_renderer {nullptr}; // El renderer de SDL
SDL_Texture *sdl_texture {nullptr}; // La textura de SDL a la que pintarem la nostra superficie "screen" i que despres volcarem a pantalla SDL_Texture *sdl_source {nullptr};
//SDL_Texture *sdl_shadertex {nullptr}; // La textura de SDL per al shader
static int screen_zoom = 1; void init(const char *titol, const uint16_t width, const uint16_t height)
static bool screen_fullscreen = false;
static bool screen_cursor = true;
static char* screen_shader = nullptr;
static bool shader_enabled = false;
static float window_ratio = 1;
static int canvas_width;
static int canvas_height;
static int desktop_width;
static int desktop_height;
static int window_width;
static int window_height;
static int offset_x = 0;
static int offset_y = 0;
char window_title[256];
surface *screen {nullptr}; // La superficie screen, que representa la pantalla. Se crea i destrueix internament
surface *destination {nullptr}; // Punter a la actual superficie de destí
surface *source {nullptr}; // Punter a la actual superficie d'oritge
surface *pushed_source {nullptr}; // Punter a la superficie d'oritge que s'ha pushat
uint32_t palette[256]; // La paleta de colors
uint32_t aux_palette[256]; // La paleta de colors, para els fadein
uint8_t color_indices[256]; // Indices dels colors per defecte
uint8_t sel_color {0}; // Color seleccionat per defecte
uint8_t transparent {0}; // El color transparent
//surface *textsurf = nullptr; // Surface on guardar el gif amb la font
SDL_Rect viewport;
bool fading_out = false;
bool fading_in = false;
void createDisplay()
{ {
// Ajustem el zoom i el ratio, per si tenen valors locs sdl_window = SDL_CreateWindow(titol, width, height, SDL_WINDOW_RESIZABLE);
if (screen_zoom <= 0) screen_zoom = 1;
if (window_ratio <= 0) window_ratio = 1;
// Ajustem el tamany de la finestra, segons el zoom i el ratio
window_width = canvas_width*screen_zoom;
window_height = window_ratio != 1 ? int(float(canvas_width)*window_ratio*float(screen_zoom)) : canvas_height*screen_zoom;
// Mentres no càpiga en la pantalla, reduïm el zoom
while (window_width > desktop_width || window_height > desktop_height) {
screen_zoom--;
window_width = canvas_width*screen_zoom;
window_height = window_ratio != 1 ? int(float(canvas_width)*window_ratio*float(screen_zoom)) : canvas_height*screen_zoom;
}
if (screen_fullscreen) {
if (desktop_width * window_ratio > desktop_height) {
offset_y = 0;
window_height = desktop_height;
window_width = desktop_height/window_ratio;
offset_x = (desktop_width - window_width)/2;
} else {
offset_x = 0;
window_width = desktop_width;
window_height = desktop_width*window_ratio;
offset_y = (desktop_height - window_height)/2;
}
} else {
offset_x = offset_y = 0;
}
sdl_window = SDL_CreateWindow(window_title, window_width, window_height, SDL_WINDOW_OPENGL|(screen_fullscreen?SDL_WINDOW_FULLSCREEN:0));
if (!sdl_window) { if (!sdl_window) {
SDL_LogCritical(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize window!\n"); SDL_LogCritical(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize window!\n");
exit(1); exit(1);
@@ -94,432 +22,123 @@ namespace draw
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize renderer!\n"); SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize renderer!\n");
exit(1); exit(1);
} }
//SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
if (screen_cursor) SDL_ShowCursor(); else SDL_HideCursor();
sdl_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, canvas_width, canvas_height);
SDL_SetTextureScaleMode(sdl_texture, SDL_SCALEMODE_NEAREST);
if (!sdl_texture) {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize texture!\n");
exit(1);
} }
// sdl_shadertex = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, window_width, window_height);
// SDL_SetTextureScaleMode(sdl_shadertex, SDL_SCALEMODE_NEAREST);
// loadShader();
}
void destroyDisplay()
{
SDL_DestroyTexture(sdl_texture);
SDL_DestroyRenderer(sdl_renderer);
SDL_DestroyWindow(sdl_window);
}
// Inicialització de tot el que fa falta per a carregar gràfics i pintar en pantalla
void init(const char *titol, const uint16_t width, const uint16_t height, const int zoom, const bool fullscreen, const float ratio)
{
screen_zoom = file::getConfigValueInteger("zoom", zoom);
screen_fullscreen = file::getConfigValueBool("fullscreen", fullscreen);
window_ratio = ratio;
canvas_width = width;
canvas_height = height;
strcpy(window_title, titol);
const SDL_DisplayMode *dm = SDL_GetDesktopDisplayMode(SDL_GetPrimaryDisplay());
if (!dm)
{
SDL_Log("SDL_GetDesktopDisplayMode failed: %s", SDL_GetError());
exit(1);
}
desktop_width = dm->w;
desktop_height = dm->h;
createDisplay();
// Inicialització de les estructures de SDL
/*
sdl_window = SDL_CreateWindow(titol, width * zoom, height * zoom, 0);
if (!sdl_window) {
SDL_LogCritical(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize window!\n");
exit(1);
}
sdl_renderer = SDL_CreateRenderer(sdl_window, NULL);
if (!sdl_renderer) {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize renderer!\n");
exit(1);
}
sdl_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height);
SDL_SetTextureScaleMode(sdl_texture, SDL_SCALEMODE_NEAREST);
if (!sdl_texture) {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize texture!\n");
exit(1);
}
*/
// Creem la superficie "screen" i la establim com a superficie destinació
screen = createSurface(width, height);
destination = screen;
viewport.x = viewport.y = 0;
viewport.w = width;
viewport.h = height;
sel_color = transparent = 0;
for (int i=0;i<256;++i) color_indices[i] = i;
//SDL_HideCursor();
//textsurf = loadSurface("font.gif");
}
// Finalització del sistema
void quit() void quit()
{ {
//if (textsurf) freeSurface(textsurf); SDL_DestroyRenderer(sdl_renderer);
SDL_DestroyWindow(sdl_window);
// Si la superficie "screen" existia, alliberem la seua memòria
if (screen != nullptr)
{
freeSurface(screen);
}
// Destruim tot el relacionat amb SDL
destroyDisplay();
// Fiquem tots els punters a nullptr, per si de cas no estem eixint del programa
// i anem a tornar a inicialitzar el sistema
sdl_window = nullptr; sdl_window = nullptr;
sdl_renderer = nullptr; sdl_renderer = nullptr;
sdl_texture = nullptr;
screen = destination = source = nullptr;
} }
void setZoom(const int value) { SDL_Texture *createSurface(const uint16_t w, const uint16_t h)
screen_zoom = value;
destroyDisplay();
createDisplay();
file::setConfigValueInteger("zoom", screen_zoom);
}
const int getZoom()
{ {
return screen_zoom; return SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, w, h);
} }
const float getScaleX() SDL_Texture *loadSurface(const char *filename, const int transparent)
{ {
return float(window_width) / float(canvas_width);
}
const float getScaleY()
{
return float(window_height) / float(canvas_height);
}
const int getOffsetX()
{
return offset_x;
}
const int getOffsetY()
{
return offset_y;
}
bool getFullscreen() {
return screen_fullscreen;
}
void setFullscreen(const bool value) {
screen_fullscreen=value;
destroyDisplay();
createDisplay();
file::setConfigValueBool("fullscreen", screen_fullscreen);
}
void hideCursor()
{
screen_cursor = false;
SDL_HideCursor();
}
void showCursor()
{
screen_cursor = true;
SDL_ShowCursor();
}
// Crea una superficie i torna un punter a ella
surface *createSurface(const uint16_t w, const uint16_t h)
{
// Primer reservem memòria per a la estructura "surface"
surface *surf = (surface *)malloc(sizeof(surface));
// Després reservem memòria per als pixels
surf->pixels = (uint8_t *)malloc(w * h);
// I apuntem el ample i alt de la superficie
surf->w = w;
surf->h = h;
// ...i tornem la superficie creada, clar
return surf;
}
// Carrega un gràfic d'un arxiu (en format GIF) a una nova superficie, i torna un punter a ella
surface *loadSurface(const char *filename, const bool loadPalette)
{
// Agafem un buffer de bytes de l'arxiu especificat
// getFileBuffer() simplement ens torna el arxiu sencer dins de un array de char
int size; int size;
uint8_t *buffer = (uint8_t *)file::getFileBuffer(filename, size); uint8_t *buffer = (uint8_t *)file::getFileBuffer(filename, size);
if (buffer == nullptr) return nullptr;
// Si ens ha tornat nullptr, es que no l'ha trobat, tornem nosaltres també nullptr ja que no s'ha pogut crear la superficie uint16_t w, h;
if (buffer == nullptr) uint8_t *pixels = LoadGif(buffer, &w, &h);
{
return nullptr;
}
// Primer reservem memòria per a la estructura "surface"
surface *surf = (surface *)malloc(sizeof(surface));
// Després li passem el buffer de bytes a la funció de carregar un GIF.
// El resultat es un array de bytes, els pixels en sí. Ja havem reservat
// la memòria necessaria en "LoadGif", així que no tenim que fer-ho ara,
// però, ojo, sí que tindrem que alliberar-la.
surf->pixels = LoadGif(buffer, &surf->w, &surf->h);
// Si li havem dit que carregue també la paleta...
if (loadPalette)
{
// Li passem el array del arxiu a LoadPalette. Ell ens torna un array de uint32_t amb la paleta
// Van a ser com a molt 256 entrades de 32 bits (pero no sempre), cada entrada es un color, amb el format 0xAARRGGBB
int paletteSize; int paletteSize;
uint32_t *pal = LoadPalette(buffer, &paletteSize); uint32_t *pal = LoadPalette(buffer, &paletteSize);
// Copiem eixe array al nostre array de la paleta de sistema. Ara ja tenim la paleta carregada. SDL_Texture *surf = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, w, h);
for (int i=0;i<256;++i) { SDL_SetTextureBlendMode(surf, SDL_BLENDMODE_BLEND);
palette[i] = i<paletteSize ? pal[i] : 0;
} Uint32 *sdl_pixels;
//memset(palette, 0, 1024); // Fiquem tot a 0, que la paleta potser no es de 256 i quedaria basura int sdl_pitch;
//memcpy(palette, pal, paletteSize*4); // 32 bits per entrada == 4 bytes x 'paletteSize' entrades
SDL_LockTexture(surf, NULL, (void **)&sdl_pixels, &sdl_pitch);
for (uint32_t i = 0; i < w*h; ++i) sdl_pixels[i] = transparent==pixels[i] ? 0x00000000 : pal[pixels[i]] | 0xff000000;
SDL_UnlockTexture(surf);
// Alliberem el array que ens habia tornat LoadPalette()
free(pal); free(pal);
} free(pixels);
// Com ja no ens fa falta, alliberem la memòria del buffer del arxiu
free(buffer); free(buffer);
// I finalment tornem la superficie
return surf; return surf;
} }
// Allibera la memòria d'una superficie, els seus pixels inclosos void freeSurface(SDL_Texture *surf)
void freeSurface(surface *surf)
{ {
// Si la superficie existeix... SDL_DestroyTexture(surf);
if (surf != nullptr)
{
// Si el array de pixels existeix, l'alliberem
if (surf->pixels != nullptr)
{
free(surf->pixels);
} }
// ... alliberem la superficie void setDestination(SDL_Texture *surf)
free(surf);
}
}
// Estableix una superficie com a superficie que rebrà les funcions de pintat (especificar nullptr per a pintar a pantalla)
void setDestination(surface *surf)
{ {
// Si han especificat nullptr, fiquem "screen" com a destinació SDL_SetRenderTarget(sdl_renderer, surf);
destination = surf == nullptr ? screen : surf;
resetViewport();
} }
// Estableix una superficie com a superficie de la que s'agafaràn els gràfics per a pintar void setSource(SDL_Texture *surf)
void setSource(surface *surf)
{ {
// Si han especificat nullptr, fiquem "screen" com a font sdl_source = surf;
source = surf == nullptr ? screen : surf;
} }
void pushSource()
void setClip(const int x, const int y, const int w, const int h)
{ {
pushed_source = source; SDL_Rect rect {x, y, w, h};
SDL_SetRenderClipRect(sdl_renderer, &rect);
} }
void popSource() void resetClip()
{ {
source = pushed_source; SDL_SetRenderClipRect(sdl_renderer, nullptr);
} }
void setViewport(const int x, const int y, const int w, const int h) SDL_Point getWindowSize()
{ {
viewport.x = x>0?x:0; SDL_Point p;
viewport.y = y>0?y:0; SDL_GetWindowSize(sdl_window, &p.x, &p.y);
viewport.w = w+x<destination->w?w:destination->w;
viewport.h = h+y<destination->h?h:destination->h;
}
void resetViewport()
{
viewport.x = viewport.y = 0;
viewport.w = destination->w;
viewport.h = destination->h;
}
const int getLocalX(const int x)
{
return x - viewport.x;
}
const int getLocalY(const int y)
{
return y - viewport.y;
}
// Carrega la paleta d'un GIF i la torna en un array de uint32_t
uint32_t *loadPalette(const char *filename, int *paletteSize)
{
// Agafem un buffer de bytes de l'arxiu especificat
// getFileBuffer() simplement ens torna el arxiu sencer dins de un array de char
int size;
uint8_t *buffer = (uint8_t *)file::getFileBuffer(filename, size);
// Li passem el array del arxiu a LoadPalette. Ell ens torna un array de uint32_t amb la paleta
// Van a ser 256 entrades de 32 bits, cada entrada es un color, amb el format 0xAARRGGBB
uint32_t *pal = LoadPalette(buffer);
// I també el buffer del arxiu
free(buffer);
if (paletteSize) *paletteSize = size;
return pal;
}
// Estableix la paleta del sistema, o part de ella, des d'un array especificat
void setPalette(const uint32_t *pal, const int len, const int pos)
{
for (int i=0; i<len; ++i)
{
palette[i+pos] = pal[i];
}
}
//Recupera la paleta del sistema, o part de ella, a un array
uint32_t *getPalette()
{
uint32_t *p = (uint32_t*)malloc(256*sizeof(uint32_t));
for (int i=0; i<256; ++i) p[i] = palette[i];
return p; return p;
} }
// Estableix una entrada de la paleta del sistema void setColor(const uint32_t col)
void setPaletteEntry(const uint8_t index, const uint8_t r, const uint8_t g, const uint8_t b)
{ {
palette[index] = (r << 16) + (g << 8) + b; SDL_SetRenderDrawColor(sdl_renderer, (col>>16)&0xff, (col>>8)&0xff, (col)&0xff, (col>>24)&0xff);
} }
// Esborra la superficie "destination" amb el color especificat void cls(const uint32_t color)
void cls(const uint8_t color)
{ {
// El tamany es width x height bytes setColor(color);
const int size = destination->w * destination->h; SDL_RenderClear(sdl_renderer);
// Omplim la memòria dels pixels de la superficie de destinació amb "color"
memset(destination->pixels, color, size);
} }
//Estableix el color especificat com a transparent void line(const int x1, const int y1, const int x2, const int y2)
void setTrans(const uint8_t color)
{ {
transparent = color; SDL_RenderLine(sdl_renderer, x1, y1, x2, y2);
} }
// Funció interna per a pintar un pixel d'una superficie sense eixir-se'n de la memòria i petar el mame void fillrect(const int x, const int y, const int w, const int h)
void pset(surface *surface, const int x, const int y, const uint8_t color)
{ {
// Si el color es transparent, eixim, ni ens molestem en mirar res més SDL_FRect rect {float(x), float(y), float(w),float(h)};
if (color == transparent) return; SDL_RenderFillRect(sdl_renderer, &rect);
// Si pintem a "destination", mirem que estiga dins del "viewport" i sinó fora
if (surface == destination) {
if (x+viewport.x >= 0 && y+viewport.y >= 0 && x < viewport.w && y < viewport.h)
surface->pixels[(viewport.x+x) + (y+viewport.y) * surface->w] = color_indices[color];
} else {
// Si no es destinations, mirem que estiga dins de la surface, i sinó fora!
if (x >= 0 && y >= 0 && x < surface->w && y < surface->h)
surface->pixels[x + y * surface->w] = color_indices[color];
}
} }
// Funció interna per a llegir un pixel d'una superficie eixir-se'n de la memòria i petar el mame void rect(const int x, const int y, const int w, const int h)
const uint8_t pget(surface *surface, const int x, const int y)
{ {
// Si estem llegint de "destination", mirar que estigam llegint dins del viewport SDL_FRect rect {float(x), float(y), float(w),float(h)};
if (surface == destination) { SDL_RenderRect(sdl_renderer, &rect);
if (x+viewport.x >= 0 && y+viewport.y >= 0 && x < viewport.w && y < viewport.h)
return surface->pixels[(viewport.x + x) + (viewport.y + y) * surface->w];
} else {
// Si no es "destination", si la coordenada està dins del rang que abarca la superficie,
if (x >= 0 && y >= 0 && x < surface->w && y < surface->h)
return surface->pixels[x + y * surface->w];
} }
return 0; void draw(const SDL_Rect source, const SDL_Rect dest, const SDL_FlipMode flip)
}
void putPixel(const int x, const int y, const uint8_t color)
{ {
pset(screen, x, y, color); if (!sdl_source) return;
} SDL_FRect src {float(source.x), float(source.y), float(source.w),float(source.h)};
SDL_FRect dst {float(dest.x), float(dest.y), float(dest.w),float(dest.h)};
// Pinta un troç de la superficie "source" en la superficie "destination". SDL_RenderTextureRotated(sdl_renderer, sdl_source, &src, &dst, 0.0, nullptr, flip);
void draw(const int dx, const int dy, const int w, const int h, const int sx, const int sy, const draw::flip flip)
{
// Si no hi ha superficie d'oritge especificada, no fem res, o petarà el mame
if (source == nullptr)
{
return;
}
// En principi, el quadrat de l'oritge començarà en (sx,sy) i avançarem 1 pixel en positiu tant en x com en y
int sdx = 1, sdy = 1, ssx = sx, ssy = sy;
// Però si s'ha especificat que fem flip en horitzontal...
if (flip & draw::flip::horizontal)
{
sdx = -1; // Avançarem 1 pixel en negatiu
ssx = sx + w - 1; // I començarem al final, o siga, sumarem a sx el ample
}
// De igual forma per al flip en vertical, per a la y
if (flip & draw::flip::vertical)
{
sdy = -1;
ssy = sy + h - 1;
}
// guardem la coordenada d'oritge en x per a restablir-la a cada linea
int csx = ssx;
// Anem linea per linea. Les variables dels dos bucles for controlen les coordenades en la destinació, que sempre van avant.
for (int y = dy; y < dy + h; ++y)
{
ssx = csx; // fiquem la coordenada de l'oritge al principi
// en cada linea, anem pixel a pixel
for (int x = dx; x < dx + w; ++x)
{
pset(destination, x, y, pget(source, ssx, ssy)); // Agafem pixel de l'oritge i el fiquem en la destinació
ssx += sdx; // avancem (o retrocedim) la coordenada x de l'oritge
}
ssy += sdy; // avancem (o retrocedim) la coordenada y de l'oritge
}
} }
/*
void draw(const int dx, const int dy, const int w, const int h, const int sx, const int sy, const int zoom) void draw(const int dx, const int dy, const int w, const int h, const int sx, const int sy, const int zoom)
{ {
// Si no hi ha superficie d'oritge especificada, no fem res, o petarà el mame // Si no hi ha superficie d'oritge especificada, no fem res, o petarà el mame
@@ -549,137 +168,16 @@ namespace draw
} }
// Carrega la superficie especificada en "source" i la pinta tota en la superficie "destination", posició (0,0). // Carrega la superficie especificada en "source" i la pinta tota en la superficie "destination", posició (0,0).
void draw(draw::surface* surf) void draw(SDL_Texture* surf)
{ {
setSource(surf); setSource(surf);
draw(); draw();
} }
*/
void swapcol(const Uint8 c1, const Uint8 c2)
{
color_indices[c1] = c2;
}
void restorecol(const Uint8 c)
{
color_indices[c] = c;
}
void color(const Uint8 col)
{
sel_color = col;
}
void hline(const int x, const int y, const int w)
{
for (int i=x;i<x+w;++i) pset(destination, i, y, sel_color);
}
void vline(const int x, const int y, const int h)
{
for (int i=y;i<y+h;++i) pset(destination, x, i, sel_color);
}
void fillrect(const int x, const int y, const int w, const int h)
{
for (int j=y;j<y+h;++j) for (int i=x;i<x+w;++i) pset(destination, i, j, sel_color);
}
void rect(const int x, const int y, const int w, const int h)
{
hline(x,y,w);
hline(x,y+h-1,w);
vline(x,y,h);
vline(x+w-1,y,h);
}
bool decPalEntry(const uint8_t index, const uint8_t val)
{
const uint32_t entry = palette[index];
uint8_t r = (entry >> 16) & 0xff;
uint8_t g = (entry >> 8) & 0xff;
uint8_t b = entry & 0xff;
r = r>=val ? r-val : 0;
g = g>=val ? g-val : 0;
b = b>=val ? b-val : 0;
palette[index] = (r << 16) + (g << 8) + b;
return palette[index] != 0;
}
bool incPalEntry(const uint8_t index, const uint8_t val)
{
const uint32_t entry = palette[index];
uint8_t r = (entry >> 16) & 0xff;
uint8_t g = (entry >> 8) & 0xff;
uint8_t b = entry & 0xff;
const uint32_t dest_entry = aux_palette[index];
const uint8_t dr = (dest_entry >> 16) & 0xff;
const uint8_t dg = (dest_entry >> 8) & 0xff;
const uint8_t db = dest_entry & 0xff;
r = (r+val > dr) ? dr : r+val;
g = (g+val > dg) ? dg : g+val;
b = (b+val > db) ? db : b+val;
palette[index] = (r << 16) + (g << 8) + b;
return palette[index] != aux_palette[index];
}
void fadein()
{
if (!fading_in) {
for (int i=0;i<256;++i) {
aux_palette[i] = palette[i];
palette[i] = 0;
}
}
fading_in = false;
for (int i=0; i<256; ++i) if (incPalEntry(i, 8)) fading_in = true;
}
void fadeout()
{
fading_out = false;
for (int i=0; i<256; ++i) if (decPalEntry(i, 8)) fading_out = true;
}
bool isfading()
{
return fading_in || fading_out;
}
// Refresca la pantalla // Refresca la pantalla
void render() void render()
{ {
Uint32 *sdl_pixels; // Punter al array de pixels que enstornarà SDL_LockTexture
int sdl_pitch; // Ací estarà guardat el pitch de la textura, com es de 32 bits, no m'afecta
const uint32_t size = screen->w * screen->h; // tamany de la superficie
if (fading_in) fadein();
if (fading_out) fadeout();
// Bloquejem la textura SDL i agafem els seus pixels (son enters de 32 bits amb format 0xAARRGGBB)
SDL_LockTexture(sdl_texture, NULL, (void **)&sdl_pixels, &sdl_pitch);
// Cada pixel de la superficie "screen" es un enter de 8 bits que representa un index en la paleta de colors
// Per tant, per a pintar en la textura SDL, pillem el color de la paleta que correspon al index en "screen"
// i el enviem a la textura SDL
for (uint32_t i = 0; i < size; ++i)
{
sdl_pixels[i] = palette[screen->pixels[i]] | 0xff000000;
}
// Desbloquejem la textura
SDL_UnlockTexture(sdl_texture);
SDL_SetRenderTarget(sdl_renderer, nullptr);
// Pintem la textura a pantalla
SDL_RenderTexture(sdl_renderer, sdl_texture, NULL, NULL);
// I ho presentem
SDL_RenderPresent(sdl_renderer); SDL_RenderPresent(sdl_renderer);
} }

View File

@@ -1,158 +1,36 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <SDL3/SDL.h>
//#define DRAW_FLIP_NONE 0
//#define DRAW_FLIP_HORIZONTAL 1
//#define DRAW_FLIP_VERTICAL 2
//#define DRAW_FLIP_BOTH 3
// Unitat per a la gestió dels recursos gràfics i dibuixat en pantalla
namespace draw namespace draw
{ {
enum flip { none, horizontal, vertical, both }; enum flip { none, horizontal, vertical, both };
// Estructura per a mantindre una superficie de pintat, la "pantalla virtual" de tota la vida void init(const char *titol, const uint16_t width, const uint16_t height);
struct surface
{
uint16_t w; // Ample de la superficie
uint16_t h; // Alt de la superficie
uint8_t *pixels; // pixels de la superficie
};
/// @brief Inicialització de tot el que fa falta per a carregar gràfics i pintar en pantalla.
/// @brief La finestra serà width*zoom x height*zoom de gran.
/// @param titol es el text que apareixerà en la finestra
/// @param width es el ample de la finestra "virtual"
/// @param height es el alt de la finestra "virtual"
/// @param zoom es com de grans son els pixels.
void init(const char *titol, const uint16_t width, const uint16_t height, const int zoom, const bool fullscreen=false, const float ratio=1.0);
/// @brief Finalització del sistema (tancar coses de SDL, superficies fixes, etc...)
void quit(); void quit();
void setZoom(const int value); SDL_Texture *createSurface(const uint16_t w, const uint16_t h);
const int getZoom(); SDL_Texture *loadSurface(const char* filename, const int transparent = -1);
const float getScaleX(); void freeSurface(SDL_Texture *surf);
const float getScaleY(); void setDestination(SDL_Texture *surf);
const int getOffsetX(); void setSource(SDL_Texture *surf);
const int getOffsetY();
bool getFullscreen(); void setClip(const int x, const int y, const int w, const int h);
void setFullscreen(const bool value); void resetClip();
void hideCursor(); SDL_Point getWindowSize();
void showCursor();
/// @brief Crea una superficie i torna un punter a ella void setColor(const uint32_t col);
/// @param w ample de la superficie void cls(const uint32_t color);
/// @param h alt de la superficie void line(const int x1, const int y1, const int x2, const int y2);
/// @return un punter a una nova superficie
surface *createSurface(const uint16_t w, const uint16_t h);
/// @brief Carrega un gràfic d'un arxiu (en format GIF) a una nova superficie, i torna un punter a ella
/// @param filename nom de l'arxiu GIF d'on carregar la superficie
/// @param loadPalette si es true també se carrega la paleta del GIF
/// @return un punter a una nova superficie
surface *loadSurface(const char* filename, const bool loadPalette = false);
/// @brief Allibera la memòria d'una superficie, els seus pixels inclosos
/// @param surf punter a la superficie a alliberar
void freeSurface(surface *surf);
/// @brief Estableix una superficie com a superficie que rebrà les funcions de pintat (especificar nullptr per a pintar a pantalla)
/// @param surf punter a la superficie a establir com a destinació
void setDestination(surface *surf);
/// @brief Estableix una superficie com a superficie de la que s'agafaràn els gràfics per a pintar
/// @param surf punter a la superficie a establir com a oritge
void setSource(surface *surf);
void pushSource();
void popSource();
void setViewport(const int x, const int y, const int w, const int h);
void resetViewport();
const int getLocalX(const int x);
const int getLocalY(const int y);
/// @brief Carrega la paleta d'un GIF i la torna en un array de uint32_t
/// @param filename nom de l'arxiu GIF d'on carregar la paleta
/// @param paletteSize si no es NULL ens torna el tamany de la paleta carregada
uint32_t *loadPalette(const char *filename, int *paletteSize = nullptr);
/// @brief Estableix la paleta del sistema, o part de ella, des d'un array especificat
/// @param pal un array de uint32_t
/// @param len quantes entrades volem trasladar a la paleta de sistema (no superar el tamany de 'pal'!)
/// @param pos des de quina posició de la paleta de sistema comencem a copiar
void setPalette(const uint32_t *pal, const int len, const int pos=0);
/// @brief Recupera la paleta del sistema, o part de ella, a un array
/// @return un array de uint32_t
uint32_t *getPalette();
/// @brief Estableix una entrada de la paleta del sistema
/// @param index l'index de l'entrada de la paleta
/// @param r la component roja de l'entrada de la paleta
/// @param g la component verda de l'entrada de la paleta
/// @param b la component blava de l'entrada de la paleta
void setPaletteEntry(const uint8_t index, const uint8_t r, const uint8_t g, const uint8_t b);
/// @brief Esborra la superficie "destination" amb el color especificat
/// @param color color a usar per a borrar la superficie de destinació
void cls(const uint8_t color);
/// @brief Estableix el color especificat com a transparent
/// @param color color a usar com a transparent
void setTrans(const uint8_t color);
void putPixel(const int x, const int y, const uint8_t color);
/// @brief Pinta un troç de la superficie "source" en la superficie "destination".
/// @param dx coordenada x de la destinació
/// @param dy coordenada y de la destinació
/// @param w ample del quadrat a pintar
/// @param h alt del quadrat a pintar
/// @param sx coordenada x de l'oritge
/// @param sy coordenada y de l'oritge
/// @param flip si s'ha de fer flip en hortizontal o vertical (o ambdos)
void draw(const int dx, const int dy, const int w, const int h, const int sx, const int sy, const draw::flip flip = draw::flip::none);
/// @brief Pinta un troç de la superficie "source" en la superficie "destination", amb zoom.
/// @param dx coordenada x de la destinació
/// @param dy coordenada y de la destinació
/// @param w ample del quadrat d'oritge a pintar
/// @param h alt del quadrat d'oritge a pintar
/// @param sx coordenada x de l'oritge
/// @param sy coordenada y de l'oritge
/// @param zoom zoom a aplicar al pintar
void draw(const int dx, const int dy, const int w, const int h, const int sx, const int sy, const int zoom);
/// @brief Pinta tota la superficie "source" en la superficie "destination", posició (x,y).
/// @param x coordenada x de la destinació
/// @param y coordenada y de la destinació
void draw(const int x, const int y);
/// @brief Pinta tota la superficie "source" en la superficie "destination", posició (0,0).
void draw();
/// @brief Carrega la superficie especificada en "source" i la pinta tota en la superficie "destination", posició (0,0).
void draw(draw::surface* surf);
void swapcol(const uint8_t c1, const uint8_t c2);
void restorecol(const uint8_t c);
void color(const uint8_t col);
void hline(const int x, const int y, const int w);
void vline(const int x, const int y, const int h);
void fillrect(const int x, const int y, const int w, const int h); void fillrect(const int x, const int y, const int w, const int h);
void rect(const int x, const int y, const int w, const int h); void rect(const int x, const int y, const int w, const int h);
void fadein(); void draw(const SDL_Rect source, const SDL_Rect dest, const SDL_FlipMode flip = SDL_FLIP_NONE);
void fadeout(); //void draw(const int x, const int y);
bool isfading(); //void draw();
//void draw(SDL_Texture* surf);
//void print(const char* text, const int x, const int y, const uint8_t color, const uint8_t borde);
/// @brief Refresca la pantalla
void render(); void render();
} }

73
source/japi/font.cpp Normal file
View File

@@ -0,0 +1,73 @@
#include "font.h"
#include "draw.h"
#include "file.h"
#include <stdlib.h>
namespace font
{
SDL_Texture *surf {nullptr};
char spaces[127];
int pos = 30;
int chars_per_line = 0;
void load(const char* filename)
{
char font_filename[256];
strcpy(font_filename, filename);
strcat(font_filename, ".txt");
int filesize;
char *buffer = file::getFileBuffer(font_filename, filesize, true);
char *p = buffer;
pos = 30;
bool eof = false;
while (true) {
if (*p=='#') while (*p!=0 && *p!=10) p++;
if (*p==0) break;
p++;
spaces[pos++] = (*p)-48;
if (pos==127) break;
p++;
if (*p==0) break;
p++;
}
free(buffer);
char gif_filename[256];
strcpy(gif_filename, filename);
strcat(gif_filename, ".gif");
surf = draw::loadSurface(gif_filename, 0);
}
void printChar(const char c, int x, int y)
{
const int w = spaces[30];
const int h = spaces[31];
const int chars_per_line = (surf->w/w);
const int sx = ((c-32) % chars_per_line)*8;
const int sy = ((c-32) / chars_per_line)*8;
//printf("char %i, sx:%i, sy:%i\n", c, sx, sy);
draw::draw({sx, sy, spaces[c], h}, {x, y, spaces[c], h});
}
void print(const char* text, int x, int y)
{
draw::setSource(surf);
while(*text) {
printChar(*text, x, y);
x += spaces[*text]+1;
text++;
}
}
const int len(const char *text)
{
int l = 0;
while (*text) {
l += spaces[*text]+1;
text++;
}
return l-1;
}
}

8
source/japi/font.h Normal file
View File

@@ -0,0 +1,8 @@
#pragma once
namespace font
{
void load(const char* filename);
void print(const char* text, int x, int y);
const int len(const char *text);
}

View File

@@ -84,19 +84,19 @@ int main(int argc, char *argv[])
} }
if (e.type==SDL_EVENT_KEY_UP) if (e.type==SDL_EVENT_KEY_UP)
{ {
switch (e.key.scancode) { // switch (e.key.scancode) {
case SDL_SCANCODE_F1: // case SDL_SCANCODE_F1:
draw::setZoom(draw::getZoom()-1); // draw::setZoom(draw::getZoom()-1);
break; // break;
case SDL_SCANCODE_F2: // case SDL_SCANCODE_F2:
draw::setZoom(draw::getZoom()+1); // draw::setZoom(draw::getZoom()+1);
break; // break;
case SDL_SCANCODE_F3: // case SDL_SCANCODE_F3:
draw::setFullscreen(!draw::getFullscreen()); // draw::setFullscreen(!draw::getFullscreen());
break; // break;
default: // default:
input::updateKeypressed(e.key.scancode); input::updateKeypressed(e.key.scancode);
} // }
} }
if (e.type==SDL_EVENT_MOUSE_BUTTON_UP) if (e.type==SDL_EVENT_MOUSE_BUTTON_UP)
{ {

View File

@@ -1,6 +1,5 @@
#include "input.h" #include "input.h"
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include "draw.h"
namespace input namespace input
{ {
@@ -79,7 +78,7 @@ namespace input
{ {
float x; float x;
SDL_GetMouseState(&x, NULL); SDL_GetMouseState(&x, NULL);
return (x-draw::getOffsetX())/draw::getScaleX(); return x;
} }
// Torna la posició Y actual del ratolí // Torna la posició Y actual del ratolí
@@ -87,7 +86,7 @@ namespace input
{ {
float y; float y;
SDL_GetMouseState(NULL, &y); SDL_GetMouseState(NULL, &y);
return (y-draw::getOffsetY())/draw::getScaleY(); return y;
} }
// Determina si el botó del ratolí especificat està sent polsada ara mateix // Determina si el botó del ratolí especificat està sent polsada ara mateix
@@ -103,6 +102,11 @@ namespace input
return btnClicked == btn; return btnClicked == btn;
} }
void mouseDiscard()
{
btnClicked = 0;
}
// Obté el valor actual de la rodeta del ratolí // Obté el valor actual de la rodeta del ratolí
const int mouseWheel() const int mouseWheel()
{ {

View File

@@ -80,6 +80,8 @@ namespace input
/// @return true si està polsat, false si no /// @return true si està polsat, false si no
const bool mouseClk(const int btn); const bool mouseClk(const int btn);
void mouseDiscard();
/// @brief Obté el valor actual de la rodeta del ratolí /// @brief Obté el valor actual de la rodeta del ratolí
/// @return 0 si no es mou, positiu si roda cap amunt, negatiu si roda cap avall /// @return 0 si no es mou, positiu si roda cap amunt, negatiu si roda cap avall
const int mouseWheel(); const int mouseWheel();

View File

@@ -1,31 +1,45 @@
#include "rooms.h"
#include "enemies.h"
#include "japi/draw.h" #include "japi/draw.h"
#include "japi/game.h" #include "japi/game.h"
#include "images.h" #include "japi/font.h"
#include "menu.h"
bool loop(); bool loop();
void game::init() void game::init()
{ {
draw::init("DILEMMAKER v0.1", 320, 240, 2, false); draw::init("DILEMMAKER v0.1", 800, 600);
game::setState(loop); game::setState(loop);
enemies::load(); font::load("font/8bithud");
rooms::load();
images::loadPalette("./data/palette/zx-spectrum.pal");
} }
bool loop() bool loop()
{ {
draw::cls(4); draw::cls(0x00000000);
draw::setTrans(255);
//rooms::drawFullMap();
rooms::draw(); menu::start();
draw::color(COLOR_BRIGHT_BLACK); if (menu::option("FILE")) {
/*for (int i=0; i<=32; ++i) draw::vline(64+i*16, 48, 256); menu::popup::start();
for (int i=0; i<=16; ++i) draw::hline(64, 48+i*16, 512); menu::popup::end();
*/ }
if (menu::option("EDIT")) {
menu::popup::start();
menu::popup::end();
}
if (menu::option("SELECTION")) {
menu::popup::start();
menu::popup::end();
}
if (menu::option("VIEW")) {
menu::popup::start();
menu::popup::end();
}
/*x1 += 6; x2 = x1 + font::len("FILE")+6;
font::print("FILE", x1, 5); x1=x2;
font::print("EDIT", x, 5); x+= font::len("EDIT")+12;
font::print("SELECTION", x, 5); x+= font::len("SELECTION")+12;
font::print("VIEW", x, 5); x+= font::len("VIEW")+12;*/
draw::render(); draw::render();
return true; return true;
} }

68
source/menu.cpp Normal file
View File

@@ -0,0 +1,68 @@
#include "menu.h"
#include "japi/input.h"
#include "japi/draw.h"
#include "japi/font.h"
namespace menu
{
int x1, x2, m;
int menu_shown = -1;
void start()
{
draw::setColor(0xff3c3c3c);
draw::fillrect(0,0,draw::getWindowSize().x,24);
x1=0; x2=0; m=-1;
}
bool option(const char* label)
{
m++;
x1 = x2 + 8; x2 = x1 + font::len(label)+8;
int mx = input::mouseX();
int my = input::mouseY();
if (mx>=x1 && my>=0 && mx<x2 && my<24) {
draw::setColor(0xff464646);
draw::fillrect(x1-8, 0, x2-x1+8, 24);
if (input::mouseClk(input::mouse::button::left)) {
input::mouseDiscard();
menu_shown = (menu_shown == m) ? -1 : m;
} else {
if (menu_shown != -1) menu_shown = m;
}
}
font::print(label, x1, 9);
return menu_shown==m;
}
namespace popup
{
void start()
{
draw::setColor(0xff3c3c3c);
draw::fillrect(x1-8,24,200,200);
int mx = input::mouseX();
int my = input::mouseY();
if (mx>=x1-8 && my>=24 && mx<x1-8+200 && my<24+200) {
if (input::mouseClk(input::mouse::button::left)) {
input::mouseDiscard();
}
}
}
bool option(const char* label)
{
return false;
}
void end()
{
if (input::mouseClk(input::mouse::button::left)) {
menu_shown = -1;
}
}
}
}

14
source/menu.h Normal file
View File

@@ -0,0 +1,14 @@
#pragma once
namespace menu
{
void start();
bool option(const char* label);
namespace popup
{
void start();
bool option(const char* label);
void end();
}
}

View File

@@ -1,387 +0,0 @@
#include "rooms.h"
#include <iostream>
#include <fstream>
#include <filesystem>
#include <vector>
#include <algorithm>
#include <string>
#include <cctype>
#include <map>
#include "images.h"
#include "enemies.h"
namespace fs = std::filesystem;
namespace rooms
{
int num_total_rooms = 0;
std::string current_room = "";
std::map<std::string, room_t> rooms;
int full_map_x = 0, full_map_y = 0;
draw::surface *map_surface {nullptr};
static const char *paletteMap[] = {
"black", "bright_black", "blue", "bright_blue",
"red", "bright_red", "magenta", "bright_magenta",
"green", "bright_green", "cyan", "bright_cyan",
"yellow", "bright_yellow", "white", "bright_white"
};
const uint8_t colorToNum(std::string value)
{
for (int i=0; i<16; ++i) if (value == paletteMap[i]) return i;
return 0;
}
inline void trim(std::string& s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); }));
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), s.end());
}
void processKeyValue(const std::string& room_name, const std::string& section, const std::string& key, const std::string& value)
{
room_t& room = rooms[room_name];
if (section == "global") {
if (key == "name") {
room.name = value;
} else if (key == "bgColor") {
room.bgColor = colorToNum(value);
} else if (key == "border") {
room.border = colorToNum(value);
//} else if (key == "tileMapFile") {
// room.tileMapFile = value;
} else if (key == "tileSetFile") {
room.tileSetFile = images::getImage("tilesets/"+value);
} else if (key == "roomUp") {
room.roomUp = value.substr(0, value.find_last_of('.'));;
} else if (key == "roomDown") {
room.roomDown = value.substr(0, value.find_last_of('.'));;
} else if (key == "roomLeft") {
room.roomLeft = value.substr(0, value.find_last_of('.'));;
} else if (key == "roomRight") {
room.roomRight = value.substr(0, value.find_last_of('.'));;
} else if (key == "itemColor1") {
room.itemColor1 = colorToNum(value);
} else if (key == "itemColor2") {
room.itemColor2 = colorToNum(value);
}
} else if (section == "enemy") {
if (key == "animation") {
room.enemies.back().animation = value.substr(0, value.find_last_of('.'));
} else if (key == "x") {
room.enemies.back().x = std::stoi(value);
} else if (key == "y") {
room.enemies.back().y = std::stoi(value);
} else if (key == "vx") {
room.enemies.back().vx = std::stof(value);
} else if (key == "vy") {
room.enemies.back().vy = std::stof(value);
} else if (key == "x1") {
room.enemies.back().x1 = std::stoi(value);
} else if (key == "y1") {
room.enemies.back().y1 = std::stoi(value);
} else if (key == "x2") {
room.enemies.back().x2 = std::stoi(value);
} else if (key == "y2") {
room.enemies.back().y2 = std::stoi(value);
} else if (key == "color") {
room.enemies.back().color = colorToNum(value);
} else if (key == "flip") {
room.enemies.back().flip = value=="true" ? true : false;
} else if (key == "mirror") {
room.enemies.back().mirror = value=="true" ? true : false;
}
} else if (section == "item") {
if (key == "tile") {
room.items.back().tile = std::stoi(value);
} else if (key == "x") {
room.items.back().x = std::stoi(value);
} else if (key == "y") {
room.items.back().y = std::stoi(value);
} else if (key == "counter") {
room.items.back().counter = std::stof(value);
}
}
}
void loadRoom(fs::path filename)
{
std::string room_name = filename.stem().string();
std::ifstream file(filename);
if (!file) {
std::cerr << "Failed to open file.\n";
return;
}
std::string current_section = "global";
std::string line;
while (std::getline(file, line))
{
//std::cout << "'" << line << "'\n";
// Trim and clean line, ignore empty lines and comments
trim(line);
if (line.empty() || line[0] == '#') continue;
// Remove inline comments
/*std::size_t comment_pos = line.find('#');
if (comment_pos != std::string::npos) {
line = line.substr(0, comment_pos);
trim(line);
}*/
// Check if entering or exiting a section
if (line[0] == '[')
{
std::size_t closing_pos = line.find(']');
if (closing_pos != std::string::npos && closing_pos > 1)
{
std::string section = line.substr(1,closing_pos-1);
if (section[0] == '/') {
current_section = "global";
} else {
current_section = section;
if (section == "enemy") rooms[room_name].enemies.emplace_back();
else if (section == "item") rooms[room_name] .items.emplace_back();
}
}
}
// Get and process key/value
std::size_t eq_pos = line.find('=');
if (eq_pos != std::string::npos) {
std::string key = line.substr(0, eq_pos);
std::string value = line.substr(eq_pos + 1);
trim(key);
trim(value);
processKeyValue(room_name, current_section, key, value);
}
}
}
void loadTiles(fs::path filename)
{
filename.replace_extension(".tmx");
std::string room_name = filename.stem().string();
std::string line;
bool inData = false;
int row = 0, col = 0;
std::ifstream file(filename);
if (!file) {
std::cerr << "Failed to open file.\n";
return;
}
room_t& room = rooms[room_name];
while (std::getline(file, line)) {
if (!inData) {
if (line.find("<data encoding=\"csv\">") != std::string::npos) inData = true;
continue;
}
if (line.find("</data>") != std::string::npos) break;
std::stringstream ss(line);
std::string value;
while (std::getline(ss, value, ',')) {
if (!value.empty()) {
room.tiles[col][row] = std::stoi(value);
++col;
if (col == 32) {
col = 0;
++row;
}
}
}
}
}
void generateThumbnail(fs::path filename)
{
std::string room_name = filename.stem().string();
room_t &room = rooms[room_name];
// Primer generem un pixel per a cada tile del gif de tiles
draw::surface *tiles = room.tileSetFile;
uint8_t pixels[tiles->w/8][tiles->h/8];
for (int ty=0; ty<tiles->h/8; ty++) {
for (int tx=0; tx<tiles->w/8; tx++) {
uint8_t count[16] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
for (int y=0;y<8;++y)
for (int x=0;x<8;x++) {
count[tiles->pixels[(x+tx*8)+(y+ty*8)*tiles->w]]++;
}
uint8_t max_count=0;
uint8_t max_index=0;
for (int i=0; i<16; ++i) if (count[i] > max_count) {max_count=count[i]; max_index=i;}
pixels[tx][ty] = max_index;
}
}
room.thumbnail = draw::createSurface(32,16);
for (int y=0; y<16; ++y) {
for (int x=0; x<32; ++x) {
uint16_t tile = room.tiles[x][y];
if (tile>0) {
tile--;
room.thumbnail->pixels[x+y*32] = pixels[tile%24][tile/24];
} else {
room.thumbnail->pixels[x+y*32] = room.bgColor!=0 ? room.bgColor : 1;
}
}
}
}
std::map<std::string, bool> visited;
void detectFullMapOffset(std::string room_name, int x, int y)
{
if (visited.find(room_name)!= visited.end()) return;
printf("Visiting '%s'...\n", room_name.c_str());
visited[room_name] = true;
if (full_map_x < x) full_map_x = x;
if (full_map_y < y) full_map_y = y;
room_t &room = rooms[room_name];
room.map_x = x;
room.map_y = y;
if (room.roomUp != "0") detectFullMapOffset(room.roomUp, x, y+1);
if (room.roomLeft != "0") detectFullMapOffset(room.roomLeft, x+1, y);
if (room.roomDown != "0") detectFullMapOffset(room.roomDown, x, y-1);
if (room.roomRight != "0") detectFullMapOffset(room.roomRight, x-1, y);
}
void load()
{
// Get all room files
fs::path target_dir = "./data/room";
std::vector<fs::path> room_files;
try {
for (const auto& entry : fs::directory_iterator(target_dir)) {
if (entry.is_regular_file() && entry.path().extension() == ".room") {
room_files.push_back(entry.path());
}
}
std::sort(room_files.begin(), room_files.end());
for (const auto& path : room_files) {
std::cout << "Processing " << path.filename() << '\n';
loadRoom(path);
loadTiles(path);
generateThumbnail(path);
}
} catch (const fs::filesystem_error& e) {
std::cerr << "Filesystem error: " << e.what() << '\n';
}
visited.clear();
detectFullMapOffset("01", 0, 0);
full_map_x++;
full_map_y++;
if (map_surface) draw::freeSurface(map_surface);
map_surface = draw::createSurface(256,128);
/*for (auto &item : rooms)
{
room_t &room = item.second;
std::cout << room.name << std::endl;
for (int y=0; y<16; ++y) {
for (int x=0; x<32; ++x)
std::cout << std::to_string(room.tiles[x][y]) << ",";
std::cout << std::endl;
}
}*/
/*room_t &room = rooms["01"];
for (int y=0; y<16; ++y) {
for (int x=0; x<32; ++x)
std::cout << std::to_string(room.tiles[x][y]) << ",";
std::cout << std::endl;
}*/
}
void draw()
{
room_t &room = rooms["01"];
draw::setTrans(16);
draw::setDestination(map_surface);
draw::resetViewport();
draw::setSource(room.tileSetFile);
draw::cls(room.bgColor);
for (int y=0; y<16; ++y) {
for (int x=0; x<32; ++x) {
uint16_t tile = room.tiles[x][y];
if (tile>0) {
tile--;
draw::draw(x*8, y*8, 8, 8, (tile%24)*8, (tile/24)*8);
}
}
}
draw::setTrans(0);
for (auto enemy : room.enemies)
{
enemies::enemy_t &anim = enemies::get(enemy.animation);
draw::setSource(anim.tileSetFile);
const int tile = anim.animations[0].frames[0];
draw::swapcol(1, enemy.color);
draw::draw(enemy.x*8, enemy.y*8, anim.frame_width, anim.frame_height, tile*anim.frame_width, 0);
draw::restorecol(1);
}
draw::setTrans(16);
draw::setDestination(nullptr);
draw::setSource(map_surface);
draw::cls(COLOR_BRIGHT_BLACK);
//draw::color(room.border);
//draw::fillrect(48, 32, 512+32, 256+32);
//draw::setViewport(64, 48, 512, 256);
draw::setViewport(32, 24, 256, 128);
draw::draw(0, 0);
draw::resetViewport();
//draw::setSource(room.thumbnail);
//draw::draw();
}
uint8_t blinking = 0;
void drawFullMap()
{
blinking = (blinking+1)%16;
for (auto &item : rooms)
{
auto room = item.second;
int x = ((full_map_x-room.map_x)*38)-1;
int y = ((full_map_y-room.map_y)*22)-1;
draw::resetViewport();
if (item.first == "01") {
draw::color(blinking);
draw::rect(x-1, y-1, 36, 20);
draw::color(COLOR_BLACK);
draw::rect(x, y, 34, 18);
} else {
draw::color(COLOR_WHITE);
draw::rect(x+1, y+1, 34, 18);
draw::color(COLOR_BLACK);
draw::rect(x, y, 34, 18);
}
draw::setSource(room.thumbnail);
draw::draw(x+1, y+1, 32, 16, 0, 0);
if (room.roomUp != "0") draw::fillrect(x+15, y-4, 4, 4);
if (room.roomLeft != "0") draw::fillrect(x-4, y+7, 4, 4);
}
}
}

View File

@@ -1,73 +0,0 @@
#pragma once
#include <stdint.h>
#include <string>
#include <vector>
#include "japi/draw.h"
#define COLOR_BLACK 0
#define COLOR_BRIGHT_BLACK 1
#define COLOR_BLUE 2
#define COLOR_BRIGHT_BLUE 3
#define COLOR_RED 4
#define COLOR_BRIGHT_RED 5
#define COLOR_MAGENTA 6
#define COLOR_BRIGHT_MAGENTA 7
#define COLOR_GREEN 8
#define COLOR_BRIGHT_GREEN 9
#define COLOR_CYAN 10
#define COLOR_BRIGHT_CYAN 11
#define COLOR_YELLOW 12
#define COLOR_BRIGHT_YELLOW 13
#define COLOR_WHITE 14
#define COLOR_BRIGHT_WHITE 15
namespace rooms
{
struct enemy_t
{
std::string animation {""};
uint8_t x {0};
uint8_t y {0};
float vx {0.0f};
float vy {0.0f};
uint8_t x1 {0};
uint8_t y1 {0};
uint8_t x2 {0};
uint8_t y2 {0};
uint8_t color {COLOR_WHITE};
bool flip {false};
bool mirror {false};
};
struct item_t
{
uint8_t tile {0};
uint8_t x {0};
uint8_t y {0};
float counter {0.0f};
};
struct room_t
{
std::string name {""};
uint8_t bgColor {COLOR_BLACK};
uint8_t border {COLOR_BLACK};
draw::surface *tileSetFile {nullptr};
std::string roomUp {0};
std::string roomDown {0};
std::string roomLeft {0};
std::string roomRight {0};
uint8_t itemColor1 {0};
uint8_t itemColor2 {0};
std::vector<enemy_t> enemies;
std::vector<item_t> items;
uint16_t tiles[32][16];
draw::surface *thumbnail {nullptr};
int map_x {0};
int map_y {0};
};
void load();
void draw();
void drawFullMap();
}