1 /*
2  * Copyright 2011-2013 Arx Libertatis Team (see the AUTHORS file)
3  *
4  * This file is part of Arx Libertatis.
5  *
6  * Arx Libertatis is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Arx Libertatis is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Arx Libertatis.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 /* Based on:
20 ===========================================================================
21 ARX FATALIS GPL Source Code
22 Copyright (C) 1999-2010 Arkane Studios SA, a ZeniMax Media company.
23 
24 This file is part of the Arx Fatalis GPL Source Code ('Arx Fatalis Source Code').
25 
26 Arx Fatalis Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
27 License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
28 
29 Arx Fatalis Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
30 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
31 
32 You should have received a copy of the GNU General Public License along with Arx Fatalis Source Code.  If not, see
33 <http://www.gnu.org/licenses/>.
34 
35 In addition, the Arx Fatalis Source Code is also subject to certain additional terms. You should have received a copy of these
36 additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Arx
37 Fatalis Source Code. If not, please request a copy in writing from Arkane Studios at the address below.
38 
39 If you have questions concerning this license or the applicable additional terms, you may contact in writing Arkane Studios, c/o
40 ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
41 ===========================================================================
42 */
43 
44 #include "gui/Credits.h"
45 
46 #include <sstream>
47 
48 #include "core/Core.h"
49 #include "core/GameTime.h"
50 
51 #include "gui/Menu.h"
52 #include "gui/Text.h"
53 #include "gui/MenuWidgets.h"
54 
55 #include "graphics/Draw.h"
56 #include "graphics/font/Font.h"
57 
58 #include "io/log/Logger.h"
59 
60 #include "scene/GameSound.h"
61 
62 using std::string;
63 using std::vector;
64 
65 // TODO extern globals
66 extern bool bFadeInOut;
67 extern bool bFade;
68 extern int iFadeAction;
69 void ARX_MENU_LaunchAmb(const string & _lpszAmb);
70 
71 
72 struct CreditsTextInformations {
73 
CreditsTextInformationsCreditsTextInformations74 	CreditsTextInformations() {
75 		sPos = Vec2i::ZERO;
76 		fColors = Color::none;
77 	}
78 
79 	string  sText;
80 	Color fColors;
81 	Vec2i sPos;
82 };
83 
84 
85 struct CreditsInformations {
86 
CreditsInformationsCreditsInformations87 	CreditsInformations() : iFirstLine(0), iFontAverageHeight(-1), sizex(0), sizey(0) { }
88 
89 	int iFirstLine;
90 	int iFontAverageHeight;
91 
92 	int sizex, sizey; // save the screen size so we know when to re-initialize the credits
93 
94 	vector<CreditsTextInformations> aCreditsInformations;
95 };
96 
97 
98 static CreditsInformations CreditsData;
99 
100 static void InitCredits();
101 static void CalculAverageWidth();
102 static void ExtractAllCreditsTextInformations();
103 
InitCredits()104 static void InitCredits() {
105 
106 	if(CreditsData.iFontAverageHeight != -1
107 		&& CreditsData.sizex == DANAESIZX && CreditsData.sizey == DANAESIZY) {
108 		return;
109 	}
110 
111 	CreditsData.sizex = DANAESIZX, CreditsData.sizey = DANAESIZY;
112 
113 	CreditsData.aCreditsInformations.clear();
114 
115 	LogDebug("InitCredits");
116 
117 	CalculAverageWidth();
118 	ExtractAllCreditsTextInformations();
119 
120 	LogDebug("Credits lines " << CreditsData.aCreditsInformations.size());
121 
122 }
123 
ExtractPhraseColor(string & phrase)124 static Color ExtractPhraseColor(string & phrase) {
125 	//Get the good color
126 	if(!phrase.empty() && phrase[0] == '~') {
127 		phrase[0] = ' ';
128 		return Color(255,255,255);
129 	} else {
130 		//print in gold color
131 		return Color(232,204,143);
132 	}
133 }
134 
addCreditsLine(string & phrase,float & drawpos)135 static void addCreditsLine(string & phrase, float & drawpos) {
136 
137 	//Create a data containers
138 	CreditsTextInformations infomations;
139 
140 	infomations.fColors = ExtractPhraseColor(phrase);
141 
142 	//int linesize = hFontCredits->GetTextSize(phrase).x;
143 
144 	static const int MARGIN_WIDTH = 20;
145 	Rect linerect(DANAESIZX - MARGIN_WIDTH - MARGIN_WIDTH, hFontCredits->getLineHeight());
146 
147 	while(!phrase.empty()) {
148 
149 		// Split long lines
150 		long n = ARX_UNICODE_ForceFormattingInRect(hFontCredits, phrase, linerect);
151 		arx_assert(n >= 0 && size_t(n) < phrase.length());
152 
153 		infomations.sText = phrase.substr(0, size_t(n + 1) == phrase.length() ? n + 1 : n);
154 		phrase = phrase.substr(n + 1);
155 
156 		// Center the text on the screen
157 		int linesize = hFontCredits->getTextSize(infomations.sText).x;
158 		infomations.sPos.x = (DANAESIZX - linesize) / 2;
159 
160 		LogDebug("credit line: '" << infomations.sText << "' (" << linesize << "," << infomations.sText.length() << ")");
161 
162 		// Calculate height position
163 		infomations.sPos.y = static_cast<int>(drawpos);
164 		drawpos += CreditsData.iFontAverageHeight;
165 
166 		CreditsData.aCreditsInformations.push_back(infomations);
167 	}
168 
169 }
170 
171 //Use to calculate an Average height for text fonts
CalculAverageWidth()172 static void CalculAverageWidth() {
173 
174 	// Calculate the average value
175 	Vec2i size = hFontCredits->getTextSize("aA(");
176 	CreditsData.iFontAverageHeight = size.y;
177 }
178 
iswhitespace(char c)179 static bool iswhitespace(char c) {
180 	return (c == ' ' || c == '\t' || c == '\r' || c == '\n');
181 }
182 
strip(string & str)183 static void strip(string & str) {
184 
185 	size_t startpos = 0;
186 	while(startpos < str.length() && iswhitespace(str[startpos])) {
187 		startpos++;
188 	}
189 
190 	size_t endpos = str.length();
191 	while(endpos > startpos && iswhitespace(str[endpos - 1])) {
192 		endpos--;
193 	}
194 
195 	if(startpos != 0) {
196 		str = str.substr(startpos, endpos - startpos);
197 	} else {
198 		str.resize(endpos);
199 	}
200 }
201 
202 
203 //Use to extract string info from src buffer
ExtractAllCreditsTextInformations()204 static void ExtractAllCreditsTextInformations() {
205 
206 	// Retrieve the rows to display
207 	std::istringstream iss(ARXmenu.mda->credits);
208 	string phrase;
209 
210 	//Use to calculate the positions
211 	float drawpos = static_cast<float>(DANAESIZY);
212 
213 	while(std::getline(iss, phrase)) {
214 
215 		strip(phrase);
216 
217 		if(phrase.empty()) {
218 			// Separator line
219 			drawpos += CreditsData.iFontAverageHeight;
220 		} else {
221 			addCreditsLine(phrase, drawpos);
222 		}
223 	}
224 }
225 
render()226 void Credits::render() {
227 
228 	//We initialize the datas
229 	InitCredits();
230 
231 	int iSize = CreditsData.aCreditsInformations.size() ;
232 
233 	//We display them
234 	if(CreditsData.iFontAverageHeight != -1) {
235 
236 		//Set the device
237 		GRenderer->BeginScene();
238 
239 		GRenderer->SetRenderState(Renderer::AlphaBlending, false);
240 		GRenderer->SetRenderState(Renderer::Fog, false);
241 		GRenderer->SetRenderState(Renderer::DepthWrite, true);
242 		GRenderer->SetRenderState(Renderer::DepthTest, false);
243 
244 		//Draw Background
245 		if(ARXmenu.mda->pTexCredits) {
246 			EERIEDrawBitmap2(0, 0, static_cast<float>(DANAESIZX), static_cast<float>(DANAESIZY + 1), .999f, ARXmenu.mda->pTexCredits, Color::white);
247 		}
248 
249 		// Use time passed between frame to create scroll effect
250 		float time = arxtime.get_updated(false);
251 		float dtime = (float)(time - ARXmenu.mda->creditstart);
252 		ARXmenu.mda->creditspos -= 0.03f * Yratio * dtime;
253 		ARXmenu.mda->creditstart = time;
254 
255 		std::vector<CreditsTextInformations>::const_iterator it = CreditsData.aCreditsInformations.begin() + CreditsData.iFirstLine ;
256 		for (; it != CreditsData.aCreditsInformations.end(); ++it)
257 		{
258 			//Update the Y word display
259 			float yy = it->sPos.y + ARXmenu.mda->creditspos;
260 
261 			//Display the text only if he is on the viewport
262 			if ((yy >= -CreditsData.iFontAverageHeight) && (yy <= DANAESIZY))
263 			{
264 				hFontCredits->draw(it->sPos.x, static_cast<int>(yy), it->sText, it->fColors);
265 			}
266 
267 			if (yy <= -CreditsData.iFontAverageHeight)
268 			{
269 				++CreditsData.iFirstLine;
270 			}
271 
272 			if ( yy >= DANAESIZY )
273 				break ; //it's useless to continue because next phrase will not be inside the viewport
274 		}
275 	} else {
276 		LogWarning << "Error initializing credits";
277 	}
278 
279 
280 
281 	if ( (iSize <= CreditsData.iFirstLine) && ( iFadeAction != AMCM_MAIN ) )
282 	{
283 		ARXmenu.mda->creditspos = 0;
284 		ARXmenu.mda->creditstart = 0 ;
285 		CreditsData.iFirstLine = 0 ;
286 
287 		bFadeInOut = true;
288 		bFade = true;
289 		iFadeAction=AMCM_MAIN;
290 
291 		ARX_MENU_LaunchAmb(AMB_MENU);
292 	}
293 
294 	if(ProcessFadeInOut(bFadeInOut,0.1f))
295 	{
296 		switch(iFadeAction)
297 		{
298 		case AMCM_MAIN:
299 			ARXmenu.currentmode=AMCM_MAIN;
300 			iFadeAction=-1;
301 			bFadeInOut=false;
302 			bFade=true;
303 			break;
304 		}
305 	}
306 
307 	GRenderer->EndScene();
308 
309 	GRenderer->SetRenderState(Renderer::DepthWrite, true);
310 	GRenderer->SetRenderState(Renderer::DepthTest, true);
311 
312 }
313 
reset()314 void Credits::reset() {
315 	ARXmenu.mda->creditstart = arxtime.get_updated(false);
316 	ARXmenu.mda->creditspos = 0;
317 	CreditsData.iFirstLine = 0;
318 }
319 
320