mirror of
https://github.com/Garux/netradiant-custom.git
synced 2026-09-28 00:00:03 +02:00
* .def .ent .fgd can be used simultaneously (removed entityclass="halflife")
This commit is contained in:
@@ -1124,6 +1124,7 @@ misc...
|
||||
support bsp without vis data
|
||||
add RBSP, FBSP support
|
||||
* bobToolz: new Aperture Door builder
|
||||
* .def .ent .fgd can be used simultaneously (removed entityclass="halflife")
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@ struct EntityClassScanner
|
||||
|
||||
void ( *scanFile )( EntityClassCollector& collector, const char* filename );
|
||||
const char* ( *getExtension )( );
|
||||
void ( *finalize )( EntityClassCollector& collector ) = []( EntityClassCollector& collector ){};
|
||||
};
|
||||
|
||||
#include "modulesystem.h"
|
||||
|
||||
+4
-4
@@ -157,9 +157,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
const auto relPath = StringStream( m_directory, name );
|
||||
|
||||
scanner.scanFile( g_collector, relPath );
|
||||
scanner.scanFile( g_collector, StringStream( m_directory, name ) );
|
||||
}
|
||||
};
|
||||
|
||||
@@ -181,7 +179,7 @@ void EntityClassQuake3_constructDirectory( const char* directory, const char* ex
|
||||
|
||||
|
||||
/* facts:
|
||||
modules are alpha sorted, so .def .ent loading order
|
||||
modules are alpha sorted, so .def .fgd .ent (xml) loading order
|
||||
all base & mod files of the same type are alpha sorted together
|
||||
equal file name in mod is ignored
|
||||
equal eclass name inserted later is ignored
|
||||
@@ -209,6 +207,8 @@ void EntityClassQuake3_Construct(){
|
||||
{
|
||||
EntityClassesLoadFile( table, path ) ( name.c_str() );
|
||||
}
|
||||
|
||||
table.finalize( g_collector );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+82
-192
@@ -24,15 +24,14 @@
|
||||
#include "debugging/debugging.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include "ifilesystem.h"
|
||||
#include "iscriplib.h"
|
||||
#include "qerplugin.h"
|
||||
|
||||
#include "string/string.h"
|
||||
#include "eclasslib.h"
|
||||
#include "os/path.h"
|
||||
#include "os/dir.h"
|
||||
#include "stream/stringstream.h"
|
||||
#include "moduleobservers.h"
|
||||
#include "stringio.h"
|
||||
@@ -42,38 +41,41 @@ namespace
|
||||
{
|
||||
typedef std::map<const char*, EntityClass*, RawStringLessNoCase> EntityClasses;
|
||||
EntityClasses g_EntityClassFGD_classes;
|
||||
typedef std::map<const char*, EntityClass*, RawStringLess> BaseClasses;
|
||||
typedef std::map<const char*, EntityClass*, RawStringLessNoCase> BaseClasses;
|
||||
BaseClasses g_EntityClassFGD_bases;
|
||||
EntityClass *g_EntityClassFGD_bad = 0;
|
||||
typedef std::map<CopiedString, ListAttributeType> ListAttributeTypes;
|
||||
ListAttributeTypes g_listTypesFGD;
|
||||
|
||||
const auto pathLess = []( const CopiedString& one, const CopiedString& other ){
|
||||
return path_less( one.c_str(), other.c_str() );
|
||||
};
|
||||
std::set<CopiedString, decltype( pathLess )> g_loadedFgds( pathLess );
|
||||
}
|
||||
|
||||
|
||||
void EntityClassFGD_clear(){
|
||||
g_EntityClassFGD_classes.clear();
|
||||
for ( auto [ name, eclass ] : g_EntityClassFGD_bases )
|
||||
{
|
||||
eclass_capture_state( eclass );
|
||||
eclass->free( eclass );
|
||||
}
|
||||
g_EntityClassFGD_classes.clear();
|
||||
g_EntityClassFGD_bases.clear();
|
||||
g_listTypesFGD.clear();
|
||||
g_loadedFgds.clear();
|
||||
}
|
||||
|
||||
EntityClass* EntityClassFGD_insertUniqueBase( EntityClass* entityClass, bool allowfree = true ){
|
||||
EntityClass* EntityClassFGD_insertUniqueBase( EntityClass* entityClass ){
|
||||
auto [ it, inserted ] = g_EntityClassFGD_bases.insert( BaseClasses::value_type( entityClass->name(), entityClass ) );
|
||||
if ( !inserted ) {
|
||||
globalErrorStream() << "duplicate base class: " << makeQuoted( entityClass->name() ) << '\n';
|
||||
if( allowfree ){
|
||||
eclass_capture_state( entityClass );
|
||||
entityClass->free( entityClass );
|
||||
}
|
||||
eclass_capture_state( entityClass );
|
||||
entityClass->free( entityClass );
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
EntityClass* EntityClassFGD_insertUnique( EntityClass* entityClass ){
|
||||
EntityClassFGD_insertUniqueBase( entityClass, false );
|
||||
auto [ it, inserted ] = g_EntityClassFGD_classes.insert( EntityClasses::value_type( entityClass->name(), entityClass ) );
|
||||
if ( !inserted ) {
|
||||
globalErrorStream() << "duplicate entity class: " << makeQuoted( entityClass->name() ) << '\n';
|
||||
@@ -83,13 +85,6 @@ EntityClass* EntityClassFGD_insertUnique( EntityClass* entityClass ){
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void EntityClassFGD_forEach( EntityClassVisitor& visitor ){
|
||||
for ( auto [ name, eclass ] : g_EntityClassFGD_classes )
|
||||
{
|
||||
visitor.visit( eclass );
|
||||
}
|
||||
}
|
||||
|
||||
#define PARSE_ERROR "error parsing fgd entity class definition at line " << tokeniser.getLine() << ':' << tokeniser.getColumn()
|
||||
|
||||
static bool s_fgd_warned = false;
|
||||
@@ -179,11 +174,11 @@ void EntityClassFGD_parseClass( Tokeniser& tokeniser, bool fixedsize, bool isBas
|
||||
entityClass->colorSpecified = true;
|
||||
ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, "(" ), PARSE_ERROR );
|
||||
Tokeniser_getFloat( tokeniser, entityClass->color.x() );
|
||||
entityClass->color.x() /= 256.0;
|
||||
entityClass->color.x() /= 255.0;
|
||||
Tokeniser_getFloat( tokeniser, entityClass->color.y() );
|
||||
entityClass->color.y() /= 256.0;
|
||||
entityClass->color.y() /= 255.0;
|
||||
Tokeniser_getFloat( tokeniser, entityClass->color.z() );
|
||||
entityClass->color.z() /= 256.0;
|
||||
entityClass->color.z() /= 255.0;
|
||||
ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, ")" ), PARSE_ERROR );
|
||||
}
|
||||
else if ( string_equal( property, "iconsprite" ) ) {
|
||||
@@ -544,9 +539,9 @@ void EntityClassFGD_parseClass( Tokeniser& tokeniser, bool fixedsize, bool isBas
|
||||
}
|
||||
}
|
||||
|
||||
using LoadInclude = std::function<void(const char *)>;
|
||||
void EntityClassFGD_loadUniqueFile( const char* filename );
|
||||
|
||||
void EntityClassFGD_parse( TextInputStream& inputStream, const char* path, const LoadInclude& loadInclude ){
|
||||
void EntityClassFGD_parse( TextInputStream& inputStream, const char* path ){
|
||||
Tokeniser& tokeniser = GlobalScriptLibrary().m_pfnNewScriptTokeniser( inputStream );
|
||||
|
||||
tokeniser.nextLine();
|
||||
@@ -573,7 +568,7 @@ void EntityClassFGD_parse( TextInputStream& inputStream, const char* path, const
|
||||
}
|
||||
// hl2 below
|
||||
else if ( string_equal( blockType, "@include" ) ) {
|
||||
loadInclude( tokeniser.getToken() );
|
||||
EntityClassFGD_loadUniqueFile( StringStream( PathFilenameless( path ), tokeniser.getToken() ) );
|
||||
}
|
||||
else if ( string_equal( blockType, "@mapsize" ) ) {
|
||||
ASSERT_MESSAGE( EntityClassFGD_parseToken( tokeniser, "(" ), PARSE_ERROR );
|
||||
@@ -594,40 +589,18 @@ void EntityClassFGD_parse( TextInputStream& inputStream, const char* path, const
|
||||
}
|
||||
|
||||
|
||||
void EntityClassFGD_loadFile( const char* filename, const LoadInclude& loadInclude ){
|
||||
void EntityClassFGD_loadFile( const char* filename ){
|
||||
TextFileInputStream file( filename );
|
||||
if ( !file.failed() ) {
|
||||
globalOutputStream() << "parsing entity classes from " << makeQuoted( filename ) << '\n';
|
||||
|
||||
EntityClassFGD_parse( file, filename, loadInclude );
|
||||
EntityClassFGD_parse( file, filename );
|
||||
}
|
||||
}
|
||||
|
||||
EntityClass* EntityClassFGD_findOrInsert( const char *name, bool has_brushes ){
|
||||
ASSERT_NOTNULL( name );
|
||||
|
||||
if ( string_empty( name ) ) {
|
||||
return g_EntityClassFGD_bad;
|
||||
}
|
||||
|
||||
EntityClasses::iterator i = g_EntityClassFGD_classes.find( name );
|
||||
if ( i != g_EntityClassFGD_classes.end()
|
||||
//&& string_equal( ( *i ).first, name )
|
||||
) {
|
||||
return ( *i ).second;
|
||||
}
|
||||
|
||||
EntityClass* e = EntityClass_Create_Default( name, has_brushes );
|
||||
return EntityClassFGD_insertUnique( e );
|
||||
}
|
||||
|
||||
const ListAttributeType* EntityClassFGD_findListType( const char *name ){
|
||||
ListAttributeTypes::iterator i = g_listTypesFGD.find( name );
|
||||
if ( i != g_listTypesFGD.end() ) {
|
||||
return &( *i ).second;
|
||||
}
|
||||
return 0;
|
||||
|
||||
void EntityClassFGD_loadUniqueFile( const char* filename ){
|
||||
if( g_loadedFgds.insert( filename ).second )
|
||||
EntityClassFGD_loadFile( filename );
|
||||
}
|
||||
|
||||
|
||||
@@ -638,9 +611,13 @@ void EntityClassFGD_resolveInheritance( EntityClass* derivedClass ){
|
||||
{
|
||||
BaseClasses::iterator i = g_EntityClassFGD_bases.find( parentName.c_str() );
|
||||
if ( i == g_EntityClassFGD_bases.end() ) {
|
||||
globalErrorStream() << "failed to find entityDef " << makeQuoted( parentName.c_str() ) << " inherited by " << makeQuoted( derivedClass->name() ) << '\n';
|
||||
i = g_EntityClassFGD_classes.find( parentName.c_str() );
|
||||
if ( i == g_EntityClassFGD_classes.end() ) {
|
||||
globalErrorStream() << "failed to find entityDef " << makeQuoted( parentName.c_str() ) << " inherited by " << makeQuoted( derivedClass->name() ) << '\n';
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
{
|
||||
EntityClass* parentClass = ( *i ).second;
|
||||
EntityClassFGD_resolveInheritance( parentClass );
|
||||
@@ -662,7 +639,12 @@ void EntityClassFGD_resolveInheritance( EntityClass* derivedClass ){
|
||||
for( size_t flag = 0; flag < MAX_FLAGS; ++flag ){
|
||||
if( !string_empty( parentClass->flagnames[flag] ) && string_empty( derivedClass->flagnames[flag] ) ){
|
||||
strncpy( derivedClass->flagnames[flag], parentClass->flagnames[flag], std::size( derivedClass->flagnames[flag] ) - 1 );
|
||||
derivedClass->flagAttributes[flag] = parentClass->flagAttributes[flag];
|
||||
// this ptr ref is cool, but requires parents to stay alive (e.g. bases)
|
||||
// non base parent also may be deleted during insertion to global entity stack, if entity is already present there
|
||||
// derivedClass->flagAttributes[flag] = parentClass->flagAttributes[flag];
|
||||
derivedClass->flagAttributes[flag] = &EntityClass_insertAttribute( *derivedClass,
|
||||
parentClass->flagAttributes[flag]->m_name.c_str(),
|
||||
*parentClass->flagAttributes[flag] ).second;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -670,154 +652,62 @@ void EntityClassFGD_resolveInheritance( EntityClass* derivedClass ){
|
||||
}
|
||||
}
|
||||
|
||||
class EntityClassFGD : public ModuleObserver
|
||||
{
|
||||
std::size_t m_unrealised;
|
||||
ModuleObservers m_observers;
|
||||
public:
|
||||
EntityClassFGD() : m_unrealised( 3 ){
|
||||
}
|
||||
void realise(){
|
||||
if ( --m_unrealised == 0 ) {
|
||||
|
||||
{
|
||||
const auto baseDirectory = StringStream( GlobalRadiant().getGameToolsPath(), GlobalRadiant().getRequiredGameDescriptionKeyValue( "basegame" ), '/' );
|
||||
const auto gameDirectory = StringStream( GlobalRadiant().getGameToolsPath(), GlobalRadiant().getGameName(), '/' );
|
||||
void Eclass_ScanFile_fgd( EntityClassCollector& collector, const char *filename ){
|
||||
EntityClassFGD_loadUniqueFile( filename );
|
||||
}
|
||||
|
||||
const auto pathLess = []( const CopiedString& one, const CopiedString& other ){
|
||||
return path_less( one.c_str(), other.c_str() );
|
||||
};
|
||||
std::map<CopiedString, const char*, decltype( pathLess )> name_path( pathLess );
|
||||
|
||||
const auto constructDirectory = [&name_path]( const char* directory, const char* extension ){
|
||||
globalOutputStream() << "EntityClass: searching " << makeQuoted( directory ) << " for *." << extension << '\n';
|
||||
Directory_forEach( directory, matchFileExtension( extension, [directory, &name_path]( const char *name ){
|
||||
name_path.emplace( name, directory );
|
||||
} ) );
|
||||
};
|
||||
|
||||
constructDirectory( baseDirectory, "fgd" );
|
||||
if ( !string_equal( baseDirectory, gameDirectory ) ) {
|
||||
constructDirectory( gameDirectory, "fgd" );
|
||||
}
|
||||
|
||||
const LoadInclude loadInclude = [&]( const char *name ){
|
||||
if( auto it = name_path.find( name ); it != name_path.end() && it->second != nullptr ) // null path == loaded
|
||||
EntityClassFGD_loadFile( StringStream( std::exchange( it->second, nullptr ), name ), loadInclude );
|
||||
};
|
||||
|
||||
for( auto& [ name, path ] : name_path ){
|
||||
if( path != nullptr ) // null path == loaded
|
||||
EntityClassFGD_loadFile( StringStream( std::exchange( path, nullptr ), name ), loadInclude );
|
||||
}
|
||||
void EClass_finalize_fgd( EntityClassCollector& collector ){
|
||||
for ( auto [ name, eclass ] : g_EntityClassFGD_classes )
|
||||
{
|
||||
EntityClassFGD_resolveInheritance( eclass );
|
||||
if ( eclass->fixedsize && eclass->m_modelpath.empty() ) {
|
||||
if ( !eclass->sizeSpecified ) {
|
||||
globalErrorStream() << "size not specified for entity class: " << makeQuoted( eclass->name() ) << '\n';
|
||||
}
|
||||
|
||||
{
|
||||
for ( auto [ name, eclass ] : g_EntityClassFGD_classes )
|
||||
{
|
||||
EntityClassFGD_resolveInheritance( eclass );
|
||||
if ( eclass->fixedsize && eclass->m_modelpath.empty() ) {
|
||||
if ( !eclass->sizeSpecified ) {
|
||||
globalErrorStream() << "size not specified for entity class: " << makeQuoted( eclass->name() ) << '\n';
|
||||
}
|
||||
if ( !eclass->colorSpecified ) {
|
||||
globalErrorStream() << "color not specified for entity class: " << makeQuoted( eclass->name() ) << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( !eclass->colorSpecified ) {
|
||||
globalErrorStream() << "color not specified for entity class: " << makeQuoted( eclass->name() ) << '\n';
|
||||
}
|
||||
{
|
||||
for ( auto [ name, eclass ] : g_EntityClassFGD_bases )
|
||||
{
|
||||
eclass_capture_state( eclass );
|
||||
}
|
||||
}
|
||||
|
||||
m_observers.realise();
|
||||
}
|
||||
}
|
||||
void unrealise(){
|
||||
if ( ++m_unrealised == 1 ) {
|
||||
m_observers.unrealise();
|
||||
EntityClassFGD_clear();
|
||||
}
|
||||
}
|
||||
void attach( ModuleObserver& observer ){
|
||||
m_observers.attach( observer );
|
||||
}
|
||||
void detach( ModuleObserver& observer ){
|
||||
m_observers.detach( observer );
|
||||
}
|
||||
};
|
||||
|
||||
EntityClassFGD g_EntityClassFGD;
|
||||
|
||||
void EntityClassFGD_attach( ModuleObserver& observer ){
|
||||
g_EntityClassFGD.attach( observer );
|
||||
}
|
||||
void EntityClassFGD_detach( ModuleObserver& observer ){
|
||||
g_EntityClassFGD.detach( observer );
|
||||
for ( auto [ name, eclass ] : g_EntityClassFGD_classes )
|
||||
{
|
||||
eclass_capture_state( eclass );
|
||||
collector.insert( eclass );
|
||||
}
|
||||
for( const auto& [ name, list ] : g_listTypesFGD )
|
||||
{
|
||||
collector.insert( name.c_str(), list );
|
||||
}
|
||||
EntityClassFGD_clear();
|
||||
}
|
||||
|
||||
void EntityClassFGD_realise(){
|
||||
g_EntityClassFGD.realise();
|
||||
}
|
||||
void EntityClassFGD_unrealise(){
|
||||
g_EntityClassFGD.unrealise();
|
||||
}
|
||||
|
||||
void EntityClassFGD_construct(){
|
||||
// start by creating the default unknown eclass
|
||||
g_EntityClassFGD_bad = EClass_Create( "UNKNOWN_CLASS", Vector3( 0.0f, 0.5f, 0.0f ), "" );
|
||||
|
||||
EntityClassFGD_realise();
|
||||
}
|
||||
|
||||
void EntityClassFGD_destroy(){
|
||||
EntityClassFGD_unrealise();
|
||||
|
||||
g_EntityClassFGD_bad->free( g_EntityClassFGD_bad );
|
||||
}
|
||||
|
||||
class EntityClassFGDDependencies : public GlobalFileSystemModuleRef, public GlobalShaderCacheModuleRef, public GlobalRadiantModuleRef
|
||||
{
|
||||
};
|
||||
|
||||
class EntityClassFGDAPI
|
||||
{
|
||||
EntityClassManager m_eclassmanager;
|
||||
public:
|
||||
typedef EntityClassManager Type;
|
||||
STRING_CONSTANT( Name, "halflife" );
|
||||
|
||||
EntityClassFGDAPI(){
|
||||
EntityClassFGD_construct();
|
||||
|
||||
m_eclassmanager.findOrInsert = &EntityClassFGD_findOrInsert;
|
||||
m_eclassmanager.findListType = &EntityClassFGD_findListType;
|
||||
m_eclassmanager.forEach = &EntityClassFGD_forEach;
|
||||
m_eclassmanager.attach = &EntityClassFGD_attach;
|
||||
m_eclassmanager.detach = &EntityClassFGD_detach;
|
||||
m_eclassmanager.realise = &EntityClassFGD_realise;
|
||||
m_eclassmanager.unrealise = &EntityClassFGD_unrealise;
|
||||
|
||||
GlobalRadiant().attachGameToolsPathObserver( g_EntityClassFGD );
|
||||
GlobalRadiant().attachGameNameObserver( g_EntityClassFGD );
|
||||
}
|
||||
~EntityClassFGDAPI(){
|
||||
GlobalRadiant().detachGameNameObserver( g_EntityClassFGD );
|
||||
GlobalRadiant().detachGameToolsPathObserver( g_EntityClassFGD );
|
||||
|
||||
EntityClassFGD_destroy();
|
||||
}
|
||||
EntityClassManager* getTable(){
|
||||
return &m_eclassmanager;
|
||||
}
|
||||
};
|
||||
|
||||
#include "modulesystem/singletonmodule.h"
|
||||
#include "modulesystem/moduleregistry.h"
|
||||
|
||||
typedef SingletonModule<EntityClassFGDAPI, EntityClassFGDDependencies> EntityClassFGDModule;
|
||||
typedef Static<EntityClassFGDModule> StaticEntityClassFGDModule;
|
||||
StaticRegisterModule staticRegisterEntityClassFGD( StaticEntityClassFGDModule::instance() );
|
||||
class EntityClassFgdDependencies : public GlobalShaderCacheModuleRef, public GlobalScripLibModuleRef
|
||||
{
|
||||
};
|
||||
|
||||
class EclassFgdAPI
|
||||
{
|
||||
EntityClassScanner m_eclassfgd;
|
||||
public:
|
||||
typedef EntityClassScanner Type;
|
||||
STRING_CONSTANT( Name, "fgd" );
|
||||
|
||||
EclassFgdAPI(){
|
||||
m_eclassfgd.scanFile = &Eclass_ScanFile_fgd;
|
||||
m_eclassfgd.getExtension = [](){ return "fgd"; };
|
||||
m_eclassfgd.finalize = &EClass_finalize_fgd;
|
||||
}
|
||||
EntityClassScanner* getTable(){
|
||||
return &m_eclassfgd;
|
||||
}
|
||||
};
|
||||
|
||||
typedef SingletonModule<EclassFgdAPI, EntityClassFgdDependencies> EclassFgdModule;
|
||||
typedef Static<EclassFgdModule> StaticEclassFgdModule;
|
||||
StaticRegisterModule staticRegisterEclassFgd( StaticEclassFgdModule::instance() );
|
||||
|
||||
Reference in New Issue
Block a user