2021-04-25 20:42:55 +00:00
|
|
|
#include "maploader.hpp"
|
|
|
|
#include "../sdlpp/sdlpp_rectrenderer.hpp"
|
2021-05-07 06:35:01 +00:00
|
|
|
#include <array>
|
2021-04-25 20:42:55 +00:00
|
|
|
#include <fstream>
|
|
|
|
#include "sprites.hpp"
|
|
|
|
#include "blocks.hpp"
|
|
|
|
#include "objectids.hpp"
|
2021-05-25 22:46:19 +00:00
|
|
|
#include "global_vars.hpp"
|
2022-06-21 12:52:36 +00:00
|
|
|
#include "filesystem.hpp"
|
2021-04-25 20:42:55 +00:00
|
|
|
|
2021-06-07 21:48:28 +00:00
|
|
|
#define TERRAIN_TYPE_HAS_ADDITIONAL 0x8
|
|
|
|
#define WIDE_TERRAIN_HAS_ADDITIONAL 0x8000
|
|
|
|
#define ADDITIONAL_IS_MOD 0x80
|
2022-06-21 06:23:36 +00:00
|
|
|
#define FILE_VERSION 0x01
|
|
|
|
|
|
|
|
void loadMapV01(std::shared_ptr<SDLPP::Scene> &scene,
|
|
|
|
std::shared_ptr<SDLPP::RenderObject> &mario,
|
|
|
|
std::ifstream &map_file, std::vector<mapColumnType> &objects,
|
|
|
|
bool editor, size_t editor_width,
|
|
|
|
std::shared_ptr<SDLPP::Renderer> &renderer);
|
2021-06-07 21:48:28 +00:00
|
|
|
|
2021-10-18 07:08:35 +00:00
|
|
|
void loadMap(std::shared_ptr<SDLPP::Scene> &scene,
|
|
|
|
std::shared_ptr<SDLPP::RenderObject> mario,
|
|
|
|
const std::string &file) {
|
|
|
|
std::vector<mapColumnType> tmp = {};
|
|
|
|
loadMap(scene, mario, file, tmp, false);
|
|
|
|
scene->moveZTop(mario);
|
2021-06-07 21:48:28 +00:00
|
|
|
}
|
|
|
|
|
2021-10-18 07:08:35 +00:00
|
|
|
uint8_t read8Bits(std::ifstream &file) {
|
2021-06-07 21:48:28 +00:00
|
|
|
uint8_t data;
|
2021-10-18 07:08:35 +00:00
|
|
|
file.read((char *)&data, sizeof(uint8_t) / sizeof(char));
|
2021-06-07 21:48:28 +00:00
|
|
|
return data;
|
|
|
|
}
|
2021-10-18 07:08:35 +00:00
|
|
|
void write8Bits(std::ofstream &file, uint8_t data) {
|
|
|
|
file.write((char *)&data, sizeof(uint8_t) / sizeof(char));
|
2021-06-07 21:48:28 +00:00
|
|
|
}
|
|
|
|
|
2021-10-18 07:08:35 +00:00
|
|
|
uint16_t read16Bits(std::ifstream &file) {
|
2021-06-07 21:48:28 +00:00
|
|
|
uint16_t data;
|
2021-10-18 07:08:35 +00:00
|
|
|
file.read((char *)&data, sizeof(uint16_t) / sizeof(char));
|
2021-06-07 21:48:28 +00:00
|
|
|
return data;
|
|
|
|
}
|
2021-10-18 07:08:35 +00:00
|
|
|
void write16Bits(std::ofstream &file, uint16_t data) {
|
|
|
|
file.write((char *)&data, sizeof(uint16_t) / sizeof(char));
|
2021-06-07 21:48:28 +00:00
|
|
|
}
|
|
|
|
|
2021-10-18 07:08:35 +00:00
|
|
|
std::pair<uint16_t, uint8_t> separateWideTerrain(uint16_t wide_terrain) {
|
|
|
|
uint8_t terrain_type = (wide_terrain & 0xF000) >> 12;
|
|
|
|
uint16_t terrain_id = (wide_terrain & 0x0FFF) | BLOCK_PREFIX;
|
2021-06-07 21:48:28 +00:00
|
|
|
return { terrain_id, terrain_type };
|
|
|
|
}
|
|
|
|
|
2021-10-18 07:08:35 +00:00
|
|
|
std::pair<uint8_t, uint8_t> separateAdditionalData(uint8_t data) {
|
2021-06-07 21:48:28 +00:00
|
|
|
auto id = data & 0x0F;
|
2021-10-18 07:08:35 +00:00
|
|
|
auto type = (data & 0xF0) >> 4;
|
2021-06-07 21:48:28 +00:00
|
|
|
return { id, type };
|
|
|
|
}
|
2022-06-21 06:23:36 +00:00
|
|
|
|
2021-10-18 07:08:35 +00:00
|
|
|
uint16_t combineTerrain(uint16_t id, uint8_t type) {
|
2021-06-07 21:48:28 +00:00
|
|
|
uint16_t wide_terrain = type;
|
|
|
|
wide_terrain = wide_terrain << 12;
|
|
|
|
wide_terrain |= 0x0FFF & id;
|
|
|
|
return wide_terrain;
|
|
|
|
}
|
2022-06-21 06:23:36 +00:00
|
|
|
|
2021-10-18 07:08:35 +00:00
|
|
|
uint8_t combineAdditionalData(uint8_t id, uint8_t type) {
|
2021-06-07 21:48:28 +00:00
|
|
|
return type << 4 | id;
|
|
|
|
}
|
|
|
|
|
2021-10-18 07:08:35 +00:00
|
|
|
MapObject parseBlock(std::ifstream &map_file) {
|
2021-08-08 19:36:47 +00:00
|
|
|
uint8_t character_type = 0;
|
|
|
|
uint8_t character_id = 0;
|
|
|
|
uint32_t modifier_id = 0;
|
|
|
|
uint8_t modifier_data = 0;
|
2021-10-18 07:08:35 +00:00
|
|
|
uint16_t wide_terrain = read16Bits(map_file);
|
|
|
|
auto terrain = separateWideTerrain(wide_terrain);
|
2021-06-07 21:48:28 +00:00
|
|
|
uint16_t terrain_id = terrain.first;
|
|
|
|
uint8_t terrain_type = terrain.second;
|
2021-10-18 07:08:35 +00:00
|
|
|
if (terrain_type & TERRAIN_TYPE_HAS_ADDITIONAL) {
|
|
|
|
uint8_t additional_data = read8Bits(map_file);
|
2021-06-07 21:48:28 +00:00
|
|
|
terrain_type &= ~TERRAIN_TYPE_HAS_ADDITIONAL;
|
2021-10-18 07:08:35 +00:00
|
|
|
if (additional_data & ADDITIONAL_IS_MOD) {
|
2021-06-07 21:48:28 +00:00
|
|
|
additional_data &= ~ADDITIONAL_IS_MOD;
|
2021-10-18 07:08:35 +00:00
|
|
|
auto modifier = separateAdditionalData(additional_data);
|
2022-06-21 06:23:36 +00:00
|
|
|
modifier_id = modifier.first | 0x6000;
|
|
|
|
modifier_data = modifier.second;
|
2021-06-07 21:48:28 +00:00
|
|
|
} else {
|
|
|
|
// character
|
2021-10-18 07:08:35 +00:00
|
|
|
auto character = separateAdditionalData(additional_data);
|
2021-06-07 21:48:28 +00:00
|
|
|
character_id = character.first;
|
|
|
|
character_type = character.second;
|
|
|
|
}
|
|
|
|
}
|
2021-10-18 07:08:35 +00:00
|
|
|
return MapObject(terrain_id, terrain_type, character_id, character_type,
|
|
|
|
modifier_id, modifier_data);
|
2021-04-30 21:12:53 +00:00
|
|
|
}
|
|
|
|
|
2021-05-02 12:14:11 +00:00
|
|
|
// editor loader
|
2022-06-21 06:23:36 +00:00
|
|
|
// TODO catch exception in calling functions
|
2021-10-18 07:08:35 +00:00
|
|
|
void loadMap(std::shared_ptr<SDLPP::Scene> &scene,
|
|
|
|
std::shared_ptr<SDLPP::RenderObject> &mario,
|
|
|
|
const std::string &file, std::vector<mapColumnType> &objects,
|
|
|
|
bool editor, size_t editor_width) {
|
2022-06-21 12:52:36 +00:00
|
|
|
if(!FSLib::exists(file)) {
|
|
|
|
// create empty array large enough for initial editor window
|
2022-06-21 06:23:36 +00:00
|
|
|
objects.resize(editor_width);
|
|
|
|
return;
|
|
|
|
}
|
2022-06-21 12:52:36 +00:00
|
|
|
auto renderer = scene->getRendererShared();
|
|
|
|
std::ifstream map_file;
|
|
|
|
map_file.open(file, std::ios::in | std::ios::binary);
|
2022-06-21 06:23:36 +00:00
|
|
|
uint16_t version;
|
|
|
|
map_file.read((char *)&version, sizeof(uint16_t) / sizeof(char));
|
|
|
|
switch(version) {
|
|
|
|
case 0x01:
|
|
|
|
loadMapV01(scene, mario, map_file, objects, editor, editor_width, renderer);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw "Invalid file version";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void loadMapV01(std::shared_ptr<SDLPP::Scene> &scene,
|
|
|
|
std::shared_ptr<SDLPP::RenderObject> &mario,
|
|
|
|
std::ifstream &map_file, std::vector<mapColumnType> &objects,
|
|
|
|
bool editor, size_t editor_width,
|
|
|
|
std::shared_ptr<SDLPP::Renderer> &renderer) {
|
2021-04-30 21:12:53 +00:00
|
|
|
uint16_t cols;
|
2021-10-18 07:08:35 +00:00
|
|
|
map_file.read((char *)&cols, sizeof(uint16_t) / sizeof(char));
|
|
|
|
if (editor) {
|
|
|
|
objects.resize(cols);
|
2021-05-31 16:54:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
mapColumnType *col = nullptr;
|
2021-10-18 07:08:35 +00:00
|
|
|
for (uint16_t i = 0; i < cols; i++) {
|
|
|
|
if (editor) {
|
2021-05-31 16:54:43 +00:00
|
|
|
col = &objects[i];
|
|
|
|
}
|
2021-10-18 07:08:35 +00:00
|
|
|
for (int j = 0; j < 16; j++) {
|
|
|
|
auto block = parseBlock(map_file);
|
|
|
|
if (editor) {
|
|
|
|
col->at(j) = block;
|
2021-05-31 16:54:43 +00:00
|
|
|
}
|
|
|
|
bool destructible = false;
|
2021-06-21 17:40:29 +00:00
|
|
|
bool removeCollisions = false;
|
2021-07-22 22:08:05 +00:00
|
|
|
int coinCount = 0;
|
2021-08-07 19:41:15 +00:00
|
|
|
bool mushroom = false;
|
2021-10-18 07:08:35 +00:00
|
|
|
if (!editor && block.getModifierId() == DESTRUCTIBLE_MODIFIER_ID) {
|
2021-05-31 16:54:43 +00:00
|
|
|
destructible = true;
|
|
|
|
}
|
2021-10-18 07:08:35 +00:00
|
|
|
if (!editor && block.getModifierId() == BACKGROUND_MODIFIER_ID) {
|
2021-06-21 17:40:29 +00:00
|
|
|
destructible = false;
|
|
|
|
removeCollisions = true;
|
|
|
|
}
|
2021-10-18 07:08:35 +00:00
|
|
|
if (!editor && block.getModifierId() == COIN_MODIFIER_ID) {
|
2021-07-22 22:08:05 +00:00
|
|
|
coinCount = block.getModifierData();
|
|
|
|
}
|
2021-10-18 07:08:35 +00:00
|
|
|
if (!editor && block.getModifierId() == MUSHROOM_MODIFIER_ID) {
|
2021-08-07 19:41:15 +00:00
|
|
|
mushroom = true;
|
|
|
|
}
|
2021-05-01 19:55:43 +00:00
|
|
|
// TODO add modifiers to createTerrainBlock
|
2021-10-18 07:08:35 +00:00
|
|
|
if (block.getTerrainId() != 0) {
|
|
|
|
auto obj = createTerrainBlock(block.getTerrainId(),
|
|
|
|
block.getTerrainType(), renderer,
|
|
|
|
i, j, destructible, editor);
|
|
|
|
if (obj != nullptr) {
|
2021-07-24 17:59:25 +00:00
|
|
|
obj->setCoinCount(coinCount);
|
2021-10-18 07:08:35 +00:00
|
|
|
if (mushroom) {
|
2021-08-07 19:41:15 +00:00
|
|
|
obj->addMushroom();
|
|
|
|
}
|
2021-10-18 07:08:35 +00:00
|
|
|
if (removeCollisions) {
|
2021-07-24 17:59:25 +00:00
|
|
|
obj->removeCollisions();
|
|
|
|
}
|
2021-10-18 07:08:35 +00:00
|
|
|
if (obj != nullptr) {
|
|
|
|
if (editor) {
|
|
|
|
obj->getCollisions()[0]->setId(EDITOR_TERRAIN_ID);
|
2021-07-24 17:59:25 +00:00
|
|
|
}
|
2021-10-18 07:08:35 +00:00
|
|
|
scene->addObject(obj);
|
2021-06-07 21:48:28 +00:00
|
|
|
}
|
2021-05-31 16:54:43 +00:00
|
|
|
}
|
|
|
|
}
|
2021-10-18 07:08:35 +00:00
|
|
|
if (block.hasCharacter()) {
|
|
|
|
if (block.getCharacterId() == MARIO_ID) {
|
|
|
|
if (editor) {
|
|
|
|
scene->addObject(createMario(block.getCharacterType(),
|
|
|
|
renderer, i, j, true));
|
2021-06-07 21:48:28 +00:00
|
|
|
mario =
|
2021-10-18 07:08:35 +00:00
|
|
|
scene->getObject(scene->getObjects().size() - 1);
|
2021-05-31 16:54:43 +00:00
|
|
|
} else {
|
2021-10-18 07:08:35 +00:00
|
|
|
mario->setPos(i * BLOCK_SIZE,
|
|
|
|
1 - (16 - j) * BLOCK_SIZE);
|
2021-05-31 16:54:43 +00:00
|
|
|
}
|
2021-08-08 19:36:47 +00:00
|
|
|
} else {
|
|
|
|
auto obj = createTerrainBlock(
|
2021-10-18 07:08:35 +00:00
|
|
|
block.getCharacterId(), block.getCharacterType(),
|
|
|
|
renderer, i, j, destructible, editor);
|
|
|
|
dynamic_cast<MarioBlock *>(obj.get())->setTerrain(false);
|
|
|
|
if (editor) {
|
|
|
|
obj->getCollisions()[0]->setId(EDITOR_CHARACTER_ID);
|
2021-08-08 19:36:47 +00:00
|
|
|
}
|
|
|
|
scene->addObject(obj);
|
2021-05-01 19:55:43 +00:00
|
|
|
}
|
|
|
|
}
|
2021-10-18 07:08:35 +00:00
|
|
|
if (editor && block.hasModifier()) {
|
2021-06-07 21:48:28 +00:00
|
|
|
// TODO createModifierBlock with data
|
2021-10-18 07:08:35 +00:00
|
|
|
auto mod = createTerrainBlock(block.getModifierId(),
|
|
|
|
LandType::OVERWORLD, renderer, i,
|
|
|
|
j, false, editor);
|
2021-07-22 22:08:05 +00:00
|
|
|
mod->setData(block.getModifierData());
|
|
|
|
mod->setTextureKeepSRC(g_translucent_mod_texture);
|
2021-10-18 07:08:35 +00:00
|
|
|
mod->getCollisions()[0]->setId(EDITOR_TERRAIN_ID);
|
|
|
|
dynamic_cast<MarioBlock *>(mod.get())->setTerrain(false);
|
|
|
|
scene->addObject(mod);
|
2021-05-25 22:46:19 +00:00
|
|
|
}
|
2021-04-30 21:12:53 +00:00
|
|
|
}
|
|
|
|
}
|
2021-10-18 07:08:35 +00:00
|
|
|
if (editor && objects.size() < editor_width) {
|
|
|
|
objects.resize(editor_width);
|
2021-05-07 06:35:01 +00:00
|
|
|
}
|
2022-06-21 06:23:36 +00:00
|
|
|
|
2021-04-30 21:12:53 +00:00
|
|
|
}
|
|
|
|
|
2022-06-21 06:23:36 +00:00
|
|
|
// TODO catch exception in calling func
|
2021-10-18 07:08:35 +00:00
|
|
|
void saveMap(const std::string &file, std::vector<mapColumnType> &objects) {
|
2021-04-30 21:12:53 +00:00
|
|
|
std::ofstream output_file;
|
2021-10-18 07:08:35 +00:00
|
|
|
output_file.open(file, std::ios::out | std::ios::binary);
|
2022-06-21 06:23:36 +00:00
|
|
|
if(!output_file.is_open()) {
|
|
|
|
throw "Could not open file '" + file + "'";
|
|
|
|
}
|
|
|
|
const uint16_t version = FILE_VERSION;
|
|
|
|
output_file.write((char *)&version, sizeof(uint16_t) / sizeof(char));
|
|
|
|
const uint16_t cols = objects.size();
|
2021-10-18 07:08:35 +00:00
|
|
|
output_file.write((char *)&cols, sizeof(uint16_t) / sizeof(char));
|
|
|
|
for (auto &col : objects) {
|
|
|
|
for (int i = 0; i < 16; i++) {
|
2021-06-07 21:48:28 +00:00
|
|
|
auto &block = col[i];
|
2021-10-18 07:08:35 +00:00
|
|
|
auto wide_terrain =
|
|
|
|
combineTerrain(block.getTerrainId(), block.getTerrainType());
|
2021-06-07 21:48:28 +00:00
|
|
|
// if block has additional data it needs to indicate it
|
2021-10-18 07:08:35 +00:00
|
|
|
if (block.hasCharacter() || block.hasModifier()) {
|
2021-06-07 21:48:28 +00:00
|
|
|
wide_terrain |= WIDE_TERRAIN_HAS_ADDITIONAL;
|
2021-05-01 19:55:43 +00:00
|
|
|
}
|
2021-06-07 21:48:28 +00:00
|
|
|
write16Bits(output_file, wide_terrain);
|
2021-05-01 19:55:43 +00:00
|
|
|
uint8_t additional_data = 0;
|
2021-10-18 07:08:35 +00:00
|
|
|
if (block.hasCharacter()) {
|
|
|
|
additional_data = combineAdditionalData(
|
|
|
|
block.getCharacterId(), block.getCharacterType());
|
|
|
|
} else if (block.hasModifier()) {
|
|
|
|
// we have IDs like 0x600X but only X is useful data, the 0x6 at
|
|
|
|
// the beginning is to differentiate mods from characters
|
|
|
|
additional_data = combineAdditionalData(
|
2022-06-21 06:23:36 +00:00
|
|
|
block.getModifierId() & 0x000F, block.getModifierData());
|
2021-06-07 21:48:28 +00:00
|
|
|
additional_data |= ADDITIONAL_IS_MOD;
|
2021-05-01 19:55:43 +00:00
|
|
|
}
|
2021-10-18 07:08:35 +00:00
|
|
|
if (additional_data) {
|
2021-06-07 21:48:28 +00:00
|
|
|
write8Bits(output_file, additional_data);
|
2021-05-01 19:55:43 +00:00
|
|
|
}
|
2021-04-30 21:12:53 +00:00
|
|
|
}
|
2021-04-30 07:10:58 +00:00
|
|
|
}
|
2021-04-30 21:12:53 +00:00
|
|
|
output_file.close();
|
2021-04-25 20:42:55 +00:00
|
|
|
}
|