/* * ===================================================================================== * * Filename: EntityConverter.cpp * * Description: Convert Reflex entities into Xonotic entities * * Version: 0.1 * Created: 06/05/2017 07:15:25 PM * Revision: none * Compiler: gcc * * Author: suhrke@teknik.io * * ===================================================================================== */ #include "EntityConverter.hpp" #include #include #include #include EntityConverter::EntityConverter(std::string entityMapFile, std::string reflexMapFile) { //Open .ent mapping file std::ifstream entFin; entFin.open(entityMapFile); if ( entFin.is_open() ) { //Read .ent contents into pickup map std::string line; while (std::getline(entFin, line)) { std::istringstream iss(line); // Reflex ID corresponds to xonotic pickup name int id; std::string pickup; if ( ! (iss >> id >> pickup)) { throw std::runtime_error( "format error in .ent file" ); } pickupMapping_.insert ( std::pair(id, pickup) ); } } else { throw std::ios::failure( "Error: EntityConverter failed to open .ent file" ); } entFin.close(); //Open .map file std::ifstream mapFin; mapFin.open(reflexMapFile); if ( mapFin.is_open() ) { //Extract the source type of targets (teleporters or jump pads) std::string line; std::string trash; std::string targetName; while (std::getline(mapFin, line)) { if ( line.find("type Teleporter") != std::string::npos) { std::getline(mapFin, line); std::istringstream iss(line); if ( ! (iss >> trash >> trash >> targetName)) { throw std::runtime_error( "format error in .map file"); } targetMap_.insert ( std::pair(targetName, "Teleporter") ); } else if ( line.find("type JumpPad") != std::string::npos) { std::getline(mapFin, line); std::istringstream iss(line); if ( ! (iss >> trash >> trash >> targetName)) { throw std::runtime_error( "format error in .map file"); } targetMap_.insert ( std::pair(targetName, "JumpPad") ); } } } else { throw std::ios::failure( "Error: EntityConverter failed to open .map file" ); } mapFin.close(); //DEBUG //printMapping(); //printTargetSources(); } // DEBUG void EntityConverter::printMapping() { std::map::iterator it; for (it=pickupMapping_.begin(); it!=pickupMapping_.end(); ++it) std::cout << it->first << " => " << it->second << std::endl; } // DEBUG void EntityConverter::printTargetSources() { std::map::iterator it; for (it=targetMap_.begin(); it!=targetMap_.end(); ++it) std::cout << it->first << " => " << it->second << std::endl; } std::string EntityConverter::getAttributeType(std::string line) { std::string type; std::string dataType; std::istringstream iss(line); if ( ! (iss >> dataType >> type )) { return std::string(); } return type; } std::vector EntityConverter::convert(std::vector lines) { std::vector convertedLines; std::string coords[3]; std::string attribute; std::string trash; //unused tokens std::string type; if ( lines.size() < 1 ) { std::cerr << "error: empty entity cannot be converted" << std::endl; return convertedLines; } // second token is the type std::istringstream iss(lines[0]); if ( ! (iss >> trash >> type)) { std::cerr << "error: type is required" << std::endl; return convertedLines; } if ( type == "Pickup" ) { ////PICKUP if ( lines.size() < 3 ) { std::cerr << "error: Pickup entity requires at least 3 lines" << std::endl; return convertedLines; } //can ignore angle of pickups in xonotic format int pickupID; bool havePosition = false; bool havePickupID = false; for (int i = 1; i < lines.size(); i++) { type = getAttributeType(lines[i]); if ( type == "position" ) { std::istringstream iss2(lines[i]); // Vector3 position coord0 coord1 coord2 if ( ! (iss2 >> trash >> trash >> coords[0] >> coords[1] >> coords[2])) { std::cerr << "error: Pickup entity requires coordinates" << std::endl; return convertedLines; } havePosition = true; } else if ( type == "pickupType" ) { std::istringstream iss2(lines[i]); // UInt8 pickupType ID if ( ! (iss2 >> trash >> trash >> pickupID) ) { std::cerr << "error: Pickup entity requires pickup ID" << std::endl; return convertedLines; } havePickupID = true; } } if ( havePosition && havePickupID ) { std::stringstream oss; oss << "\"classname\" \"" << pickupMapping_[pickupID] << "\"" << std::endl; convertedLines.push_back ( oss.str() ); // coordinates reordered to x, z, y std::stringstream oss2; oss2 << "\"origin\" \"" << coords[0] << " " << coords[2] << " " << coords[1] << "\"" << std::endl; convertedLines.push_back ( oss2.str() ); } } else if ( type == "PlayerSpawn" ) { ///PLAYER SPAWN //minimum of 3 lines, max of ? lines std::istringstream iss2(lines[1]); } else if ( type == "JumpPad" ) { ///JUMP PAD if ( lines.size() < 2 ) { std::cerr << "error: JumpPad entity requires at least 2 lines" << std::endl; } std::istringstream iss2(lines[1]); std::string targetName; // String32 target targetName if ( ! (iss2 >> trash >> trash >> targetName) ) { std::cerr << "error: JumpPad entity requires target name" << std::endl; } convertedLines.push_back ( "\"classname\" \"trigger_push\"\n" ); std::stringstream oss; oss << "\"target\" \"" << targetName << "\"" << std::endl; convertedLines.push_back ( oss.str() ); } else if ( type == "Teleporter" ) { ///TELEPORTER if ( lines.size() < 2 ) { throw std::runtime_error("error: Teleport entity requires at least 2 lines"); } std::istringstream iss2(lines[1]); std::string targetName; // String32 target targetName if ( ! (iss2 >> trash >> trash >> targetName) ) { throw std::runtime_error( "error: Teleport entity requires target name" ); } convertedLines.push_back ( "\"classname\" \"trigger_teleport\"\n" ); std::stringstream oss; oss << "\"target\" \"" << targetName << std::endl; convertedLines.push_back ( oss.str() ); } else if ( type == "Target" ) { ///TARGET if ( lines.size() < 3 ) { throw std::runtime_error("error: Target entity requires at least 3 lines"); } //position and name required, angles optional std::string targetName; std::string angle; bool havePosition = false; bool haveName = false; bool haveAngle = false; for (int i = 1; i < lines.size(); i++) { type = getAttributeType(lines[i]); if ( type == "position" ) { std::istringstream iss2(lines[i]); // Vector3 position coord0 coord1 coord2 if ( ! (iss2 >> trash >> trash >> coords[0] >> coords[1] >> coords[2])) { throw std::runtime_error( "error: Target entity requires coordinates" ); } havePosition = true; } else if ( type == "name" ) { std::istringstream iss2(lines[i]); // UInt8 name uniqueName if ( ! (iss2 >> trash >> trash >> targetName) ) { throw std::runtime_error( "error: Target entity requires target name" ); } haveName = true; } else if ( type == "angles" ) { std::istringstream iss2(lines[i]); // Vector3 angles angle notapplicable notapplicable if ( ! (iss2 >> trash >> trash >> angle) ) { throw std::runtime_error( "error: Target entity requires target angle if specified" ); } haveAngle = true; } } if ( havePosition && haveName) { //**! no way to tell if teleporter or jump pad dest from targetName alone if ( targetMap_[targetName] == "Teleporter") { convertedLines.push_back ( "\"classname\" \"misc_teleporter_dest\"\n" ); } else if ( targetMap_[targetName] == "JumpPad") { convertedLines.push_back ( "\"classname\" \"target_push\"\n" ); } std::stringstream oss; oss << "\"targetname\" \"" << targetName << "\"\n"; convertedLines.push_back ( oss.str() ); // coordinates reordered to x, z, y std::stringstream oss2; oss2 << "\"origin\" \"" << coords[0] << " " << coords[2] << " " << coords[1] << "\"" << std::endl; convertedLines.push_back ( oss2.str() ); // Write angle only if position and name exist if ( haveAngle ) { std::stringstream oss3; oss3 << "\"angle\" \"" << angle << "\"\n"; convertedLines.push_back (oss3.str() ); } } } else if ( type == "Effect" ) { ///EFFECT // to be implemented } else if ( type == "PointLight" ) { ////POINT LIGHT // to be implemented } else if ( type == "Prefab" ) { ////PREFAB // to be implemented? } else if ( type == "CameraPath" ) { ///CAMERA PATH // to be implemented? } else if ( type == "WorldSpawn" ) { ///WORLD SPAWN // do nothing } return convertedLines; }