]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - plugins/entity/entity.cpp
a251bde5c442be67c0438e89cf3ff56ef945932d
[xonotic/netradiant.git] / plugins / entity / entity.cpp
1 /*
2    Copyright (C) 2001-2006, William Joseph.
3    All Rights Reserved.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #include "entity.h"
23
24 #include "ifilter.h"
25 #include "selectable.h"
26 #include "namespace.h"
27
28 #include "scenelib.h"
29 #include "entitylib.h"
30 #include "eclasslib.h"
31 #include "pivot.h"
32
33 #include "targetable.h"
34 #include "uniquenames.h"
35 #include "namekeys.h"
36 #include "stream/stringstream.h"
37 #include "filters.h"
38
39
40 #include "miscmodel.h"
41 #include "light.h"
42 #include "group.h"
43 #include "eclassmodel.h"
44 #include "generic.h"
45 #include "doom3group.h"
46
47
48
49 EGameType g_gameType;
50
51 inline scene::Node& entity_for_eclass( EntityClass* eclass ){
52         if ( ( string_compare_nocase_n( eclass->name(), "misc_", 5 ) == 0 && string_equal_nocase( eclass->name() + string_length( eclass->name() ) - 5, "model" ) ) // misc_*model (also misc_model) // TODO make classname_* wrapper functions for this
53                  || classname_equal( eclass->name(), "model_static" ) ) {
54                 return New_MiscModel( eclass );
55         }
56         else if ( classname_equal( eclass->name(), "light" )
57                           || classname_equal( eclass->name(), "lightJunior" ) ) {
58                 return New_Light( eclass );
59         }
60         if ( !eclass->fixedsize ) {
61                 if ( g_gameType == eGameTypeDoom3 ) {
62                         return New_Doom3Group( eclass );
63                 }
64                 else
65                 {
66                         return New_Group( eclass );
67                 }
68         }
69         else if ( !string_empty( eclass->modelpath() ) ) {
70                 return New_EclassModel( eclass );
71         }
72         else
73         {
74                 return New_GenericEntity( eclass );
75         }
76 }
77
78 void Entity_setName( Entity& entity, const char* name ){
79         entity.setKeyValue( "name", name );
80 }
81 typedef ReferenceCaller<Entity, void(const char*), Entity_setName> EntitySetNameCaller;
82
83 inline Namespaced* Node_getNamespaced( scene::Node& node ){
84         return NodeTypeCast<Namespaced>::cast( node );
85 }
86
87 inline scene::Node& node_for_eclass( EntityClass* eclass ){
88         scene::Node& node = entity_for_eclass( eclass );
89         Node_getEntity( node )->setKeyValue( "classname", eclass->name() );
90
91         if ( g_gameType == eGameTypeDoom3
92                  && string_not_empty( eclass->name() )
93                  && !string_equal( eclass->name(), "worldspawn" )
94                  && !string_equal( eclass->name(), "UNKNOWN_CLASS" ) ) {
95                 char buffer[1024];
96                 strcpy( buffer, eclass->name() );
97                 strcat( buffer, "_1" );
98                 GlobalNamespace().makeUnique( buffer, EntitySetNameCaller( *Node_getEntity( node ) ) );
99         }
100
101         Namespaced* namespaced = Node_getNamespaced( node );
102         if ( namespaced != 0 ) {
103                 namespaced->setNamespace( GlobalNamespace() );
104         }
105
106         return node;
107 }
108
109 EntityCreator::KeyValueChangedFunc EntityKeyValues::m_entityKeyValueChanged = 0;
110 EntityCreator::KeyValueChangedFunc KeyValue::m_entityKeyValueChanged = 0;
111 Counter* EntityKeyValues::m_counter = 0;
112
113 bool g_showNames = true;
114 bool g_showAngles = true;
115 bool g_newLightDraw = true;
116 bool g_lightRadii = true;
117
118 class ConnectEntities
119 {
120 public:
121 Entity* m_e1;
122 Entity* m_e2;
123 int m_index;
124 ConnectEntities( Entity* e1, Entity* e2, int index ) : m_e1( e1 ), m_e2( e2 ), m_index( index ){
125 }
126 const char *keyname(){
127         StringOutputStream key( 16 );
128         if ( m_index <= 0 ) {
129                 return "target";
130         }
131         if ( m_index == 1 ) {
132                 return "killtarget";
133         }
134         key << "target" << m_index;
135         return key.c_str();
136 }
137 void connect( const char* name ){
138         m_e1->setKeyValue( keyname(), name );
139         m_e2->setKeyValue( "targetname", name );
140 }
141 typedef MemberCaller<ConnectEntities, void(const char*), &ConnectEntities::connect> ConnectCaller;
142 };
143
144 inline Entity* ScenePath_getEntity( const scene::Path& path ){
145         Entity* entity = Node_getEntity( path.top() );
146         if ( entity == 0 ) {
147                 entity = Node_getEntity( path.parent() );
148         }
149         return entity;
150 }
151
152 class Quake3EntityCreator : public EntityCreator
153 {
154 public:
155 scene::Node& createEntity( EntityClass* eclass ){
156         return node_for_eclass( eclass );
157 }
158 void setKeyValueChangedFunc( KeyValueChangedFunc func ){
159         EntityKeyValues::setKeyValueChangedFunc( func );
160 }
161 void setCounter( Counter* counter ){
162         EntityKeyValues::setCounter( counter );
163 }
164 void connectEntities( const scene::Path& path, const scene::Path& targetPath, int index ){
165         Entity* e1 = ScenePath_getEntity( path );
166         Entity* e2 = ScenePath_getEntity( targetPath );
167
168         if ( e1 == 0 || e2 == 0 ) {
169                 globalErrorStream() << "entityConnectSelected: both of the selected instances must be an entity\n";
170                 return;
171         }
172
173         if ( e1 == e2 ) {
174                 globalErrorStream() << "entityConnectSelected: the selected instances must not both be from the same entity\n";
175                 return;
176         }
177
178
179         UndoableCommand undo( "entityConnectSelected" );
180
181         if ( g_gameType == eGameTypeDoom3 ) {
182                 StringOutputStream key( 16 );
183                 if ( index >= 0 ) {
184                         key << "target";
185                         if ( index != 0 ) {
186                                 key << index;
187                         }
188                         e1->setKeyValue( key.c_str(), e2->getKeyValue( "name" ) );
189                         key.clear();
190                 }
191                 else
192                 {
193                         for ( unsigned int i = 0; ; ++i )
194                         {
195                                 key << "target";
196                                 if ( i != 0 ) {
197                                         key << i;
198                                 }
199                                 const char* value = e1->getKeyValue( key.c_str() );
200                                 if ( string_empty( value ) ) {
201                                         e1->setKeyValue( key.c_str(), e2->getKeyValue( "name" ) );
202                                         break;
203                                 }
204                                 key.clear();
205                         }
206                 }
207         }
208         else
209         {
210                 ConnectEntities connector( e1, e2, index );
211                 //killconnect
212                 if( index == 1 ){
213                         const char* value = e2->getKeyValue( "targetname" );
214                         if ( !string_empty( value ) ) {
215                                 connector.connect( value );
216                         }
217                         else
218                         {
219                                 const char* type = e2->getKeyValue( "classname" );
220                                 if ( string_empty( type ) ) {
221                                         type = "t";
222                                 }
223                                 StringOutputStream key( 64 );
224                                 key << type << "1";
225                                 GlobalNamespace().makeUnique( key.c_str(), ConnectEntities::ConnectCaller( connector ) );
226                         }
227                 }
228                 //normal connect
229                 else{
230                         //prioritize existing target key
231                         //checking, if ent got other connected ones already, could be better solution
232                         const char* value = e1->getKeyValue( "target" );
233                         if ( !string_empty( value ) ) {
234                                 connector.connect( value );
235                         }
236                         else{
237                                 value = e2->getKeyValue( "targetname" );
238                                 if ( !string_empty( value ) ) {
239                                         connector.connect( value );
240                                 }
241                                 else{
242                                         const char* type = e2->getKeyValue( "classname" );
243                                         if ( string_empty( type ) ) {
244                                                 type = "t";
245                                         }
246                                         StringOutputStream key( 64 );
247                                         key << type << "1";
248                                         GlobalNamespace().makeUnique( key.c_str(), ConnectEntities::ConnectCaller( connector ) );
249                                 }
250                         }
251                 }
252         }
253         SceneChangeNotify();
254 }
255 void setLightRadii( bool lightRadii ){
256         g_lightRadii = lightRadii;
257 }
258 bool getLightRadii() const {
259         return g_lightRadii;
260 }
261 void setShowNames( bool showNames ){
262         g_showNames = showNames;
263 }
264 bool getShowNames(){
265         return g_showNames;
266 }
267 void setShowAngles( bool showAngles ){
268         g_showAngles = showAngles;
269 }
270 bool getShowAngles(){
271         return g_showAngles;
272 }
273
274 void printStatistics() const {
275         StringPool_analyse( EntityKeyValues::getPool() );
276 }
277 };
278
279 Quake3EntityCreator g_Quake3EntityCreator;
280
281 EntityCreator& GetEntityCreator(){
282         return g_Quake3EntityCreator;
283 }
284
285
286
287 class filter_entity_classname : public EntityFilter
288 {
289 const char* m_classname;
290 public:
291 filter_entity_classname( const char* classname ) : m_classname( classname ){
292 }
293 bool filter( const Entity& entity ) const {
294         return string_equal( entity.getKeyValue( "classname" ), m_classname );
295 }
296 };
297
298 class filter_entity_classgroup : public EntityFilter
299 {
300 const char* m_classgroup;
301 std::size_t m_length;
302 public:
303 filter_entity_classgroup( const char* classgroup ) : m_classgroup( classgroup ), m_length( string_length( m_classgroup ) ){
304 }
305 bool filter( const Entity& entity ) const {
306         return string_equal_n( entity.getKeyValue( "classname" ), m_classgroup, m_length );
307 }
308 };
309
310 //filter_entity_classname g_filter_entity_world( "worldspawn" );
311 filter_entity_classname g_filter_entity_func_group( "func_group" );
312 filter_entity_classname g_filter_entity_light( "light" );
313 filter_entity_classname g_filter_entity_misc_model( "misc_model" );
314 filter_entity_classname g_filter_entity_misc_gamemodel( "misc_gamemodel" );
315 filter_entity_classgroup g_filter_entity_trigger( "trigger_" );
316 filter_entity_classgroup g_filter_entity_path( "path_" );
317
318 class filter_entity_doom3model : public EntityFilter
319 {
320 public:
321 bool filter( const Entity& entity ) const {
322         return string_equal( entity.getKeyValue( "classname" ), "func_static" )
323                    && !string_equal( entity.getKeyValue( "model" ), entity.getKeyValue( "name" ) );
324 }
325 };
326
327 filter_entity_doom3model g_filter_entity_doom3model;
328
329
330 class filter_entity_world : public EntityFilter
331 {
332 public:
333 bool filter( const Entity& entity ) const {
334         return string_equal( entity.getKeyValue( "classname" ), "worldspawn" )
335                    || string_equal( entity.getKeyValue( "classname" ), "func_group" );
336 }
337 };
338
339 filter_entity_world g_filter_entity_world;
340
341 void Entity_InitFilters(){
342         add_entity_filter( g_filter_entity_world, EXCLUDE_WORLD );
343         add_entity_filter( g_filter_entity_func_group, EXCLUDE_FUNC_GROUPS );
344         add_entity_filter( g_filter_entity_world, EXCLUDE_ENT, true );
345         add_entity_filter( g_filter_entity_trigger, EXCLUDE_TRIGGERS );
346         add_entity_filter( g_filter_entity_misc_model, EXCLUDE_MODELS );
347         add_entity_filter( g_filter_entity_misc_gamemodel, EXCLUDE_MODELS );
348         add_entity_filter( g_filter_entity_doom3model, EXCLUDE_MODELS );
349         add_entity_filter( g_filter_entity_light, EXCLUDE_LIGHTS );
350         add_entity_filter( g_filter_entity_path, EXCLUDE_PATHS );
351 }
352
353
354 #include "preferencesystem.h"
355
356 void Entity_Construct( EGameType gameType ){
357         g_gameType = gameType;
358         if ( g_gameType == eGameTypeDoom3 ) {
359                 g_targetable_nameKey = "name";
360
361                 Static<KeyIsName>::instance().m_keyIsName = keyIsNameDoom3;
362                 Static<KeyIsName>::instance().m_nameKey = "name";
363         }
364         else
365         {
366                 Static<KeyIsName>::instance().m_keyIsName = keyIsNameQuake3;
367                 Static<KeyIsName>::instance().m_nameKey = "targetname";
368         }
369
370         GlobalPreferenceSystem().registerPreference( "SI_ShowNames", make_property_string( g_showNames ) );
371         GlobalPreferenceSystem().registerPreference( "SI_ShowAngles", make_property_string( g_showAngles ) );
372         GlobalPreferenceSystem().registerPreference( "NewLightStyle", make_property_string( g_newLightDraw ) );
373         GlobalPreferenceSystem().registerPreference( "LightRadiuses", make_property_string( g_lightRadii ) );
374
375         Entity_InitFilters();
376         LightType lightType = LIGHTTYPE_DEFAULT;
377         if ( g_gameType == eGameTypeRTCW ) {
378                 lightType = LIGHTTYPE_RTCW;
379         }
380         else if ( g_gameType == eGameTypeDoom3 ) {
381                 lightType = LIGHTTYPE_DOOM3;
382         }
383         Light_Construct( lightType );
384         MiscModel_construct();
385         Doom3Group_construct();
386
387         RenderablePivot::StaticShader::instance() = GlobalShaderCache().capture( "$PIVOT" );
388
389         GlobalShaderCache().attachRenderable( StaticRenderableConnectionLines::instance() );
390 }
391
392 void Entity_Destroy(){
393         GlobalShaderCache().detachRenderable( StaticRenderableConnectionLines::instance() );
394
395         GlobalShaderCache().release( "$PIVOT" );
396
397         Doom3Group_destroy();
398         MiscModel_destroy();
399         Light_Destroy();
400 }