]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - plugins/entity/entity.cpp
reformat code! now the code is only ugly on the *inside*
[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 EGameType g_gameType;
49
50 inline scene::Node &entity_for_eclass(EntityClass *eclass)
51 {
52     if ((string_compare_nocase_n(eclass->name(), "misc_", 5) == 0 &&
53          string_equal_nocase(eclass->name() + string_length(eclass->name()) - 5,
54                              "model")) // misc_*model (also misc_model) // TODO make classname_* wrapper functions for this
55         || classname_equal(eclass->name(), "model_static")) {
56         return New_MiscModel(eclass);
57     } else if (classname_equal(eclass->name(), "light")
58                || classname_equal(eclass->name(), "lightJunior")) {
59         return New_Light(eclass);
60     }
61     if (!eclass->fixedsize) {
62         if (g_gameType == eGameTypeDoom3) {
63             return New_Doom3Group(eclass);
64         } else {
65             return New_Group(eclass);
66         }
67     } else if (!string_empty(eclass->modelpath())) {
68         return New_EclassModel(eclass);
69     } else {
70         return New_GenericEntity(eclass);
71     }
72 }
73
74 void Entity_setName(Entity &entity, const char *name)
75 {
76     entity.setKeyValue("name", name);
77 }
78
79 typedef ReferenceCaller<Entity, void(const char *), Entity_setName> EntitySetNameCaller;
80
81 inline Namespaced *Node_getNamespaced(scene::Node &node)
82 {
83     return NodeTypeCast<Namespaced>::cast(node);
84 }
85
86 inline scene::Node &node_for_eclass(EntityClass *eclass)
87 {
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 = false;
117
118 class ConnectEntities {
119 public:
120     Entity *m_e1;
121     Entity *m_e2;
122     int m_index;
123
124     ConnectEntities(Entity *e1, Entity *e2, int index) : m_e1(e1), m_e2(e2), m_index(index)
125     {
126     }
127
128     const char *keyname()
129     {
130         StringOutputStream key(16);
131         if (m_index <= 0) {
132             return "target";
133         }
134         if (m_index == 1) {
135             return "killtarget";
136         }
137         key << "target" << m_index;
138         return key.c_str();
139     }
140
141     void connect(const char *name)
142     {
143         m_e1->setKeyValue(keyname(), name);
144         m_e2->setKeyValue("targetname", name);
145     }
146
147     typedef MemberCaller<ConnectEntities, void(const char *), &ConnectEntities::connect> ConnectCaller;
148 };
149
150 inline Entity *ScenePath_getEntity(const scene::Path &path)
151 {
152     Entity *entity = Node_getEntity(path.top());
153     if (entity == 0) {
154         entity = Node_getEntity(path.parent());
155     }
156     return entity;
157 }
158
159 class Quake3EntityCreator : public EntityCreator {
160 public:
161     scene::Node &createEntity(EntityClass *eclass)
162     {
163         return node_for_eclass(eclass);
164     }
165
166     void setKeyValueChangedFunc(KeyValueChangedFunc func)
167     {
168         EntityKeyValues::setKeyValueChangedFunc(func);
169     }
170
171     void setCounter(Counter *counter)
172     {
173         EntityKeyValues::setCounter(counter);
174     }
175
176     void connectEntities(const scene::Path &path, const scene::Path &targetPath, int index)
177     {
178         Entity *e1 = ScenePath_getEntity(path);
179         Entity *e2 = ScenePath_getEntity(targetPath);
180
181         if (e1 == 0 || e2 == 0) {
182             globalErrorStream() << "entityConnectSelected: both of the selected instances must be an entity\n";
183             return;
184         }
185
186         if (e1 == e2) {
187             globalErrorStream()
188                     << "entityConnectSelected: the selected instances must not both be from the same entity\n";
189             return;
190         }
191
192
193         UndoableCommand undo("entityConnectSelected");
194
195         if (g_gameType == eGameTypeDoom3) {
196             StringOutputStream key(16);
197             if (index >= 0) {
198                 key << "target";
199                 if (index != 0) {
200                     key << index;
201                 }
202                 e1->setKeyValue(key.c_str(), e2->getKeyValue("name"));
203                 key.clear();
204             } else {
205                 for (unsigned int i = 0;; ++i) {
206                     key << "target";
207                     if (i != 0) {
208                         key << i;
209                     }
210                     const char *value = e1->getKeyValue(key.c_str());
211                     if (string_empty(value)) {
212                         e1->setKeyValue(key.c_str(), e2->getKeyValue("name"));
213                         break;
214                     }
215                     key.clear();
216                 }
217             }
218         } else {
219             ConnectEntities connector(e1, e2, index);
220             const char *value = e2->getKeyValue("targetname");
221             if (!string_empty(value)) {
222                 connector.connect(value);
223             } else {
224                 const char *type = e2->getKeyValue("classname");
225                 if (string_empty(type)) {
226                     type = "t";
227                 }
228                 StringOutputStream key(64);
229                 key << type << "1";
230                 GlobalNamespace().makeUnique(key.c_str(), ConnectEntities::ConnectCaller(connector));
231             }
232         }
233
234         SceneChangeNotify();
235     }
236
237     void setLightRadii(bool lightRadii)
238     {
239         g_lightRadii = lightRadii;
240     }
241
242     bool getLightRadii() const
243     {
244         return g_lightRadii;
245     }
246
247     void setShowNames(bool showNames)
248     {
249         g_showNames = showNames;
250     }
251
252     bool getShowNames()
253     {
254         return g_showNames;
255     }
256
257     void setShowAngles(bool showAngles)
258     {
259         g_showAngles = showAngles;
260     }
261
262     bool getShowAngles()
263     {
264         return g_showAngles;
265     }
266
267     void printStatistics() const
268     {
269         StringPool_analyse(EntityKeyValues::getPool());
270     }
271 };
272
273 Quake3EntityCreator g_Quake3EntityCreator;
274
275 EntityCreator &GetEntityCreator()
276 {
277     return g_Quake3EntityCreator;
278 }
279
280
281 class filter_entity_classname : public EntityFilter {
282     const char *m_classname;
283 public:
284     filter_entity_classname(const char *classname) : m_classname(classname)
285     {
286     }
287
288     bool filter(const Entity &entity) const
289     {
290         return string_equal(entity.getKeyValue("classname"), m_classname);
291     }
292 };
293
294 class filter_entity_classgroup : public EntityFilter {
295     const char *m_classgroup;
296     std::size_t m_length;
297 public:
298     filter_entity_classgroup(const char *classgroup) : m_classgroup(classgroup), m_length(string_length(m_classgroup))
299     {
300     }
301
302     bool filter(const Entity &entity) const
303     {
304         return string_equal_n(entity.getKeyValue("classname"), m_classgroup, m_length);
305     }
306 };
307
308 filter_entity_classname g_filter_entity_world("worldspawn");
309 filter_entity_classname g_filter_entity_func_group("func_group");
310 filter_entity_classname g_filter_entity_light("light");
311 filter_entity_classname g_filter_entity_misc_model("misc_model");
312 filter_entity_classname g_filter_entity_misc_gamemodel("misc_gamemodel");
313 filter_entity_classgroup g_filter_entity_trigger("trigger_");
314 filter_entity_classgroup g_filter_entity_path("path_");
315
316 class filter_entity_doom3model : public EntityFilter {
317 public:
318     bool filter(const Entity &entity) const
319     {
320         return string_equal(entity.getKeyValue("classname"), "func_static")
321                && !string_equal(entity.getKeyValue("model"), entity.getKeyValue("name"));
322     }
323 };
324
325 filter_entity_doom3model g_filter_entity_doom3model;
326
327
328 void Entity_InitFilters()
329 {
330     add_entity_filter(g_filter_entity_world, EXCLUDE_WORLD);
331     add_entity_filter(g_filter_entity_func_group, EXCLUDE_WORLD);
332     add_entity_filter(g_filter_entity_world, EXCLUDE_ENT, true);
333     add_entity_filter(g_filter_entity_trigger, EXCLUDE_TRIGGERS);
334     add_entity_filter(g_filter_entity_misc_model, EXCLUDE_MODELS);
335     add_entity_filter(g_filter_entity_misc_gamemodel, EXCLUDE_MODELS);
336     add_entity_filter(g_filter_entity_doom3model, EXCLUDE_MODELS);
337     add_entity_filter(g_filter_entity_light, EXCLUDE_LIGHTS);
338     add_entity_filter(g_filter_entity_path, EXCLUDE_PATHS);
339 }
340
341
342 #include "preferencesystem.h"
343
344 void Entity_Construct(EGameType gameType)
345 {
346     g_gameType = gameType;
347     if (g_gameType == eGameTypeDoom3) {
348         g_targetable_nameKey = "name";
349
350         Static<KeyIsName>::instance().m_keyIsName = keyIsNameDoom3;
351         Static<KeyIsName>::instance().m_nameKey = "name";
352     } else {
353         Static<KeyIsName>::instance().m_keyIsName = keyIsNameQuake3;
354         Static<KeyIsName>::instance().m_nameKey = "targetname";
355     }
356
357     GlobalPreferenceSystem().registerPreference("SI_ShowNames", make_property_string(g_showNames));
358     GlobalPreferenceSystem().registerPreference("SI_ShowAngles", make_property_string(g_showAngles));
359     GlobalPreferenceSystem().registerPreference("NewLightStyle", make_property_string(g_newLightDraw));
360     GlobalPreferenceSystem().registerPreference("LightRadiuses", make_property_string(g_lightRadii));
361
362     Entity_InitFilters();
363     LightType lightType = LIGHTTYPE_DEFAULT;
364     if (g_gameType == eGameTypeRTCW) {
365         lightType = LIGHTTYPE_RTCW;
366     } else if (g_gameType == eGameTypeDoom3) {
367         lightType = LIGHTTYPE_DOOM3;
368     }
369     Light_Construct(lightType);
370     MiscModel_construct();
371     Doom3Group_construct();
372
373     RenderablePivot::StaticShader::instance() = GlobalShaderCache().capture("$PIVOT");
374
375     GlobalShaderCache().attachRenderable(StaticRenderableConnectionLines::instance());
376 }
377
378 void Entity_Destroy()
379 {
380     GlobalShaderCache().detachRenderable(StaticRenderableConnectionLines::instance());
381
382     GlobalShaderCache().release("$PIVOT");
383
384     Doom3Group_destroy();
385     MiscModel_destroy();
386     Light_Destroy();
387 }