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
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

View File

@ -25,25 +25,42 @@
#include <sstream>
/*-----------------------------------------------------------------------------
* 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
areEntitiesMatched_ = false;
//MUST RUN extractMapInfo method after this constructor
haveMapInfo_ = false;
// game modes default to enabled
ws_.cts = true;
ws_.ctf = true;
ws_.ffa = true;
ws_.tdm = true;
ws_.duel = true;
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);
// Pre-scan for related entities
// Pre-scan for info needed by converter
std::ifstream fin;
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)
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();
@ -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
EntityConverter::matchRelated(std::queue<std::vector<std::string>> entities)
EntityConverter::extractMapInfo(std::queue<std::vector<std::string>> 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() ) {
std::vector<std::string> entity = entities.front();
std::vector<std::string> entity( entities.front() );
entities.pop();
std::stringstream ss;
@ -93,20 +101,47 @@ EntityConverter::matchRelated(std::queue<std::vector<std::string>> entities)
std::string 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>
EntityConverter::convert(std::vector<std::string> lines)
EntityConverter::convert(const std::vector<std::string> &lines)
{
if ( areEntitiesMatched_ )
if ( haveMapInfo_ )
{
std::string attribute;
std::string trash; //unused tokens
@ -121,8 +156,37 @@ EntityConverter::convert(std::vector<std::string> lines)
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);
}
else if ( type == "PlayerSpawn" ) {
@ -143,9 +207,12 @@ EntityConverter::convert(std::vector<std::string> lines)
else if ( type == "RaceFinish" ) {
return convertRaceFinish(lines);
}
else if ( type == "PointLight" ) {
return convertPointLight(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
@ -159,7 +226,7 @@ EntityConverter::convert(std::vector<std::string> lines)
* PROTECTED
*-----------------------------------------------------------------------------*/
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;
//can ignore angle of pickups in xonotic format
@ -195,14 +262,14 @@ EntityConverter::convertPickup(std::vector<std::string> &lines)
}
if ( havePosition && havePickupID ) {
std::stringstream oss;
oss << "\"classname\" \"" << pickupMapping_[pickupID] << "\"" << std::endl;
convertedLines.push_back ( oss.str() );
std::stringstream pickupStream;
pickupStream << "\"classname\" \"" << pickupMapping_.find(pickupID)->second << "\"" << 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 {
@ -223,7 +290,7 @@ EntityConverter::convertPickup(std::vector<std::string> &lines)
*--------------------------------------------------------------------------------------
*/
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;
// Requires position coordinate
@ -234,7 +301,11 @@ EntityConverter::convertPlayerSpawn(std::vector<std::string> &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 ) {
@ -259,9 +330,22 @@ EntityConverter::convertPlayerSpawn(std::vector<std::string> &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;
}
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
}
@ -271,7 +355,8 @@ EntityConverter::convertPlayerSpawn(std::vector<std::string> &lines)
}
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) {
case 0:
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" );
break;
}
}
else {
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" );
}
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 {
@ -311,7 +395,7 @@ EntityConverter::convertPlayerSpawn(std::vector<std::string> &lines)
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::string targetName;
@ -336,7 +420,7 @@ EntityConverter::convertJumpPad(std::vector<std::string> &lines)
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::string targetName;
@ -361,7 +445,7 @@ EntityConverter::convertTeleporter(std::vector<std::string> &lines)
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;
//position and name required, angles optional
@ -409,7 +493,7 @@ EntityConverter::convertTarget(std::vector<std::string> &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
@ -418,7 +502,7 @@ EntityConverter::convertTarget(std::vector<std::string> &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;
@ -426,15 +510,15 @@ EntityConverter::convertTarget(std::vector<std::string> &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;
}
@ -447,7 +531,7 @@ EntityConverter::convertTarget(std::vector<std::string> &lines)
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;
convertedLines.push_back ("\"classname\" \"trigger_race_checkpoint\"\n");
@ -459,7 +543,7 @@ EntityConverter::convertRaceStart(std::vector<std::string> &lines)
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;
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
*-----------------------------------------------------------------------------*/
std::string
EntityConverter::getAttributeType(std::string line)
EntityConverter::getAttributeType(const std::string &line) const
{
std::string type;
std::string dataType;
@ -490,7 +653,7 @@ EntityConverter::getAttributeType(std::string line)
void
EntityConverter::mapEntities(std::string mapFile)
EntityConverter::mapEntities(const std::string &mapFile)
{
std::ifstream fin;
fin.open(mapFile);
@ -519,32 +682,34 @@ EntityConverter::mapEntities(std::string mapFile)
void
EntityConverter::addIfRelated(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<std::string, std::string>(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");
}
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);
float 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
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 )
std::cout << it->first << " => " << it->second << std::endl;
}
// DEBUG
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 )
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
{
public:
@ -43,24 +56,24 @@ 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
*--------------------------------------------------------------------------------------
*/
EntityConverter(std::string entityMapFile);
EntityConverter(const std::string &entityMapFile);
/* *--------------------------------------------------------------------------------------
* 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
* 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
@ -70,21 +83,32 @@ 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<std::string> convert(std::vector<std::string> lines);
std::vector<std::string> convert(const std::vector<std::string> &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<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
* 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<std::string> convertPickup(std::vector<std::string> &entity);
std::vector<std::string> convertPlayerSpawn(std::vector<std::string> &entity);
std::vector<std::string> convertJumpPad(std::vector<std::string> &entity);
std::vector<std::string> convertTeleporter(std::vector<std::string> &entity);
std::vector<std::string> convertTarget(std::vector<std::string> &entity);
std::vector<std::string> convertRaceStart(std::vector<std::string> &entity);
std::vector<std::string> convertRaceFinish(std::vector<std::string> &entity);
std::vector<std::string> convertPickup(const std::vector<std::string> &lines) const;
std::vector<std::string> convertPlayerSpawn(const std::vector<std::string> &lines) const;
std::vector<std::string> convertJumpPad(const std::vector<std::string> &lines) const;
std::vector<std::string> convertTeleporter(const std::vector<std::string> &lines) const;
std::vector<std::string> convertTarget(const std::vector<std::string> &lines) const;
std::vector<std::string> convertRaceStart(const std::vector<std::string> &lines) const;
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
std::map<int, std::string> pickupMapping_;
// Map targets (by name) to their source type
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
const float OFFSET_PLAYER;
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
*--------------------------------------------------------------------------------------
*/
std::string getAttributeType(std::string line);
std::string getAttributeType(const std::string &line) const;
/*
*--------------------------------------------------------------------------------------
* Class: EntityConverter
@ -141,18 +170,18 @@ class EntityConverter
* Return: true if no error, false if error
*--------------------------------------------------------------------------------------
*/
void mapEntities(std::string mapFile);
void mapEntities(const std::string &mapFile);
/*
*--------------------------------------------------------------------------------------
* 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(const std::string &line, std::istream &is);
/*
*--------------------------------------------------------------------------------------
* Class: EntityConverter
@ -164,12 +193,33 @@ 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;
/*
*--------------------------------------------------------------------------------------
* 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 printTargetSources(); //DEBUG
void printMapping() const; //DEBUG
void printTargetSources() const; //DEBUG
};

View File

@ -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

View File

@ -37,14 +37,14 @@ TEST_CASE( "r2x: Unsupported entity types cause return of empty vector", "[Entit
// Mock up entity
std::vector<std::string> entity;
entity.push_back(" type Worldspawn");
entity.push_back(" type NotAType");
// Mock up entity queue
std::queue<std::vector<std::string>> q;
q.push( entity );
// Match related entities (none)
ec.matchRelated( q );
ec.extractMapInfo( q );
// Convert a single 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 );
// Match related entities (none)
ec.matchRelated( q );
ec.extractMapInfo( q );
// Convert a single 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
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
std::vector<std::string> entity;
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
std::queue<std::vector<std::string>> q;
q.push( worldspawn );
q.push( entity );
// 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);
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[1] == "-1488.000488" );
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 );
// Match related entities (none)
ec.matchRelated( q );
ec.extractMapInfo( q );
// Convert a single 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]" ) {
// Instantiate object
@ -196,7 +263,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<std::string> converted = ec.convert(entity);
@ -222,7 +289,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<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 );
// Match related entities (one pair)
ec.matchRelated( q );
ec.extractMapInfo( q );
// Convert two entities
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;
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;
@ -308,7 +375,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<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 );
}