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