1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
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 "sludge/allfiles.h"
24 #include "sludge/backdrop.h"
25 #include "sludge/fonttext.h"
26 #include "sludge/freeze.h"
27 #include "sludge/graphics.h"
28 #include "sludge/moreio.h"
29 #include "sludge/newfatal.h"
30 #include "sludge/objtypes.h"
31 #include "sludge/people.h"
32 #include "sludge/region.h"
33 #include "sludge/sludge.h"
34 #include "sludge/sludger.h"
35 #include "sludge/sound.h"
36 #include "sludge/speech.h"
37 #include "sludge/sprbanks.h"
38 #include "sludge/sprites.h"
39 
40 namespace Sludge {
41 
init()42 void SpeechManager::init() {
43 	_speechMode = 0;
44 	_speechSpeed = 1;
45 	_speech = new SpeechStruct;
46 	if (checkNew(_speech)) {
47 		_speech->currentTalker = NULL;
48 		_speech->allSpeech.clear();
49 		_speech->speechY = 0;
50 		_speech->lastFile = -1;
51 	}
52 }
53 
kill()54 void SpeechManager::kill() {
55 	if (!_speech)
56 		return;
57 
58 	if (_speech->lastFile != -1) {
59 		_vm->_soundMan->huntKillSound(_speech->lastFile);
60 		_speech->lastFile = -1;
61 	}
62 
63 	if (_speech->currentTalker) {
64 		_speech->currentTalker->makeSilent();
65 		_speech->currentTalker = nullptr;
66 	}
67 
68 	for (SpeechLineList::iterator it = _speech->allSpeech.begin(); it != _speech->allSpeech.end(); ++it) {
69 		SpeechLine *killMe = *it;
70 		delete killMe;
71 		killMe = nullptr;
72 	}
73 	_speech->allSpeech.clear();
74 }
75 
setObjFontColour(ObjectType * t)76 void SpeechManager::setObjFontColour(ObjectType *t) {
77 	_speech->talkCol.setColor(t->r, t->g, t->b);
78 }
79 
addSpeechLine(const Common::String & theLine,int x,int & offset)80 void SpeechManager::addSpeechLine(const Common::String &theLine, int x, int &offset) {
81 	float cameraZoom = g_sludge->_gfxMan->getCamZoom();
82 	int halfWidth = (g_sludge->_txtMan->stringWidth(theLine) >> 1) / cameraZoom;
83 	int xx1 = x - (halfWidth);
84 	int xx2 = x + (halfWidth);
85 
86 	// Create new speech line
87 	SpeechLine *newLine = new SpeechLine;
88 	checkNew(newLine);
89 	newLine->textLine.clear();
90 	newLine->textLine = theLine;
91 	newLine->x = xx1;
92 	_speech->allSpeech.push_front(newLine);
93 
94 	// Calculate offset
95 	if ((xx1 < 5) && (offset < (5 - xx1))) {
96 		offset = 5 - xx1;
97 	} else if ((xx2 >= ((float) g_system->getWidth() / cameraZoom) - 5)
98 			&& (offset > (((float) g_system->getWidth() / cameraZoom) - 5 - xx2))) {
99 		offset = ((float) g_system->getWidth() / cameraZoom) - 5 - xx2;
100 	}
101 }
102 
isThereAnySpeechGoingOn()103 int SpeechManager::isThereAnySpeechGoingOn() {
104 	return _speech->allSpeech.empty() ? -1 : _speech->lookWhosTalking;
105 }
106 
getLastSpeechSound()107 int SpeechManager::getLastSpeechSound() {
108 	return _vm->_soundMan->findInSoundCache(_speech->lastFile);
109 }
110 
wrapSpeechXY(const Common::String & theText,int x,int y,int wrap,int sampleFile)111 int SpeechManager::wrapSpeechXY(const Common::String &theText, int x, int y, int wrap, int sampleFile) {
112 	float cameraZoom = g_sludge->_gfxMan->getCamZoom();
113 	int fontHeight = g_sludge->_txtMan->getFontHeight();
114 	int cameraY = g_sludge->_gfxMan->getCamY();
115 
116 	int a, offset = 0;
117 
118 	kill();
119 
120 	int speechTime = (theText.size() + 20) * _speechSpeed;
121 	if (speechTime < 1)
122 		speechTime = 1;
123 	if (sampleFile != -1) {
124 		if (_speechMode >= 1) {
125 			if (g_sludge->_soundMan->startSound(sampleFile, false)) {
126 				speechTime = -10;
127 				_speech->lastFile = sampleFile;
128 				if (_speechMode == 2) return -10;
129 			}
130 
131 		}
132 	}
133 	_speech->speechY = y;
134 
135 	char *tmp, *txt;
136 	tmp = txt = createCString(theText);
137 	while ((int)strlen(txt) > wrap) {
138 		a = wrap;
139 		while (txt[a] != ' ') {
140 			a--;
141 			if (a == 0) {
142 				a = wrap;
143 				break;
144 			}
145 		}
146 		txt[a] = 0;
147 		addSpeechLine(txt, x, offset);
148 		txt[a] = ' ';
149 		txt += a + 1;
150 		y -= fontHeight / cameraZoom;
151 	}
152 	addSpeechLine(txt, x, offset);
153 	y -= fontHeight / cameraZoom;
154 	delete []tmp;
155 
156 	if (y < 0)
157 		_speech->speechY -= y;
158 	else if (_speech->speechY > cameraY + (float) (g_system->getHeight() - fontHeight / 3) / cameraZoom)
159 		_speech->speechY = cameraY
160 				+ (float) (g_system->getHeight() - fontHeight / 3) / cameraZoom;
161 
162 	if (offset) {
163 		for (SpeechLineList::iterator it = _speech->allSpeech.begin(); it != _speech->allSpeech.end(); ++it) {
164 			(*it)->x += offset;
165 		}
166 	}
167 	return speechTime;
168 }
169 
wrapSpeechPerson(const Common::String & theText,OnScreenPerson & thePerson,int sampleFile,bool animPerson)170 int SpeechManager::wrapSpeechPerson(const Common::String &theText, OnScreenPerson &thePerson, int sampleFile, bool animPerson) {
171 	int cameraX = g_sludge->_gfxMan->getCamX();
172 	int cameraY = g_sludge->_gfxMan->getCamY();
173 	int i = wrapSpeechXY(theText, thePerson.x - cameraX,
174 			thePerson.y - cameraY
175 					- (thePerson.scale * (thePerson.height - thePerson.floaty))
176 					- thePerson.thisType->speechGap,
177 			thePerson.thisType->wrapSpeech, sampleFile);
178 	if (animPerson) {
179 		thePerson.makeTalker();
180 		_speech->currentTalker = &thePerson;
181 	}
182 	return i;
183 }
184 
wrapSpeech(const Common::String & theText,int objT,int sampleFile,bool animPerson)185 int SpeechManager::wrapSpeech(const Common::String &theText, int objT, int sampleFile, bool animPerson) {
186 	int i;
187 	int cameraX = g_sludge->_gfxMan->getCamX();
188 	int cameraY = g_sludge->_gfxMan->getCamY();
189 
190 	_speech->lookWhosTalking = objT;
191 	OnScreenPerson *thisPerson = g_sludge->_peopleMan->findPerson(objT);
192 	if (thisPerson) {
193 		setObjFontColour(thisPerson->thisType);
194 		i = wrapSpeechPerson(theText, *thisPerson, sampleFile, animPerson);
195 	} else {
196 		ScreenRegion *thisRegion = g_sludge->_regionMan->getRegionForObject(objT);
197 		if (thisRegion) {
198 			setObjFontColour(thisRegion->thisType);
199 			i = wrapSpeechXY(theText,
200 					((thisRegion->x1 + thisRegion->x2) >> 1) - cameraX,
201 					thisRegion->y1 - thisRegion->thisType->speechGap - cameraY,
202 					thisRegion->thisType->wrapSpeech, sampleFile);
203 		} else {
204 			ObjectType *temp = g_sludge->_objMan->findObjectType(objT);
205 			setObjFontColour(temp);
206 			i = wrapSpeechXY(theText, g_system->getWidth() >> 1, 10, temp->wrapSpeech,
207 					sampleFile);
208 		}
209 	}
210 	return i;
211 }
212 
display()213 void SpeechManager::display() {
214 	float cameraZoom = g_sludge->_gfxMan->getCamZoom();
215 	int fontHeight = g_sludge->_txtMan->getFontHeight();
216 	int viewY = _speech->speechY;
217 	for (SpeechLineList::iterator it = _speech->allSpeech.begin(); it != _speech->allSpeech.end(); ++it) {
218 		g_sludge->_txtMan->pasteString((*it)->textLine, (*it)->x, viewY, _speech->talkCol);
219 		viewY -= fontHeight / cameraZoom;
220 	}
221 }
222 
save(Common::WriteStream * stream)223 void SpeechManager::save(Common::WriteStream *stream) {
224 	stream->writeByte(_speechMode);
225 	stream->writeByte(_speech->talkCol.originalRed);
226 	stream->writeByte(_speech->talkCol.originalGreen);
227 	stream->writeByte(_speech->talkCol.originalBlue);
228 
229 	stream->writeFloatLE(_speechSpeed);
230 
231 	// Write y co-ordinate
232 	stream->writeUint16BE(_speech->speechY);
233 
234 	// Write which character's talking
235 	stream->writeUint16BE(_speech->lookWhosTalking);
236 	if (_speech->currentTalker) {
237 		stream->writeByte(1);
238 		stream->writeUint16BE(_speech->currentTalker->thisType->objectNum);
239 	} else {
240 		stream->writeByte(0);
241 	}
242 
243 	// Write what's being said
244 	for (SpeechLineList::iterator it = _speech->allSpeech.begin(); it != _speech->allSpeech.end(); ++it) {
245 		stream->writeByte(1);
246 		writeString((*it)->textLine, stream);
247 		stream->writeUint16BE((*it)->x);
248 	}
249 	stream->writeByte(0);
250 }
251 
load(Common::SeekableReadStream * stream)252 bool SpeechManager::load(Common::SeekableReadStream *stream) {
253 	// read speech mode
254 	_speechMode = stream->readByte();
255 
256 	_speech->currentTalker = nullptr;
257 	kill();
258 	byte r = stream->readByte();
259 	byte g = stream->readByte();
260 	byte b = stream->readByte();
261 	_speech->talkCol.setColor(r, g, b);
262 	_speechSpeed = stream->readFloatLE();
263 
264 	// Read y co-ordinate
265 	_speech->speechY = stream->readUint16BE();
266 
267 	// Read which character's talking
268 	_speech->lookWhosTalking = stream->readUint16BE();
269 
270 	if (stream->readByte()) {
271 		_speech->currentTalker = g_sludge->_peopleMan->findPerson(stream->readUint16BE());
272 	} else {
273 		_speech->currentTalker = NULL;
274 	}
275 
276 	// Read what's being said
277 	_speech->lastFile = -1;
278 	while (stream->readByte()) {
279 		SpeechLine *newOne = new SpeechLine;
280 		if (!checkNew(newOne))
281 			return false;
282 		newOne->textLine = readString(stream);
283 		newOne->x = stream->readUint16BE();
284 		_speech->allSpeech.push_back(newOne);
285 	}
286 	return true;
287 }
288 
freeze(FrozenStuffStruct * frozenStuff)289 void SpeechManager::freeze(FrozenStuffStruct *frozenStuff) {
290 	frozenStuff->speech = _speech;
291 	init();
292 }
293 
restore(FrozenStuffStruct * frozenStuff)294 void SpeechManager::restore(FrozenStuffStruct *frozenStuff) {
295 	kill();
296 	delete _speech;
297 	_speech = frozenStuff->speech;
298 }
299 
300 } // End of namespace Sludge
301