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