1 /* ResidualVM - A 3D game interpreter
2 *
3 * ResidualVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the AUTHORS
5 * file distributed with this source distribution.
6 *
7 * This program 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 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program 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 this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "engines/stark/resources/speech.h"
24
25 #include "engines/stark/formats/xrc.h"
26
27 #include "engines/stark/services/services.h"
28 #include "engines/stark/services/settings.h"
29 #include "engines/stark/services/dialogplayer.h"
30 #include "engines/stark/services/global.h"
31 #include "engines/stark/services/stateprovider.h"
32
33 #include "engines/stark/resources/anim.h"
34 #include "engines/stark/resources/item.h"
35 #include "engines/stark/resources/level.h"
36 #include "engines/stark/resources/lipsync.h"
37 #include "engines/stark/resources/location.h"
38 #include "engines/stark/resources/sound.h"
39
40 namespace Stark {
41 namespace Resources {
42
~Speech()43 Speech::~Speech() {
44 }
45
Speech(Object * parent,byte subType,uint16 index,const Common::String & name)46 Speech::Speech(Object *parent, byte subType, uint16 index, const Common::String &name) :
47 Object(parent, subType, index, name),
48 _character(0),
49 _soundResource(nullptr),
50 _playTalkAnim(true),
51 _removeTalkAnimWhenComplete(true),
52 _lipSync(nullptr),
53 _waitTimeRemaining(-1) {
54 _type = TYPE;
55 }
56
getPhrase() const57 Common::String Speech::getPhrase() const {
58 return _phrase;
59 }
60
playSound()61 void Speech::playSound() {
62 StarkGlobal->setNormalSpeed();
63
64 if (_playTalkAnim) {
65 setCharacterTalkAnim();
66 }
67
68 stopOtherSpeechesFromSameCharacter();
69
70 _soundResource = findChild<Sound>();
71 _soundResource->play();
72 }
73
setCharacterTalkAnim()74 void Speech::setCharacterTalkAnim() {
75 ItemVisual *characterItem = getCharacterItem();
76 if (characterItem) {
77 characterItem->setAnimActivity(Anim::kActorActivityTalk);
78
79 _lipSync = findChild<LipSync>();
80 if (_lipSync) {
81 _lipSync->setItem(characterItem, _playTalkAnim);
82 }
83 }
84 }
85
removeCharacterTalkAnim() const86 void Speech::removeCharacterTalkAnim() const {
87 ItemVisual *characterItem = getCharacterItem();
88 if (characterItem && characterItem->getAnimActivity() == Anim::kActorActivityTalk) {
89 characterItem->setAnimActivity(Anim::kActorActivityIdle);
90 }
91 }
92
getCharacterItem() const93 ItemVisual *Speech::getCharacterItem() const {
94 Current *current = StarkGlobal->getCurrent();
95 if (!current) {
96 return nullptr;
97 }
98
99 Location *location = current->getLocation();
100 if (!location) {
101 return nullptr;
102 }
103
104 return location->getCharacterItem(_character);
105 }
106
getCharacterId()107 int32 Speech::getCharacterId() {
108 return _character;
109 }
110
isPlaying()111 bool Speech::isPlaying() {
112 return _soundResource || _waitTimeRemaining > 0;
113 }
114
stop()115 void Speech::stop() {
116 if (_soundResource) {
117 _soundResource->stop();
118 _soundResource = nullptr;
119 }
120
121 _waitTimeRemaining = -1;
122
123 if (_lipSync) {
124 _lipSync->reset();
125 }
126
127 if (_removeTalkAnimWhenComplete) {
128 removeCharacterTalkAnim();
129 }
130
131 _removeTalkAnimWhenComplete = true;
132 _playTalkAnim = true;
133 }
134
characterIsApril() const135 bool Speech::characterIsApril() const {
136 int32 aprilCharacterIndex = StarkGlobal->getApril()->getCharacterIndex();
137 return _character == aprilCharacterIndex;
138 }
139
getPauseAfterSpeechDuration() const140 int32 Speech::getPauseAfterSpeechDuration() const {
141 if (_phrase.hasSuffix("...")) {
142 return 1400;
143 } else if (_phrase.hasSuffix("--")) {
144 return 0;
145 } else {
146 return 1000;
147 }
148 }
149
readData(Formats::XRCReadStream * stream)150 void Speech::readData(Formats::XRCReadStream *stream) {
151 Object::readData(stream);
152
153 _phrase = stream->readString();
154 _character = stream->readSint32LE();
155
156 // bug fix for #12967 (STARK: Cortez says "no", but subtitles say "si")
157 if (StarkSettings->getLanguage() == Common::EN_ANY
158 && _character == 1 // Cortez
159 && getIndex() == 1
160 && getSubType() == 0
161 && getName().equals("Cortez_Laying low #1")) {
162 _phrase = "Nyo! So it was a good thing I didn't stick my head out the door to look for you, then, no?";
163 }
164
165 // For debug purposes
166 //printData();
167 }
168
onGameLoop()169 void Speech::onGameLoop() {
170 Object::onGameLoop();
171
172 if (_soundResource && !_soundResource->isPlaying()) {
173 _soundResource->stop();
174 _soundResource = nullptr;
175 _waitTimeRemaining = getPauseAfterSpeechDuration();
176 }
177
178 if (_waitTimeRemaining >= 0) {
179 _waitTimeRemaining -= StarkGlobal->getMillisecondsPerGameloop();
180
181 if (StarkGlobal->isFastForward()) {
182 _waitTimeRemaining = -1;
183 }
184
185 if (_waitTimeRemaining <= 0) {
186 stop();
187 }
188 }
189 }
190
onExitLocation()191 void Speech::onExitLocation() {
192 stop();
193 }
194
onPreDestroy()195 void Speech::onPreDestroy() {
196 stop();
197 }
198
printData()199 void Speech::printData() {
200 Object::printData();
201
202 debug("phrase: %s", _phrase.c_str());
203 debug("character: %d", _character);
204 }
205
setPlayTalkAnim(bool playTalkAnim)206 void Speech::setPlayTalkAnim(bool playTalkAnim) {
207 _playTalkAnim = playTalkAnim;
208 }
209
stopOtherSpeechesFromSameCharacter()210 void Speech::stopOtherSpeechesFromSameCharacter() {
211 Level *globalLevel = StarkGlobal->getLevel();
212 Level *currentLevel = StarkGlobal->getCurrent()->getLevel();
213 Location *currentLocation = StarkGlobal->getCurrent()->getLocation();
214
215 Common::Array<Speech *> globalLevelSpeeches = globalLevel->listChildrenRecursive<Speech>();
216 Common::Array<Speech *> currentLevelSpeeches = currentLevel->listChildrenRecursive<Speech>();
217 Common::Array<Speech *> currentLocationSpeeches = currentLocation->listChildrenRecursive<Speech>();
218
219 Common::Array<Speech *> speeches;
220 speeches.push_back(globalLevelSpeeches);
221 speeches.push_back(currentLevelSpeeches);
222 speeches.push_back(currentLocationSpeeches);
223
224 for (uint i = 0; i < speeches.size(); i++) {
225 Speech *speech = speeches[i];
226 if (speech->_character == _character && speech->isPlaying()) {
227 speech->stop();
228 }
229 }
230 }
231
saveLoadCurrent(ResourceSerializer * serializer)232 void Speech::saveLoadCurrent(ResourceSerializer *serializer) {
233 bool playing = isPlaying();
234 serializer->syncAsUint32LE(playing);
235
236 if (playing) {
237 serializer->syncAsUint32LE(_removeTalkAnimWhenComplete);
238 serializer->syncAsResourceReference(&_soundResource);
239 serializer->syncAsResourceReference(&_lipSync);
240
241 if (serializer->isLoading()) {
242 StarkDialogPlayer->playSingle(this);
243 }
244 }
245 }
246
247 } // End of namespace Resources
248 } // End of namespace Stark
249