2017-06-07 06:52:54 +05:30
|
|
|
/*
|
|
|
|
* =====================================================================================
|
|
|
|
*
|
|
|
|
* Filename: EntityConverter.cpp
|
|
|
|
*
|
|
|
|
* Description: Convert Reflex entities into Xonotic entities
|
|
|
|
*
|
|
|
|
* Version: 0.1
|
|
|
|
* Created: 06/05/2017 07:15:25 PM
|
|
|
|
* Revision: none
|
|
|
|
* Compiler: gcc
|
|
|
|
*
|
|
|
|
* Author: suhrke@teknik.io
|
|
|
|
*
|
|
|
|
* =====================================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "EntityConverter.hpp"
|
|
|
|
|
|
|
|
#include <exception>
|
|
|
|
#include <fstream>
|
2017-07-05 17:40:31 +05:30
|
|
|
#include <iomanip>
|
2017-06-07 06:52:54 +05:30
|
|
|
#include <iostream>
|
2017-07-04 10:49:50 +05:30
|
|
|
#include <iterator>
|
2017-06-07 06:52:54 +05:30
|
|
|
#include <sstream>
|
|
|
|
|
2017-07-06 00:23:45 +05:30
|
|
|
|
2017-07-03 08:52:09 +05:30
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
|
|
* PUBLIC
|
|
|
|
*-----------------------------------------------------------------------------*/
|
|
|
|
|
2017-07-06 04:30:22 +05:30
|
|
|
EntityConverter::EntityConverter(std::string entityMapFile) : OFFSET_PLAYER(32.0), OFFSET_PICKUP(2.0)
|
2017-06-07 06:52:54 +05:30
|
|
|
{
|
2017-07-06 07:28:07 +05:30
|
|
|
//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;
|
|
|
|
|
2017-07-03 08:52:09 +05:30
|
|
|
mapEntities(entityMapFile);
|
|
|
|
}
|
2017-06-07 06:52:54 +05:30
|
|
|
|
2017-07-03 08:52:09 +05:30
|
|
|
|
|
|
|
|
2017-07-06 04:30:22 +05:30
|
|
|
EntityConverter::EntityConverter(std::string entityMapFile, std::string reflexMapFile) : OFFSET_PLAYER(32.0), OFFSET_PICKUP(2.0)
|
2017-07-03 08:52:09 +05:30
|
|
|
{
|
2017-07-06 07:28:07 +05:30
|
|
|
haveMapInfo_ = false;
|
|
|
|
// game modes default to enabled
|
|
|
|
mapinfo_.cts = true;
|
|
|
|
mapinfo_.ctf = true;
|
|
|
|
mapinfo_.ffa = true;
|
|
|
|
mapinfo_.tdm = true;
|
|
|
|
mapinfo_.duel = true;
|
|
|
|
|
2017-07-03 08:52:09 +05:30
|
|
|
mapEntities(entityMapFile);
|
2017-06-07 14:16:43 +05:30
|
|
|
|
2017-07-06 07:28:07 +05:30
|
|
|
// Pre-scan for info needed by converter
|
2017-07-03 08:52:09 +05:30
|
|
|
std::ifstream fin;
|
|
|
|
fin.open(reflexMapFile);
|
2017-06-07 06:52:54 +05:30
|
|
|
|
2017-07-03 08:52:09 +05:30
|
|
|
if ( fin.is_open() ) {
|
2017-06-07 14:16:43 +05:30
|
|
|
//Extract the source type of targets (teleporters or jump pads)
|
|
|
|
std::string line;
|
2017-07-03 08:52:09 +05:30
|
|
|
while (std::getline(fin, line)) {
|
2017-07-06 07:28:07 +05:30
|
|
|
extractFromEntity(line, fin);
|
2017-06-07 06:52:54 +05:30
|
|
|
}
|
|
|
|
}
|
2017-06-07 14:16:43 +05:30
|
|
|
else {
|
|
|
|
throw std::ios::failure( "Error: EntityConverter failed to open .map file" );
|
|
|
|
}
|
2017-07-03 08:52:09 +05:30
|
|
|
fin.close();
|
2017-07-06 07:28:07 +05:30
|
|
|
haveMapInfo_ = true;
|
2017-06-07 14:16:43 +05:30
|
|
|
|
|
|
|
//DEBUG
|
|
|
|
//printMapping();
|
|
|
|
//printTargetSources();
|
2017-06-07 06:52:54 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-07-03 12:11:59 +05:30
|
|
|
/*
|
|
|
|
*--------------------------------------------------------------------------------------
|
2017-07-04 10:49:50 +05:30
|
|
|
* Class: EntityConverter
|
2017-07-06 07:28:07 +05:30
|
|
|
* Method: EntityConverter :: extractMapInfo
|
2017-07-04 10:49:50 +05:30
|
|
|
* Description: Read through entities, matching related as necessary
|
|
|
|
* Note: For now, accomplishes the same goal as the pre-scan
|
|
|
|
* constructor
|
2017-07-03 12:11:59 +05:30
|
|
|
*--------------------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
void
|
2017-07-06 07:28:07 +05:30
|
|
|
EntityConverter::extractMapInfo(std::queue<std::vector<std::string>> entities)
|
2017-07-03 12:11:59 +05:30
|
|
|
{
|
2017-07-06 07:28:07 +05:30
|
|
|
if( haveMapInfo_ ) {
|
|
|
|
std::cerr << "Map info already extracted, doing nothing" << std::endl;
|
2017-07-03 12:11:59 +05:30
|
|
|
}
|
|
|
|
else {
|
2017-07-04 10:49:50 +05:30
|
|
|
while ( ! entities.empty() ) {
|
|
|
|
std::vector<std::string> entity = entities.front();
|
|
|
|
entities.pop();
|
|
|
|
|
|
|
|
std::stringstream ss;
|
|
|
|
std::copy(entity.begin(), entity.end(),
|
|
|
|
std::ostream_iterator<std::string>(ss, "\n"));
|
|
|
|
|
|
|
|
std::string nextLine;
|
|
|
|
if ( getline(ss, nextLine )) {
|
2017-07-06 07:28:07 +05:30
|
|
|
extractFromEntity(nextLine, ss);
|
2017-07-04 10:49:50 +05:30
|
|
|
}
|
|
|
|
}
|
2017-07-03 12:11:59 +05:30
|
|
|
}
|
2017-07-04 10:49:50 +05:30
|
|
|
|
2017-07-06 07:28:07 +05:30
|
|
|
haveMapInfo_ = true;
|
2017-07-03 12:11:59 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-07-03 08:52:09 +05:30
|
|
|
std::vector<std::string>
|
|
|
|
EntityConverter::convert(std::vector<std::string> lines)
|
2017-06-07 14:16:43 +05:30
|
|
|
{
|
2017-07-06 07:28:07 +05:30
|
|
|
if ( haveMapInfo_ )
|
2017-07-03 08:52:09 +05:30
|
|
|
{
|
|
|
|
std::string attribute;
|
|
|
|
std::string trash; //unused tokens
|
|
|
|
|
|
|
|
std::string type;
|
|
|
|
if ( lines.size() < 1 ) {
|
|
|
|
throw std::runtime_error("error: empty entity cannot be converted");
|
|
|
|
}
|
|
|
|
// second token is the type
|
|
|
|
std::istringstream iss(lines[0]);
|
|
|
|
if ( ! (iss >> trash >> type)) {
|
|
|
|
throw std::runtime_error("error: type is required");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-06 08:19:03 +05:30
|
|
|
// 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) {
|
|
|
|
mapinfo_.cts = false;
|
|
|
|
}
|
|
|
|
else if ( lines[i].find("modeCTF 0") != std::string::npos) {
|
|
|
|
mapinfo_.ctf = false;
|
|
|
|
}
|
|
|
|
else if ( lines[i].find("modeFFA 0") != std::string::npos) {
|
|
|
|
mapinfo_.ffa = false;
|
|
|
|
}
|
|
|
|
else if ( lines[i].find("modeTDM 0") != std::string::npos) {
|
|
|
|
mapinfo_.tdm = false;
|
|
|
|
}
|
|
|
|
else if ( lines[i].find("mode1v1 0") != std::string::npos) {
|
|
|
|
mapinfo_.duel = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
else if ( type == "Pickup" ) {
|
2017-07-03 08:52:09 +05:30
|
|
|
return convertPickup(lines);
|
|
|
|
}
|
|
|
|
else if ( type == "PlayerSpawn" ) {
|
|
|
|
return convertPlayerSpawn(lines);
|
|
|
|
}
|
|
|
|
else if ( type == "JumpPad" ) {
|
|
|
|
return convertJumpPad(lines);
|
|
|
|
}
|
|
|
|
else if ( type == "Teleporter" ) {
|
|
|
|
return convertTeleporter(lines);
|
|
|
|
}
|
|
|
|
else if ( type == "Target" ) {
|
|
|
|
return convertTarget(lines);
|
|
|
|
}
|
|
|
|
else if ( type == "RaceStart" ) {
|
|
|
|
return convertRaceStart(lines);
|
|
|
|
}
|
|
|
|
else if ( type == "RaceFinish" ) {
|
|
|
|
return convertRaceFinish(lines);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2017-07-06 07:28:07 +05:30
|
|
|
throw std::runtime_error( "error: Map info must be extracted prior to conversion" );
|
2017-07-03 08:52:09 +05:30
|
|
|
}
|
2017-07-05 15:51:51 +05:30
|
|
|
|
|
|
|
// If unsupported entity, return empty vector
|
|
|
|
std::vector<std::string> empty;
|
|
|
|
return empty;
|
2017-06-07 14:16:43 +05:30
|
|
|
}
|
2017-06-07 06:52:54 +05:30
|
|
|
|
|
|
|
|
2017-07-03 08:52:09 +05:30
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
2017-07-06 04:30:22 +05:30
|
|
|
* PROTECTED
|
2017-07-03 08:52:09 +05:30
|
|
|
*-----------------------------------------------------------------------------*/
|
|
|
|
std::vector<std::string>
|
|
|
|
EntityConverter::convertPickup(std::vector<std::string> &lines)
|
2017-06-07 06:52:54 +05:30
|
|
|
{
|
|
|
|
std::vector<std::string> convertedLines;
|
2017-07-03 12:11:59 +05:30
|
|
|
//can ignore angle of pickups in xonotic format
|
2017-06-07 06:52:54 +05:30
|
|
|
std::string coords[3];
|
2017-07-03 12:11:59 +05:30
|
|
|
int pickupID;
|
2017-07-03 08:52:09 +05:30
|
|
|
std::string trash;
|
2017-07-03 12:11:59 +05:30
|
|
|
bool havePosition = false;
|
|
|
|
bool havePickupID = false;
|
2017-06-07 06:52:54 +05:30
|
|
|
|
2017-07-03 08:52:09 +05:30
|
|
|
if ( lines.size() < 3 ) {
|
|
|
|
throw std::runtime_error("error: Pickup entity requires at least 3 lines");
|
2017-06-07 06:52:54 +05:30
|
|
|
}
|
|
|
|
|
2017-07-03 08:52:09 +05:30
|
|
|
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: Pickup entity requires coordinates");
|
2017-06-07 06:52:54 +05:30
|
|
|
}
|
2017-07-03 08:52:09 +05:30
|
|
|
havePosition = true;
|
|
|
|
}
|
|
|
|
else if ( type == "pickupType" ) {
|
|
|
|
std::istringstream iss(lines[i]);
|
|
|
|
// UInt8 pickupType ID
|
|
|
|
if ( ! (iss >> trash >> trash >> pickupID) ) {
|
|
|
|
throw std::runtime_error("error: Pickup entity requires Pickup ID");
|
2017-06-07 06:52:54 +05:30
|
|
|
}
|
2017-07-03 08:52:09 +05:30
|
|
|
havePickupID = true;
|
2017-06-07 06:52:54 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-03 08:52:09 +05:30
|
|
|
if ( havePosition && havePickupID ) {
|
2017-06-07 06:52:54 +05:30
|
|
|
std::stringstream oss;
|
2017-07-03 08:52:09 +05:30
|
|
|
oss << "\"classname\" \"" << pickupMapping_[pickupID] << "\"" << std::endl;
|
2017-06-07 06:52:54 +05:30
|
|
|
convertedLines.push_back ( oss.str() );
|
2017-07-03 08:52:09 +05:30
|
|
|
// coordinates reordered to x, z, y
|
|
|
|
std::stringstream oss2;
|
|
|
|
oss2 << "\"origin\" \"" << coords[0] << " " << coords[2] << " " <<
|
2017-07-06 00:23:45 +05:30
|
|
|
offset(coords[1], OFFSET_PICKUP) << "\"" << std::endl;
|
2017-07-03 08:52:09 +05:30
|
|
|
convertedLines.push_back ( oss2.str() );
|
|
|
|
return convertedLines;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
throw std::runtime_error("error: Pickup requires position and pickup ID, missing 1 or both");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-07-03 12:11:59 +05:30
|
|
|
/*
|
|
|
|
*--------------------------------------------------------------------------------------
|
|
|
|
* Class: EntityConverter
|
|
|
|
* Method: EntityConverter :: convertPlayerSpawn
|
|
|
|
* Notes: REFLEX
|
|
|
|
* -Optionally includes angle, team indicator, and 0 or more game
|
|
|
|
* mode indicators (defaults to all modes enabled so this line
|
|
|
|
* is used to disable a mode. eg Bool8 modeRace 0)
|
|
|
|
*--------------------------------------------------------------------------------------
|
|
|
|
*/
|
2017-07-03 08:52:09 +05:30
|
|
|
std::vector<std::string>
|
|
|
|
EntityConverter::convertPlayerSpawn(std::vector<std::string> &lines)
|
|
|
|
{
|
2017-07-03 12:11:59 +05:30
|
|
|
std::vector<std::string> convertedLines;
|
2017-07-04 06:40:05 +05:30
|
|
|
// Requires position coordinate
|
2017-07-03 12:11:59 +05:30
|
|
|
std::string coords[3];
|
2017-07-04 06:40:05 +05:30
|
|
|
// Requires an angle so if no reflex one is given, use 0
|
2017-07-03 12:11:59 +05:30
|
|
|
std::string angle("0");
|
2017-07-04 06:40:05 +05:30
|
|
|
// 1-2 for corresponding team, 0 for deathmatch spawn
|
2017-07-04 05:34:06 +05:30
|
|
|
int team = 0;
|
2017-07-03 12:11:59 +05:30
|
|
|
std::string trash;
|
|
|
|
bool havePosition = false;
|
2017-07-04 06:40:05 +05:30
|
|
|
bool isModeRace = true;
|
2017-07-03 12:11:59 +05:30
|
|
|
|
2017-07-03 08:52:09 +05:30
|
|
|
|
2017-07-03 12:11:59 +05:30
|
|
|
if ( lines.size() < 2 ) {
|
|
|
|
throw std::runtime_error("error: PlayerSpawn entity requires at least 2 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: PlayerSpawn entity requires position coordinates");
|
|
|
|
}
|
|
|
|
havePosition = true;
|
|
|
|
}
|
|
|
|
else if ( type == "angles" ) {
|
|
|
|
std::istringstream iss(lines[i]);
|
|
|
|
// UInt8 pickupType ID
|
2017-07-04 06:40:05 +05:30
|
|
|
if ( ! (iss >> trash >> trash >> angle )) {
|
2017-07-03 12:11:59 +05:30
|
|
|
throw std::runtime_error("error: Pickup entity requires Pickup ID");
|
|
|
|
}
|
|
|
|
}
|
2017-07-04 06:40:05 +05:30
|
|
|
else if ( type == "modeRace" ) {
|
2017-07-06 07:28:07 +05:30
|
|
|
isModeRace = false; // Bool8 modeRace 0 indicates this spawn is not for race
|
2017-07-04 06:40:05 +05:30
|
|
|
}
|
2017-07-03 12:11:59 +05:30
|
|
|
else if ( type == "teamA" ) {
|
|
|
|
team = 2; // Bool8 teamA 0 indicates teamB only
|
|
|
|
}
|
2017-07-04 06:40:05 +05:30
|
|
|
else if ( type == "teamB" ) {
|
2017-07-03 12:11:59 +05:30
|
|
|
team = 1; // Bool8 teamB 0 indicates teamA only
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( havePosition ) {
|
2017-07-06 08:19:03 +05:30
|
|
|
// 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 ) {
|
2017-07-04 06:40:05 +05:30
|
|
|
switch (team) {
|
|
|
|
case 0:
|
2017-07-04 12:36:13 +05:30
|
|
|
convertedLines.push_back ( "\"classname\" \"info_player_deathmatch\"\n" );
|
2017-07-04 06:40:05 +05:30
|
|
|
break;
|
|
|
|
case 1:
|
2017-07-04 12:36:13 +05:30
|
|
|
convertedLines.push_back ( "\"classname\" \"info_player_team1\"\n" );
|
2017-07-04 06:40:05 +05:30
|
|
|
break;
|
|
|
|
case 2:
|
2017-07-04 12:36:13 +05:30
|
|
|
convertedLines.push_back ( "\"classname\" \"info_player_team2\"\n" );
|
2017-07-04 06:40:05 +05:30
|
|
|
break;
|
|
|
|
}
|
2017-07-03 12:11:59 +05:30
|
|
|
}
|
2017-07-06 08:19:03 +05:30
|
|
|
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" );
|
|
|
|
}
|
2017-07-03 12:11:59 +05:30
|
|
|
|
2017-07-06 07:28:07 +05:30
|
|
|
|
2017-07-03 12:11:59 +05:30
|
|
|
std::stringstream oss;
|
|
|
|
// coordinates reordered to x, z, y
|
|
|
|
oss << "\"origin\" \"" << coords[0] << " " << coords[2] << " " <<
|
2017-07-06 00:23:45 +05:30
|
|
|
offset(coords[1], OFFSET_PLAYER) << "\"" << std::endl;
|
2017-07-03 12:11:59 +05:30
|
|
|
convertedLines.push_back ( oss.str() );
|
|
|
|
std::stringstream oss2;
|
2017-07-04 06:40:05 +05:30
|
|
|
oss2 << "\"angle\" \"" << angle << "\"" << std::endl;
|
2017-07-03 12:11:59 +05:30
|
|
|
convertedLines.push_back ( oss2.str() );
|
|
|
|
return convertedLines;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
throw std::runtime_error("error: PlayerSpawn entity requires position coordinates");
|
|
|
|
}
|
2017-07-03 08:52:09 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<std::string>
|
|
|
|
EntityConverter::convertJumpPad(std::vector<std::string> &lines)
|
|
|
|
{
|
|
|
|
std::vector<std::string> convertedLines;
|
2017-07-03 12:11:59 +05:30
|
|
|
std::string targetName;
|
2017-07-03 08:52:09 +05:30
|
|
|
std::string trash;
|
|
|
|
|
|
|
|
if ( lines.size() < 2 ) {
|
|
|
|
throw std::runtime_error("error: JumpPad entity requires at least 2 lines");
|
|
|
|
}
|
|
|
|
std::istringstream iss(lines[1]);
|
|
|
|
// String32 target targetName
|
|
|
|
if ( ! (iss >> trash >> trash >> targetName) ) {
|
|
|
|
throw std::runtime_error("error: JumpPad entity requires target name");
|
|
|
|
}
|
|
|
|
|
|
|
|
convertedLines.push_back ( "\"classname\" \"trigger_push\"\n" );
|
|
|
|
std::stringstream oss;
|
|
|
|
oss << "\"target\" \"" << targetName << "\"" << std::endl;
|
|
|
|
convertedLines.push_back ( oss.str() );
|
|
|
|
return convertedLines;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<std::string>
|
|
|
|
EntityConverter::convertTeleporter(std::vector<std::string> &lines)
|
|
|
|
{
|
|
|
|
std::vector<std::string> convertedLines;
|
2017-07-03 12:11:59 +05:30
|
|
|
std::string targetName;
|
2017-07-03 08:52:09 +05:30
|
|
|
std::string trash;
|
|
|
|
|
|
|
|
if ( lines.size() < 2 ) {
|
|
|
|
throw std::runtime_error("error: Teleport entity requires at least 2 lines");
|
|
|
|
}
|
|
|
|
std::istringstream iss(lines[1]);
|
|
|
|
// String32 target targetName
|
|
|
|
if ( ! (iss >> trash >> trash >> targetName) ) {
|
|
|
|
throw std::runtime_error( "error: Teleport entity requires target name" );
|
|
|
|
}
|
|
|
|
|
|
|
|
convertedLines.push_back ( "\"classname\" \"trigger_teleport\"\n" );
|
|
|
|
std::stringstream oss;
|
2017-07-03 12:11:59 +05:30
|
|
|
oss << "\"target\" \"" << targetName << "\"" << std::endl;
|
2017-07-03 08:52:09 +05:30
|
|
|
convertedLines.push_back ( oss.str() );
|
|
|
|
return convertedLines;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<std::string>
|
|
|
|
EntityConverter::convertTarget(std::vector<std::string> &lines)
|
|
|
|
{
|
|
|
|
std::vector<std::string> convertedLines;
|
|
|
|
//position and name required, angles optional
|
2017-07-03 12:11:59 +05:30
|
|
|
std::string coords[3];
|
2017-07-03 08:52:09 +05:30
|
|
|
std::string targetName;
|
|
|
|
std::string angle;
|
2017-07-03 12:11:59 +05:30
|
|
|
std::string trash;
|
2017-07-03 08:52:09 +05:30
|
|
|
bool havePosition = false;
|
|
|
|
bool haveName = false;
|
|
|
|
bool haveAngle = false;
|
|
|
|
|
2017-07-04 07:57:18 +05:30
|
|
|
|
2017-07-03 12:11:59 +05:30
|
|
|
if ( lines.size() < 3 ) {
|
|
|
|
throw std::runtime_error("error: Target entity requires at least 3 lines");
|
|
|
|
}
|
|
|
|
|
2017-07-03 08:52:09 +05:30
|
|
|
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: Target entity requires coordinates" );
|
2017-06-07 06:52:54 +05:30
|
|
|
}
|
2017-07-03 08:52:09 +05:30
|
|
|
havePosition = true;
|
|
|
|
}
|
|
|
|
else if ( type == "name" ) {
|
|
|
|
std::istringstream iss(lines[i]);
|
|
|
|
// UInt8 name uniqueName
|
|
|
|
if ( ! (iss >> trash >> trash >> targetName) ) {
|
|
|
|
throw std::runtime_error( "error: Target entity requires target name" );
|
2017-06-07 06:52:54 +05:30
|
|
|
}
|
2017-07-03 08:52:09 +05:30
|
|
|
haveName = true;
|
|
|
|
}
|
|
|
|
else if ( type == "angles" ) {
|
2017-07-03 12:11:59 +05:30
|
|
|
std::istringstream iss(lines[i]);
|
2017-07-03 08:52:09 +05:30
|
|
|
// Vector3 angles angle notapplicable notapplicable
|
2017-07-03 12:11:59 +05:30
|
|
|
if ( ! (iss >> trash >> trash >> angle) ) {
|
2017-07-03 08:52:09 +05:30
|
|
|
throw std::runtime_error( "error: Target entity requires target angle if specified" );
|
2017-06-07 06:52:54 +05:30
|
|
|
}
|
2017-07-03 08:52:09 +05:30
|
|
|
haveAngle = true;
|
|
|
|
}
|
2017-06-07 06:52:54 +05:30
|
|
|
|
2017-07-03 08:52:09 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
if ( havePosition && haveName) {
|
|
|
|
if ( targetMap_[targetName] == "Teleporter") {
|
|
|
|
convertedLines.push_back ( "\"classname\" \"misc_teleporter_dest\"\n" );
|
2017-07-05 17:40:31 +05:30
|
|
|
// coordinates reordered to x, z, y
|
|
|
|
// teleporter height is OFFSET
|
|
|
|
std::stringstream oss;
|
|
|
|
oss << "\"origin\" \"" << coords[0] << " " << coords[2] << " " <<
|
2017-07-06 00:23:45 +05:30
|
|
|
offset(coords[1], OFFSET_PLAYER) << "\"" << std::endl;
|
2017-07-05 17:40:31 +05:30
|
|
|
convertedLines.push_back ( oss.str() );
|
2017-06-07 06:52:54 +05:30
|
|
|
}
|
2017-07-03 08:52:09 +05:30
|
|
|
else if ( targetMap_[targetName] == "JumpPad") {
|
2017-07-04 12:36:13 +05:30
|
|
|
convertedLines.push_back ( "\"classname\" \"target_position\"\n" );
|
2017-07-05 17:40:31 +05:30
|
|
|
// coordinates reordered to x, z, y
|
|
|
|
std::stringstream oss;
|
|
|
|
oss << "\"origin\" \"" << coords[0] << " " << coords[2] << " " <<
|
|
|
|
coords[1] << "\"" << std::endl;
|
|
|
|
convertedLines.push_back ( oss.str() );
|
2017-07-03 08:52:09 +05:30
|
|
|
}
|
2017-07-04 12:36:13 +05:30
|
|
|
std::stringstream oss;
|
|
|
|
oss << "\"targetname\" \"" << targetName << "\"" << std::endl;
|
|
|
|
convertedLines.push_back ( oss.str() );
|
2017-07-03 08:52:09 +05:30
|
|
|
|
|
|
|
// Write angle only if position and name exist
|
|
|
|
if ( haveAngle ) {
|
2017-07-05 17:40:31 +05:30
|
|
|
std::stringstream oss2;
|
|
|
|
oss2 << "\"angle\" \"" << angle << "\"" << std::endl;
|
|
|
|
convertedLines.push_back (oss2.str() );
|
2017-06-07 06:52:54 +05:30
|
|
|
}
|
2017-07-04 07:57:18 +05:30
|
|
|
return convertedLines;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
throw std::runtime_error("error: Target entity requires position coordinates and targetname");
|
2017-06-07 06:52:54 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2017-07-03 08:52:09 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<std::string>
|
|
|
|
EntityConverter::convertRaceStart(std::vector<std::string> &lines)
|
|
|
|
{
|
|
|
|
std::vector<std::string> convertedLines;
|
2017-07-04 12:36:13 +05:30
|
|
|
convertedLines.push_back ("\"classname\" \"trigger_race_checkpoint\"\n");
|
|
|
|
convertedLines.push_back ("\"targetname\" \"cp1\"\n");
|
|
|
|
convertedLines.push_back ("\"cnt\" \"1\"\n");
|
2017-07-03 08:52:09 +05:30
|
|
|
return convertedLines;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<std::string>
|
|
|
|
EntityConverter::convertRaceFinish(std::vector<std::string> &lines)
|
|
|
|
{
|
|
|
|
std::vector<std::string> convertedLines;
|
2017-07-04 12:36:13 +05:30
|
|
|
convertedLines.push_back ("\"classname\" \"trigger_race_checkpoint\"\n");
|
|
|
|
convertedLines.push_back ("\"targetname\" \"finish\"\n");
|
|
|
|
convertedLines.push_back ("\"cnt\" \"0\"\n");
|
2017-07-03 08:52:09 +05:30
|
|
|
return convertedLines;
|
|
|
|
}
|
|
|
|
|
2017-07-06 04:30:22 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
|
|
* PRIVATE
|
|
|
|
*-----------------------------------------------------------------------------*/
|
|
|
|
std::string
|
|
|
|
EntityConverter::getAttributeType(std::string line)
|
|
|
|
{
|
|
|
|
std::string type;
|
|
|
|
std::string dataType;
|
|
|
|
std::istringstream iss(line);
|
|
|
|
if ( ! (iss >> dataType >> type )) {
|
|
|
|
return std::string();
|
|
|
|
}
|
|
|
|
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
EntityConverter::mapEntities(std::string mapFile)
|
|
|
|
{
|
|
|
|
std::ifstream fin;
|
|
|
|
fin.open(mapFile);
|
|
|
|
|
|
|
|
if ( fin.is_open() ) {
|
|
|
|
//Read .ent contents into pickup map
|
|
|
|
std::string line;
|
|
|
|
while (std::getline(fin, line)) {
|
|
|
|
std::istringstream iss(line);
|
|
|
|
// Reflex ID corresponds to xonotic pickup name
|
|
|
|
int id;
|
|
|
|
std::string pickup;
|
|
|
|
if ( ! (iss >> id >> pickup)) {
|
|
|
|
throw std::runtime_error( "format error in .ent file" );
|
|
|
|
}
|
|
|
|
pickupMapping_.insert ( std::pair<int, std::string>(id, pickup) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
throw std::ios::failure( "Error: EntityConverter failed to open .ent file" );
|
|
|
|
}
|
|
|
|
fin.close();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2017-07-06 07:28:07 +05:30
|
|
|
EntityConverter::extractFromEntity(std::string &line, std::istream &is)
|
2017-07-06 04:30:22 +05:30
|
|
|
{
|
|
|
|
std::string trash;
|
|
|
|
std::string targetName;
|
|
|
|
if ( line.find("type Teleporter") != std::string::npos) {
|
|
|
|
std::getline(is, line);
|
|
|
|
std::istringstream iss(line);
|
|
|
|
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);
|
|
|
|
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::istringstream iss(value);
|
|
|
|
float c;
|
|
|
|
iss >> c;
|
|
|
|
c += amount;
|
|
|
|
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << std::fixed << std::setprecision(5) << c;
|
|
|
|
return ss.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-07-03 08:52:09 +05:30
|
|
|
// DEBUG
|
|
|
|
void
|
|
|
|
EntityConverter::printMapping()
|
|
|
|
{
|
|
|
|
std::map<int, std::string>::iterator it;
|
|
|
|
for (it=pickupMapping_.begin(); it!=pickupMapping_.end(); ++it)
|
|
|
|
std::cout << it->first << " => " << it->second << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
// DEBUG
|
|
|
|
void
|
|
|
|
EntityConverter::printTargetSources()
|
|
|
|
{
|
|
|
|
std::map<std::string, std::string>::iterator it;
|
|
|
|
for (it=targetMap_.begin(); it!=targetMap_.end(); ++it)
|
|
|
|
std::cout << it->first << " => " << it->second << std::endl;
|
|
|
|
}
|
|
|
|
|