1 /** @file entitydef.cpp  World playsim data structures.
2  *
3  * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4  * @authors Copyright © 2006-2015 Daniel Swanson <danij@dengine.net>
5  *
6  * @par License
7  * GPL: http://www.gnu.org/licenses/gpl.html
8  *
9  * <small>This program is M_Free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by the
11  * Free Software Foundation; either version 2 of the License, or (at your
12  * option) any later version. This program is distributed in the hope that it
13  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15  * Public License for more details. You should have received a copy of the GNU
16  * General Public License along with this program; if not, write to the Free
17  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18  * 02110-1301 USA</small>
19  */
20 
21 //#include "de_base.h"
22 #include "doomsday/world/entitydef.h"
23 #include "doomsday/world/world.h"
24 #include "doomsday/world/map.h"
25 #include "doomsday/world/propertyvalue.h"
26 #include "doomsday/EntityDatabase"
27 
28 #include <cmath>
29 #include <map>
30 #include <de/memory.h>
31 #include <de/Error>
32 #include <de/Log>
33 #include <de/String>
34 #include <de/StringPool>
35 
36 using namespace de;
37 
38 // Map entity definitions.
39 static StringPool *entityDefs;
40 typedef std::map<int, StringPool::Id> EntityDefIdMap;
41 static EntityDefIdMap entityDefIdMap;
42 
clearEntityDefs()43 static void clearEntityDefs()
44 {
45     if (!::entityDefs) return;
46 
47     ::entityDefs->forAll([] (StringPool::Id id)
48     {
49         auto *def = static_cast<MapEntityDef *>( ::entityDefs->userPointer(id) );
50         DENG2_ASSERT(def);
51         for (duint i = 0; i < def->numProps; ++i)
52         {
53             M_Free(def->props[i].name);
54         }
55         M_Free(def->props);
56         delete def;
57 
58         return LoopContinue;
59     });
60     delete ::entityDefs; ::entityDefs = nullptr;
61 
62     ::entityDefIdMap.clear();
63 }
64 
P_MapEntityDef(int id)65 MapEntityDef *P_MapEntityDef(int id)
66 {
67     EntityDefIdMap::iterator i = entityDefIdMap.find(id);
68     if (i != entityDefIdMap.end())
69     {
70         StringPool::Id id = i->second;
71         return static_cast<MapEntityDef *>( entityDefs->userPointer(id) );
72     }
73     return nullptr;  // Not found.
74 }
75 
P_MapEntityDefByName(char const * name)76 MapEntityDef *P_MapEntityDefByName(char const *name)
77 {
78     if (name && entityDefs)
79     {
80         StringPool::Id id = entityDefs->isInterned(String(name));
81         return static_cast<MapEntityDef *>( entityDefs->userPointer(id) );
82     }
83     return nullptr;  // Not found.
84 }
85 
P_NameForMapEntityDef(MapEntityDef const * def)86 AutoStr *P_NameForMapEntityDef(MapEntityDef const *def)
87 {
88     String name;  // Not found.
89     if (def)
90     {
91         ::entityDefs->forAll([&def, &name] (StringPool::Id id)
92         {
93             if (::entityDefs->userPointer(id) == def)
94             {
95                 name = ::entityDefs->string(id);
96                 return LoopAbort;
97             }
98             return LoopContinue;
99         });
100     }
101     return AutoStr_FromText(name.toUtf8().constData());
102 }
103 
MapEntityDef_Property(MapEntityDef * def,int propertyId,MapEntityPropertyDef ** retDef=0)104 int MapEntityDef_Property(MapEntityDef *def, int propertyId,
105                           MapEntityPropertyDef **retDef = 0)
106 {
107     DENG2_ASSERT(def);
108     MapEntityPropertyDef *found = 0;
109     for (uint i = 0; i < def->numProps; ++i)
110     {
111         MapEntityPropertyDef *prop = def->props + i;
112         if (prop->id == propertyId)
113         {
114             found = prop;
115             break;
116         }
117     }
118     if (retDef) *retDef = found;
119     return found? found - def->props : -1/* not found */;
120 }
121 
MapEntityDef_PropertyByName(MapEntityDef * def,char const * propertyName,MapEntityPropertyDef ** retDef)122 int MapEntityDef_PropertyByName(MapEntityDef *def, char const *propertyName,
123                                 MapEntityPropertyDef **retDef)
124 {
125     DENG2_ASSERT(def);
126     MapEntityPropertyDef *found = 0;
127     if (propertyName && propertyName[0])
128     {
129         for (uint i = 0; i < def->numProps; ++i)
130         {
131             MapEntityPropertyDef *prop = def->props + i;
132             if (!qstricmp(prop->name, propertyName))
133             {
134                 found = prop;
135                 break;
136             }
137         }
138     }
139     if (retDef) *retDef = found;
140     return found? found - def->props : -1/* not found */;
141 }
142 
MapEntityDef_AddProperty(MapEntityDef * def,int propertyId,const char * propertyName,valuetype_t type)143 void MapEntityDef_AddProperty(MapEntityDef* def, int propertyId, const char* propertyName,
144                               valuetype_t type)
145 {
146     DENG2_ASSERT(def);
147 
148     if (propertyId == 0) // Not a valid identifier.
149         throw Error("MapEntityDef_AddProperty", "0 is not a valid propertyId");
150 
151     if (!propertyName || !propertyName[0]) // Must have a name.
152         throw Error("MapEntityDef_AddProperty", "Invalid propertyName (zero-length string)");
153 
154     // A supported value type?
155     switch (type)
156     {
157     case DDVT_BYTE:
158     case DDVT_SHORT:
159     case DDVT_INT:
160     case DDVT_FIXED:
161     case DDVT_ANGLE:
162     case DDVT_FLOAT:
163     case DDVT_DOUBLE:
164         break;
165 
166     default:
167         throw Error("MapEntityDef_AddProperty", QString("Unknown/not supported value type %1").arg(type));
168     }
169 
170     // Ensure both the identifer and the name for the new property are unique.
171     if (MapEntityDef_Property(def, propertyId) >= 0)
172         throw Error("MapEntityDef_AddProperty", QString("propertyId %1 not unique for %2")
173                                                     .arg(propertyId).arg(Str_Text(P_NameForMapEntityDef(def))));
174     if (MapEntityDef_PropertyByName(def, propertyName) >= 0)
175         throw Error("MapEntityDef_AddProperty", QString("propertyName \"%1\" not unique for %2")
176                                                     .arg(propertyName).arg(Str_Text(P_NameForMapEntityDef(def))));
177 
178     // Looks good! Add it to the list of properties.
179     def->props = (MapEntityPropertyDef*) M_Realloc(def->props, ++def->numProps * sizeof(*def->props));
180     if (!def->props)
181         throw Error("MapEntityDef_AddProperty",
182                         QString("Failed on (re)allocation of %1 bytes for new MapEntityPropertyDef array")
183                             .arg((unsigned long) sizeof(*def->props)));
184 
185     MapEntityPropertyDef* prop = &def->props[def->numProps - 1];
186     prop->id = propertyId;
187 
188     int len = (int)strlen(propertyName);
189     prop->name = (char *) M_Malloc(sizeof(*prop->name) * (len + 1));
190     if (!prop->name)
191         throw Error("MapEntityDef_AddProperty",
192                         QString("Failed on allocation of %1 bytes for property name")
193                             .arg((unsigned long) (sizeof(*prop->name) * (len + 1))));
194 
195     strncpy(prop->name, propertyName, len);
196     prop->name[len] = '\0';
197     prop->type = type;
198     prop->entity = def;
199 }
200 
201 /**
202  * Look up a mapobj definition.
203  *
204  * @param identifer   If objName is @c NULL, compare using this unique identifier.
205  * @param entityName  If not @c NULL, compare using this unique name.
206  * @param canCreate   @c true= create a new definition if not found.
207  */
findMapEntityDef(int identifier,char const * entityName,bool canCreate)208 static MapEntityDef *findMapEntityDef(int identifier, char const *entityName,
209                                       bool canCreate)
210 {
211     if (identifier == 0 && (!entityName || !entityName[0])) return 0;
212 
213     // Is this an already known entity?
214     if (entityName && entityName[0])
215     {
216         MapEntityDef *found = P_MapEntityDefByName(entityName);
217         if (found) return found;
218     }
219     else
220     {
221         MapEntityDef *found = P_MapEntityDef(identifier);
222         if (found) return found;
223     }
224 
225     // An unknown entity. Are we creating?
226     if (!canCreate) return 0;
227 
228     // Ensure the name is unique.
229     if (P_MapEntityDefByName(entityName)) return 0;
230 
231     // Ensure the identifier is unique.
232     if (P_MapEntityDef(identifier)) return 0;
233 
234     // Have we yet to initialize the map entity definition dataset?
235     if (!entityDefs)
236     {
237         entityDefs = new StringPool;
238     }
239 
240     StringPool::Id id = entityDefs->intern(String(entityName));
241     MapEntityDef *def = new MapEntityDef(identifier);
242     entityDefs->setUserPointer(id, def);
243 
244     entityDefIdMap.insert(std::pair<int, StringPool::Id>(identifier, id));
245 
246     return def;
247 }
248 
P_RegisterMapObj(int identifier,char const * name)249 DENG_EXTERN_C dd_bool P_RegisterMapObj(int identifier, char const *name)
250 {
251     return findMapEntityDef(identifier, name, true /*do create*/) != 0;
252 }
253 
P_RegisterMapObjProperty(int entityId,int propertyId,char const * propertyName,valuetype_t type)254 DENG_EXTERN_C dd_bool P_RegisterMapObjProperty(int entityId, int propertyId,
255                                                char const *propertyName, valuetype_t type)
256 {
257     try
258     {
259         MapEntityDef *def = findMapEntityDef(entityId, 0, false /*do not create*/);
260         if (!def) throw Error("P_RegisterMapObjProperty", QString("Unknown entityId %1").arg(entityId));
261 
262         MapEntityDef_AddProperty(def, propertyId, propertyName, type);
263         return true; // Success!
264     }
265     catch (Error const &er)
266     {
267         LOG_WARNING("%s. Ignoring.") << er.asText();
268     }
269     return false;
270 }
271 
P_InitMapEntityDefs()272 void P_InitMapEntityDefs()
273 {
274     // Allow re-init.
275     clearEntityDefs();
276 }
277 
P_ShutdownMapEntityDefs()278 void P_ShutdownMapEntityDefs()
279 {
280     clearEntityDefs();
281 }
282 
entityPropertyDef(int entityId,int propertyId)283 static MapEntityPropertyDef *entityPropertyDef(int entityId, int propertyId)
284 {
285     // Is this a known entity?
286     MapEntityDef *entity = P_MapEntityDef(entityId);
287     if (!entity) throw Error("entityPropertyDef", QString("Unknown entity definition id %1").arg(entityId));
288 
289     // Is this a known property?
290     MapEntityPropertyDef *property;
291     if (MapEntityDef_Property(entity, propertyId, &property) < 0)
292         throw Error("entityPropertyDef", QString("Entity definition %1 has no property with id %2")
293                                                  .arg(Str_Text(P_NameForMapEntityDef(entity)))
294                                                  .arg(propertyId));
295 
296     return property; // Found it.
297 }
298 
setValue(void * dst,valuetype_t dstType,PropertyValue const & pvalue)299 static void setValue(void *dst, valuetype_t dstType, PropertyValue const &pvalue)
300 {
301     switch (dstType)
302     {
303     case DDVT_FIXED:  *((fixed_t *) dst) = pvalue.asFixed();  break;
304     case DDVT_FLOAT:  *(  (float *) dst) = pvalue.asFloat();  break;
305     case DDVT_DOUBLE: *( (double *) dst) = pvalue.asDouble(); break;
306     case DDVT_BYTE:   *(   (byte *) dst) = pvalue.asByte();   break;
307     case DDVT_INT:    *(    (int *) dst) = pvalue.asInt32();  break;
308     case DDVT_SHORT:  *(  (short *) dst) = pvalue.asInt16();  break;
309     case DDVT_ANGLE:  *((angle_t *) dst) = pvalue.asAngle();  break;
310     default:
311         throw Error("setValue", QString("Unknown value type %d").arg(dstType));
312     }
313 }
314 
P_GMOPropertyIsSet(int entityId,int elementIndex,int propertyId)315 dd_bool P_GMOPropertyIsSet(int entityId, int elementIndex, int propertyId)
316 {
317     if (World::get().hasMap())
318     {
319         World::get().map().entityDatabase()
320                 .hasPropertyValue(entityPropertyDef(entityId, propertyId), elementIndex);
321     }
322     return false;
323 }
324 
325 template <typename Type, valuetype_t returnValueType>
326 Type getEntityValue(int entityId, int elementIndex, int propertyId)
327 {
328     try
329     {
330         Type returnVal = 0;
331         if (World::get().hasMap())
332         {
333             EntityDatabase const &db = World::get().map().entityDatabase();
334             MapEntityPropertyDef const *propDef = entityPropertyDef(entityId, propertyId);
335             setValue(&returnVal, returnValueType, db.property(propDef, elementIndex));
336         }
337         return returnVal;
338     }
339     catch (Error const &er)
340     {
341         LOG_WARNING("%s. Returning 0.") << er.asText();
342         return 0;
343     }
344 }
345 
P_GetGMOByte(int entityId,int elementIndex,int propertyId)346 DENG_EXTERN_C byte P_GetGMOByte(int entityId, int elementIndex, int propertyId)
347 {
348     return getEntityValue<byte, DDVT_BYTE>(entityId, elementIndex, propertyId);
349 }
350 
P_GetGMOShort(int entityId,int elementIndex,int propertyId)351 DENG_EXTERN_C short P_GetGMOShort(int entityId, int elementIndex, int propertyId)
352 {
353     return getEntityValue<short, DDVT_SHORT>(entityId, elementIndex, propertyId);
354 }
355 
P_GetGMOInt(int entityId,int elementIndex,int propertyId)356 DENG_EXTERN_C int P_GetGMOInt(int entityId, int elementIndex, int propertyId)
357 {
358     return getEntityValue<int, DDVT_INT>(entityId, elementIndex, propertyId);
359 }
360 
P_GetGMOFixed(int entityId,int elementIndex,int propertyId)361 DENG_EXTERN_C fixed_t P_GetGMOFixed(int entityId, int elementIndex, int propertyId)
362 {
363     return getEntityValue<fixed_t, DDVT_FIXED>(entityId, elementIndex, propertyId);
364 }
365 
P_GetGMOAngle(int entityId,int elementIndex,int propertyId)366 DENG_EXTERN_C angle_t P_GetGMOAngle(int entityId, int elementIndex, int propertyId)
367 {
368     return getEntityValue<angle_t, DDVT_ANGLE>(entityId, elementIndex, propertyId);
369 }
370 
P_GetGMOFloat(int entityId,int elementIndex,int propertyId)371 DENG_EXTERN_C float P_GetGMOFloat(int entityId, int elementIndex, int propertyId)
372 {
373     return getEntityValue<float, DDVT_FLOAT>(entityId, elementIndex, propertyId);
374 }
375 
P_GetGMODouble(int entityId,int elementIndex,int propertyId)376 DENG_EXTERN_C double P_GetGMODouble(int entityId, int elementIndex, int propertyId)
377 {
378     return getEntityValue<double, DDVT_DOUBLE>(entityId, elementIndex, propertyId);
379 }
380