This commit is contained in:
2017-07-06 13:52:51 -07:00
commit 9f32ca0568
5 changed files with 473 additions and 116 deletions

View File

@ -4,7 +4,7 @@ CFLAGS=-std=c++11 -I"./includes" -I"./includes/Catch/single_include" -I"./includ
TESTEX=test/test-parser TESTEX=test/test-parser
UNITEX=test/catch UNITEX=test/catch
all: main all: main unittest
main: planes.o brushdef.o oopless-parser.o EntityConverter.o main: planes.o brushdef.o oopless-parser.o EntityConverter.o
$(CC) $^ main.cpp $(CFLAGS) -o $(EX) 2>error8.log $(CC) $^ main.cpp $(CFLAGS) -o $(EX) 2>error8.log

View File

@ -25,25 +25,42 @@
#include <sstream> #include <sstream>
/*----------------------------------------------------------------------------- /*-----------------------------------------------------------------------------
* PUBLIC * 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), BRIGHTNESS_ADJUST(50.0)
{ {
//MUST RUN matchRelated method after this constructor //MUST RUN extractMapInfo method after this constructor
areEntitiesMatched_ = false; haveMapInfo_ = false;
// game modes default to enabled
ws_.cts = true;
ws_.ctf = true;
ws_.ffa = true;
ws_.tdm = true;
ws_.duel = true;
mapEntities(entityMapFile); mapEntities(entityMapFile);
} }
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), BRIGHTNESS_ADJUST(50.0)
{ {
haveMapInfo_ = false;
// game modes default to enabled
ws_.cts = true;
ws_.ctf = true;
ws_.ffa = true;
ws_.tdm = true;
ws_.duel = true;
mapEntities(entityMapFile); mapEntities(entityMapFile);
// Pre-scan for related entities // Pre-scan for info needed by converter
std::ifstream fin; std::ifstream fin;
fin.open(reflexMapFile); fin.open(reflexMapFile);
@ -51,14 +68,14 @@ EntityConverter::EntityConverter(std::string entityMapFile, std::string reflexMa
//Extract the source type of targets (teleporters or jump pads) //Extract the source type of targets (teleporters or jump pads)
std::string line; std::string line;
while (std::getline(fin, line)) { while (std::getline(fin, line)) {
addIfRelated(line, fin); extractFromEntity(line, fin);
} }
} }
else { else {
throw std::ios::failure( "Error: EntityConverter failed to open .map file" ); throw std::ios::failure( "Error: EntityConverter failed to open .map file" );
} }
fin.close(); fin.close();
areEntitiesMatched_ = true; haveMapInfo_ = true;
//DEBUG //DEBUG
//printMapping(); //printMapping();
@ -67,24 +84,15 @@ EntityConverter::EntityConverter(std::string entityMapFile, std::string reflexMa
/*
*--------------------------------------------------------------------------------------
* Class: EntityConverter
* Method: EntityConverter :: matchRelated
* Description: Read through entities, matching related as necessary
* Note: For now, accomplishes the same goal as the pre-scan
* constructor
*--------------------------------------------------------------------------------------
*/
void void
EntityConverter::matchRelated(std::queue<std::vector<std::string>> entities) EntityConverter::extractMapInfo(std::queue<std::vector<std::string>> entities)
{ {
if( areEntitiesMatched_ ) { if( haveMapInfo_ ) {
std::cerr << "Related entities are already matched, doing nothing" << std::endl; std::cerr << "Map info already extracted, doing nothing" << std::endl;
} }
else { else {
while ( ! entities.empty() ) { while ( ! entities.empty() ) {
std::vector<std::string> entity = entities.front(); std::vector<std::string> entity( entities.front() );
entities.pop(); entities.pop();
std::stringstream ss; std::stringstream ss;
@ -93,20 +101,47 @@ EntityConverter::matchRelated(std::queue<std::vector<std::string>> entities)
std::string nextLine; std::string nextLine;
if ( getline(ss, nextLine )) { if ( getline(ss, nextLine )) {
addIfRelated(nextLine, ss); extractFromEntity(nextLine, ss);
} }
} }
} }
areEntitiesMatched_ = true; haveMapInfo_ = true;
}
void
EntityConverter::extractMapInfo(const std::vector<std::vector<std::string>> &entities)
{
if( haveMapInfo_ ) {
std::cerr << "Map info already extracted, doing nothing" << std::endl;
}
else {
std::vector<std::vector<std::string>>::const_iterator it;
for ( it=entities.begin(); it!=entities.end(); ++it ) {
std::vector<std::string> entity( *it );
std::stringstream ss;
std::copy(entity.begin(), entity.end(),
std::ostream_iterator<std::string>(ss, "\n"));
std::string nextLine;
if ( getline(ss, nextLine )) {
extractFromEntity(nextLine, ss);
}
}
}
haveMapInfo_ = true;
} }
std::vector<std::string> std::vector<std::string>
EntityConverter::convert(std::vector<std::string> lines) EntityConverter::convert(const std::vector<std::string> &lines)
{ {
if ( areEntitiesMatched_ ) if ( haveMapInfo_ )
{ {
std::string attribute; std::string attribute;
std::string trash; //unused tokens std::string trash; //unused tokens
@ -121,8 +156,37 @@ EntityConverter::convert(std::vector<std::string> lines)
throw std::runtime_error("error: type is required"); throw std::runtime_error("error: type is required");
} }
// If worldspawn, first reenable all gamemodes
// then check worldspawn for disabled modes
// then RETURN EMPTY VECTOR
if ( type == "WorldSpawn" ) {
ws_.cts = true;
ws_.ctf = true;
ws_.ffa = true;
ws_.tdm = true;
ws_.duel = true;
if ( type == "Pickup" ) { // Each worldspawn can specify modes enabled/disabled
for ( int i = 1; i < lines.size(); ++i ) {
if ( lines[i].find("modeRace 0") != std::string::npos) {
ws_.cts = false;
}
else if ( lines[i].find("modeCTF 0") != std::string::npos) {
ws_.ctf = false;
}
else if ( lines[i].find("modeFFA 0") != std::string::npos) {
ws_.ffa = false;
}
else if ( lines[i].find("modeTDM 0") != std::string::npos) {
ws_.tdm = false;
}
else if ( lines[i].find("mode1v1 0") != std::string::npos) {
ws_.duel = false;
}
}
}
else if ( type == "Pickup" ) {
return convertPickup(lines); return convertPickup(lines);
} }
else if ( type == "PlayerSpawn" ) { else if ( type == "PlayerSpawn" ) {
@ -143,9 +207,12 @@ EntityConverter::convert(std::vector<std::string> lines)
else if ( type == "RaceFinish" ) { else if ( type == "RaceFinish" ) {
return convertRaceFinish(lines); return convertRaceFinish(lines);
} }
else if ( type == "PointLight" ) {
return convertPointLight(lines);
}
} }
else { 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 // If unsupported entity, return empty vector
@ -159,7 +226,7 @@ EntityConverter::convert(std::vector<std::string> lines)
* PROTECTED * PROTECTED
*-----------------------------------------------------------------------------*/ *-----------------------------------------------------------------------------*/
std::vector<std::string> std::vector<std::string>
EntityConverter::convertPickup(std::vector<std::string> &lines) EntityConverter::convertPickup(const std::vector<std::string> &lines) const
{ {
std::vector<std::string> convertedLines; std::vector<std::string> convertedLines;
//can ignore angle of pickups in xonotic format //can ignore angle of pickups in xonotic format
@ -195,14 +262,14 @@ EntityConverter::convertPickup(std::vector<std::string> &lines)
} }
if ( havePosition && havePickupID ) { if ( havePosition && havePickupID ) {
std::stringstream oss; std::stringstream pickupStream;
oss << "\"classname\" \"" << pickupMapping_[pickupID] << "\"" << std::endl; pickupStream << "\"classname\" \"" << pickupMapping_.find(pickupID)->second << "\"" << std::endl;
convertedLines.push_back ( oss.str() ); convertedLines.push_back ( pickupStream.str() );
// coordinates reordered to x, z, y // coordinates reordered to x, z, y
std::stringstream oss2; std::stringstream positionStream;
oss2 << "\"origin\" \"" << coords[0] << " " << coords[2] << " " << positionStream << "\"origin\" \"" << coords[0] << " " << coords[2] << " " <<
offset(coords[1], OFFSET_PICKUP) << "\"" << std::endl; offset(coords[1], OFFSET_PICKUP) << "\"" << std::endl;
convertedLines.push_back ( oss2.str() ); convertedLines.push_back ( positionStream.str() );
return convertedLines; return convertedLines;
} }
else { else {
@ -223,7 +290,7 @@ EntityConverter::convertPickup(std::vector<std::string> &lines)
*-------------------------------------------------------------------------------------- *--------------------------------------------------------------------------------------
*/ */
std::vector<std::string> std::vector<std::string>
EntityConverter::convertPlayerSpawn(std::vector<std::string> &lines) EntityConverter::convertPlayerSpawn(const std::vector<std::string> &lines) const
{ {
std::vector<std::string> convertedLines; std::vector<std::string> convertedLines;
// Requires position coordinate // Requires position coordinate
@ -234,7 +301,11 @@ EntityConverter::convertPlayerSpawn(std::vector<std::string> &lines)
int team = 0; int team = 0;
std::string trash; std::string trash;
bool havePosition = false; 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 ) { if ( lines.size() < 2 ) {
@ -259,9 +330,22 @@ EntityConverter::convertPlayerSpawn(std::vector<std::string> &lines)
throw std::runtime_error("error: Pickup entity requires Pickup ID"); 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" ) { else if ( type == "modeRace" ) {
isModeRace = false; 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" ) { else if ( type == "teamA" ) {
team = 2; // Bool8 teamA 0 indicates teamB only team = 2; // Bool8 teamA 0 indicates teamB only
} }
@ -271,7 +355,8 @@ EntityConverter::convertPlayerSpawn(std::vector<std::string> &lines)
} }
if ( havePosition ) { if ( havePosition ) {
if ( ! isModeRace ) { // Will convert all race points to dm/team spawns on maps that support race AND others
if ( isModeCtf || isModeTdm || isModeFfa || isModeDuel ) {
switch (team) { switch (team) {
case 0: case 0:
convertedLines.push_back ( "\"classname\" \"info_player_deathmatch\"\n" ); convertedLines.push_back ( "\"classname\" \"info_player_deathmatch\"\n" );
@ -283,7 +368,6 @@ EntityConverter::convertPlayerSpawn(std::vector<std::string> &lines)
convertedLines.push_back ( "\"classname\" \"info_player_team2\"\n" ); convertedLines.push_back ( "\"classname\" \"info_player_team2\"\n" );
break; break;
} }
} }
else { else {
convertedLines.push_back ( "\"classname\" \"info_player_race\"\n" ); convertedLines.push_back ( "\"classname\" \"info_player_race\"\n" );
@ -293,14 +377,14 @@ EntityConverter::convertPlayerSpawn(std::vector<std::string> &lines)
convertedLines.push_back ( "\"race_place\" \"-1\"\n" ); convertedLines.push_back ( "\"race_place\" \"-1\"\n" );
} }
std::stringstream oss; std::stringstream positionStream;
// coordinates reordered to x, z, y // 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; offset(coords[1], OFFSET_PLAYER) << "\"" << std::endl;
convertedLines.push_back ( oss.str() ); convertedLines.push_back ( positionStream.str() );
std::stringstream oss2; std::stringstream angleStream;
oss2 << "\"angle\" \"" << angle << "\"" << std::endl; angleStream << "\"angle\" \"" << angle << "\"" << std::endl;
convertedLines.push_back ( oss2.str() ); convertedLines.push_back (angleStream.str() );
return convertedLines; return convertedLines;
} }
else { else {
@ -311,7 +395,7 @@ EntityConverter::convertPlayerSpawn(std::vector<std::string> &lines)
std::vector<std::string> std::vector<std::string>
EntityConverter::convertJumpPad(std::vector<std::string> &lines) EntityConverter::convertJumpPad(const std::vector<std::string> &lines) const
{ {
std::vector<std::string> convertedLines; std::vector<std::string> convertedLines;
std::string targetName; std::string targetName;
@ -336,7 +420,7 @@ EntityConverter::convertJumpPad(std::vector<std::string> &lines)
std::vector<std::string> std::vector<std::string>
EntityConverter::convertTeleporter(std::vector<std::string> &lines) EntityConverter::convertTeleporter(const std::vector<std::string> &lines) const
{ {
std::vector<std::string> convertedLines; std::vector<std::string> convertedLines;
std::string targetName; std::string targetName;
@ -361,7 +445,7 @@ EntityConverter::convertTeleporter(std::vector<std::string> &lines)
std::vector<std::string> std::vector<std::string>
EntityConverter::convertTarget(std::vector<std::string> &lines) EntityConverter::convertTarget(const std::vector<std::string> &lines) const
{ {
std::vector<std::string> convertedLines; std::vector<std::string> convertedLines;
//position and name required, angles optional //position and name required, angles optional
@ -409,7 +493,7 @@ EntityConverter::convertTarget(std::vector<std::string> &lines)
} }
if ( havePosition && haveName) { if ( havePosition && haveName) {
if ( targetMap_[targetName] == "Teleporter") { if ( targetMap_.find(targetName)->second == "Teleporter") {
convertedLines.push_back ( "\"classname\" \"misc_teleporter_dest\"\n" ); convertedLines.push_back ( "\"classname\" \"misc_teleporter_dest\"\n" );
// coordinates reordered to x, z, y // coordinates reordered to x, z, y
// teleporter height is OFFSET // teleporter height is OFFSET
@ -418,7 +502,7 @@ EntityConverter::convertTarget(std::vector<std::string> &lines)
offset(coords[1], OFFSET_PLAYER) << "\"" << std::endl; offset(coords[1], OFFSET_PLAYER) << "\"" << std::endl;
convertedLines.push_back ( oss.str() ); convertedLines.push_back ( oss.str() );
} }
else if ( targetMap_[targetName] == "JumpPad") { else if ( targetMap_.find(targetName)->second == "JumpPad") {
convertedLines.push_back ( "\"classname\" \"target_position\"\n" ); convertedLines.push_back ( "\"classname\" \"target_position\"\n" );
// coordinates reordered to x, z, y // coordinates reordered to x, z, y
std::stringstream oss; std::stringstream oss;
@ -426,15 +510,15 @@ EntityConverter::convertTarget(std::vector<std::string> &lines)
coords[1] << "\"" << std::endl; coords[1] << "\"" << std::endl;
convertedLines.push_back ( oss.str() ); convertedLines.push_back ( oss.str() );
} }
std::stringstream oss; std::stringstream targetStream;
oss << "\"targetname\" \"" << targetName << "\"" << std::endl; targetStream << "\"targetname\" \"" << targetName << "\"" << std::endl;
convertedLines.push_back ( oss.str() ); convertedLines.push_back ( targetStream.str() );
// Write angle only if position and name exist // Write angle only if position and name exist
if ( haveAngle ) { if ( haveAngle ) {
std::stringstream oss2; std::stringstream angleStream;
oss2 << "\"angle\" \"" << angle << "\"" << std::endl; angleStream << "\"angle\" \"" << angle << "\"" << std::endl;
convertedLines.push_back (oss2.str() ); convertedLines.push_back( angleStream.str() );
} }
return convertedLines; return convertedLines;
} }
@ -447,7 +531,7 @@ EntityConverter::convertTarget(std::vector<std::string> &lines)
std::vector<std::string> std::vector<std::string>
EntityConverter::convertRaceStart(std::vector<std::string> &lines) EntityConverter::convertRaceStart(const std::vector<std::string> &lines) const
{ {
std::vector<std::string> convertedLines; std::vector<std::string> convertedLines;
convertedLines.push_back ("\"classname\" \"trigger_race_checkpoint\"\n"); convertedLines.push_back ("\"classname\" \"trigger_race_checkpoint\"\n");
@ -459,7 +543,7 @@ EntityConverter::convertRaceStart(std::vector<std::string> &lines)
std::vector<std::string> std::vector<std::string>
EntityConverter::convertRaceFinish(std::vector<std::string> &lines) EntityConverter::convertRaceFinish(const std::vector<std::string> &lines) const
{ {
std::vector<std::string> convertedLines; std::vector<std::string> convertedLines;
convertedLines.push_back ("\"classname\" \"trigger_race_checkpoint\"\n"); convertedLines.push_back ("\"classname\" \"trigger_race_checkpoint\"\n");
@ -470,12 +554,91 @@ EntityConverter::convertRaceFinish(std::vector<std::string> &lines)
std::vector<std::string>
EntityConverter::convertPointLight(const std::vector<std::string> &lines) const
{
std::vector<std::string> 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 * PRIVATE
*-----------------------------------------------------------------------------*/ *-----------------------------------------------------------------------------*/
std::string std::string
EntityConverter::getAttributeType(std::string line) EntityConverter::getAttributeType(const std::string &line) const
{ {
std::string type; std::string type;
std::string dataType; std::string dataType;
@ -490,7 +653,7 @@ EntityConverter::getAttributeType(std::string line)
void void
EntityConverter::mapEntities(std::string mapFile) EntityConverter::mapEntities(const std::string &mapFile)
{ {
std::ifstream fin; std::ifstream fin;
fin.open(mapFile); fin.open(mapFile);
@ -519,32 +682,34 @@ EntityConverter::mapEntities(std::string mapFile)
void void
EntityConverter::addIfRelated(std::string &line, std::istream &is) EntityConverter::extractFromEntity(const std::string &line, std::istream &is)
{ {
std::string trash; std::string trash;
std::string targetName; std::string targetName;
std::string nextLine;
if ( line.find("type Teleporter") != std::string::npos) { if ( line.find("type Teleporter") != std::string::npos) {
std::getline(is, line); std::getline(is, nextLine);
std::istringstream iss(line); std::istringstream iss(nextLine);
if ( ! (iss >> trash >> trash >> targetName)) { if ( ! (iss >> trash >> trash >> targetName)) {
throw std::runtime_error( "format error in .map file"); throw std::runtime_error( "format error in .map file");
} }
targetMap_.insert ( std::pair<std::string, std::string>(targetName, "Teleporter") ); targetMap_.insert ( std::pair<std::string, std::string>(targetName, "Teleporter") );
} }
else if ( line.find("type JumpPad") != std::string::npos) { else if ( line.find("type JumpPad") != std::string::npos) {
std::getline(is, line); std::getline(is, nextLine);
std::istringstream iss(line); std::istringstream iss(nextLine);
if ( ! (iss >> trash >> trash >> targetName)) { if ( ! (iss >> trash >> trash >> targetName)) {
throw std::runtime_error( "format error in .map file"); throw std::runtime_error( "format error in .map file");
} }
targetMap_.insert ( std::pair<std::string, std::string>(targetName, "JumpPad") ); targetMap_.insert ( std::pair<std::string, std::string>(targetName, "JumpPad") );
} }
} }
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); std::istringstream iss(value);
float c; float c;
iss >> c; iss >> c;
@ -557,21 +722,50 @@ std::string EntityConverter::offset(std::string value, float amount) {
void
EntityConverter::hexToRGB(const std::string &hex, float &r, float &g, float &b) const
{
unsigned int value;
std::stringstream ss;
ss << std::hex << hex;
ss >> value;
// 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;
}
int
EntityConverter::adjustBrightness(const std::string &value) const
{
float inputBright;
std::stringstream ss(value);
ss >> inputBright;
return static_cast<int>(inputBright * BRIGHTNESS_ADJUST);
}
// DEBUG // DEBUG
void void
EntityConverter::printMapping() EntityConverter::printMapping() const
{ {
std::map<int, std::string>::iterator it; std::cout << std::endl << "Reflex pickup ID mapped to Xonotic pickup names: " << std::endl;
std::map<int, std::string>::const_iterator it;
for ( it=pickupMapping_.begin(); it!=pickupMapping_.end(); ++it ) for ( it=pickupMapping_.begin(); it!=pickupMapping_.end(); ++it )
std::cout << it->first << " => " << it->second << std::endl; std::cout << it->first << " => " << it->second << std::endl;
} }
// DEBUG // DEBUG
void void
EntityConverter::printTargetSources() EntityConverter::printTargetSources() const
{ {
std::map<std::string, std::string>::iterator it; std::cout << std::endl << "Target and Sources: " << std::endl;
std::map<std::string, std::string>::const_iterator it;
for ( it=targetMap_.begin(); it!=targetMap_.end(); ++it ) for ( it=targetMap_.begin(); it!=targetMap_.end(); ++it )
std::cout << it->first << " => " << it->second << std::endl; std::cout << it->first << " => " << it->second << std::endl;
} }

View File

@ -35,6 +35,19 @@
struct WorldSpawn
{
bool cts;
bool ctf;
bool ffa;
bool tdm;
bool duel;
};
class EntityConverter class EntityConverter
{ {
public: public:
@ -43,24 +56,24 @@ class EntityConverter
* Class: EntityConverter * Class: EntityConverter
* Method: Constructor * Method: Constructor
* Description: Creates entity format mapping * 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 * Requires: .ent filename for mapping entities from reflex format to xonotic format
* THROWS: runtime_error on .ent format error * THROWS: runtime_error on .ent format error
* THROWS: std::ios::failure on IO failure * THROWS: std::ios::failure on IO failure
*-------------------------------------------------------------------------------------- *--------------------------------------------------------------------------------------
*/ */
EntityConverter(std::string entityMapFile); EntityConverter(const std::string &entityMapFile);
/* *-------------------------------------------------------------------------------------- /* *--------------------------------------------------------------------------------------
* Class: EntityConverter * Class: EntityConverter
* Method: Constructor * 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 entityMapFile, file maps source to target entity formats
* Parameter: string reflexMapFile, for pre-scan * Parameter: string reflexMapFile, for pre-scan
* THROWS: runtime_error on .ent format error * THROWS: runtime_error on .ent format error
* THROWS: std::ios::failure on IO failure * 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 * Class: EntityConverter
@ -70,21 +83,32 @@ class EntityConverter
* Return: vector of strings, single entity in the converted format * Return: vector of strings, single entity in the converted format
* *IF entity is not supported, returns EMPTY vector * *IF entity is not supported, returns EMPTY vector
* THROWS: runtime_error on malformed .map file * 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<std::string> convert(std::vector<std::string> lines); std::vector<std::string> convert(const std::vector<std::string> &lines);
/* /*
*-------------------------------------------------------------------------------------- *--------------------------------------------------------------------------------------
* Class: EntityConverter * Class: EntityConverter
* Method: EntityConverter :: matchRelated * Method: EntityConverter :: extractMapInfo
* Description: Finds related entities (targets of teleports, etc), call after parsing * Description: Get information needed by the converter that can't be obtained
* the entire .map * in entity-by-entity conversion (teleport and jump pad
* Parameter: queue of vector of string entities, ALL entities in a .map file * Parameter: queue of vector of string entities, ALL entities in a .map file
* THROWS: runtime_error when encountering malformed entity * THROWS: runtime_error when encountering malformed entity
*-------------------------------------------------------------------------------------- *--------------------------------------------------------------------------------------
*/ */
void matchRelated(std::queue<std::vector<std::string>> entities); void extractMapInfo(std::queue<std::vector<std::string>> 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<std::vector<std::string>> &entities);
@ -96,29 +120,34 @@ class EntityConverter
* Class: EntityConverter * Class: EntityConverter
* Method: EntityConverter :: convert~EntityName~ * Method: EntityConverter :: convert~EntityName~
* Description: Multiple methods to convert entity from reflex to xonotic format * 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 * Return: vector of strings, the converted entity
*-------------------------------------------------------------------------------------- *--------------------------------------------------------------------------------------
*/ */
std::vector<std::string> convertPickup(std::vector<std::string> &entity); std::vector<std::string> convertPickup(const std::vector<std::string> &lines) const;
std::vector<std::string> convertPlayerSpawn(std::vector<std::string> &entity); std::vector<std::string> convertPlayerSpawn(const std::vector<std::string> &lines) const;
std::vector<std::string> convertJumpPad(std::vector<std::string> &entity); std::vector<std::string> convertJumpPad(const std::vector<std::string> &lines) const;
std::vector<std::string> convertTeleporter(std::vector<std::string> &entity); std::vector<std::string> convertTeleporter(const std::vector<std::string> &lines) const;
std::vector<std::string> convertTarget(std::vector<std::string> &entity); std::vector<std::string> convertTarget(const std::vector<std::string> &lines) const;
std::vector<std::string> convertRaceStart(std::vector<std::string> &entity); std::vector<std::string> convertRaceStart(const std::vector<std::string> &lines) const;
std::vector<std::string> convertRaceFinish(std::vector<std::string> &entity); std::vector<std::string> convertRaceFinish(const std::vector<std::string> &lines) const;
std::vector<std::string> convertPointLight(const std::vector<std::string> &lines) const;
// Related entities must be matched prior to entity conversion
bool areEntitiesMatched_;
// Map Reflex pickup IDs to Xonotic pickup identifiers // Map Reflex pickup IDs to Xonotic pickup identifiers
std::map<int, std::string> pickupMapping_; std::map<int, std::string> pickupMapping_;
// Map targets (by name) to their source type // Map targets (by name) to their source type
std::map<std::string, std::string> targetMap_; std::map<std::string, std::string> targetMap_;
// Related entities must be matched prior to entity conversion
bool haveMapInfo_;
WorldSpawn ws_;
// Offsets for item/spawn height // Offsets for item/spawn height
const float OFFSET_PLAYER; const float OFFSET_PLAYER;
const float OFFSET_PICKUP; const float OFFSET_PICKUP;
// Brightness adjustment factor
const float BRIGHTNESS_ADJUST;
@ -131,7 +160,7 @@ class EntityConverter
* Parameter: string "line", entity keyword followed by the type * 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 * Class: EntityConverter
@ -141,18 +170,18 @@ class EntityConverter
* Return: true if no error, false if error * Return: true if no error, false if error
*-------------------------------------------------------------------------------------- *--------------------------------------------------------------------------------------
*/ */
void mapEntities(std::string mapFile); void mapEntities(const std::string &mapFile);
/* /*
*-------------------------------------------------------------------------------------- *--------------------------------------------------------------------------------------
* Class: EntityConverter * Class: EntityConverter
* Method: EntityConverter :: addIfRelated * Method: EntityConverter :: extractFromEntity
* Description: If the entity contains a related target/etc, add to map * Description: Get map info from a single entity
* Paramater: string line, the previous line (contains entity type) * Paramater: string line, the previous line (contains entity type)
* Parameter: istream is, an ifstream or a stringstream containing a * Parameter: istream is, an ifstream or a stringstream containing a
* single entity * single entity
*-------------------------------------------------------------------------------------- *--------------------------------------------------------------------------------------
*/ */
void addIfRelated(std::string &line, std::istream &is); void extractFromEntity(const std::string &line, std::istream &is);
/* /*
*-------------------------------------------------------------------------------------- *--------------------------------------------------------------------------------------
* Class: EntityConverter * Class: EntityConverter
@ -164,12 +193,33 @@ class EntityConverter
* Return: string, float value passed as string * 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;
/*
*--------------------------------------------------------------------------------------
* 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) const;
/*
*--------------------------------------------------------------------------------------
* 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;
void printMapping(); //DEBUG void printMapping() const; //DEBUG
void printTargetSources(); //DEBUG void printTargetSources() const; //DEBUG
}; };

View File

@ -1,11 +1,11 @@
40 item_health_small 40 item_health_small
41 item_health_medium 41 item_health_medium
42 item_health_large 42 item_health_big
43 item_health_mega 43 item_health_mega
50 item_armor_small 50 item_armor_small
51 item_armor_medium 51 item_armor_medium
52 item_armor_large 52 item_armor_big
53 item_armor_big 53 item_armor_mega
1 weapon_uzi 1 weapon_uzi
2 weapon_grenadelauncher 2 weapon_grenadelauncher
3 weapon_hagar 3 weapon_hagar

View File

@ -37,14 +37,14 @@ TEST_CASE( "r2x: Unsupported entity types cause return of empty vector", "[Entit
// Mock up entity // Mock up entity
std::vector<std::string> entity; std::vector<std::string> entity;
entity.push_back(" type Worldspawn"); entity.push_back(" type NotAType");
// Mock up entity queue // Mock up entity queue
std::queue<std::vector<std::string>> q; std::queue<std::vector<std::string>> q;
q.push( entity ); q.push( entity );
// Match related entities (none) // Match related entities (none)
ec.matchRelated( q ); ec.extractMapInfo( q );
// Convert a single entity // Convert a single entity
std::vector<std::string> converted = ec.convert(entity); std::vector<std::string> converted = ec.convert(entity);
@ -71,7 +71,7 @@ TEST_CASE( "r2x: a single Pickup entity can be converted", "[EntityConverter]" )
q.push( entity ); q.push( entity );
// Match related entities (none) // Match related entities (none)
ec.matchRelated( q ); ec.extractMapInfo( q );
// Convert a single entity // Convert a single entity
std::vector<std::string> converted = ec.convert(entity); std::vector<std::string> converted = ec.convert(entity);
@ -98,6 +98,16 @@ TEST_CASE( "r2x: a single PlayerSpawn (race) entity can be converted", "[EntityC
// Instantiate object // Instantiate object
EntityConverter ec (PICKUP_FILENAME); EntityConverter ec (PICKUP_FILENAME);
// Mock up WorldSpawn entity
// (needed for mode info)
std::vector<std::string> 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 // Mock up entity
std::vector<std::string> entity; std::vector<std::string> entity;
entity.push_back(" type PlayerSpawn"); entity.push_back(" type PlayerSpawn");
@ -112,12 +122,15 @@ TEST_CASE( "r2x: a single PlayerSpawn (race) entity can be converted", "[EntityC
// Mock up entity queue // Mock up entity queue
std::queue<std::vector<std::string>> q; std::queue<std::vector<std::string>> q;
q.push( worldspawn );
q.push( entity ); q.push( entity );
// Match related entities (none) // Match related entities (none)
ec.matchRelated( q ); 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<std::string> unused = ec.convert(worldspawn);
std::vector<std::string> converted = ec.convert(entity); std::vector<std::string> converted = ec.convert(entity);
REQUIRE( converted[0] == "\"classname\" \"info_player_race\"\n" ); REQUIRE( converted[0] == "\"classname\" \"info_player_race\"\n" );
@ -136,6 +149,20 @@ TEST_CASE( "r2x: a single PlayerSpawn (race) entity can be converted", "[EntityC
REQUIRE( coords[0] == "\"-216.00000" ); REQUIRE( coords[0] == "\"-216.00000" );
REQUIRE( coords[1] == "-1488.000488" ); REQUIRE( coords[1] == "-1488.000488" );
REQUIRE( fabs(-100.00000 - offsetCoord) <= DELTA ); REQUIRE( fabs(-100.00000 - offsetCoord) <= DELTA );
SECTION( "Encountering a new worldspawn reenables all modes" ) {
std::vector<std::string> basicWorldspawn;
basicWorldspawn.push_back(" type WorldSpawn");
std::vector<std::string> 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<std::string> u = ec.convert(basicWorldspawn);
std::vector<std::string> c = ec.convert(e);
REQUIRE( c[0] == "\"classname\" \"info_player_deathmatch\"\n" );
}
} }
@ -159,7 +186,7 @@ TEST_CASE( "r2x: a single PlayerSpawn (teamA) entity can be converted", "[Entity
q.push( entity ); q.push( entity );
// Match related entities (none) // Match related entities (none)
ec.matchRelated( q ); ec.extractMapInfo( q );
// Convert a single entity // Convert a single entity
std::vector<std::string> converted = ec.convert(entity); std::vector<std::string> converted = ec.convert(entity);
@ -182,6 +209,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<std::string> 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<std::vector<std::string>> q;
q.push( entity );
// Match related entities (none)
ec.extractMapInfo( q );
// Convert a single entity
std::vector<std::string> 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]" ) { TEST_CASE( "r2x: a single RaceStart entity can be converted", "[EntityConverter]" ) {
// Instantiate object // Instantiate object
@ -196,7 +263,7 @@ TEST_CASE( "r2x: a single RaceStart entity can be converted", "[EntityConverter]
q.push( entity ); q.push( entity );
// Match related entities (none) // Match related entities (none)
ec.matchRelated( q ); ec.extractMapInfo( q );
// Convert a single entity // Convert a single entity
std::vector<std::string> converted = ec.convert(entity); std::vector<std::string> converted = ec.convert(entity);
@ -222,7 +289,7 @@ TEST_CASE( "r2x: a single RaceFinish entity can be converted", "[EntityConverter
q.push( entity ); q.push( entity );
// Match related entities (none) // Match related entities (none)
ec.matchRelated( q ); ec.extractMapInfo( q );
// Convert a single entity // Convert a single entity
std::vector<std::string> converted = ec.convert(entity); std::vector<std::string> converted = ec.convert(entity);
@ -256,7 +323,7 @@ TEST_CASE( "r2x: a single Teleporter and related Target can be converted", "[Ent
q.push( entity2 ); q.push( entity2 );
// Match related entities (one pair) // Match related entities (one pair)
ec.matchRelated( q ); ec.extractMapInfo( q );
// Convert two entities // Convert two entities
std::vector<std::string> converted = ec.convert(entity); std::vector<std::string> converted = ec.convert(entity);
@ -274,7 +341,7 @@ TEST_CASE( "r2x: a single Teleporter and related Target can be converted", "[Ent
float offsetCoord; float offsetCoord;
iss >> attribute >> coords[0] >> coords[1] >> 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++ ) for( int i = 0; i < 10000000; i++ )
int x = i; int x = i;
@ -308,7 +375,7 @@ TEST_CASE( "r2x: a single JumpPad and related Target can be converted", "[Entity
q.push( entity2 ); q.push( entity2 );
// Match related entities (one pair) // Match related entities (one pair)
ec.matchRelated( q ); ec.extractMapInfo( q );
// Convert two entities // Convert two entities
std::vector<std::string> converted = ec.convert(entity); std::vector<std::string> converted = ec.convert(entity);
@ -323,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<std::string> 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<std::vector<std::string>> q;
q.push( entity );
// Match related entities (none)
ec.extractMapInfo( q );
// Convert a single entity
std::vector<std::string> 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 );
}