2
0
Эх сурвалжийг харах

- chinese fonts now use fallback to H3 fonts for ASCII characters
- proper messages for not implemented main menu entries
- some cleanup of CMessage::breakText()

Ivan Savenko 12 жил өмнө
parent
commit
f6a3d6770f

+ 33 - 28
client/CMessage.cpp

@@ -129,44 +129,54 @@ SDL_Surface * CMessage::drawDialogBox(int w, int h, PlayerColor playerColor)
 	return ret;
 }
 
-std::vector<std::string> CMessage::breakText( std::string text, size_t maxLineSize, EFonts font )
+std::vector<std::string> CMessage::breakText( std::string text, size_t maxLineWidth, EFonts font )
 {
 	std::vector<std::string> ret;
 
 	boost::algorithm::trim_right_if(text,boost::algorithm::is_any_of(std::string(" ")));
 
+	// each interation generates one output line
 	while (text.length())
 	{
-		ui32 lineLength = 0;	//in characters or given char metric
-		ui32 wordBreak = -1;
-		ui32 currPos = 0; //our position in text
-		bool opened = false;//if we have an unclosed brace in current line
-		bool lineManuallyBroken = false;
+		ui32 lineWidth = 0;    //in characters or given char metric
+		ui32 wordBreak = -1;    //last position for line break (last space character)
+		ui32 currPos = 0;       //current position in text
+		bool opened = false;    //set to true when opening brace is found
 
-		while(currPos < text.length()  &&  text[currPos] != 0x0a  &&  lineLength < maxLineSize)
+		size_t symbolSize = 0; // width of character, in bytes
+		size_t glyphWidth = 0; // width of printable glyph, pixels
+
+		// loops till line is full or end of text reached
+		while(currPos < text.length()  &&  text[currPos] != 0x0a  &&  lineWidth < maxLineWidth)
 		{
-			if (ui8(text[currPos]) <= ui8(' ')) // candidate for line break
+			symbolSize = graphics->fonts[font]->getCharacterSize(text[currPos]);
+			glyphWidth = graphics->fonts[font]->getGlyphWidth(text.data() + currPos);
+
+			// candidate for line break
+			if (ui8(text[currPos]) <= ui8(' '))
 				wordBreak = currPos;
+
 			/* We don't count braces in string length. */
 			if (text[currPos] == '{')
 				opened=true;
 			else if (text[currPos]=='}')
 				opened=false;
 			else
-				lineLength += graphics->fonts[font]->getGlyphWidth(text.data() + currPos);
-			currPos += graphics->fonts[font]->getCharacterSize(text[currPos]);
+				lineWidth += glyphWidth;
+			currPos += symbolSize;
 		}
 
+		// long line, create line break
 		if (currPos < text.length()  &&  (text[currPos] != 0x0a))
 		{
-			// We have a long line. Try to do a nice line break, if possible
 			if (wordBreak != ui32(-1))
 				currPos = wordBreak;
 			else
-				currPos--;
+				currPos -= symbolSize;
 		}
 
-		if(currPos) //non-blank line
+		//non-blank line
+		if(currPos != 0)
 		{
 			ret.push_back(text.substr(0, currPos));
 
@@ -176,39 +186,34 @@ std::vector<std::string> CMessage::breakText( std::string text, size_t maxLineSi
 
 			text.erase(0, currPos);
 		}
-		else if(text[currPos] == 0x0a) //blank line
+		else if(text[currPos] == 0x0a)
 		{
 			ret.push_back(""); //add empty string, no extra actions needed
 		}
 
-		if (text.length() && text[0] == 0x0a)
+		if (text.length() != 0 && text[0] == 0x0a)
 		{
-			/* Braces do not carry over lines. The map author forgot
-			 * to close it. */
-			opened = false;
-
 			/* Remove LF */
 			text.erase(0, 1);
-
-			lineManuallyBroken = true;
 		}
-
-		//if(!allowLeadingWhitespace || !lineManuallyBroken)
-		if(!lineManuallyBroken)
+		else
+		{
+			// trim only if line does not starts with LF
+			// FIXME: necessary? All lines will be trimmed before returning anyway
 			boost::algorithm::trim_left_if(text,boost::algorithm::is_any_of(std::string(" ")));
+		}
 
 		if (opened)
 		{
 			/* Add an opening brace for the next line. */
-			if (text.length())
+			if (text.length() != 0)
 				text.insert(0, "{");
 		}
 	}
 
 	/* Trim whitespaces of every line. */
-	//if(!allowLeadingWhitespace)
-		for (auto & elem : ret)
-			boost::algorithm::trim(elem);
+	for (auto & elem : ret)
+		boost::algorithm::trim(elem);
 
 	return ret;
 }

+ 8 - 4
client/CPreGame.cpp

@@ -332,7 +332,8 @@ static std::function<void()> genCommand(CMenuScreen* menu, std::vector<std::stri
 						case 0: return boost::bind(&CGPreGame::openSel, CGP, CMenuScreen::newGame, CMenuScreen::SINGLE_PLAYER);
 						case 1: return &pushIntT<CMultiMode>;
 						case 2: return boost::bind(&CGPreGame::openSel, CGP, CMenuScreen::campaignList, CMenuScreen::SINGLE_PLAYER);
-						case 3: return std::function<void()>();//TODO: start tutorial
+						//TODO: start tutorial
+						case 3: return boost::bind(CInfoWindow::showInfoDialog, "Sorry, tutorial is not implemented yet\n", (const std::vector<CComponent*>*)nullptr, false, PlayerColor(1));
 					}
 				}
 				break; case 3://load
@@ -341,8 +342,10 @@ static std::function<void()> genCommand(CMenuScreen* menu, std::vector<std::stri
 					{
 						case 0: return boost::bind(&CGPreGame::openSel, CGP, CMenuScreen::loadGame, CMenuScreen::SINGLE_PLAYER);
 						case 1: return boost::bind(&CGPreGame::openSel, CGP, CMenuScreen::loadGame, CMenuScreen::MULTI_HOT_SEAT);
-						case 2: return std::function<void()>();//TODO: load campaign
-						case 3: return std::function<void()>();//TODO: load tutorial
+						//TODO: load campaign
+						case 2: return boost::bind(CInfoWindow::showInfoDialog, "This function is not implemented yet. Campaign saves can be loaded from \"Single Player\" menu", (const std::vector<CComponent*>*)nullptr, false, PlayerColor(1));
+						//TODO: load tutorial
+						case 3: return boost::bind(CInfoWindow::showInfoDialog, "Sorry, tutorial is not implemented yet\n", (const std::vector<CComponent*>*)nullptr, false, PlayerColor(1));
 					}
 				}
 				break; case 4://exit
@@ -351,7 +354,8 @@ static std::function<void()> genCommand(CMenuScreen* menu, std::vector<std::stri
 				}
 				break; case 5://highscores
 				{
-					return std::function<void()>(); //TODO: high scores &pushIntT<CHighScores>;
+					//TODO: high scores
+					return boost::bind(CInfoWindow::showInfoDialog, "Sorry, high scores menu is not implemented yet\n", (const std::vector<CComponent*>*)nullptr, false, PlayerColor(1));
 				}
 			}
 		}

+ 6 - 0
client/GUIClasses.cpp

@@ -721,6 +721,12 @@ void CInfoWindow::showAll(SDL_Surface * to)
 	CIntObject::showAll(to);
 }
 
+void CInfoWindow::showInfoDialog(const std::string &text, const std::vector<CComponent *> *components, bool DelComps, PlayerColor player)
+{
+	CInfoWindow * window = CInfoWindow::create(text, player, components, DelComps);
+	GH.pushInt(window);
+}
+
 void CInfoWindow::showYesNoDialog(const std::string & text, const std::vector<CComponent*> *components, const CFunctionList<void( ) > &onYes, const CFunctionList<void()> &onNo, bool DelComps, PlayerColor player)
 {
 	assert(!LOCPLINT || LOCPLINT->showingDialog->get());

+ 3 - 1
client/GUIClasses.h

@@ -104,7 +104,9 @@ public:
 	CInfoWindow(); //c-tor
 	~CInfoWindow(); //d-tor
 
-	static void showYesNoDialog( const std::string & text, const std::vector<CComponent*> *components, const CFunctionList<void( ) > &onYes, const CFunctionList<void()> &onNo, bool DelComps = true, PlayerColor player = PlayerColor(1)); //use only before the game starts! (showYesNoDialog in LOCPLINT must be used then)
+	//use only before the game starts! (showYesNoDialog in LOCPLINT must be used then)
+	static void showInfoDialog( const std::string & text, const std::vector<CComponent*> *components, bool DelComps = true, PlayerColor player = PlayerColor(1));
+	static void showYesNoDialog( const std::string & text, const std::vector<CComponent*> *components, const CFunctionList<void( ) > &onYes, const CFunctionList<void()> &onNo, bool DelComps = true, PlayerColor player = PlayerColor(1));
 	static CInfoWindow *create(const std::string &text, PlayerColor playerID = PlayerColor(1), const std::vector<CComponent*> *components = nullptr, bool DelComps = false);
 
 	/// create text from title and description: {title}\n\n description

+ 3 - 1
client/Graphics.cpp

@@ -365,7 +365,9 @@ void Graphics::addImageListEntry(size_t index, std::string listName, std::string
 {
 	if (!imageName.empty())
 	{
-		if (!CResourceHandler::get()->existsResource(ResourceID("SPRITES/" + imageName, EResType::IMAGE)))
+		ResourceID resID("SPRITES/" + imageName, EResType::IMAGE);
+		if (!CResourceHandler::get()->existsResource(resID) && // file not found
+		    imageName.find(':') == std::string::npos)          // and entry does not refers to frame in def file
 			logGlobal->errorStream() << "Required image " << "SPRITES/" << imageName << " is missing!";
 
 		JsonNode entry;

+ 4 - 1
client/gui/Fonts.cpp

@@ -353,7 +353,7 @@ void CBitmapHanFont::renderText(SDL_Surface * surface, const std::string & data,
 	for(size_t i=0; i<data.size(); i += getCharacterSize(data[i]))
 	{
 		if (ui8(data[i]) < 0x80)
-			renderCharacter(surface, getCharacterIndex(0xa3, data[i] + 0x80), color, posX, posY);
+			fallback->renderCharacter(surface, fallback->chars[data[i]], color, posX, posY);
 		else
 			renderCharacter(surface, getCharacterIndex(data[i], data[i+1]), color, posX, posY);
 	}
@@ -361,6 +361,7 @@ void CBitmapHanFont::renderText(SDL_Surface * surface, const std::string & data,
 }
 
 CBitmapHanFont::CBitmapHanFont(const JsonNode &config):
+    fallback(new CBitmapFont(config["fallback"].String())),
     data(CResourceHandler::get()->load(ResourceID("data/" + config["name"].String(), EResType::OTHER))->readAll()),
     size(config["size"].Float())
 {
@@ -378,6 +379,8 @@ size_t CBitmapHanFont::getLineHeight() const
 
 size_t CBitmapHanFont::getGlyphWidth(const char * data) const
 {
+	if (ui8(data[0]) < 0x80)
+		return fallback->getGlyphWidth(data);
 	return size + 1;
 }
 

+ 1 - 0
client/gui/Fonts.h

@@ -97,6 +97,7 @@ public:
 /// supports multi-byte characters for such languages like Chinese
 class CBitmapHanFont : public IFont
 {
+	std::unique_ptr<CBitmapFont> fallback;
 	// data, directly copied from file
 	const std::pair<std::unique_ptr<ui8[]>, ui64> data;