388 lines
13 KiB
C++
388 lines
13 KiB
C++
#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);
|
|
}
|
|
}
|
|
|
|
}
|