Advanced Text Handling with DirectDraw

Started by
2 comments, last by HeavyBlade 18 years, 7 months ago
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. :)
Join us at: http://www.blade2k.net/piracysucks/to help stop game piracy!
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]
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]
Join us at: http://www.blade2k.net/piracysucks/to help stop game piracy!
**BUMP**

Sorry for the bump, but does anyone have any ideas as to why this is occuring?
Much thanks.

-HeavyBlade
Join us at: http://www.blade2k.net/piracysucks/to help stop game piracy!

This topic is closed to new replies.

Advertisement