xoreos  0.0.5
creature.cpp
Go to the documentation of this file.
1 /* xoreos - A reimplementation of BioWare's Aurora engine
2  *
3  * xoreos is the legal property of its developers, whose names
4  * can be found in the AUTHORS file distributed with this source
5  * distribution.
6  *
7  * xoreos is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 3
10  * of the License, or (at your option) any later version.
11  *
12  * xoreos 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 xoreos. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
25 #include "src/common/scopedptr.h"
26 #include "src/common/util.h"
27 #include "src/common/strutil.h"
28 #include "src/common/maths.h"
29 #include "src/common/error.h"
30 
31 #include "src/aurora/language.h"
32 #include "src/aurora/gff3file.h"
33 #include "src/aurora/gff4file.h"
34 #include "src/aurora/gdafile.h"
35 
37 
40 
43 
44 namespace Engines {
45 
46 namespace DragonAge {
47 
48 static const uint32 kMORPID = MKTAG('M', 'O', 'R', 'P');
49 static const uint32 kVersion01 = MKTAG('V', '0', '.', '1');
50 
51 static const uint32 kUTCID = MKTAG('U', 'T', 'C', ' ');
52 static const uint32 kUTIID = MKTAG('U', 'T', 'I', ' ');
53 
54 using ::Aurora::GFF3File;
55 using ::Aurora::GFF3Struct;
57 
58 using ::Aurora::GFF4File;
59 using ::Aurora::GFF4Struct;
61 
62 using namespace ::Aurora::GFF4FieldNamesEnum;
63 
65 
67  init();
68 }
69 
70 Creature::Creature(const GFF3Struct &creature) : Object(kObjectTypeCreature) {
71  init();
72 
73  load(creature);
74 }
75 
77  hide();
78 }
79 
81  _isPC = false;
82 
83  _appearanceID = 0xFFFFFFFF;
85 
86  for (size_t i = 0; i < kPartVariationCount; i++)
87  _partVariation[i] = 0xFFFFFFFF;
88 }
89 
90 void Creature::setPosition(float x, float y, float z) {
91  Object::setPosition(x, y, z);
92  Object::getPosition(x, y, z);
93 
94  for (Models::iterator m = _models.begin(); m != _models.end(); ++m)
95  (*m)->setPosition(x, y, z);
96 }
97 
98 void Creature::setOrientation(float x, float y, float z, float angle) {
99  Object::setOrientation(x, y, z, angle);
100  Object::getOrientation(x, y, z, angle);
101 
102  for (Models::iterator m = _models.begin(); m != _models.end(); ++m)
103  (*m)->setOrientation(x, y, z, angle);
104 }
105 
107  for (Models::iterator m = _models.begin(); m != _models.end(); ++m)
108  (*m)->show();
109 }
110 
112  for (Models::iterator m = _models.begin(); m != _models.end(); ++m)
113  (*m)->hide();
114 }
115 
116 bool Creature::isPC() const {
117  return _isPC;
118 }
119 
121  _name.setString(LangMan.getCurrentLanguageText(), LangMan.getCurrentGender(), "Fakoo McFakeston");
122  _tag = Common::UString::format("[PC: %s]", _name.getString().c_str());
123 
124  _isPC = true;
125 
126  enableEvents(false);
127 }
128 
130  status("Creature \"%s\" (\"%s\"): \"%s\"",
132 
133  highlight(true);
134 }
135 
137  highlight(false);
138 }
139 
140 void Creature::highlight(bool enabled) {
141  for (Models::iterator m = _models.begin(); m != _models.end(); ++m)
142  (*m)->drawBound(enabled);
143 }
144 
145 bool Creature::click(Object *triggerer) {
146  runScript(kEventTypeClick , this, triggerer);
147  runScript(kEventTypeDialogue, this, triggerer);
148 
149  return true;
150 }
151 
152 void Creature::load(const GFF3Struct &creature) {
153  _resRef = creature.getString("TemplateResRef");
154 
156  if (!_resRef.empty())
158 
159  load(creature, utc ? &utc->getTopLevel() : 0);
160 }
161 
163  if (row == Aurora::GDAFile::kInvalidRow)
164  return "";
165 
166  static const char * const kGenderChars[4] = { "N", "M", "F", "N" };
167 
168  Common::UString genderChar = gda.getString(row, "ModelGenderOverride");
169  if (genderChar.empty() && (gender < ARRAYSIZE(kGenderChars)))
170  genderChar = kGenderChars[gender];
171 
172  const Common::UString modelRace = gda.getString(row, "ModelRace");
173 
174  return modelRace + genderChar;
175 }
176 
178  const Common::UString &prefix) {
179  if (row == Aurora::GDAFile::kInvalidRow)
180  return "";
181 
182  const Common::UString type = gda.getString(row, "ModelType");
183  const Common::UString subType = gda.getString(row, "ModelSubType");
184  const Common::UString variation = gda.getString(row, "ModelVariation");
185 
186  if (type.empty() || subType.empty())
187  return "";
188 
189  return prefix + "_" + type + "_" + subType + variation;
190 }
191 
192 void Creature::loadModelsSimple(const Aurora::GDAFile &gda, size_t row) {
193  // Simple, single mesh
194 
195  Model *model = loadModelObject(gda.getString(row, "ModelName"));
196  if (model)
197  _models.push_back(model);
198 }
199 
200 void Creature::loadModelsWelded(const Aurora::GDAFile &gda, size_t row) {
201  // Welded, single mesh with baked weapons
202 
203  Model *model = loadModelObject(gda.getString(row, "ModelName"));
204  if (model)
205  _models.push_back(model);
206 }
207 
208 void Creature::loadModelsHead(const Aurora::GDAFile &gda, size_t row) {
209  // Head, single mesh for body + head model
210 
211  Model *model = loadModelObject(gda.getString(row, "ModelName"));
212  if (model)
213  _models.push_back(model);
214 
215  // Helm
216 
217  const Common::UString prefix = createModelPrefix(gda, row, _appearanceGender);
218 
219  bool haveHelm = false;
220  if ((model = loadModelObject(findEquipModel(kInventorySlotHead, prefix)))) {
221  haveHelm = true;
222 
223  _models.push_back(model);
224  }
225 
226  if (!_headMorph.empty())
227  loadModelsHeadMorph(!haveHelm);
228  else
229  loadModelsHeadList(gda, row, !haveHelm);
230 }
231 
232 void Creature::loadModelsHeadMorph(bool loadHair) {
233  static const size_t kHairPart = 2;
234 
235  try {
236  GFF4File mor(_headMorph, Aurora::kFileTypeMOR, kMORPID);
237  if (mor.getTypeVersion() != kVersion01)
238  throw Common::Exception("Unsupported MOR version %s", Common::debugTag(mor.getTypeVersion()).c_str());
239 
240  std::vector<Common::UString> parts;
241  mor.getTopLevel().getString(kGFF4MorphParts, parts);
242 
243  Model *model = 0;
244  for (size_t i = 0; i < parts.size(); i++) {
245  if ((i == kHairPart) && !loadHair)
246  continue;
247 
248  if ((model = loadModelObject(parts[i])))
249  _models.push_back(model);
250  }
251 
252  } catch (...) {
253  }
254 }
255 
256 void Creature::loadModelsHeadList(const Aurora::GDAFile &gda, size_t row, bool loadHair) {
257  static const size_t kHairPart = 2;
258 
259  static const char * const kSheetColumns[kPartVariationCount] =
260  {"Head_Worksheet", "Eyes_Worksheet", "Hair_Worksheet", "Beard_Worksheet"};
261 
262  const Common::UString prefix = createModelPrefix(gda, row, _appearanceGender);
263 
264  for (size_t i = 0; i < kPartVariationCount; i++) {
265  const size_t sheetIndex = gda.getInt(row, kSheetColumns[i]);
266  if (sheetIndex == 0)
267  continue;
268 
269  if ((i == kHairPart) && !loadHair)
270  continue;
271 
272  const Aurora::GDAFile &sheet = getMGDA(sheetIndex);
273 
274  const size_t sheetRow = sheet.findRow(_partVariation[i]);
275  if (sheetRow == Aurora::GDAFile::kInvalidRow)
276  continue;
277 
278  Model *model = loadModelObject(createModelPart(sheet, sheetRow, prefix));
279  if (model)
280  _models.push_back(model);
281  }
282 }
283 
284 void Creature::loadModelsParts(const Aurora::GDAFile &gda, size_t row) {
285  // Parts, various models for each body part
286 
287  const Common::UString prefix = createModelPrefix(gda, row, _appearanceGender);
288 
290 
291  static const uint32 kNakedTorso = 0;
292  static const uint32 kNakedGloves = 1;
293  static const uint32 kNakedBoots = 2;
294 
295  uint8 armorType = 0;
296  Model *model = 0;
297 
298  // Torso: creature model -> equipped chest item -> naked
299 
300  model = loadModelObject(gda.getString(row, "ModelName"));
301  if (!model)
302  model = loadModelObject(findEquipModel(kInventorySlotChest, prefix, &armorType));
303  if (!model)
304  model = loadModelObject(createModelPart(naked, naked.findRow(kNakedTorso), prefix));
305 
306  if (model)
307  _models.push_back(model);
308 
309  // Armor of type 5 (clothing) already includes hands and feet in the model...
310  if (armorType != 5) {
311  // Gloves: equipped gloves item -> naked
312 
314  if (!model)
315  model = loadModelObject(createModelPart(naked, naked.findRow(kNakedGloves), prefix));
316 
317  if (model)
318  _models.push_back(model);
319 
320  // Boots: equipped boots item -> naked
321 
323  if (!model)
324  model = loadModelObject(createModelPart(naked, naked.findRow(kNakedBoots), prefix));
325 
326  if (model)
327  _models.push_back(model);
328  }
329 
330  // Helm
331 
332  bool haveHelm = false;
333  if ((model = loadModelObject(findEquipModel(kInventorySlotHead, prefix)))) {
334  haveHelm = true;
335 
336  _models.push_back(model);
337  }
338 
339  // Head: morph -> part list
340 
341  if (!_headMorph.empty())
342  loadModelsHeadMorph(!haveHelm);
343  else
344  loadModelsHeadList(gda, row, !haveHelm);
345 }
346 
348  uint8 *armorType) const {
349  if (armorType)
350  *armorType = 0;
351 
352  const Aurora::GDAFile &baseItems = getMGDA(kWorksheetItems);
353 
354  for (Items::const_iterator item = _items.begin(); item != _items.end(); ++item) {
355  if (item->slot != slot)
356  continue;
357 
358  try {
359  GFF3File uti(item->resRef, Aurora::kFileTypeUTI, kUTIID);
360  const GFF3Struct &utiTop = uti.getTopLevel();
361 
362  const uint32 baseItem = (uint32) ((int32) utiTop.getSint("BaseItem", -1));
363  const uint32 variation = utiTop.getUint("ModelVariation", 0xFFFFFFFF);
364 
365  const size_t itemRow = baseItems.findRow(baseItem);
366  if (itemRow == Aurora::GDAFile::kInvalidRow)
367  continue;
368 
369  if (armorType)
370  *armorType = baseItems.getInt(itemRow, "ArmorType");
371 
372  const uint32 varSheet = (uint32) baseItems.getInt(itemRow, "Variation_Worksheet", -1);
373  const Aurora::GDAFile &variations = getMGDA(varSheet);
374 
375  const size_t variationRow = variations.findRow(variation);
376  if (variationRow == Aurora::GDAFile::kInvalidRow)
377  continue;
378 
379  return createModelPart(variations, variationRow, prefix);
380 
381  } catch (...) {
382  }
383  }
384 
385  return "";
386 }
387 
388 void Creature::load(const GFF3Struct &instance, const GFF3Struct *blueprint = 0) {
389  if (blueprint)
390  loadProperties(*blueprint);
391  loadProperties(instance);
392 
394  const size_t row = gda.findRow(_appearanceID);
395 
396  const Common::UString modelType = gda.getString(row, "ModelType");
397 
398  if (modelType == "S")
399  loadModelsSimple(gda, row);
400  else if (modelType == "W")
401  loadModelsWelded(gda, row);
402  else if (modelType == "H")
403  loadModelsHead(gda, row);
404  else if (modelType == "P")
405  loadModelsParts(gda, row);
406 
407  const float scaleX = gda.getFloat(row, "ModelScaleX", 1.0f);
408  const float scaleY = gda.getFloat(row, "ModelScaleY", 1.0f);
409  const float scaleZ = gda.getFloat(row, "ModelScaleZ", 1.0f);
410 
411  for (Models::iterator m = _models.begin(); m != _models.end(); ++m) {
412  (*m)->setScale(scaleX, scaleY, scaleZ);
413 
414  (*m)->setTag(_tag);
415  (*m)->setClickable(isClickable());
416 
417  _ids.push_back((*m)->getID());
418  }
419 
420  syncPosition();
421  syncOrientation();
422 }
423 
424 void Creature::loadProperties(const GFF3Struct &gff) {
425  // Tag
426  _tag = gff.getString("Tag", _tag);
427 
428  // Name and description
429  gff.getLocString("LocName" , _name);
430  gff.getLocString("Description", _description);
431 
432  // Conversation
433  _conversation = gff.getString("DialogResRef", _conversation);
434 
435  // Static and usable
436  _static = !gff.getBool("Active" , !_static);
437  _usable = gff.getBool("IsSelectable", _usable);
438 
439  // Appearance
440  _appearanceID = gff.getUint("Appearance", _appearanceID);
441 
442  if (gff.hasField("Appearance Data")) {
443  const GFF3Struct &app = gff.getStruct("Appearance Data");
444 
445  _appearanceID = (uint32) ((int32) app.getSint("Appearance_Type", (int32) _appearanceID));
446  _appearanceGender = app.getUint("Gender", _appearanceGender);
447 
448  _headMorph = app.getString("HeadMorph", _headMorph);
449 
450  if (app.hasField("PartList")) {
451  const GFF3List &parts = app.getList("PartList");
452 
453  for (size_t i = 0; i < kPartVariationCount; i++)
454  _partVariation[i] = (i < parts.size()) ? parts[i]->getUint("PartVariation") : 0xFFFFFFFF;
455  }
456  }
457 
458  // Equipped items
459  if (gff.hasField("Equip_ItemList")) {
460  const GFF3List &itemList = gff.getList("Equip_ItemList");
461  _items.resize(itemList.size());
462 
463  for (size_t i = 0; i < itemList.size(); i++) {
464  _items[i].slot = (InventorySlot) itemList[i]->getID();
465 
466  _items[i].resRef = itemList[i]->getString("TemplateResRef");
467 
468  _items[i].stealable = itemList[i]->getBool("Stealable");
469  _items[i].droopable = itemList[i]->getBool("Droppable");
470 
471  _items[i].setNumber = itemList[i]->getSint("SetNumber", -1);
472  }
473  }
474 
475  // Position
476  if (gff.hasField("XPosition")) {
477  const float position[3] = {
478  (float) gff.getDouble("XPosition"),
479  (float) gff.getDouble("YPosition"),
480  (float) gff.getDouble("ZPosition")
481  };
482 
483  setPosition(position[0], position[1], position[2]);
484  }
485 
486  // Orientation
487  if (gff.hasField("XOrientation")) {
488  const float orientation[4] = {
489  (float) gff.getDouble("XOrientation"),
490  (float) gff.getDouble("YOrientation"),
491  (float) gff.getDouble("ZOrientation"),
492  (float) Common::rad2deg(acos(gff.getDouble("WOrientation")) * 2.0)
493  };
494 
495  setOrientation(orientation[0], orientation[1], orientation[2], orientation[3]);
496  }
497 
498  // Variables and script
499  readVarTable(gff);
500  readScript(gff);
501  enableEvents(true);
502 }
503 
504 } // End of namespace Dragon Age
505 
506 } // End of namespace Engines
void loadModelsHead(const Aurora::GDAFile &gda, size_t row)
Definition: creature.cpp:208
Models _models
The models making up this creature.
Definition: creature.h:124
Handling version V3.2/V3.3 of BioWare&#39;s GFFs (generic file format).
#define MKTAG(a0, a1, a2, a3)
A wrapper macro used around four character constants, like &#39;DATA&#39;, to ensure portability.
Definition: endianness.h:140
void loadModelsHeadMorph(bool loadHair=true)
Definition: creature.cpp:232
Handling BioWare&#39;s GDAs (2DAs, two-dimensional array, within V4.0 GFFs).
InventorySlot
Slot in a creature&#39;s inventory.
Definition: types.h:85
int32 getInt(size_t row, uint32 columnHash, int32 def=0) const
Definition: gdafile.cpp:184
Head Morph.
Definition: types.h:299
void setString(Language language, LanguageGender gender, const Common::UString &str)
Set the string of that language.
Definition: locstring.cpp:95
void setPosition(float x, float y, float z)
Set the creature&#39;s position.
Definition: creature.cpp:90
bool _static
Is the object static?
Definition: object.h:128
A class holding an UTF-8 string.
Definition: ustring.h:48
float getFloat(size_t row, uint32 columnHash, float def=0.0f) const
Definition: gdafile.cpp:202
uint32 _appearanceID
The creature&#39;s appearance; index into the Appearances MGDA.
Definition: creature.h:111
void reset(PointerType o=0)
Resets the pointer with the new value.
Definition: scopedptr.h:87
static const uint32 kVersion01
Definition: campaign.cpp:48
uint8_t uint8
Definition: types.h:200
A creature in a Dragon Age: Origins area.
uint32 getID() const
Definition: object.h:45
Mathematical helpers.
void highlight(bool enabled)
(Un)Highlight the creature.
Definition: creature.cpp:140
static Common::UString createModelPrefix(const Aurora::GDAFile &gda, size_t row, uint8 gender)
Definition: creature.cpp:162
#define ARRAYSIZE(x)
Macro which determines the number of entries in a fixed size array.
Definition: util.h:131
size_t findRow(uint32 id) const
Find a row by its ID value.
Definition: gdafile.cpp:95
Aurora::GFF3File * loadOptionalGFF3(const Common::UString &gff3, Aurora::FileType type, uint32 id, bool repairNWNPremium)
Load a GFF3, but return 0 instead of throwing on error.
Definition: util.cpp:150
void loadModelsParts(const Aurora::GDAFile &gda, size_t row)
Definition: creature.cpp:284
Aurora::LocString _description
The object&#39;s description.
Definition: object.h:120
Utility templates and functions for working with strings and streams.
const Aurora::GDAFile & getMGDA(uint32 id)
Definition: util.cpp:36
const Common::UString & getString(Language language, LanguageGender gender=kLanguageGenderCurrent) const
Get the string of that language.
Definition: locstring.cpp:82
static Common::UString createModelPart(const Aurora::GDAFile &gda, size_t row, const Common::UString &prefix)
Definition: creature.cpp:177
Exception that provides a stack of explanations.
Definition: error.h:36
A simple scoped smart pointer template.
bool _isPC
Is the creature a PC?
Definition: creature.h:108
bool _usable
Is the object usable?
Definition: object.h:129
std::vector< const GFF4Struct * > GFF4List
Definition: types.h:453
Handling version V4.0/V4.1 of BioWare&#39;s GFFs (generic file format).
static const size_t kPartVariationCount
Max number of model parts for a creature&#39;s head.
Definition: creature.h:90
Basic exceptions to throw.
static const uint32 kMORPID
Definition: creature.cpp:48
void loadProperties(const Aurora::GFF3Struct &gff)
const char * c_str() const
Return the (utf8 encoded) string data.
Definition: ustring.cpp:249
uint32 _partVariation[kPartVariationCount]
Indices into the MGDAs describing the creature&#39;s head model parts.
Definition: creature.h:118
static UString format(const char *s,...) GCC_PRINTF(1
Print formatted data into an UString object, similar to sprintf().
Definition: ustring.cpp:718
static const uint32 kUTIID
Definition: creature.cpp:52
Utility templates and functions.
Dragon Age: Origins utility functions.
Types and functions related to language.
A 3D model of an object.
bool empty() const
Is the string empty?
Definition: ustring.cpp:245
void loadModelsHeadList(const Aurora::GDAFile &gda, size_t row, bool loadHair=true)
Definition: creature.cpp:256
Creature()
Create a dummy creature instance.
Definition: creature.cpp:66
virtual void getOrientation(float &x, float &y, float &z, float &angle) const
Return the object&#39;s orientation.
Definition: object.cpp:126
A scoped plain pointer, allowing pointer-y access and normal deletion.
Definition: scopedptr.h:120
Common::UString _resRef
The object&#39;s resource reference.
Definition: object.h:117
void loadModelsSimple(const Aurora::GDAFile &gda, size_t row)
Definition: creature.cpp:192
std::vector< const GFF3Struct * > GFF3List
Definition: types.h:449
Aurora::LocString _name
The object&#39;s display name.
Definition: object.h:119
void readVarTable(const Aurora::GFF3List &varTable)
Common::UString _headMorph
Name of the morph file describing the creature&#39;s head.
Definition: creature.h:116
Common::UString getString(size_t row, uint32 columnHash, const Common::UString &def="") const
Definition: gdafile.cpp:165
Items _items
All item the creature has currently equipped.
Definition: creature.h:121
Common::UString findEquipModel(InventorySlot slot, const Common::UString &prefix, uint8 *armorType=0) const
Definition: creature.cpp:347
void loadModelsWelded(const Aurora::GDAFile &gda, size_t row)
Definition: creature.cpp:200
std::list< uint32 > _ids
The object&#39;s model IDs.
Definition: object.h:131
static const uint32 kUTCID
Definition: creature.cpp:51
void enter()
The cursor entered the creature.
Definition: creature.cpp:129
#define LangMan
Shortcut for accessing the language manager.
Definition: language.h:275
virtual void setPosition(float x, float y, float z)
Set the object&#39;s position within its area.
Definition: object.cpp:134
uint32_t uint32
Definition: types.h:204
UString debugTag(uint32 tag, bool trim)
Create an elaborate string from an integer tag, for debugging purposes.
Definition: strutil.cpp:117
Item template (user), GFF.
Definition: types.h:91
Common::UString _tag
Definition: object.h:56
static const size_t kInvalidRow
Definition: gdafile.h:65
void status(const char *s,...)
Definition: util.cpp:52
bool click(Object *triggerer=0)
The creature was clicked.
Definition: creature.cpp:145
void load(const Aurora::GFF3Struct &placeable)
void readScript(const Aurora::GFF3Struct &gff)
Definition: container.cpp:90
static float rad2deg(float rad)
Definition: maths.h:93
Generic Aurora engines utility functions.
bool isClickable() const
Can the player click the object?
Definition: object.cpp:116
void setOrientation(float x, float y, float z, float angle)
Set the creature&#39;s orientation.
Definition: creature.cpp:98
Graphics::Aurora::Model * loadModelObject(const Common::UString &resref, const Common::UString &texture)
Definition: model.cpp:47
bool isPC() const
Is the creature a player character?
Definition: creature.cpp:116
virtual void getPosition(float &x, float &y, float &z) const
Return the object&#39;s position within its area.
Definition: object.cpp:120
Creature template (user), GFF.
Definition: types.h:93
Common::UString _conversation
The object&#39;s default conversation.
Definition: object.h:124
void show()
Show the creature&#39;s model.
Definition: creature.cpp:106
void createFakePC()
Create a fake player character creature for testing purposes.
Definition: creature.cpp:120
uint8 _appearanceGender
The gender of the creature&#39;s model files.
Definition: creature.h:113
bool runScript(EventType event, const Aurora::NWScript::ObjectReference owner=Aurora::NWScript::ObjectReference(), const Aurora::NWScript::ObjectReference triggerer=Aurora::NWScript::ObjectReference())
Definition: container.cpp:104
void leave()
The cursor left the creature.
Definition: creature.cpp:136
virtual void setOrientation(float x, float y, float z, float angle)
Set the object&#39;s orientation.
Definition: object.cpp:140
void enableEvents(bool enabled)
Enable/Disable the handling of all events.
Definition: container.cpp:76
Class to hold the GFF&#39;d two-dimensional array of a GDA file.
Definition: gdafile.h:62
void hide()
Hide the creature&#39;s model.
Definition: creature.cpp:111
int32_t int32
Definition: types.h:203
Generic Aurora engines model functions.