* layers system (stored in comments of .map file)

menus	* View->Layers Browser (L)
	* Edit->Expand Selection To Layers (ctrl + q)
	* shift + q: move-to LayersMenu
closes #133
This commit is contained in:
Garux
2025-08-31 16:25:12 +05:00
parent 581dd79acb
commit 1599d0a64b
40 changed files with 1042 additions and 82 deletions
+108 -5
View File
@@ -32,6 +32,98 @@
#include "string/string.h"
#include "stringio.h"
#include "eclasslib.h"
#include "layers.h"
class LayersParser
{
Layers& m_layers;
std::vector<Layer::iterator> m_layersVec; // vector to access layers by index
int m_currentLayer = LAYERIDX0;
public:
LayersParser( scene::Node& root ) : m_layers( *Node_getLayers( root ) ){
}
void construct_tree(){
{
auto& list = m_layers.m_children;
if( list.size() > 1 ) // something was parsed, remove default inserted layer
list.pop_front();
m_layersVec.reserve( list.size() );
for( auto it = list.begin(); it != list.end(); ++it )
m_layersVec.push_back( it );
// check safety // note possible circular reference is not checked
if( std::ranges::find( list, LAYERIDXPARENT, &Layer::m_parentIndex ) == list.cend() ){
globalErrorStream() << "No layers in the root\n";
list.front().m_parentIndex = LAYERIDXPARENT;
}
}
for( auto it : m_layersVec ){
// check safety
if( it->m_parentIndex < LAYERIDXPARENT || it->m_parentIndex >= static_cast<int>( m_layersVec.size() ) ){
globalErrorStream() << it->m_parentIndex << " parent layer index out of bounds\n";
it->m_parentIndex = LAYERIDXPARENT;
}
if( it->m_parentIndex > LAYERIDXPARENT && m_layersVec[ it->m_parentIndex ] == it ){
globalErrorStream() << it->m_parentIndex << " parent layer index self reference\n";
it->m_parentIndex = LAYERIDXPARENT;
}
// link & reference parent
Layer& parent = it->m_parentIndex == LAYERIDXPARENT? m_layers : *m_layersVec[ it->m_parentIndex ];
parent.m_children.splice( parent.m_children.cend(), m_layers.m_children, it );
it->m_parent = &parent;
}
m_layers.m_currentLayer = &m_layers.m_children.front();
}
bool read_layers( Tokeniser& tokeniser ){
for (;; )
{
tokeniser.nextLine();
const char *token;
if( !( token = tokeniser.getToken() ) )
return false;
if( !string_equal( token, "layerdef" ) ){
tokeniser.ungetToken();
return true;
}
if( !( token = tokeniser.getToken() ) ){
Tokeniser_unexpectedError( tokeniser, token, "#layer-name" );
return false;
}
auto& layer = m_layers.m_children.emplace_back( token, nullptr ); // initially construct plain list
if( !Tokeniser_getInteger( tokeniser, layer.m_parentIndex )
|| !Tokeniser_getInteger( tokeniser, layer.m_color[ 0 ] )
|| !Tokeniser_getInteger( tokeniser, layer.m_color[ 1 ] )
|| !Tokeniser_getInteger( tokeniser, layer.m_color[ 2 ] ) )
return false;
Tokeniser_skipToNextLine( tokeniser ); // skip possible format extension
}
}
bool read_layer( Tokeniser& tokeniser ){
const char *token = tokeniser.getToken();
if ( token == nullptr )
return false;
if( !string_equal( token, "layer" ) ){
tokeniser.ungetToken();
return true;
}
if( !Tokeniser_getInteger( tokeniser, m_currentLayer ) ){
return false;
}
else if( m_currentLayer <= LAYERIDXPARENT || m_currentLayer >= static_cast<int>( m_layersVec.size() ) ){
globalErrorStream() << m_currentLayer << " primitive layer index out of bounds\n";
m_currentLayer = LAYERIDX0;
}
Tokeniser_skipToNextLine( tokeniser ); // skip possible format extension
return true;
}
Layer* getCurrentLayer(){
return m_layersVec[ m_currentLayer ].operator->();
}
};
inline MapImporter* Node_getMapImporter( scene::Node& node ){
return NodeTypeCast<MapImporter>::cast( node );
@@ -52,7 +144,7 @@ NodeSmartReference Entity_create( EntityCreator& entityTable, EntityClass* entit
return NodeSmartReference( entity );
}
NodeSmartReference Entity_parseTokens( Tokeniser& tokeniser, EntityCreator& entityTable, const PrimitiveParser& parser, int index ){
NodeSmartReference Entity_parseTokens( Tokeniser& tokeniser, EntityCreator& entityTable, const PrimitiveParser& parser, int index, LayersParser& layersParser ){
NodeSmartReference entity( g_nullNode );
KeyValues keyValues;
const char* classname = "";
@@ -61,8 +153,8 @@ NodeSmartReference Entity_parseTokens( Tokeniser& tokeniser, EntityCreator& enti
while ( 1 )
{
tokeniser.nextLine();
const char* token = tokeniser.getToken();
if ( token == 0 ) {
const char* token;
if ( !layersParser.read_layer( tokeniser ) || !( token = tokeniser.getToken() ) ) {
Tokeniser_unexpectedError( tokeniser, token, "#entity-token" );
return g_nullNode;
}
@@ -86,6 +178,7 @@ NodeSmartReference Entity_parseTokens( Tokeniser& tokeniser, EntityCreator& enti
globalErrorStream() << "brush " << count_primitives << ": parse error\n";
return g_nullNode;
}
primitive.get().m_layer = layersParser.getCurrentLayer();
scene::Traversable* traversable = Node_getTraversable( entity );
if ( Node_getEntity( entity )->isContainer() && traversable != 0 ) {
@@ -116,15 +209,23 @@ NodeSmartReference Entity_parseTokens( Tokeniser& tokeniser, EntityCreator& enti
}
void Map_Read( scene::Node& root, Tokeniser& tokeniser, EntityCreator& entityTable, const PrimitiveParser& parser ){
LayersParser layersParser( root );
if( !layersParser.read_layers( tokeniser ) ){
layersParser.construct_tree(); // contruct anytime to have at least one layer, e.g. when empty .map
return;
}
layersParser.construct_tree();
int count_entities = 0;
for (;; )
{
tokeniser.nextLine();
if ( !tokeniser.getToken() ) { // { or 0
if ( !layersParser.read_layer( tokeniser ) || !tokeniser.getToken() ) { // { or 0
break;
}
Layer *entityLayer = layersParser.getCurrentLayer();
NodeSmartReference entity( Entity_parseTokens( tokeniser, entityTable, parser, count_entities ) );
NodeSmartReference entity( Entity_parseTokens( tokeniser, entityTable, parser, count_entities, layersParser ) );
if ( entity == g_nullNode ) {
globalErrorStream() << "entity " << count_entities << ": parse error\n";
@@ -132,6 +233,8 @@ void Map_Read( scene::Node& root, Tokeniser& tokeniser, EntityCreator& entityTab
}
Node_getTraversable( root )->insert( entity );
if( !Node_getEntity( entity )->isContainer() )
entity.get().m_layer = entityLayer;
++count_entities;
}