From fde15235bdcb91bba4bd8d643947094cb7c83f42 Mon Sep 17 00:00:00 2001 From: suhrke Date: Wed, 5 Jul 2017 18:58:07 -0700 Subject: [PATCH 01/12] EC extracts mode info where it can, race spawns still an issue --- ReflexToQ3/includes/EntityConverter.cpp | 83 ++++++++++++++++++------- ReflexToQ3/includes/EntityConverter.hpp | 39 ++++++++---- ReflexToQ3/test/catch.cpp | 58 ++++++++++++++--- 3 files changed, 136 insertions(+), 44 deletions(-) diff --git a/ReflexToQ3/includes/EntityConverter.cpp b/ReflexToQ3/includes/EntityConverter.cpp index da3e24e..3da30cc 100644 --- a/ReflexToQ3/includes/EntityConverter.cpp +++ b/ReflexToQ3/includes/EntityConverter.cpp @@ -32,8 +32,15 @@ EntityConverter::EntityConverter(std::string entityMapFile) : OFFSET_PLAYER(32.0), OFFSET_PICKUP(2.0) { - //MUST RUN matchRelated method after this constructor - areEntitiesMatched_ = false; + //MUST RUN extractMapInfo method after this constructor + haveMapInfo_ = false; + // game modes default to enabled + mapinfo_.cts = true; + mapinfo_.ctf = true; + mapinfo_.ffa = true; + mapinfo_.tdm = true; + mapinfo_.duel = true; + mapEntities(entityMapFile); } @@ -41,9 +48,17 @@ EntityConverter::EntityConverter(std::string entityMapFile) : OFFSET_PLAYER(32.0 EntityConverter::EntityConverter(std::string entityMapFile, std::string reflexMapFile) : OFFSET_PLAYER(32.0), OFFSET_PICKUP(2.0) { + haveMapInfo_ = false; + // game modes default to enabled + mapinfo_.cts = true; + mapinfo_.ctf = true; + mapinfo_.ffa = true; + mapinfo_.tdm = true; + mapinfo_.duel = true; + mapEntities(entityMapFile); - // Pre-scan for related entities + // Pre-scan for info needed by converter std::ifstream fin; fin.open(reflexMapFile); @@ -51,14 +66,14 @@ EntityConverter::EntityConverter(std::string entityMapFile, std::string reflexMa //Extract the source type of targets (teleporters or jump pads) std::string line; while (std::getline(fin, line)) { - addIfRelated(line, fin); + extractFromEntity(line, fin); } } else { throw std::ios::failure( "Error: EntityConverter failed to open .map file" ); } fin.close(); - areEntitiesMatched_ = true; + haveMapInfo_ = true; //DEBUG //printMapping(); @@ -70,17 +85,17 @@ EntityConverter::EntityConverter(std::string entityMapFile, std::string reflexMa /* *-------------------------------------------------------------------------------------- * Class: EntityConverter - * Method: EntityConverter :: matchRelated + * Method: EntityConverter :: extractMapInfo * Description: Read through entities, matching related as necessary * Note: For now, accomplishes the same goal as the pre-scan * constructor *-------------------------------------------------------------------------------------- */ void -EntityConverter::matchRelated(std::queue> entities) +EntityConverter::extractMapInfo(std::queue> entities) { - if( areEntitiesMatched_ ) { - std::cerr << "Related entities are already matched, doing nothing" << std::endl; + if( haveMapInfo_ ) { + std::cerr << "Map info already extracted, doing nothing" << std::endl; } else { while ( ! entities.empty() ) { @@ -93,12 +108,12 @@ EntityConverter::matchRelated(std::queue> entities) std::string nextLine; if ( getline(ss, nextLine )) { - addIfRelated(nextLine, ss); + extractFromEntity(nextLine, ss); } } } - areEntitiesMatched_ = true; + haveMapInfo_ = true; } @@ -106,7 +121,7 @@ EntityConverter::matchRelated(std::queue> entities) std::vector EntityConverter::convert(std::vector lines) { - if ( areEntitiesMatched_ ) + if ( haveMapInfo_ ) { std::string attribute; std::string trash; //unused tokens @@ -145,7 +160,7 @@ EntityConverter::convert(std::vector lines) } } else { - throw std::runtime_error( "error: related entities must be matched prior to conversion" ); + throw std::runtime_error( "error: Map info must be extracted prior to conversion" ); } // If unsupported entity, return empty vector @@ -260,7 +275,7 @@ EntityConverter::convertPlayerSpawn(std::vector &lines) } } else if ( type == "modeRace" ) { - isModeRace = false; + isModeRace = false; // Bool8 modeRace 0 indicates this spawn is not for race } else if ( type == "teamA" ) { team = 2; // Bool8 teamA 0 indicates teamB only @@ -271,7 +286,14 @@ EntityConverter::convertPlayerSpawn(std::vector &lines) } if ( havePosition ) { - if ( ! isModeRace ) { + if ( mapinfo_.cts && isModeRace ) { + convertedLines.push_back ( "\"classname\" \"info_player_race\"\n" ); + // Reflex maps have only start and finish, point to start on spawn + convertedLines.push_back ( "\"target\" \"cp1\"\n" ); + // Reflex maps are only cts, set spawn to cts-only type + convertedLines.push_back ( "\"race_place\" \"-1\"\n" ); + } + else { switch (team) { case 0: convertedLines.push_back ( "\"classname\" \"info_player_deathmatch\"\n" ); @@ -283,15 +305,8 @@ EntityConverter::convertPlayerSpawn(std::vector &lines) convertedLines.push_back ( "\"classname\" \"info_player_team2\"\n" ); break; } + } - } - else { - convertedLines.push_back ( "\"classname\" \"info_player_race\"\n" ); - // Reflex maps have only start and finish, point to start on spawn - convertedLines.push_back ( "\"target\" \"cp1\"\n" ); - // Reflex maps are only cts, set spawn to cts-only type - convertedLines.push_back ( "\"race_place\" \"-1\"\n" ); - } std::stringstream oss; // coordinates reordered to x, z, y @@ -519,7 +534,7 @@ EntityConverter::mapEntities(std::string mapFile) void -EntityConverter::addIfRelated(std::string &line, std::istream &is) +EntityConverter::extractFromEntity(std::string &line, std::istream &is) { std::string trash; std::string targetName; @@ -539,6 +554,26 @@ EntityConverter::addIfRelated(std::string &line, std::istream &is) } targetMap_.insert ( std::pair(targetName, "JumpPad") ); } + else if ( line.find("type WorldSpawn") != std::string::npos) { + while ( std::getline(is, line)) { + // only works for maps that support race AND other modes + if ( line.find("modeRace 0") != std::string::npos) { + mapinfo_.cts = false; + } + else if ( line.find("modeCTF 0") != std::string::npos) { + mapinfo_.ctf = false; + } + else if ( line.find("modeFFA 0") != std::string::npos) { + mapinfo_.ffa = false; + } + else if ( line.find("modeTDM 0") != std::string::npos) { + mapinfo_.tdm = false; + } + else if ( line.find("mode1v1 0") != std::string::npos) { + mapinfo_.duel = false; + } + } + } } diff --git a/ReflexToQ3/includes/EntityConverter.hpp b/ReflexToQ3/includes/EntityConverter.hpp index ce6145d..96aabf3 100644 --- a/ReflexToQ3/includes/EntityConverter.hpp +++ b/ReflexToQ3/includes/EntityConverter.hpp @@ -35,6 +35,19 @@ + + +struct MapInfo +{ + bool cts; + bool ctf; + bool ffa; + bool tdm; + bool duel; +}; + + + class EntityConverter { public: @@ -43,7 +56,7 @@ class EntityConverter * Class: EntityConverter * Method: Constructor * Description: Creates entity format mapping - * CAUTION: Requires matchRelated method to be called after this + * CAUTION: Requires extractMapInfo method to be called after this * Requires: .ent filename for mapping entities from reflex format to xonotic format * THROWS: runtime_error on .ent format error * THROWS: std::ios::failure on IO failure @@ -53,7 +66,7 @@ class EntityConverter /* *-------------------------------------------------------------------------------------- * Class: EntityConverter * Method: Constructor - * Description: Creates entity format mapping and pre-scans for related entities + * Description: Creates entity format mapping and pre-scans for map info * Parameter: string entityMapFile, file maps source to target entity formats * Parameter: string reflexMapFile, for pre-scan * THROWS: runtime_error on .ent format error @@ -70,21 +83,21 @@ class EntityConverter * Return: vector of strings, single entity in the converted format * *IF entity is not supported, returns EMPTY vector * THROWS: runtime_error on malformed .map file - * THROWS: runtime_error when called before related entities are matched + * THROWS: runtime_error when called before map info has been extracted *-------------------------------------------------------------------------------------- */ std::vector convert(std::vector lines); /* *-------------------------------------------------------------------------------------- * Class: EntityConverter - * Method: EntityConverter :: matchRelated - * Description: Finds related entities (targets of teleports, etc), call after parsing - * the entire .map + * Method: EntityConverter :: extractMapInfo + * Description: Get information needed by the converter that can't be obtained + * in entity-by-entity conversion (teleport and jump pad * Parameter: queue of vector of string entities, ALL entities in a .map file * THROWS: runtime_error when encountering malformed entity *-------------------------------------------------------------------------------------- */ - void matchRelated(std::queue> entities); + void extractMapInfo(std::queue> entities); @@ -110,12 +123,14 @@ class EntityConverter - // Related entities must be matched prior to entity conversion - bool areEntitiesMatched_; // Map Reflex pickup IDs to Xonotic pickup identifiers std::map pickupMapping_; // Map targets (by name) to their source type std::map targetMap_; + // Related entities must be matched prior to entity conversion + bool haveMapInfo_; + MapInfo mapinfo_; + // Offsets for item/spawn height const float OFFSET_PLAYER; const float OFFSET_PICKUP; @@ -145,14 +160,14 @@ class EntityConverter /* *-------------------------------------------------------------------------------------- * Class: EntityConverter - * Method: EntityConverter :: addIfRelated - * Description: If the entity contains a related target/etc, add to map + * Method: EntityConverter :: extractFromEntity + * Description: Get map info from a single entity * Paramater: string line, the previous line (contains entity type) * Parameter: istream is, an ifstream or a stringstream containing a * single entity *-------------------------------------------------------------------------------------- */ - void addIfRelated(std::string &line, std::istream &is); + void extractFromEntity(std::string &line, std::istream &is); /* *-------------------------------------------------------------------------------------- * Class: EntityConverter diff --git a/ReflexToQ3/test/catch.cpp b/ReflexToQ3/test/catch.cpp index 97b0067..292f7d3 100644 --- a/ReflexToQ3/test/catch.cpp +++ b/ReflexToQ3/test/catch.cpp @@ -44,7 +44,7 @@ TEST_CASE( "r2x: Unsupported entity types cause return of empty vector", "[Entit q.push( entity ); // Match related entities (none) - ec.matchRelated( q ); + ec.extractMapInfo( q ); // Convert a single entity std::vector converted = ec.convert(entity); @@ -71,7 +71,7 @@ TEST_CASE( "r2x: a single Pickup entity can be converted", "[EntityConverter]" ) q.push( entity ); // Match related entities (none) - ec.matchRelated( q ); + ec.extractMapInfo( q ); // Convert a single entity std::vector converted = ec.convert(entity); @@ -110,12 +110,14 @@ TEST_CASE( "r2x: a single PlayerSpawn (race) entity can be converted", "[EntityC entity.push_back(" Bool8 modeTDM 0"); entity.push_back(" Bool8 mode1v1 0"); + // With how map info works, at least one other spawn must be + // Mock up entity queue std::queue> q; q.push( entity ); // Match related entities (none) - ec.matchRelated( q ); + ec.extractMapInfo( q ); // Convert a single entity std::vector converted = ec.convert(entity); @@ -159,7 +161,7 @@ TEST_CASE( "r2x: a single PlayerSpawn (teamA) entity can be converted", "[Entity q.push( entity ); // Match related entities (none) - ec.matchRelated( q ); + ec.extractMapInfo( q ); // Convert a single entity std::vector converted = ec.convert(entity); @@ -182,6 +184,46 @@ TEST_CASE( "r2x: a single PlayerSpawn (teamA) entity can be converted", "[Entity +TEST_CASE( "r2x: a single PlayerSpawn (non-team) entity can be converted", "[EntityConverter]" ) { + + // Instantiate object + EntityConverter ec (PICKUP_FILENAME); + + // Mock up entity + std::vector entity; + entity.push_back(" type PlayerSpawn"); + entity.push_back(" Vector3 position -216.00000 -132.00000 -1488.000488"); + entity.push_back(" Vector3 angles 180.00000 0.00000 0.00000"); + + // Mock up entity queue + std::queue> q; + q.push( entity ); + + // Match related entities (none) + ec.extractMapInfo( q ); + + // Convert a single entity + std::vector converted = ec.convert(entity); + + REQUIRE( converted[0] == "\"classname\" \"info_player_deathmatch\"\n" ); + REQUIRE( converted[2] == "\"angle\" \"180.00000\"\n" ); + + // The z (vertical) is offset by +32 + std::istringstream iss(converted[1]); + std::string attribute; + std::string coords[2]; + float offsetCoord; + iss >> attribute >> coords[0] >> coords[1] >> offsetCoord; + + REQUIRE( attribute == "\"origin\"" ); + REQUIRE( coords[0] == "\"-216.00000" ); + REQUIRE( coords[1] == "-1488.000488" ); + REQUIRE( fabs(-100.00000 - offsetCoord) <= DELTA ); +} + + + + TEST_CASE( "r2x: a single RaceStart entity can be converted", "[EntityConverter]" ) { // Instantiate object @@ -196,7 +238,7 @@ TEST_CASE( "r2x: a single RaceStart entity can be converted", "[EntityConverter] q.push( entity ); // Match related entities (none) - ec.matchRelated( q ); + ec.extractMapInfo( q ); // Convert a single entity std::vector converted = ec.convert(entity); @@ -222,7 +264,7 @@ TEST_CASE( "r2x: a single RaceFinish entity can be converted", "[EntityConverter q.push( entity ); // Match related entities (none) - ec.matchRelated( q ); + ec.extractMapInfo( q ); // Convert a single entity std::vector converted = ec.convert(entity); @@ -256,7 +298,7 @@ TEST_CASE( "r2x: a single Teleporter and related Target can be converted", "[Ent q.push( entity2 ); // Match related entities (one pair) - ec.matchRelated( q ); + ec.extractMapInfo( q ); // Convert two entities std::vector converted = ec.convert(entity); @@ -308,7 +350,7 @@ TEST_CASE( "r2x: a single JumpPad and related Target can be converted", "[Entity q.push( entity2 ); // Match related entities (one pair) - ec.matchRelated( q ); + ec.extractMapInfo( q ); // Convert two entities std::vector converted = ec.convert(entity); From 802f0bc99af53e46d4bdc0cc98eda8a62bb56a90 Mon Sep 17 00:00:00 2001 From: suhrke Date: Wed, 5 Jul 2017 19:49:03 -0700 Subject: [PATCH 02/12] race maps and maps without race convert spawns correctly, possible issue with maps with both --- ReflexToQ3/includes/EntityConverter.cpp | 67 ++++++++++++++----------- ReflexToQ3/test/catch.cpp | 19 +++++-- 2 files changed, 52 insertions(+), 34 deletions(-) diff --git a/ReflexToQ3/includes/EntityConverter.cpp b/ReflexToQ3/includes/EntityConverter.cpp index 3da30cc..30c9033 100644 --- a/ReflexToQ3/includes/EntityConverter.cpp +++ b/ReflexToQ3/includes/EntityConverter.cpp @@ -137,7 +137,34 @@ EntityConverter::convert(std::vector lines) } - if ( type == "Pickup" ) { + // RETURN EMPTY VECTOR if type is WorldSpawn + if ( type == "WorldSpawn" ) { + for ( int i = 1; i < lines.size(); ++i ) { + // only works for maps that support race AND other modes + if ( lines[i].find("modeRace 0") != std::string::npos) { + std::cout << "disabled race because worldspawn" << std::endl; + mapinfo_.cts = false; + } + else if ( lines[i].find("modeCTF 0") != std::string::npos) { + std::cout << "disabled ctf because worldspawn" << std::endl; + mapinfo_.ctf = false; + } + else if ( lines[i].find("modeFFA 0") != std::string::npos) { + std::cout << "disabled ffa because worldspawn" << std::endl; + mapinfo_.ffa = false; + } + else if ( lines[i].find("modeTDM 0") != std::string::npos) { + std::cout << "disabled tdm because worldspawn" << std::endl; + mapinfo_.tdm = false; + } + else if ( lines[i].find("mode1v1 0") != std::string::npos) { + std::cout << "disabled duel because worldspawn" << std::endl; + mapinfo_.duel = false; + } + } + + } + else if ( type == "Pickup" ) { return convertPickup(lines); } else if ( type == "PlayerSpawn" ) { @@ -286,14 +313,8 @@ EntityConverter::convertPlayerSpawn(std::vector &lines) } if ( havePosition ) { - if ( mapinfo_.cts && isModeRace ) { - convertedLines.push_back ( "\"classname\" \"info_player_race\"\n" ); - // Reflex maps have only start and finish, point to start on spawn - convertedLines.push_back ( "\"target\" \"cp1\"\n" ); - // Reflex maps are only cts, set spawn to cts-only type - convertedLines.push_back ( "\"race_place\" \"-1\"\n" ); - } - else { + // Will convert all race points to dm/team spawns on maps that support race AND others + if ( mapinfo_.ctf || mapinfo_.tdm || mapinfo_.ffa || mapinfo_.duel ) { switch (team) { case 0: convertedLines.push_back ( "\"classname\" \"info_player_deathmatch\"\n" ); @@ -306,6 +327,13 @@ EntityConverter::convertPlayerSpawn(std::vector &lines) break; } } + else if ( mapinfo_.cts && isModeRace ) { + convertedLines.push_back ( "\"classname\" \"info_player_race\"\n" ); + // Reflex maps have only start and finish, point to start on spawn + convertedLines.push_back ( "\"target\" \"cp1\"\n" ); + // Reflex maps are only cts, set spawn to cts-only type + convertedLines.push_back ( "\"race_place\" \"-1\"\n" ); + } std::stringstream oss; @@ -554,27 +582,6 @@ EntityConverter::extractFromEntity(std::string &line, std::istream &is) } targetMap_.insert ( std::pair(targetName, "JumpPad") ); } - else if ( line.find("type WorldSpawn") != std::string::npos) { - while ( std::getline(is, line)) { - // only works for maps that support race AND other modes - if ( line.find("modeRace 0") != std::string::npos) { - mapinfo_.cts = false; - } - else if ( line.find("modeCTF 0") != std::string::npos) { - mapinfo_.ctf = false; - } - else if ( line.find("modeFFA 0") != std::string::npos) { - mapinfo_.ffa = false; - } - else if ( line.find("modeTDM 0") != std::string::npos) { - mapinfo_.tdm = false; - } - else if ( line.find("mode1v1 0") != std::string::npos) { - mapinfo_.duel = false; - } - } - } - } diff --git a/ReflexToQ3/test/catch.cpp b/ReflexToQ3/test/catch.cpp index 292f7d3..e0b33a5 100644 --- a/ReflexToQ3/test/catch.cpp +++ b/ReflexToQ3/test/catch.cpp @@ -37,7 +37,7 @@ TEST_CASE( "r2x: Unsupported entity types cause return of empty vector", "[Entit // Mock up entity std::vector entity; - entity.push_back(" type Worldspawn"); + entity.push_back(" type NotAType"); // Mock up entity queue std::queue> q; @@ -98,6 +98,16 @@ TEST_CASE( "r2x: a single PlayerSpawn (race) entity can be converted", "[EntityC // Instantiate object EntityConverter ec (PICKUP_FILENAME); + // Mock up WorldSpawn entity + // (needed for mode info) + std::vector worldspawn; + worldspawn.push_back(" type WorldSpawn"); + worldspawn.push_back(" Bool8 modeCTF 0"); + worldspawn.push_back(" Bool8 modeFFA 0"); + worldspawn.push_back(" Bool8 modeTDM 0"); + worldspawn.push_back(" Bool8 mode1v1 0"); + + // Mock up entity std::vector entity; entity.push_back(" type PlayerSpawn"); @@ -110,16 +120,17 @@ TEST_CASE( "r2x: a single PlayerSpawn (race) entity can be converted", "[EntityC entity.push_back(" Bool8 modeTDM 0"); entity.push_back(" Bool8 mode1v1 0"); - // With how map info works, at least one other spawn must be - // Mock up entity queue std::queue> q; + q.push( worldspawn ); q.push( entity ); // Match related entities (none) ec.extractMapInfo( q ); - // Convert a single entity + // Convert a single entity (worldspawn conversion returns empty vector + // BUT sets the supported game modes for entities in that worldspawn + std::vector unused = ec.convert(worldspawn); std::vector converted = ec.convert(entity); REQUIRE( converted[0] == "\"classname\" \"info_player_race\"\n" ); From 774688180d917e6a9d2472318251296e2ad50190 Mon Sep 17 00:00:00 2001 From: suhrke Date: Wed, 5 Jul 2017 19:53:09 -0700 Subject: [PATCH 03/12] added unittest to main recipe, removed debug couts --- ReflexToQ3/Makefile | 2 +- ReflexToQ3/includes/EntityConverter.cpp | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/ReflexToQ3/Makefile b/ReflexToQ3/Makefile index 097d470..701b505 100644 --- a/ReflexToQ3/Makefile +++ b/ReflexToQ3/Makefile @@ -4,7 +4,7 @@ CFLAGS=-std=c++11 -I"./includes" -I"./includes/Catch/single_include" -I"./includ TESTEX=test/test-parser UNITEX=test/catch -all: main +all: main unittest main: planes.o brushdef.o oopless-parser.o EntityConverter.o $(CC) $^ main.cpp $(CFLAGS) -o $(EX) 2>error8.log diff --git a/ReflexToQ3/includes/EntityConverter.cpp b/ReflexToQ3/includes/EntityConverter.cpp index 30c9033..e046e0b 100644 --- a/ReflexToQ3/includes/EntityConverter.cpp +++ b/ReflexToQ3/includes/EntityConverter.cpp @@ -142,23 +142,18 @@ EntityConverter::convert(std::vector lines) for ( int i = 1; i < lines.size(); ++i ) { // only works for maps that support race AND other modes if ( lines[i].find("modeRace 0") != std::string::npos) { - std::cout << "disabled race because worldspawn" << std::endl; mapinfo_.cts = false; } else if ( lines[i].find("modeCTF 0") != std::string::npos) { - std::cout << "disabled ctf because worldspawn" << std::endl; mapinfo_.ctf = false; } else if ( lines[i].find("modeFFA 0") != std::string::npos) { - std::cout << "disabled ffa because worldspawn" << std::endl; mapinfo_.ffa = false; } else if ( lines[i].find("modeTDM 0") != std::string::npos) { - std::cout << "disabled tdm because worldspawn" << std::endl; mapinfo_.tdm = false; } else if ( lines[i].find("mode1v1 0") != std::string::npos) { - std::cout << "disabled duel because worldspawn" << std::endl; mapinfo_.duel = false; } } From 26e287d9b4066a575714defdc58fe573dc70ce2e Mon Sep 17 00:00:00 2001 From: suhrke Date: Wed, 5 Jul 2017 20:32:49 -0700 Subject: [PATCH 04/12] EC: const correct prototypes, pass by ref where possible --- ReflexToQ3/includes/EntityConverter.cpp | 40 +++++++++++++------------ ReflexToQ3/includes/EntityConverter.hpp | 30 +++++++++---------- 2 files changed, 36 insertions(+), 34 deletions(-) diff --git a/ReflexToQ3/includes/EntityConverter.cpp b/ReflexToQ3/includes/EntityConverter.cpp index e046e0b..b86cc4e 100644 --- a/ReflexToQ3/includes/EntityConverter.cpp +++ b/ReflexToQ3/includes/EntityConverter.cpp @@ -30,7 +30,7 @@ * PUBLIC *-----------------------------------------------------------------------------*/ -EntityConverter::EntityConverter(std::string entityMapFile) : OFFSET_PLAYER(32.0), OFFSET_PICKUP(2.0) +EntityConverter::EntityConverter(const std::string &entityMapFile) : OFFSET_PLAYER(32.0), OFFSET_PICKUP(2.0) { //MUST RUN extractMapInfo method after this constructor haveMapInfo_ = false; @@ -46,7 +46,7 @@ EntityConverter::EntityConverter(std::string entityMapFile) : OFFSET_PLAYER(32.0 -EntityConverter::EntityConverter(std::string entityMapFile, std::string reflexMapFile) : OFFSET_PLAYER(32.0), OFFSET_PICKUP(2.0) +EntityConverter::EntityConverter(const std::string &entityMapFile, const std::string &reflexMapFile) : OFFSET_PLAYER(32.0), OFFSET_PICKUP(2.0) { haveMapInfo_ = false; // game modes default to enabled @@ -92,7 +92,7 @@ EntityConverter::EntityConverter(std::string entityMapFile, std::string reflexMa *-------------------------------------------------------------------------------------- */ void -EntityConverter::extractMapInfo(std::queue> entities) +EntityConverter::extractMapInfo(std::queue> &entities) { if( haveMapInfo_ ) { std::cerr << "Map info already extracted, doing nothing" << std::endl; @@ -119,7 +119,7 @@ EntityConverter::extractMapInfo(std::queue> entities) std::vector -EntityConverter::convert(std::vector lines) +EntityConverter::convert(const std::vector &lines) { if ( haveMapInfo_ ) { @@ -196,7 +196,7 @@ EntityConverter::convert(std::vector lines) * PROTECTED *-----------------------------------------------------------------------------*/ std::vector -EntityConverter::convertPickup(std::vector &lines) +EntityConverter::convertPickup(const std::vector &lines) { std::vector convertedLines; //can ignore angle of pickups in xonotic format @@ -260,7 +260,7 @@ EntityConverter::convertPickup(std::vector &lines) *-------------------------------------------------------------------------------------- */ std::vector -EntityConverter::convertPlayerSpawn(std::vector &lines) +EntityConverter::convertPlayerSpawn(const std::vector &lines) { std::vector convertedLines; // Requires position coordinate @@ -349,7 +349,7 @@ EntityConverter::convertPlayerSpawn(std::vector &lines) std::vector -EntityConverter::convertJumpPad(std::vector &lines) +EntityConverter::convertJumpPad(const std::vector &lines) { std::vector convertedLines; std::string targetName; @@ -374,7 +374,7 @@ EntityConverter::convertJumpPad(std::vector &lines) std::vector -EntityConverter::convertTeleporter(std::vector &lines) +EntityConverter::convertTeleporter(const std::vector &lines) { std::vector convertedLines; std::string targetName; @@ -399,7 +399,7 @@ EntityConverter::convertTeleporter(std::vector &lines) std::vector -EntityConverter::convertTarget(std::vector &lines) +EntityConverter::convertTarget(const std::vector &lines) { std::vector convertedLines; //position and name required, angles optional @@ -485,7 +485,7 @@ EntityConverter::convertTarget(std::vector &lines) std::vector -EntityConverter::convertRaceStart(std::vector &lines) +EntityConverter::convertRaceStart(const std::vector &lines) { std::vector convertedLines; convertedLines.push_back ("\"classname\" \"trigger_race_checkpoint\"\n"); @@ -497,7 +497,7 @@ EntityConverter::convertRaceStart(std::vector &lines) std::vector -EntityConverter::convertRaceFinish(std::vector &lines) +EntityConverter::convertRaceFinish(const std::vector &lines) { std::vector convertedLines; convertedLines.push_back ("\"classname\" \"trigger_race_checkpoint\"\n"); @@ -513,7 +513,7 @@ EntityConverter::convertRaceFinish(std::vector &lines) * PRIVATE *-----------------------------------------------------------------------------*/ std::string -EntityConverter::getAttributeType(std::string line) +EntityConverter::getAttributeType(const std::string &line) const { std::string type; std::string dataType; @@ -528,7 +528,7 @@ EntityConverter::getAttributeType(std::string line) void -EntityConverter::mapEntities(std::string mapFile) +EntityConverter::mapEntities(const std::string &mapFile) { std::ifstream fin; fin.open(mapFile); @@ -557,21 +557,22 @@ EntityConverter::mapEntities(std::string mapFile) void -EntityConverter::extractFromEntity(std::string &line, std::istream &is) +EntityConverter::extractFromEntity(const std::string &line, std::istream &is) { std::string trash; std::string targetName; + std::string nextLine; if ( line.find("type Teleporter") != std::string::npos) { - std::getline(is, line); - std::istringstream iss(line); + std::getline(is, nextLine); + std::istringstream iss(nextLine); 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(is, line); - std::istringstream iss(line); + std::getline(is, nextLine); + std::istringstream iss(nextLine); if ( ! (iss >> trash >> trash >> targetName)) { throw std::runtime_error( "format error in .map file"); } @@ -581,7 +582,8 @@ EntityConverter::extractFromEntity(std::string &line, std::istream &is) -std::string EntityConverter::offset(std::string value, float amount) { +std::string EntityConverter::offset(const std::string &value, const float amount) const +{ std::istringstream iss(value); float c; iss >> c; diff --git a/ReflexToQ3/includes/EntityConverter.hpp b/ReflexToQ3/includes/EntityConverter.hpp index 96aabf3..2161054 100644 --- a/ReflexToQ3/includes/EntityConverter.hpp +++ b/ReflexToQ3/includes/EntityConverter.hpp @@ -62,7 +62,7 @@ class EntityConverter * THROWS: std::ios::failure on IO failure *-------------------------------------------------------------------------------------- */ - EntityConverter(std::string entityMapFile); + EntityConverter(const std::string &entityMapFile); /* *-------------------------------------------------------------------------------------- * Class: EntityConverter * Method: Constructor @@ -73,7 +73,7 @@ class EntityConverter * THROWS: std::ios::failure on IO failure *-------------------------------------------------------------------------------------- */ - EntityConverter(std::string entityMapFile, std::string reflexMapFile); + EntityConverter(const std::string &entityMapFile, const std::string &reflexMapFile); /* *-------------------------------------------------------------------------------------- * Class: EntityConverter @@ -86,7 +86,7 @@ class EntityConverter * THROWS: runtime_error when called before map info has been extracted *-------------------------------------------------------------------------------------- */ - std::vector convert(std::vector lines); + std::vector convert(const std::vector &lines); /* *-------------------------------------------------------------------------------------- * Class: EntityConverter @@ -97,7 +97,7 @@ class EntityConverter * THROWS: runtime_error when encountering malformed entity *-------------------------------------------------------------------------------------- */ - void extractMapInfo(std::queue> entities); + void extractMapInfo(std::queue> &entities); @@ -113,13 +113,13 @@ class EntityConverter * Return: vector of strings, the converted entity *-------------------------------------------------------------------------------------- */ - std::vector convertPickup(std::vector &entity); - std::vector convertPlayerSpawn(std::vector &entity); - std::vector convertJumpPad(std::vector &entity); - std::vector convertTeleporter(std::vector &entity); - std::vector convertTarget(std::vector &entity); - std::vector convertRaceStart(std::vector &entity); - std::vector convertRaceFinish(std::vector &entity); + std::vector convertPickup(const std::vector &entity); + std::vector convertPlayerSpawn(const std::vector &entity); + std::vector convertJumpPad(const std::vector &entity); + std::vector convertTeleporter(const std::vector &entity); + std::vector convertTarget(const std::vector &entity); + std::vector convertRaceStart(const std::vector &entity); + std::vector convertRaceFinish(const std::vector &entity); @@ -146,7 +146,7 @@ class EntityConverter * Parameter: string "line", entity keyword followed by the type *-------------------------------------------------------------------------------------- */ - std::string getAttributeType(std::string line); + std::string getAttributeType(const std::string &line) const; /* *-------------------------------------------------------------------------------------- * Class: EntityConverter @@ -156,7 +156,7 @@ class EntityConverter * Return: true if no error, false if error *-------------------------------------------------------------------------------------- */ - void mapEntities(std::string mapFile); + void mapEntities(const std::string &mapFile); /* *-------------------------------------------------------------------------------------- * Class: EntityConverter @@ -167,7 +167,7 @@ class EntityConverter * single entity *-------------------------------------------------------------------------------------- */ - void extractFromEntity(std::string &line, std::istream &is); + void extractFromEntity(const std::string &line, std::istream &is); /* *-------------------------------------------------------------------------------------- * Class: EntityConverter @@ -179,7 +179,7 @@ class EntityConverter * Return: string, float value passed as string *-------------------------------------------------------------------------------------- */ - std::string offset(std::string value, float offset); + std::string offset(const std::string &value, const float offset) const; From 725904143ca601f4dfb7e9cfc5e0a44808d61df7 Mon Sep 17 00:00:00 2001 From: suhrke Date: Wed, 5 Jul 2017 20:38:12 -0700 Subject: [PATCH 05/12] updated entity tags for xon 0.8.2 --- ReflexToQ3/r2x.pck | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ReflexToQ3/r2x.pck b/ReflexToQ3/r2x.pck index 85f81ed..2edc648 100644 --- a/ReflexToQ3/r2x.pck +++ b/ReflexToQ3/r2x.pck @@ -1,11 +1,11 @@ 40 item_health_small 41 item_health_medium -42 item_health_large +42 item_health_big 43 item_health_mega 50 item_armor_small 51 item_armor_medium -52 item_armor_large -53 item_armor_big +52 item_armor_big +53 item_armor_mega 1 weapon_uzi 2 weapon_grenadelauncher 3 weapon_hagar From e622f495a94b148de57ba1d4a89a65aa27522f4c Mon Sep 17 00:00:00 2001 From: suhrke Date: Wed, 5 Jul 2017 22:36:39 -0700 Subject: [PATCH 06/12] PointLights converted, looking for ways to test accuracy of bright+color conversion --- ReflexToQ3/includes/EntityConverter.cpp | 153 ++++++++++++++++++++---- ReflexToQ3/includes/EntityConverter.hpp | 40 +++++-- 2 files changed, 163 insertions(+), 30 deletions(-) diff --git a/ReflexToQ3/includes/EntityConverter.cpp b/ReflexToQ3/includes/EntityConverter.cpp index b86cc4e..c077a15 100644 --- a/ReflexToQ3/includes/EntityConverter.cpp +++ b/ReflexToQ3/includes/EntityConverter.cpp @@ -25,12 +25,11 @@ #include - /*----------------------------------------------------------------------------- * PUBLIC *-----------------------------------------------------------------------------*/ -EntityConverter::EntityConverter(const std::string &entityMapFile) : OFFSET_PLAYER(32.0), OFFSET_PICKUP(2.0) +EntityConverter::EntityConverter(const std::string &entityMapFile) : OFFSET_PLAYER(32.0), OFFSET_PICKUP(2.0), BRIGHTNESS_ADJUST(50) { //MUST RUN extractMapInfo method after this constructor haveMapInfo_ = false; @@ -46,7 +45,7 @@ EntityConverter::EntityConverter(const std::string &entityMapFile) : OFFSET_PLAY -EntityConverter::EntityConverter(const std::string &entityMapFile, const std::string &reflexMapFile) : OFFSET_PLAYER(32.0), OFFSET_PICKUP(2.0) +EntityConverter::EntityConverter(const std::string &entityMapFile, const std::string &reflexMapFile) : OFFSET_PLAYER(32.0), OFFSET_PICKUP(2.0), BRIGHTNESS_ADJUST(50) { haveMapInfo_ = false; // game modes default to enabled @@ -180,6 +179,9 @@ EntityConverter::convert(const std::vector &lines) else if ( type == "RaceFinish" ) { return convertRaceFinish(lines); } + else if ( type == "PointLight" ) { + return convertPointLight(lines); + } } else { throw std::runtime_error( "error: Map info must be extracted prior to conversion" ); @@ -232,14 +234,14 @@ EntityConverter::convertPickup(const std::vector &lines) } if ( havePosition && havePickupID ) { - std::stringstream oss; - oss << "\"classname\" \"" << pickupMapping_[pickupID] << "\"" << std::endl; - convertedLines.push_back ( oss.str() ); + std::stringstream pickupStream; + pickupStream << "\"classname\" \"" << pickupMapping_[pickupID] << "\"" << std::endl; + convertedLines.push_back ( pickupStream.str() ); // coordinates reordered to x, z, y - std::stringstream oss2; - oss2 << "\"origin\" \"" << coords[0] << " " << coords[2] << " " << + std::stringstream positionStream; + positionStream << "\"origin\" \"" << coords[0] << " " << coords[2] << " " << offset(coords[1], OFFSET_PICKUP) << "\"" << std::endl; - convertedLines.push_back ( oss2.str() ); + convertedLines.push_back ( positionStream.str() ); return convertedLines; } else { @@ -331,14 +333,14 @@ EntityConverter::convertPlayerSpawn(const std::vector &lines) } - std::stringstream oss; + std::stringstream positionStream; // coordinates reordered to x, z, y - oss << "\"origin\" \"" << coords[0] << " " << coords[2] << " " << + positionStream << "\"origin\" \"" << coords[0] << " " << coords[2] << " " << offset(coords[1], OFFSET_PLAYER) << "\"" << std::endl; - convertedLines.push_back ( oss.str() ); - std::stringstream oss2; - oss2 << "\"angle\" \"" << angle << "\"" << std::endl; - convertedLines.push_back ( oss2.str() ); + convertedLines.push_back ( positionStream.str() ); + std::stringstream angleStream; + angleStream << "\"angle\" \"" << angle << "\"" << std::endl; + convertedLines.push_back (angleStream.str() ); return convertedLines; } else { @@ -464,15 +466,15 @@ EntityConverter::convertTarget(const std::vector &lines) coords[1] << "\"" << std::endl; convertedLines.push_back ( oss.str() ); } - std::stringstream oss; - oss << "\"targetname\" \"" << targetName << "\"" << std::endl; - convertedLines.push_back ( oss.str() ); + std::stringstream targetStream; + targetStream << "\"targetname\" \"" << targetName << "\"" << std::endl; + convertedLines.push_back ( targetStream.str() ); // Write angle only if position and name exist if ( haveAngle ) { - std::stringstream oss2; - oss2 << "\"angle\" \"" << angle << "\"" << std::endl; - convertedLines.push_back (oss2.str() ); + std::stringstream angleStream; + angleStream << "\"angle\" \"" << angle << "\"" << std::endl; + convertedLines.push_back( angleStream.str() ); } return convertedLines; } @@ -508,6 +510,85 @@ EntityConverter::convertRaceFinish(const std::vector &lines) +std::vector +EntityConverter::convertPointLight(const std::vector &lines) +{ + std::vector convertedLines; + //position and intensity required, color optional + std::string coords[3]; + std::string intensity; + //color is hex 8 digits + std::string color; + std::string trash; + bool havePosition = false; + bool haveIntensity = false; + bool haveColor = false; + + + if ( lines.size() < 3 ) { + throw std::runtime_error("error: PointLight entity requires at least 3 lines"); + } + + for (int i = 1; i < lines.size(); i++) { + std::string type = getAttributeType(lines[i]); + if ( type == "position" ) { + std::istringstream iss(lines[i]); + // Vector3 position coord0 coord1 coord2 + if ( ! (iss >> trash >> trash >> + coords[0] >> coords[1] >> coords[2])) { + throw std::runtime_error( "error: PointLight entity requires position coordinates" ); + } + havePosition = true; + } + else if ( type == "intensity" ) { + std::istringstream iss(lines[i]); + // Float intensity validFloat + if ( ! (iss >> trash >> trash >> intensity) ) { + throw std::runtime_error( "error: PointLight entity requires intensity value" ); + } + haveIntensity = true; + } + else if ( type == "color" ) { + std::istringstream iss(lines[i]); + // ColourXRGB32 color eightDigitHexValue + if ( ! (iss >> trash >> trash >> color) ) { + throw std::runtime_error( "error: PointLight entity requires valid float value if specified" ); + } + haveColor = true; + } + + } + + + if ( havePosition && haveIntensity) { + convertedLines.push_back ( "\"classname\" \"light\"\n" ); + // coordinates reordered to x, z, y + std::stringstream positionStream; + positionStream << "\"origin\" \"" << coords[0] << " " << coords[2] << " " << + coords[1] << "\"" << std::endl; + convertedLines.push_back ( positionStream.str() ); + // convert intensity + std::stringstream intensityStream; + intensityStream << "\"light\" \"" << adjustBrightness(intensity) << "\"\n"; + convertedLines.push_back ( intensityStream.str() ); + + if ( haveColor ) { + std::stringstream colorStream; + float red; + float green; + float blue; + // Convert 32bit hex value into RGB values + hexToRGB(color, red, green, blue); + colorStream << "\"_color\" \"" << red << " " << green << " " << blue << "\"" << std::endl; + convertedLines.push_back (colorStream.str() ); + } + return convertedLines; + } + else { + throw std::runtime_error("error: Target entity requires position coordinates and targetname"); + } + +} /*----------------------------------------------------------------------------- * PRIVATE @@ -582,7 +663,8 @@ EntityConverter::extractFromEntity(const std::string &line, std::istream &is) -std::string EntityConverter::offset(const std::string &value, const float amount) const +std::string +EntityConverter::offset(const std::string &value, const float amount) const { std::istringstream iss(value); float c; @@ -596,6 +678,33 @@ std::string EntityConverter::offset(const std::string &value, const float amount +void +EntityConverter::hexToRGB(const std::string &hex, float &r, float &g, float &b) +{ + unsigned int value; + std::stringstream ss; + ss << std::hex << hex; + ss >> value; + + // get the necessary components from the right bytes + r = ((value >> 16) & 0xFF) / 255.0; + g = ((value >> 8) & 0xFF) / 255.0; + b = ((value) & 0xFF) / 255.0; +} + + + +int +EntityConverter::adjustBrightness(const std::string &value) const +{ + float inputBright; + std::stringstream ss(value); + ss >> inputBright; + + return static_cast(inputBright) * BRIGHTNESS_ADJUST; +} + + // DEBUG void diff --git a/ReflexToQ3/includes/EntityConverter.hpp b/ReflexToQ3/includes/EntityConverter.hpp index 2161054..adcd8a7 100644 --- a/ReflexToQ3/includes/EntityConverter.hpp +++ b/ReflexToQ3/includes/EntityConverter.hpp @@ -109,17 +109,18 @@ class EntityConverter * Class: EntityConverter * Method: EntityConverter :: convert~EntityName~ * Description: Multiple methods to convert entity from reflex to xonotic format - * Parameter: vector of strings entity, multi-lined entity + * Parameter: vector of strings lines, multi-lined entity * Return: vector of strings, the converted entity *-------------------------------------------------------------------------------------- */ - std::vector convertPickup(const std::vector &entity); - std::vector convertPlayerSpawn(const std::vector &entity); - std::vector convertJumpPad(const std::vector &entity); - std::vector convertTeleporter(const std::vector &entity); - std::vector convertTarget(const std::vector &entity); - std::vector convertRaceStart(const std::vector &entity); - std::vector convertRaceFinish(const std::vector &entity); + std::vector convertPickup(const std::vector &lines); + std::vector convertPlayerSpawn(const std::vector &lines); + std::vector convertJumpPad(const std::vector &lines); + std::vector convertTeleporter(const std::vector &lines); + std::vector convertTarget(const std::vector &lines); + std::vector convertRaceStart(const std::vector &lines); + std::vector convertRaceFinish(const std::vector &lines); + std::vector convertPointLight(const std::vector &lines); @@ -134,6 +135,8 @@ class EntityConverter // Offsets for item/spawn height const float OFFSET_PLAYER; const float OFFSET_PICKUP; + // Brightness adjustment factor + const int BRIGHTNESS_ADJUST; @@ -180,6 +183,27 @@ class EntityConverter *-------------------------------------------------------------------------------------- */ std::string offset(const std::string &value, const float offset) const; + /* + *-------------------------------------------------------------------------------------- + * Class: EntityConverter + * Method: EntityConverter :: hexToRGB + * Description: Convert 8 digit hex value into separate red, green, and blue values + * Parameter: string hex, inputted hex value + * Parameter: float r, RETURN BY REFERENCE: converted red value + * Parameter: float g, RETURN BY REFERENCE: converted green value + * Parameter: float b, RETURN BY REFERENCE: converted blue value + *-------------------------------------------------------------------------------------- + */ + void hexToRGB(const std::string &hex, float &r, float &g, float &b); + /* + *-------------------------------------------------------------------------------------- + * Class: EntityConverter + * Method: EntityConverter :: adjustBrightness + * Description: Reflex uses significantly smaller values than Xonotic -> adjust + * Parameter: string value, original brightness value + *-------------------------------------------------------------------------------------- + */ + int adjustBrightness(const std::string &value) const; From c88cf1405b8e4552416b30a243f6b5ee49ad0753 Mon Sep 17 00:00:00 2001 From: suhrke Date: Wed, 5 Jul 2017 22:43:24 -0700 Subject: [PATCH 07/12] Fixed truncation of PointLight intensity value --- ReflexToQ3/includes/EntityConverter.cpp | 6 +++--- ReflexToQ3/includes/EntityConverter.hpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ReflexToQ3/includes/EntityConverter.cpp b/ReflexToQ3/includes/EntityConverter.cpp index c077a15..fca2678 100644 --- a/ReflexToQ3/includes/EntityConverter.cpp +++ b/ReflexToQ3/includes/EntityConverter.cpp @@ -29,7 +29,7 @@ * PUBLIC *-----------------------------------------------------------------------------*/ -EntityConverter::EntityConverter(const std::string &entityMapFile) : OFFSET_PLAYER(32.0), OFFSET_PICKUP(2.0), BRIGHTNESS_ADJUST(50) +EntityConverter::EntityConverter(const std::string &entityMapFile) : OFFSET_PLAYER(32.0), OFFSET_PICKUP(2.0), BRIGHTNESS_ADJUST(50.0) { //MUST RUN extractMapInfo method after this constructor haveMapInfo_ = false; @@ -45,7 +45,7 @@ EntityConverter::EntityConverter(const std::string &entityMapFile) : OFFSET_PLAY -EntityConverter::EntityConverter(const std::string &entityMapFile, const std::string &reflexMapFile) : OFFSET_PLAYER(32.0), OFFSET_PICKUP(2.0), BRIGHTNESS_ADJUST(50) +EntityConverter::EntityConverter(const std::string &entityMapFile, const std::string &reflexMapFile) : OFFSET_PLAYER(32.0), OFFSET_PICKUP(2.0), BRIGHTNESS_ADJUST(50.0) { haveMapInfo_ = false; // game modes default to enabled @@ -701,7 +701,7 @@ EntityConverter::adjustBrightness(const std::string &value) const std::stringstream ss(value); ss >> inputBright; - return static_cast(inputBright) * BRIGHTNESS_ADJUST; + return static_cast(inputBright * BRIGHTNESS_ADJUST); } diff --git a/ReflexToQ3/includes/EntityConverter.hpp b/ReflexToQ3/includes/EntityConverter.hpp index adcd8a7..f817bc9 100644 --- a/ReflexToQ3/includes/EntityConverter.hpp +++ b/ReflexToQ3/includes/EntityConverter.hpp @@ -136,7 +136,7 @@ class EntityConverter const float OFFSET_PLAYER; const float OFFSET_PICKUP; // Brightness adjustment factor - const int BRIGHTNESS_ADJUST; + const float BRIGHTNESS_ADJUST; From d16f3f85033d8d89e4c8d15475068a1bc76e6a2c Mon Sep 17 00:00:00 2001 From: suhrke Date: Thu, 6 Jul 2017 08:52:59 -0700 Subject: [PATCH 08/12] Race and other mode spawns play nice when on same map now --- ReflexToQ3/includes/EntityConverter.cpp | 72 +++++++++++++++++-------- ReflexToQ3/includes/EntityConverter.hpp | 4 +- 2 files changed, 51 insertions(+), 25 deletions(-) diff --git a/ReflexToQ3/includes/EntityConverter.cpp b/ReflexToQ3/includes/EntityConverter.cpp index fca2678..5f3933a 100644 --- a/ReflexToQ3/includes/EntityConverter.cpp +++ b/ReflexToQ3/includes/EntityConverter.cpp @@ -29,31 +29,34 @@ * PUBLIC *-----------------------------------------------------------------------------*/ -EntityConverter::EntityConverter(const std::string &entityMapFile) : OFFSET_PLAYER(32.0), OFFSET_PICKUP(2.0), BRIGHTNESS_ADJUST(50.0) +EntityConverter::EntityConverter(const std::string &entityMapFile) : + OFFSET_PLAYER(32.0), OFFSET_PICKUP(2.0), BRIGHTNESS_ADJUST(50.0) { //MUST RUN extractMapInfo method after this constructor haveMapInfo_ = false; // game modes default to enabled - mapinfo_.cts = true; - mapinfo_.ctf = true; - mapinfo_.ffa = true; - mapinfo_.tdm = true; - mapinfo_.duel = true; + ws_.cts = true; + ws_.ctf = true; + ws_.ffa = true; + ws_.tdm = true; + ws_.duel = true; mapEntities(entityMapFile); } -EntityConverter::EntityConverter(const std::string &entityMapFile, const std::string &reflexMapFile) : OFFSET_PLAYER(32.0), OFFSET_PICKUP(2.0), BRIGHTNESS_ADJUST(50.0) +EntityConverter::EntityConverter(const std::string &entityMapFile, + const std::string &reflexMapFile) : OFFSET_PLAYER(32.0), + OFFSET_PICKUP(2.0), BRIGHTNESS_ADJUST(50.0) { haveMapInfo_ = false; // game modes default to enabled - mapinfo_.cts = true; - mapinfo_.ctf = true; - mapinfo_.ffa = true; - mapinfo_.tdm = true; - mapinfo_.duel = true; + ws_.cts = true; + ws_.ctf = true; + ws_.ffa = true; + ws_.tdm = true; + ws_.duel = true; mapEntities(entityMapFile); @@ -138,22 +141,29 @@ EntityConverter::convert(const std::vector &lines) // RETURN EMPTY VECTOR if type is WorldSpawn if ( type == "WorldSpawn" ) { + //All modes enabled by default, reset for new worldspawn + ws_.cts = true; + ws_.ctf = true; + ws_.ffa = true; + ws_.tdm = true; + ws_.duel = true; + + // Each worldspawn can specify modes enabled/disabled for ( int i = 1; i < lines.size(); ++i ) { - // only works for maps that support race AND other modes if ( lines[i].find("modeRace 0") != std::string::npos) { - mapinfo_.cts = false; + ws_.cts = false; } else if ( lines[i].find("modeCTF 0") != std::string::npos) { - mapinfo_.ctf = false; + ws_.ctf = false; } else if ( lines[i].find("modeFFA 0") != std::string::npos) { - mapinfo_.ffa = false; + ws_.ffa = false; } else if ( lines[i].find("modeTDM 0") != std::string::npos) { - mapinfo_.tdm = false; + ws_.tdm = false; } else if ( lines[i].find("mode1v1 0") != std::string::npos) { - mapinfo_.duel = false; + ws_.duel = false; } } @@ -273,7 +283,11 @@ EntityConverter::convertPlayerSpawn(const std::vector &lines) int team = 0; std::string trash; bool havePosition = false; - bool isModeRace = true; + bool isModeRace = ws_.cts; + bool isModeCtf = ws_.ctf; + bool isModeTdm = ws_.tdm; + bool isModeFfa = ws_.ffa; + bool isModeDuel = ws_.duel; if ( lines.size() < 2 ) { @@ -298,8 +312,21 @@ EntityConverter::convertPlayerSpawn(const std::vector &lines) throw std::runtime_error("error: Pickup entity requires Pickup ID"); } } + // Bool8 modeX 0 indicates this spawn is not for game mode X else if ( type == "modeRace" ) { - isModeRace = false; // Bool8 modeRace 0 indicates this spawn is not for race + isModeRace = false; + } + else if ( type == "modeCTF" ) { + isModeCtf = false; + } + else if ( type == "modeTDM" ) { + isModeTdm = false; + } + else if ( type == "modeFFA" ) { + isModeFfa = false; + } + else if ( type == "mode1v1" ) { + isModeDuel = false; } else if ( type == "teamA" ) { team = 2; // Bool8 teamA 0 indicates teamB only @@ -311,7 +338,7 @@ EntityConverter::convertPlayerSpawn(const std::vector &lines) if ( havePosition ) { // Will convert all race points to dm/team spawns on maps that support race AND others - if ( mapinfo_.ctf || mapinfo_.tdm || mapinfo_.ffa || mapinfo_.duel ) { + if ( isModeCtf || isModeTdm || isModeFfa || isModeDuel ) { switch (team) { case 0: convertedLines.push_back ( "\"classname\" \"info_player_deathmatch\"\n" ); @@ -324,7 +351,7 @@ EntityConverter::convertPlayerSpawn(const std::vector &lines) break; } } - else if ( mapinfo_.cts && isModeRace ) { + else { convertedLines.push_back ( "\"classname\" \"info_player_race\"\n" ); // Reflex maps have only start and finish, point to start on spawn convertedLines.push_back ( "\"target\" \"cp1\"\n" ); @@ -332,7 +359,6 @@ EntityConverter::convertPlayerSpawn(const std::vector &lines) convertedLines.push_back ( "\"race_place\" \"-1\"\n" ); } - std::stringstream positionStream; // coordinates reordered to x, z, y positionStream << "\"origin\" \"" << coords[0] << " " << coords[2] << " " << diff --git a/ReflexToQ3/includes/EntityConverter.hpp b/ReflexToQ3/includes/EntityConverter.hpp index f817bc9..4e251c3 100644 --- a/ReflexToQ3/includes/EntityConverter.hpp +++ b/ReflexToQ3/includes/EntityConverter.hpp @@ -37,7 +37,7 @@ -struct MapInfo +struct WorldSpawn { bool cts; bool ctf; @@ -130,7 +130,7 @@ class EntityConverter std::map targetMap_; // Related entities must be matched prior to entity conversion bool haveMapInfo_; - MapInfo mapinfo_; + WorldSpawn ws_; // Offsets for item/spawn height const float OFFSET_PLAYER; From 640ce93ff4b1441875f9530476f13e3f0b8136d1 Mon Sep 17 00:00:00 2001 From: suhrke Date: Thu, 6 Jul 2017 10:09:15 -0700 Subject: [PATCH 09/12] Added tests for PointLight conversion + spawns --- ReflexToQ3/includes/EntityConverter.cpp | 8 ++-- ReflexToQ3/test/catch.cpp | 62 ++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 5 deletions(-) diff --git a/ReflexToQ3/includes/EntityConverter.cpp b/ReflexToQ3/includes/EntityConverter.cpp index 5f3933a..d2acdd5 100644 --- a/ReflexToQ3/includes/EntityConverter.cpp +++ b/ReflexToQ3/includes/EntityConverter.cpp @@ -138,10 +138,10 @@ EntityConverter::convert(const std::vector &lines) throw std::runtime_error("error: type is required"); } - - // RETURN EMPTY VECTOR if type is WorldSpawn + // If worldspawn, first reenable all gamemodes + // then check worldspawn for disabled modes + // then RETURN EMPTY VECTOR if ( type == "WorldSpawn" ) { - //All modes enabled by default, reset for new worldspawn ws_.cts = true; ws_.ctf = true; ws_.ffa = true; @@ -712,7 +712,7 @@ EntityConverter::hexToRGB(const std::string &hex, float &r, float &g, float &b) ss << std::hex << hex; ss >> value; - // get the necessary components from the right bytes + // get the necessary components from the top 3 bytes r = ((value >> 16) & 0xFF) / 255.0; g = ((value >> 8) & 0xFF) / 255.0; b = ((value) & 0xFF) / 255.0; diff --git a/ReflexToQ3/test/catch.cpp b/ReflexToQ3/test/catch.cpp index e0b33a5..3eed42f 100644 --- a/ReflexToQ3/test/catch.cpp +++ b/ReflexToQ3/test/catch.cpp @@ -149,6 +149,20 @@ TEST_CASE( "r2x: a single PlayerSpawn (race) entity can be converted", "[EntityC REQUIRE( coords[0] == "\"-216.00000" ); REQUIRE( coords[1] == "-1488.000488" ); REQUIRE( fabs(-100.00000 - offsetCoord) <= DELTA ); + + SECTION( "Encountering a new worldspawn reenables all modes" ) { + std::vector basicWorldspawn; + basicWorldspawn.push_back(" type WorldSpawn"); + + std::vector e; + e.push_back(" type PlayerSpawn"); + e.push_back(" Vector3 position -216.00000 -132.00000 -1488.000488"); + e.push_back(" Vector3 angles 180.00000 0.00000 0.00000"); + + std::vector u = ec.convert(basicWorldspawn); + std::vector c = ec.convert(e); + REQUIRE( c[0] == "\"classname\" \"info_player_deathmatch\"\n" ); + } } @@ -327,7 +341,7 @@ TEST_CASE( "r2x: a single Teleporter and related Target can be converted", "[Ent float offsetCoord; iss >> attribute >> coords[0] >> coords[1] >> offsetCoord; - // first test fails without busy wait + // next REQUIRE fails without busy wait for( int i = 0; i < 10000000; i++ ) int x = i; @@ -376,6 +390,52 @@ TEST_CASE( "r2x: a single JumpPad and related Target can be converted", "[Entity +TEST_CASE( "r2x: a single PointLight entity can be converted", "[EntityConverter]" ) { + + // Instantiate object + EntityConverter ec (PICKUP_FILENAME); + + // Mock up entity + std::vector entity; + entity.push_back(" type PointLight"); + entity.push_back(" Vector3 position -216.00000 -132.00000 -1488.000488"); + entity.push_back(" ColourXRGB32 color ffffc400"); + entity.push_back(" Float intensity 1.500000"); + entity.push_back(" Float nearAttenuation 32.000000"); + entity.push_back(" Float farAttenuation 160.000000"); + + // Mock up entity queue + std::queue> q; + q.push( entity ); + + // Match related entities (none) + ec.extractMapInfo( q ); + + // Convert a single entity + std::vector converted = ec.convert(entity); + + REQUIRE( converted[0] == "\"classname\" \"light\"\n" ); + REQUIRE( converted[1] == "\"origin\" \"-216.00000 -1488.000488 -132.00000\"\n" ); + REQUIRE( converted[2] == "\"light\" \"75\"\n" ); + + INFO( converted[3] ); + std::istringstream iss(converted[3]); + std::string attribute; + std::string r; + float red; + float green; + float blue; + iss >> attribute >> r >> green >> blue; + r.erase(r.begin()); //removing preceding quote is necessary + std::stringstream redStream(r); + redStream >> red; + + REQUIRE( attribute == "\"_color\"" ); + + REQUIRE( fabs(1.0 - red) <= DELTA ); + REQUIRE( fabs(0.768627 - green) <= DELTA ); + REQUIRE( fabs(0.0 - blue) <= DELTA ); +} From 3b73c4300a97335b56c7b3f44bcd08ba8510e088 Mon Sep 17 00:00:00 2001 From: suhrke Date: Thu, 6 Jul 2017 10:29:54 -0700 Subject: [PATCH 10/12] extractMapInfo now passes queue by value so parser can use it --- ReflexToQ3/includes/EntityConverter.cpp | 2 +- ReflexToQ3/includes/EntityConverter.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ReflexToQ3/includes/EntityConverter.cpp b/ReflexToQ3/includes/EntityConverter.cpp index d2acdd5..8dac498 100644 --- a/ReflexToQ3/includes/EntityConverter.cpp +++ b/ReflexToQ3/includes/EntityConverter.cpp @@ -94,7 +94,7 @@ EntityConverter::EntityConverter(const std::string &entityMapFile, *-------------------------------------------------------------------------------------- */ void -EntityConverter::extractMapInfo(std::queue> &entities) +EntityConverter::extractMapInfo(std::queue> entities) { if( haveMapInfo_ ) { std::cerr << "Map info already extracted, doing nothing" << std::endl; diff --git a/ReflexToQ3/includes/EntityConverter.hpp b/ReflexToQ3/includes/EntityConverter.hpp index 4e251c3..456a00f 100644 --- a/ReflexToQ3/includes/EntityConverter.hpp +++ b/ReflexToQ3/includes/EntityConverter.hpp @@ -97,7 +97,7 @@ class EntityConverter * THROWS: runtime_error when encountering malformed entity *-------------------------------------------------------------------------------------- */ - void extractMapInfo(std::queue> &entities); + void extractMapInfo(std::queue> entities); From 100c830c8390221393f58f785f889d5623f21629 Mon Sep 17 00:00:00 2001 From: suhrke Date: Thu, 6 Jul 2017 11:10:04 -0700 Subject: [PATCH 11/12] Added extractMapInfo(vector---) + finish const correct functions --- ReflexToQ3/includes/EntityConverter.cpp | 75 ++++++++++++++++--------- ReflexToQ3/includes/EntityConverter.hpp | 33 +++++++---- 2 files changed, 69 insertions(+), 39 deletions(-) diff --git a/ReflexToQ3/includes/EntityConverter.cpp b/ReflexToQ3/includes/EntityConverter.cpp index 8dac498..96e2614 100644 --- a/ReflexToQ3/includes/EntityConverter.cpp +++ b/ReflexToQ3/includes/EntityConverter.cpp @@ -84,15 +84,6 @@ EntityConverter::EntityConverter(const std::string &entityMapFile, -/* - *-------------------------------------------------------------------------------------- - * Class: EntityConverter - * Method: EntityConverter :: extractMapInfo - * Description: Read through entities, matching related as necessary - * Note: For now, accomplishes the same goal as the pre-scan - * constructor - *-------------------------------------------------------------------------------------- - */ void EntityConverter::extractMapInfo(std::queue> entities) { @@ -101,7 +92,7 @@ EntityConverter::extractMapInfo(std::queue> entities) } else { while ( ! entities.empty() ) { - std::vector entity = entities.front(); + std::vector entity( entities.front() ); entities.pop(); std::stringstream ss; @@ -120,6 +111,32 @@ EntityConverter::extractMapInfo(std::queue> entities) +void +EntityConverter::extractMapInfo(const std::vector> &entities) +{ + if( haveMapInfo_ ) { + std::cerr << "Map info already extracted, doing nothing" << std::endl; + } + else { + for ( int i = 0; i < entities.size(); i++ ) { + std::vector entity( entities[i] ); + + std::stringstream ss; + std::copy(entity.begin(), entity.end(), + std::ostream_iterator(ss, "\n")); + + std::string nextLine; + if ( getline(ss, nextLine )) { + extractFromEntity(nextLine, ss); + } + } + } + + haveMapInfo_ = true; +} + + + std::vector EntityConverter::convert(const std::vector &lines) { @@ -208,7 +225,7 @@ EntityConverter::convert(const std::vector &lines) * PROTECTED *-----------------------------------------------------------------------------*/ std::vector -EntityConverter::convertPickup(const std::vector &lines) +EntityConverter::convertPickup(const std::vector &lines) const { std::vector convertedLines; //can ignore angle of pickups in xonotic format @@ -245,7 +262,7 @@ EntityConverter::convertPickup(const std::vector &lines) if ( havePosition && havePickupID ) { std::stringstream pickupStream; - pickupStream << "\"classname\" \"" << pickupMapping_[pickupID] << "\"" << std::endl; + pickupStream << "\"classname\" \"" << pickupMapping_.find(pickupID)->second << "\"" << std::endl; convertedLines.push_back ( pickupStream.str() ); // coordinates reordered to x, z, y std::stringstream positionStream; @@ -272,7 +289,7 @@ EntityConverter::convertPickup(const std::vector &lines) *-------------------------------------------------------------------------------------- */ std::vector -EntityConverter::convertPlayerSpawn(const std::vector &lines) +EntityConverter::convertPlayerSpawn(const std::vector &lines) const { std::vector convertedLines; // Requires position coordinate @@ -377,7 +394,7 @@ EntityConverter::convertPlayerSpawn(const std::vector &lines) std::vector -EntityConverter::convertJumpPad(const std::vector &lines) +EntityConverter::convertJumpPad(const std::vector &lines) const { std::vector convertedLines; std::string targetName; @@ -402,7 +419,7 @@ EntityConverter::convertJumpPad(const std::vector &lines) std::vector -EntityConverter::convertTeleporter(const std::vector &lines) +EntityConverter::convertTeleporter(const std::vector &lines) const { std::vector convertedLines; std::string targetName; @@ -427,7 +444,7 @@ EntityConverter::convertTeleporter(const std::vector &lines) std::vector -EntityConverter::convertTarget(const std::vector &lines) +EntityConverter::convertTarget(const std::vector &lines) const { std::vector convertedLines; //position and name required, angles optional @@ -475,7 +492,7 @@ EntityConverter::convertTarget(const std::vector &lines) } if ( havePosition && haveName) { - if ( targetMap_[targetName] == "Teleporter") { + if ( targetMap_.find(targetName)->second == "Teleporter") { convertedLines.push_back ( "\"classname\" \"misc_teleporter_dest\"\n" ); // coordinates reordered to x, z, y // teleporter height is OFFSET @@ -484,7 +501,7 @@ EntityConverter::convertTarget(const std::vector &lines) offset(coords[1], OFFSET_PLAYER) << "\"" << std::endl; convertedLines.push_back ( oss.str() ); } - else if ( targetMap_[targetName] == "JumpPad") { + else if ( targetMap_.find(targetName)->second == "JumpPad") { convertedLines.push_back ( "\"classname\" \"target_position\"\n" ); // coordinates reordered to x, z, y std::stringstream oss; @@ -513,7 +530,7 @@ EntityConverter::convertTarget(const std::vector &lines) std::vector -EntityConverter::convertRaceStart(const std::vector &lines) +EntityConverter::convertRaceStart(const std::vector &lines) const { std::vector convertedLines; convertedLines.push_back ("\"classname\" \"trigger_race_checkpoint\"\n"); @@ -525,7 +542,7 @@ EntityConverter::convertRaceStart(const std::vector &lines) std::vector -EntityConverter::convertRaceFinish(const std::vector &lines) +EntityConverter::convertRaceFinish(const std::vector &lines) const { std::vector convertedLines; convertedLines.push_back ("\"classname\" \"trigger_race_checkpoint\"\n"); @@ -537,7 +554,7 @@ EntityConverter::convertRaceFinish(const std::vector &lines) std::vector -EntityConverter::convertPointLight(const std::vector &lines) +EntityConverter::convertPointLight(const std::vector &lines) const { std::vector convertedLines; //position and intensity required, color optional @@ -705,7 +722,7 @@ EntityConverter::offset(const std::string &value, const float amount) const void -EntityConverter::hexToRGB(const std::string &hex, float &r, float &g, float &b) +EntityConverter::hexToRGB(const std::string &hex, float &r, float &g, float &b) const { unsigned int value; std::stringstream ss; @@ -734,19 +751,21 @@ EntityConverter::adjustBrightness(const std::string &value) const // DEBUG void -EntityConverter::printMapping() +EntityConverter::printMapping() const { - std::map::iterator it; - for (it=pickupMapping_.begin(); it!=pickupMapping_.end(); ++it) + std::cout << std::endl << "Reflex pickup ID mapped to Xonotic pickup names: " << std::endl; + std::map::const_iterator it; + for ( it=pickupMapping_.begin(); it!=pickupMapping_.end(); ++it ) std::cout << it->first << " => " << it->second << std::endl; } // DEBUG void -EntityConverter::printTargetSources() +EntityConverter::printTargetSources() const { - std::map::iterator it; - for (it=targetMap_.begin(); it!=targetMap_.end(); ++it) + std::cout << std::endl << "Target and Sources: " << std::endl; + std::map::const_iterator it; + for ( it=targetMap_.begin(); it!=targetMap_.end(); ++it ) std::cout << it->first << " => " << it->second << std::endl; } diff --git a/ReflexToQ3/includes/EntityConverter.hpp b/ReflexToQ3/includes/EntityConverter.hpp index 456a00f..52cce09 100644 --- a/ReflexToQ3/includes/EntityConverter.hpp +++ b/ReflexToQ3/includes/EntityConverter.hpp @@ -98,6 +98,17 @@ class EntityConverter *-------------------------------------------------------------------------------------- */ void extractMapInfo(std::queue> entities); + /* + *-------------------------------------------------------------------------------------- + * Class: EntityConverter + * Method: EntityConverter :: extractMapInfo + * Description: Get information needed by the converter that can't be obtained + * in entity-by-entity conversion (teleport and jump pad + * Parameter: vector of vector of string entities, ALL entities in a .map file + * THROWS: runtime_error when encountering malformed entity + *-------------------------------------------------------------------------------------- + */ + void extractMapInfo(const std::vector> &entities); @@ -113,14 +124,14 @@ class EntityConverter * Return: vector of strings, the converted entity *-------------------------------------------------------------------------------------- */ - std::vector convertPickup(const std::vector &lines); - std::vector convertPlayerSpawn(const std::vector &lines); - std::vector convertJumpPad(const std::vector &lines); - std::vector convertTeleporter(const std::vector &lines); - std::vector convertTarget(const std::vector &lines); - std::vector convertRaceStart(const std::vector &lines); - std::vector convertRaceFinish(const std::vector &lines); - std::vector convertPointLight(const std::vector &lines); + std::vector convertPickup(const std::vector &lines) const; + std::vector convertPlayerSpawn(const std::vector &lines) const; + std::vector convertJumpPad(const std::vector &lines) const; + std::vector convertTeleporter(const std::vector &lines) const; + std::vector convertTarget(const std::vector &lines) const; + std::vector convertRaceStart(const std::vector &lines) const; + std::vector convertRaceFinish(const std::vector &lines) const; + std::vector convertPointLight(const std::vector &lines) const; @@ -194,7 +205,7 @@ class EntityConverter * Parameter: float b, RETURN BY REFERENCE: converted blue value *-------------------------------------------------------------------------------------- */ - void hexToRGB(const std::string &hex, float &r, float &g, float &b); + void hexToRGB(const std::string &hex, float &r, float &g, float &b) const; /* *-------------------------------------------------------------------------------------- * Class: EntityConverter @@ -207,8 +218,8 @@ class EntityConverter - void printMapping(); //DEBUG - void printTargetSources(); //DEBUG + void printMapping() const; //DEBUG + void printTargetSources() const; //DEBUG }; From 47eb891dfe636596f797701a7c1980c5d467103b Mon Sep 17 00:00:00 2001 From: suhrke Date: Thu, 6 Jul 2017 11:29:14 -0700 Subject: [PATCH 12/12] changed count loops into iterator loops where intent was more clear --- ReflexToQ3/includes/EntityConverter.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ReflexToQ3/includes/EntityConverter.cpp b/ReflexToQ3/includes/EntityConverter.cpp index 96e2614..f5fd1da 100644 --- a/ReflexToQ3/includes/EntityConverter.cpp +++ b/ReflexToQ3/includes/EntityConverter.cpp @@ -118,8 +118,9 @@ EntityConverter::extractMapInfo(const std::vector> &ent std::cerr << "Map info already extracted, doing nothing" << std::endl; } else { - for ( int i = 0; i < entities.size(); i++ ) { - std::vector entity( entities[i] ); + std::vector>::const_iterator it; + for ( it=entities.begin(); it!=entities.end(); ++it ) { + std::vector entity( *it ); std::stringstream ss; std::copy(entity.begin(), entity.end(),