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