2017-06-07 06:52:54 +05:30
/*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*
* Filename : EntityConverter . cpp
*
* Description : Convert Reflex entities into Xonotic entities
*
2017-07-08 15:20:37 +05:30
* Version : 1.0
2017-06-07 06:52:54 +05:30
* 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-08 19:50:55 +05:30
2017-07-03 08:52:09 +05:30
/*-----------------------------------------------------------------------------
* PUBLIC
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2017-07-08 19:50:55 +05:30
2017-07-06 21:22:59 +05:30
EntityConverter : : EntityConverter ( const std : : string & entityMapFile ) :
2017-07-08 15:20:37 +05:30
OFFSET_PLAYER ( 32.0 ) , OFFSET_PICKUP ( 2.0 ) , BRIGHTNESS_ADJUST ( 50.0 ) ,
OUTPUT_PRECISION ( 10 )
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
2017-07-06 21:22:59 +05:30
ws_ . cts = true ;
ws_ . ctf = true ;
ws_ . ffa = true ;
ws_ . tdm = true ;
ws_ . duel = true ;
2017-07-06 07:28:07 +05:30
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 21:22:59 +05:30
EntityConverter : : EntityConverter ( const std : : string & entityMapFile ,
const std : : string & reflexMapFile ) : OFFSET_PLAYER ( 32.0 ) ,
2017-07-08 15:20:37 +05:30
OFFSET_PICKUP ( 2.0 ) , BRIGHTNESS_ADJUST ( 50.0 ) , OUTPUT_PRECISION ( 10 )
2017-07-03 08:52:09 +05:30
{
2017-07-06 07:28:07 +05:30
haveMapInfo_ = false ;
// game modes default to enabled
2017-07-06 21:22:59 +05:30
ws_ . cts = true ;
ws_ . ctf = true ;
ws_ . ffa = true ;
ws_ . tdm = true ;
ws_ . duel = true ;
2017-07-06 07:28:07 +05:30
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 {
2017-07-08 19:50:55 +05:30
throw std : : ios : : failure ( " Error: EntityConverter failed to open .map file " + reflexMapFile ) ;
2017-06-07 14:16:43 +05:30
}
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
void
2017-07-06 22:59:54 +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 ( ) ) {
2017-07-06 23:40:04 +05:30
std : : vector < std : : string > entity ( entities . front ( ) ) ;
2017-07-04 10:49:50 +05:30
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-06 23:40:04 +05:30
void
EntityConverter : : extractMapInfo ( const std : : vector < std : : vector < std : : string > > & entities )
{
if ( haveMapInfo_ ) {
std : : cerr < < " Map info already extracted, doing nothing " < < std : : endl ;
}
else {
2017-07-06 23:59:14 +05:30
std : : vector < std : : vector < std : : string > > : : const_iterator it ;
for ( it = entities . begin ( ) ; it ! = entities . end ( ) ; + + it ) {
std : : vector < std : : string > entity ( * it ) ;
2017-07-06 23:40:04 +05:30
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 ;
}
2017-07-03 08:52:09 +05:30
std : : vector < std : : string >
2017-07-06 09:02:49 +05:30
EntityConverter : : convert ( const 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 ) {
2017-07-08 19:50:55 +05:30
throw std : : runtime_error (
makeErrorMessage ( " error: empty entity cannot be converted " , lines ) ) ;
2017-07-03 08:52:09 +05:30
}
// second token is the type
std : : istringstream iss ( lines [ 0 ] ) ;
if ( ! ( iss > > trash > > type ) ) {
2017-07-08 19:50:55 +05:30
throw std : : runtime_error (
makeErrorMessage ( " error: type is required " , lines ) ) ;
2017-07-03 08:52:09 +05:30
}
2017-07-06 22:39:15 +05:30
// If worldspawn, first reenable all gamemodes
// then check worldspawn for disabled modes
// then RETURN EMPTY VECTOR
2017-07-06 08:19:03 +05:30
if ( type = = " WorldSpawn " ) {
2017-07-06 21:22:59 +05:30
ws_ . cts = true ;
ws_ . ctf = true ;
ws_ . ffa = true ;
ws_ . tdm = true ;
ws_ . duel = true ;
// Each worldspawn can specify modes enabled/disabled
2017-07-06 08:19:03 +05:30
for ( int i = 1 ; i < lines . size ( ) ; + + i ) {
if ( lines [ i ] . find ( " modeRace 0 " ) ! = std : : string : : npos ) {
2017-07-06 21:22:59 +05:30
ws_ . cts = false ;
2017-07-06 08:19:03 +05:30
}
else if ( lines [ i ] . find ( " modeCTF 0 " ) ! = std : : string : : npos ) {
2017-07-06 21:22:59 +05:30
ws_ . ctf = false ;
2017-07-06 08:19:03 +05:30
}
else if ( lines [ i ] . find ( " modeFFA 0 " ) ! = std : : string : : npos ) {
2017-07-06 21:22:59 +05:30
ws_ . ffa = false ;
2017-07-06 08:19:03 +05:30
}
else if ( lines [ i ] . find ( " modeTDM 0 " ) ! = std : : string : : npos ) {
2017-07-06 21:22:59 +05:30
ws_ . tdm = false ;
2017-07-06 08:19:03 +05:30
}
else if ( lines [ i ] . find ( " mode1v1 0 " ) ! = std : : string : : npos ) {
2017-07-06 21:22:59 +05:30
ws_ . duel = false ;
2017-07-06 08:19:03 +05:30
}
}
}
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 ) ;
}
2017-07-06 11:06:39 +05:30
else if ( type = = " PointLight " ) {
return convertPointLight ( lines ) ;
}
2017-07-03 08:52:09 +05:30
}
else {
2017-07-08 19:50:55 +05:30
throw std : : runtime_error (
makeErrorMessage ( " error: Map info must be extracted prior to conversion " , lines ) ) ;
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-08 19:50:55 +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
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2017-07-08 19:50:55 +05:30
2017-07-03 08:52:09 +05:30
std : : vector < std : : string >
2017-07-06 23:40:04 +05:30
EntityConverter : : convertPickup ( const std : : vector < std : : string > & lines ) const
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 ) {
2017-07-08 19:50:55 +05:30
throw std : : runtime_error (
makeErrorMessage ( " error: Pickup entity requires at least 3 lines " , 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 ] ) ) {
2017-07-08 19:50:55 +05:30
throw std : : runtime_error (
makeErrorMessage ( " error: Pickup entity requires coordinates " , lines ) ) ;
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 ) ) {
2017-07-08 19:50:55 +05:30
throw std : : runtime_error (
makeErrorMessage ( " error: Pickup entity requires Pickup ID " , lines ) ) ;
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-07-07 04:44:06 +05:30
auto pickupIter = pickupMap_ . find ( pickupID ) ;
if ( pickupIter = = pickupMap_ . end ( ) ) {
2017-07-08 19:50:55 +05:30
throw std : : runtime_error (
makeErrorMessage ( " error: Pickup ID must be valid " , lines ) ) ;
2017-07-07 04:44:06 +05:30
}
2017-07-06 11:06:39 +05:30
std : : stringstream pickupStream ;
2017-07-07 04:44:06 +05:30
pickupStream < < " \" classname \" \" " < < pickupIter - > second < < " \" " < < std : : endl ;
2017-07-06 11:06:39 +05:30
convertedLines . push_back ( pickupStream . str ( ) ) ;
2017-07-03 08:52:09 +05:30
// coordinates reordered to x, z, y
2017-07-06 11:06:39 +05:30
std : : stringstream positionStream ;
positionStream < < " \" origin \" \" " < < coords [ 0 ] < < " " < < coords [ 2 ] < < " " < <
2017-07-06 00:23:45 +05:30
offset ( coords [ 1 ] , OFFSET_PICKUP ) < < " \" " < < std : : endl ;
2017-07-06 11:06:39 +05:30
convertedLines . push_back ( positionStream . str ( ) ) ;
2017-07-03 08:52:09 +05:30
return convertedLines ;
}
else {
2017-07-08 19:50:55 +05:30
throw std : : runtime_error (
makeErrorMessage ( " error: Pickup requires position and pickup ID, missing 1 or both " , lines ) ) ;
2017-07-03 08:52:09 +05:30
}
}
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 >
2017-07-06 23:40:04 +05:30
EntityConverter : : convertPlayerSpawn ( const std : : vector < std : : string > & lines ) const
2017-07-03 08:52:09 +05:30
{
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-12 10:56:21 +05:30
std : : string coords [ 3 ] = { " 0.0 " , " 0.0 " , " 0.0 " } ;
2017-07-08 15:20:37 +05:30
// Requires an angle, default to 0 degrees (floating point)
std : : string angle ( " 0.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 ;
2017-07-06 21:22:59 +05:30
bool isModeRace = ws_ . cts ;
bool isModeCtf = ws_ . ctf ;
bool isModeTdm = ws_ . tdm ;
bool isModeFfa = ws_ . ffa ;
bool isModeDuel = ws_ . duel ;
2017-07-03 12:11:59 +05:30
2017-07-03 08:52:09 +05:30
2017-07-03 12:11:59 +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 ] ) ) {
2017-07-08 19:50:55 +05:30
throw std : : runtime_error (
2017-07-12 10:56:21 +05:30
makeErrorMessage ( " error: PlayerSpawn entity must have valid position coordinates if specified " , lines ) ) ;
2017-07-03 12:11:59 +05:30
}
}
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-08 19:50:55 +05:30
throw std : : runtime_error (
makeErrorMessage ( " error: Pickup entity requires Pickup ID " , lines ) ) ;
2017-07-03 12:11:59 +05:30
}
}
2017-07-06 21:22:59 +05:30
// Bool8 modeX 0 indicates this spawn is not for game mode X
2017-07-04 06:40:05 +05:30
else if ( type = = " modeRace " ) {
2017-07-06 21:22:59 +05:30
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 ;
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
}
}
2017-07-12 10:56:21 +05:30
if ( isModeCtf | | isModeTdm | | isModeFfa | | isModeDuel ) {
switch ( team ) {
case 0 :
convertedLines . push_back ( " \" classname \" \" info_player_deathmatch \" \n " ) ;
break ;
case 1 :
convertedLines . push_back ( " \" classname \" \" info_player_team1 \" \n " ) ;
break ;
case 2 :
convertedLines . push_back ( " \" classname \" \" info_player_team2 \" \n " ) ;
break ;
2017-07-03 12:11:59 +05:30
}
}
else {
2017-07-12 10:56:21 +05:30
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-12 10:56:21 +05:30
std : : stringstream positionStream ;
// coordinates reordered to x, z, y
positionStream < < " \" origin \" \" " < < coords [ 0 ] < < " " < < coords [ 2 ] < < " " < <
offset ( coords [ 1 ] , OFFSET_PLAYER ) < < " \" " < < std : : endl ;
convertedLines . push_back ( positionStream . str ( ) ) ;
std : : stringstream angleStream ;
angleStream < < " \" angle \" \" " < < adjustAngleForHandedness ( angle ) < < " \" " < < std : : endl ;
convertedLines . push_back ( angleStream . str ( ) ) ;
return convertedLines ;
2017-07-03 08:52:09 +05:30
}
std : : vector < std : : string >
2017-07-06 23:40:04 +05:30
EntityConverter : : convertJumpPad ( const std : : vector < std : : string > & lines ) const
2017-07-03 08:52:09 +05:30
{
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 ) {
2017-07-08 19:50:55 +05:30
throw std : : runtime_error (
makeErrorMessage ( " error: JumpPad entity requires at least 2 lines " , lines ) ) ;
2017-07-03 08:52:09 +05:30
}
std : : istringstream iss ( lines [ 1 ] ) ;
// String32 target targetName
if ( ! ( iss > > trash > > trash > > targetName ) ) {
2017-07-08 19:50:55 +05:30
throw std : : runtime_error (
makeErrorMessage ( " error: JumpPad entity requires target name " , lines ) ) ;
2017-07-03 08:52:09 +05:30
}
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 >
2017-07-06 23:40:04 +05:30
EntityConverter : : convertTeleporter ( const std : : vector < std : : string > & lines ) const
2017-07-03 08:52:09 +05:30
{
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 ) {
2017-07-08 19:50:55 +05:30
throw std : : runtime_error (
makeErrorMessage ( " error: Teleport entity requires at least 2 lines " , lines ) ) ;
2017-07-03 08:52:09 +05:30
}
std : : istringstream iss ( lines [ 1 ] ) ;
// String32 target targetName
if ( ! ( iss > > trash > > trash > > targetName ) ) {
2017-07-08 19:50:55 +05:30
throw std : : runtime_error (
makeErrorMessage ( " error: Teleport entity requires target name " , lines ) ) ;
2017-07-03 08:52:09 +05:30
}
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 >
2017-07-06 23:40:04 +05:30
EntityConverter : : convertTarget ( const std : : vector < std : : string > & lines ) const
2017-07-03 08:52:09 +05:30
{
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 ) {
2017-07-08 19:50:55 +05:30
throw std : : runtime_error (
makeErrorMessage ( " error: Target entity requires at least 3 lines " , lines ) ) ;
2017-07-03 12:11:59 +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 ] ) ) {
2017-07-08 19:50:55 +05:30
throw std : : runtime_error (
makeErrorMessage ( " error: Target entity requires coordinates " , lines ) ) ;
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 ) ) {
2017-07-08 19:50:55 +05:30
throw std : : runtime_error (
makeErrorMessage ( " error: Target entity requires target name " , lines ) ) ;
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-08 19:50:55 +05:30
throw std : : runtime_error (
makeErrorMessage ( " error: Target entity requires target angle if specified " , lines ) ) ;
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 ) {
2017-07-07 04:44:06 +05:30
auto targetIter = targetMap_ . find ( targetName ) ;
if ( targetIter = = targetMap_ . end ( ) ) {
2017-07-08 19:50:55 +05:30
std : : cerr < < makeErrorMessage ( " EntityConverter doesn't know what the source of a Target entity with the following attributes. This entity will not be converted. It is probably an unsupported entity type or feature. (e.g. game over camera) " , lines ) ;
2017-07-07 04:44:06 +05:30
std : : vector < std : : string > empty ;
return empty ;
}
if ( targetIter - > second = = " Teleporter " ) {
2017-07-03 08:52:09 +05:30
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-07 04:44:06 +05:30
else if ( targetIter - > second = = " 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-06 11:06:39 +05:30
std : : stringstream targetStream ;
targetStream < < " \" targetname \" \" " < < targetName < < " \" " < < std : : endl ;
convertedLines . push_back ( targetStream . str ( ) ) ;
2017-07-03 08:52:09 +05:30
// Write angle only if position and name exist
if ( haveAngle ) {
2017-07-06 11:06:39 +05:30
std : : stringstream angleStream ;
2017-07-08 15:20:37 +05:30
angleStream < < " \" angle \" \" " < < adjustAngleForHandedness ( angle ) < < " \" " < < std : : endl ;
2017-07-06 11:06:39 +05:30
convertedLines . push_back ( angleStream . str ( ) ) ;
2017-06-07 06:52:54 +05:30
}
2017-07-04 07:57:18 +05:30
return convertedLines ;
}
else {
2017-07-08 19:50:55 +05:30
throw std : : runtime_error (
makeErrorMessage ( " error: Target entity requires position coordinates and targetname " , lines ) ) ;
2017-06-07 06:52:54 +05:30
}
}
2017-07-03 08:52:09 +05:30
std : : vector < std : : string >
2017-07-06 23:40:04 +05:30
EntityConverter : : convertRaceStart ( const std : : vector < std : : string > & lines ) const
2017-07-03 08:52:09 +05:30
{
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 >
2017-07-06 23:40:04 +05:30
EntityConverter : : convertRaceFinish ( const std : : vector < std : : string > & lines ) const
2017-07-03 08:52:09 +05:30
{
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
2017-07-06 11:06:39 +05:30
std : : vector < std : : string >
2017-07-06 23:40:04 +05:30
EntityConverter : : convertPointLight ( const std : : vector < std : : string > & lines ) const
2017-07-06 11:06:39 +05:30
{
std : : vector < std : : string > convertedLines ;
//position and intensity required, color optional
std : : string coords [ 3 ] ;
2017-07-08 19:50:55 +05:30
//default to a typical value
std : : string intensity = " 1.0 " ;
2017-07-06 11:06:39 +05:30
//color is hex 8 digits
2017-07-08 19:50:55 +05:30
//default to white if no color specified
std : : string color = " ff000000 " ;
2017-07-06 11:06:39 +05:30
std : : string trash ;
bool havePosition = false ;
2017-07-08 19:50:55 +05:30
bool haveColor = false ;
2017-07-06 11:06:39 +05:30
2017-07-08 19:50:55 +05:30
if ( lines . size ( ) < 2 ) {
throw std : : runtime_error (
makeErrorMessage ( " error: PointLight entity requires at least 2 lines " , lines ) ) ;
2017-07-06 11:06:39 +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 ] ) ) {
2017-07-08 19:50:55 +05:30
throw std : : runtime_error (
makeErrorMessage ( " error: PointLight entity requires valid position coordinates " , lines ) ) ;
2017-07-06 11:06:39 +05:30
}
havePosition = true ;
}
else if ( type = = " intensity " ) {
std : : istringstream iss ( lines [ i ] ) ;
// Float intensity validFloat
if ( ! ( iss > > trash > > trash > > intensity ) ) {
2017-07-08 19:50:55 +05:30
throw std : : runtime_error (
makeErrorMessage ( " error: PointLight intensity keyword must be followed by a value " , lines ) ) ;
2017-07-06 11:06:39 +05:30
}
}
else if ( type = = " color " ) {
std : : istringstream iss ( lines [ i ] ) ;
// ColourXRGB32 color eightDigitHexValue
if ( ! ( iss > > trash > > trash > > color ) ) {
2017-07-08 19:50:55 +05:30
throw std : : runtime_error (
makeErrorMessage ( " error: PointLight color keyword must be followed by a value " , lines ) ) ;
2017-07-06 11:06:39 +05:30
}
haveColor = true ;
}
}
2017-07-08 19:50:55 +05:30
if ( havePosition ) {
2017-07-06 11:06:39 +05:30
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 ( ) ) ;
2017-07-08 19:50:55 +05:30
float red ;
float green ;
float blue ;
2017-07-06 11:06:39 +05:30
if ( haveColor ) {
2017-07-07 06:30:27 +05:30
// Convert 32bit hex RGBA value (ALPHA ALWAYS FULL) into RGB values
hexToRGB ( color , red , green , blue ) ;
2017-07-06 11:06:39 +05:30
}
2017-07-08 19:50:55 +05:30
std : : stringstream colorStream ;
colorStream < < " \" _color \" \" " < < red < < " " < < green < < " " < < blue < < " \" " < < std : : endl ;
convertedLines . push_back ( colorStream . str ( ) ) ;
2017-07-06 11:06:39 +05:30
return convertedLines ;
}
else {
2017-07-08 19:50:55 +05:30
throw std : : runtime_error (
makeErrorMessage ( " error: PointLight entity requires position coordinates " , lines ) ) ;
2017-07-06 11:06:39 +05:30
}
}
2017-07-06 04:30:22 +05:30
2017-07-08 19:50:55 +05:30
2017-07-06 04:30:22 +05:30
std : : string
2017-07-06 09:02:49 +05:30
EntityConverter : : getAttributeType ( const std : : string & line ) const
2017-07-06 04:30:22 +05:30
{
std : : string type ;
std : : string dataType ;
std : : istringstream iss ( line ) ;
if ( ! ( iss > > dataType > > type ) ) {
return std : : string ( ) ;
}
return type ;
}
void
2017-07-06 09:02:49 +05:30
EntityConverter : : mapEntities ( const std : : string & mapFile )
2017-07-06 04:30:22 +05:30
{
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 ) ) {
2017-07-08 19:50:55 +05:30
throw std : : runtime_error ( " format error in Pickup .pck file " + mapFile ) ;
2017-07-06 04:30:22 +05:30
}
2017-07-07 04:44:06 +05:30
pickupMap_ . insert ( std : : pair < int , std : : string > ( id , pickup ) ) ;
2017-07-06 04:30:22 +05:30
}
}
else {
throw std : : ios : : failure ( " Error: EntityConverter failed to open .ent file " ) ;
}
fin . close ( ) ;
}
void
2017-07-06 09:02:49 +05:30
EntityConverter : : extractFromEntity ( const std : : string & line , std : : istream & is )
2017-07-06 04:30:22 +05:30
{
std : : string trash ;
std : : string targetName ;
2017-07-06 09:02:49 +05:30
std : : string nextLine ;
2017-07-06 04:30:22 +05:30
if ( line . find ( " type Teleporter " ) ! = std : : string : : npos ) {
2017-07-06 09:02:49 +05:30
std : : getline ( is , nextLine ) ;
std : : istringstream iss ( nextLine ) ;
2017-07-06 04:30:22 +05:30
if ( ! ( iss > > trash > > trash > > targetName ) ) {
2017-07-08 19:50:55 +05:30
throw std : : runtime_error ( " Format error in .map file " ) ;
2017-07-06 04:30:22 +05:30
}
targetMap_ . insert ( std : : pair < std : : string , std : : string > ( targetName , " Teleporter " ) ) ;
}
else if ( line . find ( " type JumpPad " ) ! = std : : string : : npos ) {
2017-07-06 09:02:49 +05:30
std : : getline ( is , nextLine ) ;
std : : istringstream iss ( nextLine ) ;
2017-07-06 04:30:22 +05:30
if ( ! ( iss > > trash > > trash > > targetName ) ) {
2017-07-08 19:50:55 +05:30
throw std : : runtime_error ( " Format error in .map file " ) ;
2017-07-06 04:30:22 +05:30
}
targetMap_ . insert ( std : : pair < std : : string , std : : string > ( targetName , " JumpPad " ) ) ;
}
}
2017-07-06 11:06:39 +05:30
std : : string
EntityConverter : : offset ( const std : : string & value , const float amount ) const
2017-07-06 09:02:49 +05:30
{
2017-07-08 15:20:37 +05:30
std : : istringstream iss ( value ) ;
float c ;
iss > > c ;
c + = amount ;
std : : stringstream ss ;
ss < < std : : fixed < < std : : setprecision ( OUTPUT_PRECISION ) < < c ;
return ss . str ( ) ;
}
std : : string
EntityConverter : : adjustAngleForHandedness ( const std : : string & angle ) const
{
std : : istringstream iss ( angle ) ;
float a ;
iss > > a ;
a = - a + 90.0 ;
std : : stringstream ss ;
ss < < std : : fixed < < std : : setprecision ( OUTPUT_PRECISION ) < < a ;
return ss . str ( ) ;
2017-07-06 04:30:22 +05:30
}
2017-07-06 11:06:39 +05:30
void
2017-07-07 06:30:27 +05:30
EntityConverter : : hexToRGB ( const std : : string & hex , float & r , float & g , float & b ) const
2017-07-06 11:06:39 +05:30
{
unsigned int value ;
std : : stringstream ss ;
ss < < std : : hex < < hex ;
ss > > value ;
2017-07-07 06:00:21 +05:30
// BYTE ORDER IS ARGB
2017-07-07 06:30:27 +05:30
// Alpha value is always full -> can be ignored safely
2017-07-07 06:00:21 +05:30
// Get each value and normalize
2017-07-07 06:30:27 +05:30
r = ( ( value > > 16 ) & 0xFF ) / 255.0 ;
g = ( ( value > > 8 ) & 0xFF ) / 255.0 ;
b = ( ( value ) & 0xFF ) / 255.0 ;
2017-07-06 11:06:39 +05:30
}
int
EntityConverter : : adjustBrightness ( const std : : string & value ) const
{
float inputBright ;
std : : stringstream ss ( value ) ;
ss > > inputBright ;
2017-07-06 11:13:24 +05:30
return static_cast < int > ( inputBright * BRIGHTNESS_ADJUST ) ;
2017-07-06 11:06:39 +05:30
}
2017-07-06 04:30:22 +05:30
2017-07-08 19:50:55 +05:30
std : : string
EntityConverter : : makeErrorMessage ( const std : : string message ,
const std : : vector < std : : string > entity ) const
{
std : : stringstream ss ;
ss < < std : : endl < < message < < std : : endl ;
std : : vector < std : : string > : : const_iterator it ;
for ( it = entity . begin ( ) ; it ! = entity . end ( ) ; + + it ) {
ss < < * it < < std : : endl ;
}
ss < < std : : endl ;
return ss . str ( ) ;
}
/*-----------------------------------------------------------------------------
* PRIVATE
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2017-07-03 08:52:09 +05:30
// DEBUG
void
2017-07-06 23:40:04 +05:30
EntityConverter : : printMapping ( ) const
2017-07-03 08:52:09 +05:30
{
2017-07-06 23:40:04 +05:30
std : : cout < < std : : endl < < " Reflex pickup ID mapped to Xonotic pickup names: " < < std : : endl ;
std : : map < int , std : : string > : : const_iterator it ;
2017-07-07 04:44:06 +05:30
for ( it = pickupMap_ . begin ( ) ; it ! = pickupMap_ . end ( ) ; + + it )
2017-07-03 08:52:09 +05:30
std : : cout < < it - > first < < " => " < < it - > second < < std : : endl ;
}
// DEBUG
void
2017-07-06 23:40:04 +05:30
EntityConverter : : printTargetSources ( ) const
2017-07-03 08:52:09 +05:30
{
2017-07-06 23:40:04 +05:30
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 )
2017-07-03 08:52:09 +05:30
std : : cout < < it - > first < < " => " < < it - > second < < std : : endl ;
}