Jump to content
  • Advertisement
Sign in to follow this  
HeavyBlade

Advanced Text Handling with DirectDraw

This topic is 4811 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I've seen a lot of tutorials in books and the web regarding text, but unfortunately for me, they all take the lazy way (:P) out and use Direct3D... with it's pretty font engine... grrrr... anyway. I'm making a minor system of my own. One problem with it - it doesn't work. Well, I lie, it works, but it doesn't parse lines correctly. Here's the code:
[source="cpp"]
#include "stdafx.h"
#include "CDialogManager.h"

#include "Log.h"

const unsigned int GKUITEXTSIZE = 3u;

CDialogManager::CDialogManager(void)
{
	// Dialog_Handler should only be set active by an actual script call
	// for dialog mode.
	m_bActive = false;
	
	// Empty out our strings to clear them of crap.
	m_szDispDlg = _T("");
	m_szRemDlg = _T("");

	// Set size of dialog.
	m_uiDlgHeight = 200u;					// FIXME: Guesstimate.
	m_uiDlgRectHeight = m_uiDlgHeight - 6u; // FIXME: Guesstimate.

	// Now set the values of the rects themselves.
	Set_DlgRects();

	// Set default line parameters.
	m_uiDispNumLines = kuiDISPNUMLINES;
	m_uiCurLine = 0;
}

CDialogManager::~CDialogManager(void)
{

}

/**********************************************************
Function: void CDialogManager::Set_DlgRects(void)
Description: Sets the position and dimensions of a text rect
in which to place dialog text.
Parameters: None. 
Returns: None.
***********************************************************/
void CDialogManager::Set_DlgRects(void)
{
	unsigned int uiDlg_top = 0;
	unsigned int uiDlg_bottom = 0;
	unsigned int uiDlgRect_top = 0;
	unsigned int uiDlgRect_bottom = 0;
	unsigned int uiDlg_YOffset = 0;		// Determined by pos.

	m_uiDlgWidth = (uiDefWidth - 2u);
	m_uiDlgRectWidth = (uiDefWidth - 16u);

	// Set current pos, used for both rect calculations.
	switch(m_ePos)
	{
	case TOP:
		uiDlg_YOffset = 0;
	break;
	case MIDDLE:
		uiDlg_YOffset = uiCurHeight/2;		// FIXME: Can I just bitshift here? Also, need a curheight var.  Stores Current Height of Screen as opposed to DefHeight - default height of screen.	
	break;
	case BOTTOM:
		uiDlg_YOffset = (uiCurHeight - m_uiDlgHeight);	
	break;
	};

	// Leave a two pixel buffer to prevent crashing(1 pix. on each side).
	uiDlg_top = (uiDlg_YOffset + 1u);
	uiDlg_bottom = ((uiDlg_top + m_uiDlgHeight) - 1u);

	uiDlgRect_top = (uiDlg_YOffset + 6u);
	uiDlgRect_bottom = ((uiDlgRect_top + m_uiDlgRectHeight) - 6u);

	// Now that the values have been calculated, dump them into the rect.
	m_rDlgArea.top = uiDlg_top;
	m_rDlgArea.bottom = uiDlg_bottom;

	m_rDlgTextArea.top = uiDlgRect_top;
	m_rDlgTextArea.bottom = uiDlgRect_bottom;

	// Left and right are unaffected by position.
	m_rDlgArea.left = 1l;							// 1 pix. border.
	m_rDlgArea.right = (m_uiDlgWidth + 1l);			// 1 pix. border.

	m_rDlgTextArea.left = 8l;						// 8 pix. border.
	m_rDlgTextArea.right = (m_uiDlgRectWidth + 8l);	// 8 pix. border.

	// Added return.
	return;
}

/**********************************************************
Function: void CDialogManager::ParseDialog(void)
Description: Parses the next group of text appropriately for dialog
format.  Text, once finished, is completely ready for a call to draw
text, string changes and all are contained inside this function.
Parameters: None.
Returns: None.
***********************************************************/
// This function only needs to be in charge of parsing the dialog to be displayed.
// drawing it out character by character can be accomplished in a single loop in the
// drawDlgText function.
void CDialogManager::ParseDialog(void)
{
	TCHAR cCur;								// Holds the current character.
	std::wstring szDlgLine = _T("");		// Holds the currently formatted line of dialog.
	std::wstring szPotentialLine = _T("");	// Holds the current szDlgLine + the next word.  Used as
	// a check to see if the word can fit onto the current line.
	std::wstring szWord = _T("");			// Holds the currently formatted word.  Important to
	// have this variable in order to ensure whole words
	// don't break into chunks between lines.
	unsigned int uiLine = 0;				// Holds the current line being parsed.

	// Seperators
	// Seperators are: \n (newline), " ".
	//  "." and "  " will be read in automatically as chars.
	static const int MAX_SEPS( 2 );
	TCHAR cSeps[MAX_SEPS+1] = _T("\n ");

	// Display <m_uiDispNumLines> of the remaining dialog (to be displayed...).
	// or if there are <m_uiDispNumLines> or less dialog remaining, the length of the message.
	for(uiLine = m_uiCurLine; m_szRemDlg.length() && uiLine > (m_uiCurLine+m_uiDispNumLines); )
	{
		// Remove the first single char from m_szRemDlg.
		// this is the next value we'll be adding into the displayable
		// text.
		cCur = m_szRemDlg[0];
		m_szRemDlg = m_szRemDlg.substr(1);

		// Append the new character to the word, making sure not
		// to append linebreak.
		if(cCur != cSeps[0])
			szWord += cCur;

		// End a word when it is complete.
		for(int iIndex = 0; iIndex < MAX_SEPS; iIndex++)
		{
			// If the character is equal to any of the seperators, then
			// the word has been completed.
			if(cCur == cSeps[iIndex])
			{
				// Now, check to see if the completed word can fit into the current line.
				szPotentialLine = szDlgLine + szWord;

				// If szDlgLine has gotten too wide, end the line
				// and start a new one.
				if((szPotentialLine.length()*GKUITEXTSIZE) >= m_uiDlgRectWidth)
				{
					// Trim the line to fit.
					szDlgLine = TrimLeft(szPotentialLine);

					// Update the displayable dialog to reflect the changes.
					m_szDispDlg += szDlgLine;
					m_szDispDlg += '\n';
					uiLine++;

					// Reset the line, and add in the completed word.
					szDlgLine = _T("");
					szDlgLine += szWord;	// NOTE: I assume here that no word is too big for a line.  FIXME:?
				}
				// Otherwise, we still have sufficient space on the line.
				else
					szDlgLine += szWord;

				// With the word added into the dialog line appropriately,
				// reset it for the next line...
				szWord = _T("");

				// Make sure the lineBreaks transfer over.
				if(cCur == cSeps[0])
				{
					// Add the current line and lineBreak, reset DlgLine.
					m_szDispDlg += szDlgLine;
					m_szDispDlg += cSeps[0];
					szDlgLine = _T("");
				}
				break;
			}
		}
	} // End for loop.

	// Before we exit, update our m_uiLine variable.  Done here so
	// we only call it once, and not 4 times unnecessarily.
	// FIXME: Should this be uiLine++?
	m_uiCurLine = uiLine;

	// Added return.
	return;
}

std::wstring CDialogManager::TrimLeft(std::wstring &szPotentialLine)
{
	std::wstring szResult = _T("");
	std::wstring szLine = _T("");
	unsigned int uiDiff = 0;

	// Just used to seek really.
	for(unsigned int uiPos = 0; uiPos < (szPotentialLine.length()*GKUITEXTSIZE); uiPos+=GKUITEXTSIZE)
	{
		// Get the total number of chars we can cram in.
		if((uiPos + GKUITEXTSIZE) > m_uiDlgRectWidth)
		{
			// Since we need a substr preformed before we modify potentialline, we need a cloned var.
			szLine = szPotentialLine;

			// Get the remainder of the string that exceeds the current boundaries.
			uiDiff = (szPotentialLine.length()*GKUITEXTSIZE) - uiPos;

			// Set a new string to hold all the text we can cram up UNTIL the boundaries.
			szResult = szLine.substr(0, uiPos);

			// Reformat the remaining line to hold all but the formatted chars held in szResult.
			szPotentialLine.substr(uiDiff);
			break;
		}
	}

	return szResult;
}

//void CDialogManager::UpdateDialog(void)
//{
//	// Empty out the display dialog - it's time to load in the next set.
//	m_szDispDlg = _T("");
//
//}

// Overload one of two, the first will load from file, this just
// loads a text message to print directly.
bool CDialogManager::LoadDialog(std::wstring szMessage)
{
	bool bSuccess = true;

	m_szRemDlg = szMessage;
	if(m_szRemDlg == _T(""))
		bSuccess = false;

	// hack.
	m_szDispDlg = szMessage;
	if(m_szDispDlg == _T(""))
		bSuccess = false;

	return bSuccess;
}

[source="cpp"]
#ifndef _DOTS_WIN32APP_DIRECTX9C_CDIALOGMANAGER_H
#define _DOTS_WIN32APP_DIRECTX9C_CDIALOGMANAGER_H

enum ePos
{
	TOP,
	MIDDLE,
	BOTTOM
};

// Used for dealing with dialog.
// Prior to this seperate classes were needed for the font
// and font handling, but no more.  Yea~
class CDialogManager
{
public:
	CDialogManager(void);
	~CDialogManager(void);

	std::wstring TrimLeft(std::wstring &szPotentialLine);
	bool LoadDialog(std::wstring szMessage);
	void Set_DlgRects(void);
	void ParseDialog(void);

	bool IsActive(void) { return m_bActive; }
	void SetActive(bool bActive) { m_bActive = bActive; return; }

	//--Dialog bounds information
	CRect m_rDlgArea;				// Stores the Rect representing our dialog area.
	CRect m_rDlgTextArea;			// Stores the Rect representing our dialog text area.

	//--Dialog contents information
	std::wstring m_szRemDlg;		// Holds the remaining dialog(dialog that still needs to be printed out to screen).
	std::wstring m_szDispDlg;		// Holds the dialog to display.
	unsigned int m_uiCurLine;		// Holds the current line of dialog we are on.
	unsigned int m_uiDispNumLines;	// Holds the number of lines to display at once.
	
	//--Dialog sizing information
	unsigned int m_uiDlgHeight;		// Holds the size of the full dialog window.
	unsigned int m_uiDlgWidth;		// Holds the size of the full dialog window.
	unsigned int m_uiDlgRectHeight;	// Holds the size of the full text-fillable rect of the dialog window.
	unsigned int m_uiDlgRectWidth;	// Holds the size of the full text-fillable rect of the dialog window.
	
	//--Other vars.
	ePos m_ePos;					// Holds the enum value of the position of the dialog box.

private:
	bool m_bActive;					// Bool indicating whether to show the dialog box and do
									// our dialog related logics.
};

#endif// _DOTS_WIN32APP_DIRECTX9C_CDIALOGMANAGER_H

Can anyone see where my mistake lies? I'm sure it's a simple one... it's all just a matter of finding it, and a second point of view always helps :) (by the way, the actual rendering of said lines is done outside the file using the standard "textout" function):
[source="cpp"]
HRESULT CDirectDraw_Manager::DDraw_Text(LPCWSTR szMessage, COLORREF TextColor, int posx, int posy)
{
	HRESULT hr = S_OK;
	HDC hdc;

	// We have to have a backbuffer to do this.
	if(!m_lpDDSBack)
		return E_FAIL;

	hr = m_lpDDSBack->GetDC(&hdc);
	if(SUCCEEDED(hr))
	{
		SetBkMode(hdc, TRANSPARENT);
		SetBkColor(hdc, RGB(0,0,0));
		SetTextColor(hdc, TextColor);
		TextOut(hdc, posx, posy, szMessage, lstrlen(szMessage));
		m_lpDDSBack->ReleaseDC(hdc);
	}

	// Return result of function.
	return hr;
}

Thanks again for your time. :)

Share this post


Link to post
Share on other sites
Advertisement
What do you mean by "it doesn't parse lines correctly"? Does it crash? Cut them short? Add gibberish?
Any chance of a screenshot of the problem?

Also: // FIXME: Can I just bitshift here?
Yes, you can just shift right by 1 (uiDlg_YOffset = uiCurHeight>>1;), but there's absolutely no point in doing that, because the compiler will optimize it for you, and it just makes your code harder to read. So don't worry about it [smile]

Share this post


Link to post
Share on other sites
Ah, no, I mean it just prints the line from the desired point straight across and off the screen (amazingly enough, this doesn't crash the program).

ie.
|
|stuffstuffstuffstuffstuffstuffstuffstuffstuffstuffstuffstuffst|
|

What is should do is detect the portion that would be displayed offscreen, and print it out on a new line, so for dialog, you'd get something like this:

ie.
|
|stuffstuffstuffstuffstuffstuffstuffstuffstuffstuffstuffstuff|
|stuffstuffstuffstuffstuffstuffstuffstuffstuffstuffstuffstuff|
|stuffstuffstuffstuffstuffstuffstuffstuffstuffstuffstuffstuff|
|stuffstuffstuffstuffstuffstuff.
|

[Edited by - HeavyBlade on September 14, 2005 11:15:31 AM]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!