xoreos  0.0.5
dlgfile.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 /* See BioWare's own specs released for Neverwinter Nights modding
26  * (<https://github.com/xoreos/xoreos-docs/tree/master/specs/bioware>)
27  */
28 
29 #include <cassert>
30 
31 #include "src/common/error.h"
32 #include "src/common/readstream.h"
33 
34 #include "src/aurora/gff3file.h"
35 #include "src/aurora/dlgfile.h"
36 
40 
41 static const uint32 kDLGID = MKTAG('D', 'L', 'G', ' ');
42 
43 namespace Aurora {
44 
45 DLGFile::DLGFile(Common::SeekableReadStream *dlg, NWScript::Object *owner, bool repairNWNPremium) :
46  _owner(owner), _ended(true) {
47 
48  assert(dlg);
49 
50  GFF3File gff(dlg, kDLGID, repairNWNPremium);
51 
52  load(gff.getTopLevel());
53 
54  _currentEntry = _entriesNPC.end();
55 }
56 
57 DLGFile::DLGFile(const Common::UString &dlg, NWScript::Object *owner, bool repairNWNPremium) :
58  _owner(owner), _ended(true) {
59 
60  GFF3File gff(dlg, kFileTypeDLG, kDLGID, repairNWNPremium);
61 
62  load(gff.getTopLevel());
63 }
64 
67 }
68 
69 bool DLGFile::getNoZoomIn() const {
70  return _noZoomIn;
71 }
72 
74  return _delayEntry;
75 }
76 
78  return _delayReply;
79 }
80 
81 bool DLGFile::hasEnded() const {
82  return _ended;
83 }
84 
87 
88  _currentEntry = _entriesNPC.end();
89  _currentReplies.clear();
90 
93 
94  runScript(_currentEntry->script);
95  }
96 
97  _ended = false;
98 }
99 
101  if (_ended)
102  return;
103 
105 
106  _currentEntry = _entriesNPC.end();
107  _currentReplies.clear();
108 
109  _ended = true;
110 }
111 
113  if (_ended || (id == kInvalidLine))
114  return;
115 
116  if ((id == kEndLine) || ((_currentEntry == _entriesNPC.end()))) {
118 
119  _ended = true;
120  return;
121  }
122 
123  assert(id < _entriesPC.size());
124 
125  _currentEntry = _entriesPC.begin() + id;
126  _currentReplies.clear();
127 
128  runScript(_currentEntry->script);
129 
132 
133  runScript(_currentEntry->script);
134  } else {
136  _ended = true;
137  }
138 }
139 
141  if (_currentEntry == _entriesNPC.end())
142  return 0;
143 
144  return &_currentEntry->line;
145 }
146 
147 const std::vector<const DLGFile::Line *> &DLGFile::getCurrentReplies() const {
148  return _currentReplies;
149 }
150 
152  for (std::vector<Link>::const_iterator e = _entriesStart.begin();
153  e != _entriesStart.end(); ++e) {
154 
155  std::vector<Entry>::const_iterator line = _entriesNPC.begin() + e->index;
156  if (!line->replies.empty() || !runScript(e->active))
157  continue;
158 
159  return &line->line;
160  }
161 
162  return 0;
163 }
164 
165 void DLGFile::load(const GFF3Struct &dlg) {
166  // General properties
167 
168  _delayEntry = dlg.getUint("DelayEntry", 0);
169  _delayReply = dlg.getUint("DelayReply", 0);
170 
171  _convAbort = dlg.getString("EndConverAbort");
172  _convEnd = dlg.getString("EndConversation");
173 
174  _noZoomIn = !dlg.getBool("PreventZoomIn", true);
175 
176  // NPC lines ("entries")
177 
178  const GFF3List &entries = dlg.getList("EntryList");
179  _entriesNPC.reserve(entries.size());
180 
181  readEntries(entries, _entriesNPC, false);
182 
183  // PC lines ("replies")
184 
185  const GFF3List &replies = dlg.getList("ReplyList");
186  _entriesPC.reserve(replies.size());
187 
188  readEntries(replies, _entriesPC, true);
189 
190  // Starting lines (greetings)
191 
192  const GFF3List &starters = dlg.getList("StartingList");
193  _entriesStart.reserve(starters.size());
194 
195  readLinks(starters, _entriesStart);
196 }
197 
198 void DLGFile::readEntries(const GFF3List &list, std::vector<Entry> &entries, bool isPC) {
199  for (GFF3List::const_iterator e = list.begin(); e != list.end(); ++e) {
200  entries.push_back(Entry());
201 
202  Entry &entry = entries.back();
203 
204  entry.isPC = isPC;
205 
206  entry.line.id = entries.size() - 1;
207 
208  readEntry(**e, entry);
209  }
210 }
211 
212 void DLGFile::readLinks(const GFF3List &list, std::vector<Link> &links) {
213  for (GFF3List::const_iterator l = list.begin(); l != list.end(); ++l) {
214  links.push_back(Link());
215 
216  readLink(**l, links.back());
217  }
218 }
219 
220 void DLGFile::readEntry(const GFF3Struct &gff, Entry &entry) {
221  entry.script = gff.getString("Script");
222 
223  entry.line.speaker = gff.getString("Speaker");
224 
225  gff.getLocString("Text", entry.line.text);
226 
227  entry.line.sound = gff.getString("Sound");
228 
229  entry.line.voice = gff.getString("VO_ResRef");
230 
231  entry.line.animation = gff.getUint("Animation", 0);
232 
233  entry.line.quest = gff.getString("Quest");
234  entry.line.questEntry = gff.getUint("QuestEntry", 0xFFFFFFFF);
235 
236  const GFF3List *replies = 0;
237 
238  if (gff.hasField("RepliesList"))
239  replies = &gff.getList("RepliesList");
240  else if (gff.hasField("EntriesList"))
241  replies = &gff.getList("EntriesList");
242 
243  if (replies) {
244  entry.replies.reserve(replies->size());
245 
246  readLinks(*replies, entry.replies);
247  }
248 
249  entry.line.isEnd = entry.replies.empty();
250 }
251 
252 void DLGFile::readLink(const GFF3Struct &gff, Link &link) {
253  link.index = gff.getUint("Index", 0xFFFFFFFF);
254  link.active = gff.getString("Active");
255 }
256 
257 bool DLGFile::evaluateEntries(const std::vector<Link> &entries,
258  std::vector<Entry>::iterator &active) {
259 
260  active = _entriesNPC.end();
261 
262  for (std::vector<Link>::const_iterator e = entries.begin(); e != entries.end(); ++e) {
263  if (!runScript(e->active))
264  continue;
265 
266  assert(e->index < _entriesNPC.size());
267 
268  active = _entriesNPC.begin() + e->index;
269  break;
270  }
271 
272  return active != _entriesNPC.end();
273 }
274 
275 bool DLGFile::evaluateReplies(const std::vector<Link> &entries,
276  std::vector<const Line *> &active) {
277 
278  active.clear();
279 
280  active.reserve(entries.size());
281  for (std::vector<Link>::const_iterator e = entries.begin(); e != entries.end(); ++e) {
282  if (!runScript(e->active))
283  continue;
284 
285  assert(e->index < _entriesPC.size());
286 
287  active.push_back(&_entriesPC[e->index].line);
288  }
289 
290  return !active.empty();
291 }
292 
293 bool DLGFile::runScript(const Common::UString &script) const {
294  if (script.empty())
295  return true;
296 
297  try {
298  NWScript::NCSFile ncs(script);
299 
300  const NWScript::Variable &retVal = ncs.run(_owner);
301  if (retVal.getType() == NWScript::kTypeInt)
302  return retVal.getInt() != 0;
303  if (retVal.getType() == NWScript::kTypeFloat)
304  return retVal.getFloat() != 0.0f;
305 
306  return true;
307 
308  } catch (...) {
309  Common::exceptionDispatcherWarning("Failed running dialog script \"%s\"", script.c_str());
310  return false;
311  }
312 
313  return true;
314 }
315 
316 } // End of namespace Aurora
Handling version V3.2/V3.3 of BioWare&#39;s GFFs (generic file format).
void abortConversation()
Definition: dlgfile.cpp:100
#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
uint32 getDelayReply() const
Return the number of seconds to wait before showing each reply.
Definition: dlgfile.cpp:77
void pickReply(uint32 id)
Definition: dlgfile.cpp:112
void readEntry(const GFF3Struct &gff, Entry &entry)
Definition: dlgfile.cpp:220
NWScript variable.
std::vector< Entry > _entriesNPC
NPC dialog lines ("entries").
Definition: dlgfile.h:141
bool getBool(const Common::UString &field, bool def=false) const
Definition: gff3file.cpp:510
bool getLocString(const Common::UString &field, LocString &str) const
Definition: gff3file.cpp:614
DLGFile(Common::SeekableReadStream *dlg, NWScript::Object *owner=0, bool repairNWNPremium=false)
Take over this stream and read a DLG file out of it.
Definition: dlgfile.cpp:45
A class holding an UTF-8 string.
Definition: ustring.h:48
std::vector< const Line * > _currentReplies
The current replies.
Definition: dlgfile.h:147
void readLink(const GFF3Struct &gff, Link &link)
Definition: dlgfile.cpp:252
bool hasField(const Common::UString &field) const
Does this specific field exist?
Definition: gff3file.cpp:400
NWScript::Object * _owner
Definition: dlgfile.h:131
std::vector< Link > replies
Reply lines.
Definition: dlgfile.h:127
const GFF3Struct & getTopLevel() const
Returns the top-level struct.
Definition: gff3file.cpp:91
uint64 getUint(const Common::UString &field, uint64 def=0) const
Definition: gff3file.cpp:436
bool _noZoomIn
Starting the conversation does not zoom the camera onto the speaker.
Definition: dlgfile.h:139
An NCS, BioWare&#39;s NWN Compile Script.
Definition: ncsfile.h:86
bool hasEnded() const
Definition: dlgfile.cpp:81
bool evaluateEntries(const std::vector< Link > &entries, std::vector< Entry >::iterator &active)
Definition: dlgfile.cpp:257
Common::UString sound
ResRef of the sound to play while speaking this entry.
Definition: dlgfile.h:73
void exceptionDispatcherWarning(const char *s,...)
Exception dispatcher that prints the exception as a warning, and adds another reason on top...
Definition: error.cpp:158
Common::UString voice
ResRef of the voice over for KotOR games.
Definition: dlgfile.h:74
const Line * getOneLiner() const
Return the first active non-branching entry.
Definition: dlgfile.cpp:151
bool getNoZoomIn() const
Does starting the conversation zoom in the camera onto the speaker or not?
Definition: dlgfile.cpp:69
Basic exceptions to throw.
Line line
The line&#39;s contents.
Definition: dlgfile.h:125
const char * c_str() const
Return the (utf8 encoded) string data.
Definition: ustring.cpp:249
const Variable & run(Object *owner=0, Object *triggerer=0)
Run the current script, from start to finish.
Definition: ncsfile.cpp:350
uint32 id
ID of this line (entry-local).
Definition: dlgfile.h:68
Dialog tree, GFF.
Definition: types.h:94
bool isPC
Is this a PC or NPC line?
Definition: dlgfile.h:121
void readEntries(const GFF3List &list, std::vector< Entry > &entries, bool isPC)
Definition: dlgfile.cpp:198
A GFF (generic file format) V3.2/V3.3 file, found in all Aurora games except Sonic Chronicles: The Da...
Definition: gff3file.h:85
bool runScript(const Common::UString &script) const
Definition: dlgfile.cpp:293
bool empty() const
Is the string empty?
Definition: ustring.cpp:245
uint32 _delayReply
Number of seconds to wait before showing each reply.
Definition: dlgfile.h:134
void load(const GFF3Struct &dlg)
Definition: dlgfile.cpp:165
Common::UString _convAbort
Script to run when the conversation was aborted.
Definition: dlgfile.h:136
static const uint32 kDLGID
Definition: dlgfile.cpp:41
uint32 questEntry
Entry ID to set the quest to.
Definition: dlgfile.h:79
Common::UString _convEnd
Script to run when the conversation ended normally.
Definition: dlgfile.h:137
std::vector< Entry > _entriesPC
PC dialog lines ("replies").
Definition: dlgfile.h:142
Common::UString quest
Quest name to modify when speaking this entry.
Definition: dlgfile.h:78
const std::vector< const Line * > & getCurrentReplies() const
Definition: dlgfile.cpp:147
Basic reading stream interfaces.
std::vector< const GFF3Struct * > GFF3List
Definition: types.h:449
Handling BioWare&#39;s NWN Compiled Scripts.
A dialog entry.
Definition: dlgfile.h:120
static const uint32 kEndLine
Definition: dlgfile.h:64
bool isEnd
Are there no replies to this line?
Definition: dlgfile.h:81
const GFF3List & getList(const Common::UString &field) const
Definition: gff3file.cpp:741
bool _ended
Has the conversation ended?
Definition: dlgfile.h:149
A struct within a GFF3.
Definition: gff3file.h:164
uint32_t uint32
Definition: types.h:204
Common::UString getString(const Common::UString &field, const Common::UString &def="") const
Definition: gff3file.cpp:527
std::vector< Link > _entriesStart
NPC starting lines (greetings).
Definition: dlgfile.h:144
uint32 _delayEntry
Number of seconds to wait before showing each entry.
Definition: dlgfile.h:133
void readLinks(const GFF3List &list, std::vector< Link > &links)
Definition: dlgfile.cpp:212
uint32 getDelayEntry() const
Return the number of seconds to wait before showing each entry.
Definition: dlgfile.cpp:73
uint32 animation
Animation to play while speaking this entry.
Definition: dlgfile.h:76
NWScript types.
Handling BioWare&#39;s DLGs (dialog / conversation files).
Interface for a seekable & readable data stream.
Definition: readstream.h:265
static const uint32 kInvalidLine
Definition: dlgfile.h:65
void startConversation()
Definition: dlgfile.cpp:85
std::vector< Entry >::iterator _currentEntry
The current entry.
Definition: dlgfile.h:146
Common::UString speaker
Tag of the speaker, empty if default.
Definition: dlgfile.h:70
Common::UString script
Script to run when speaking this entry.
Definition: dlgfile.h:123
LocString text
The actual text of the entry.
Definition: dlgfile.h:71
const Line * getCurrentEntry() const
Definition: dlgfile.cpp:140
bool evaluateReplies(const std::vector< Link > &entries, std::vector< const Line *> &active)
Definition: dlgfile.cpp:275