#include "maploader.hpp" #include "../sdlpp/sdlpp_rectrenderer.hpp" #include #include #include "sprites.hpp" #include "blocks.hpp" #include "objectids.hpp" #include "global_vars.hpp" #define TERRAIN_TYPE_HAS_ADDITIONAL 0x8 #define WIDE_TERRAIN_HAS_ADDITIONAL 0x8000 #define ADDITIONAL_IS_MOD 0x80 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 ); } 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< 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; return { terrain_id, terrain_type }; } std::pair< uint8_t, uint8_t > 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, character_id = 0, modifier_id = 0, 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 ); // TODO swap modifier id and data modifier_id = modifier.second; modifier_data = modifier.first; } 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 ); } // editor loader 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 ) { auto renderer = scene->getRendererShared(); std::ifstream map_file; map_file.open( file, std::ios::in | std::ios::binary ); 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 ) { scene->addObject( createMario( block.getCharacterType(), renderer, i, j, true ) ); mario = scene->getObject( scene->getObjects().size() - 1 ); } else { mario->setPos( i * BLOCK_SIZE, 1 - ( 16 - j ) * BLOCK_SIZE ); } } } 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< MarioBlock * >( mod.get() )->setTerrain( false ); scene->addObject( mod ); } } } if ( editor && objects.size() < editor_width ) { objects.resize( editor_width ); } } void saveMap( const std::string &file, std::vector< mapColumnType > &objects ) { std::ofstream output_file; output_file.open( file, std::ios::out | std::ios::binary ); 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() ) { // TODO seriously change order of id/data!!! additional_data = combineAdditionalData(block.getModifierData(), block.getModifierId()); additional_data |= ADDITIONAL_IS_MOD; } if ( additional_data ) { write8Bits(output_file, additional_data); } } } output_file.close(); }