#include "maploader.hpp" #include "../sdlpp/sdlpp_rectrenderer.hpp" #include #include #include "mario.hpp" #include "sprites.hpp" #include "blocks.hpp" #include "objectids.hpp" #include "global_vars.hpp" #include "filesystem.hpp" #define TERRAIN_TYPE_HAS_ADDITIONAL 0x8 #define WIDE_TERRAIN_HAS_ADDITIONAL 0x8000 #define ADDITIONAL_IS_MOD 0x80 #define FILE_VERSION 0x01 void loadMapV01(std::shared_ptr &scene, std::shared_ptr &mario, std::ifstream &map_file, std::vector &objects, bool editor, size_t editor_width, std::shared_ptr &renderer); void loadMap(std::shared_ptr &scene, std::shared_ptr mario, const std::string &file) { std::vector tmp = {}; loadMap(scene, mario, file, tmp, false); scene->moveZTop(mario); } uint8_t read8Bits(std::ifstream &file) { uint8_t data; file.read((char *)&data, sizeof(uint8_t) / sizeof(char)); return data; } void write8Bits(std::ofstream &file, uint8_t data) { file.write((char *)&data, sizeof(uint8_t) / sizeof(char)); } uint16_t read16Bits(std::ifstream &file) { uint16_t data; file.read((char *)&data, sizeof(uint16_t) / sizeof(char)); return data; } void write16Bits(std::ofstream &file, uint16_t data) { file.write((char *)&data, sizeof(uint16_t) / sizeof(char)); } std::pair separateWideTerrain(uint16_t wide_terrain) { uint8_t terrain_type = (wide_terrain & 0xF000) >> 12; uint16_t terrain_id = (wide_terrain & 0x0FFF) | BLOCK_PREFIX; return { terrain_id, terrain_type }; } std::pair separateAdditionalData(uint8_t data) { auto id = data & 0x0F; auto type = (data & 0xF0) >> 4; return { id, type }; } uint16_t combineTerrain(uint16_t id, uint8_t type) { uint16_t wide_terrain = type; wide_terrain = wide_terrain << 12; wide_terrain |= 0x0FFF & id; return wide_terrain; } uint8_t combineAdditionalData(uint8_t id, uint8_t type) { return type << 4 | id; } MapObject parseBlock(std::ifstream &map_file) { uint8_t character_type = 0; uint8_t character_id = 0; uint32_t modifier_id = 0; uint8_t modifier_data = 0; uint16_t wide_terrain = read16Bits(map_file); auto terrain = separateWideTerrain(wide_terrain); uint16_t terrain_id = terrain.first; uint8_t terrain_type = terrain.second; if (terrain_type & TERRAIN_TYPE_HAS_ADDITIONAL) { uint8_t additional_data = read8Bits(map_file); terrain_type &= ~TERRAIN_TYPE_HAS_ADDITIONAL; if (additional_data & ADDITIONAL_IS_MOD) { additional_data &= ~ADDITIONAL_IS_MOD; auto modifier = separateAdditionalData(additional_data); modifier_id = modifier.first | 0x6000; modifier_data = modifier.second; } else { // character auto character = separateAdditionalData(additional_data); character_id = character.first; character_type = character.second; } } return MapObject(terrain_id, terrain_type, character_id, character_type, modifier_id, modifier_data); } void loadEmptyMap(std::vector &objects, size_t editor_width) { objects.resize(editor_width); } // editor loader // TODO catch exception in calling functions void loadMap(std::shared_ptr &scene, std::shared_ptr &mario, const std::string &file, std::vector &objects, bool editor, size_t editor_width) { if (!FSLib::exists(file)) { // create empty array large enough for initial editor window loadEmptyMap(objects, editor_width); return; } auto renderer = scene->getRendererShared(); std::ifstream map_file; map_file.open(file, std::ios::in | std::ios::binary); 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 &scene, std::shared_ptr &mario, std::ifstream &map_file, std::vector &objects, bool editor, size_t editor_width, std::shared_ptr &renderer) { uint16_t cols; map_file.read((char *)&cols, sizeof(uint16_t) / sizeof(char)); if (editor) { objects.resize(cols); } mapColumnType *col = nullptr; for (uint16_t i = 0; i < cols; i++) { if (editor) { col = &objects[i]; } for (int j = 0; j < 16; j++) { auto block = parseBlock(map_file); if (editor) { col->at(j) = block; } bool destructible = false; bool removeCollisions = false; int coinCount = 0; bool mushroom = false; if (!editor && block.getModifierId() == DESTRUCTIBLE_MODIFIER_ID) { destructible = true; } if (!editor && block.getModifierId() == BACKGROUND_MODIFIER_ID) { destructible = false; removeCollisions = true; } if (!editor && block.getModifierId() == COIN_MODIFIER_ID) { coinCount = block.getModifierData(); } if (!editor && block.getModifierId() == MUSHROOM_MODIFIER_ID) { mushroom = true; } // TODO add modifiers to createTerrainBlock if (block.getTerrainId() != 0) { auto obj = createTerrainBlock(block.getTerrainId(), block.getTerrainType(), renderer, i, j, destructible, editor); if (obj != nullptr) { obj->setCoinCount(coinCount); if (mushroom) { obj->addMushroom(); } if (removeCollisions) { obj->removeCollisions(); } if (obj != nullptr) { if (editor) { obj->getCollisions()[0]->setId(EDITOR_TERRAIN_ID); } scene->addObject(obj); } } } if (block.hasCharacter()) { if (block.getCharacterId() == MARIO_ID) { if (editor) { auto mario2 = createMario(block.getCharacterType(), renderer, i, j, true); scene->addObject(mario2); #ifndef EDITOR std::dynamic_pointer_cast(mario2)->setAddObjFunc(std::dynamic_pointer_cast(mario)->getAddObjFunc()); #endif mario = mario2; } else { mario->setPos(i * BLOCK_SIZE, 1 - (16 - j) * BLOCK_SIZE); } } else { auto obj = createTerrainBlock( block.getCharacterId(), block.getCharacterType(), renderer, i, j, destructible, editor); dynamic_cast(obj.get())->setTerrain(false); if (editor) { obj->getCollisions()[0]->setId(EDITOR_CHARACTER_ID); } scene->addObject(obj); } } if (editor && block.hasModifier()) { // TODO createModifierBlock with data auto mod = createTerrainBlock(block.getModifierId(), LandType::OVERWORLD, renderer, i, j, false, editor); mod->setData(block.getModifierData()); mod->setTextureKeepSRC(g_translucent_mod_texture); mod->getCollisions()[0]->setId(EDITOR_TERRAIN_ID); dynamic_cast(mod.get())->setTerrain(false); scene->addObject(mod); } } } if (editor && objects.size() < editor_width) { objects.resize(editor_width); } } // TODO catch exception in calling func void saveMap(const std::string &file, std::vector &objects) { std::ofstream output_file; output_file.open(file, std::ios::out | std::ios::binary); 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(); output_file.write((char *)&cols, sizeof(uint16_t) / sizeof(char)); for (auto &col : objects) { for (int i = 0; i < 16; i++) { auto &block = col[i]; auto wide_terrain = combineTerrain(block.getTerrainId(), block.getTerrainType()); // if block has additional data it needs to indicate it if (block.hasCharacter() || block.hasModifier()) { wide_terrain |= WIDE_TERRAIN_HAS_ADDITIONAL; } write16Bits(output_file, wide_terrain); uint8_t additional_data = 0; 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( block.getModifierId() & 0x000F, block.getModifierData()); additional_data |= ADDITIONAL_IS_MOD; } if (additional_data) { write8Bits(output_file, additional_data); } } } output_file.close(); }