As for messaging I leave that to you to figure out.
As for the text string problems:
Instead of storing the text descriptions of rooms as lines of text, there is another way.
To use this method you will need to create an editor to create the text file and the text file information.
Store the text descriptions in one huge array. Load it at startup. To find the description for the current room, you need a starting index and ending index in the array. Print from start to end and wallah...description - and fast too.
Code:
struct RoomDescripInfo
{
DWORD dwRoomID;
DWORD dwStartIndex;
DWORD dwEndIndex;
};
So your text descrip file is a huge file full of these structs. The first DWORD of the file would be how many structs (or rooms) there are. Then read in that many structs. Then read in the entire text file containing the room descriptions and stick it into a buffer. Using the information in the map, you retrieve the ID for the current room, then you look in the array of structs and find out the starting and ending index into the description buffer or array. Print the description starting at dwStartIndex and stop printing at dwEndIndex.
This way all descriptions are in memory and you are simply indexing into a huge array to get them. With tons of memory at your disposal I see no reason not to load everything at load time and then access it as you need it.
You decide on how to interpret new line and carriage return characters.
Code:
#define MAP_ERROR 0xFFFFFFFF
#pragma pack(1)
class CMapHeader
{
DWORD dwWidth;
DWORD dwHeight;
DWORD dwChunkSize;
};
#pragma
class CMap
{
friend class CMapContainer;
DWORD *m_pMap;
DWORD m_dwWidth;
DWORD m_dwHeight;
DWORD m_dwMaxOffset;
bool m_bInitFlag;
CMap(void):m_pMap(NULL),m_dwWidth(0),m_dwHeight(0),
m_bInitFlag(false) {}
virtual ~CMap(void)
{
if (m_pMap) //MSCV bug for delete on NULL pointers
{
delete [] m_pMap;
m_pMap=NULL;
}
}
bool Create(DWORD dwWidth,DWORD dwHeight)
{
//Can't init more than once
if (m_bInitFlag) return true;
//Setup class data
m_dwMaxOffset=dwWidth*dwHeight;
m_dwWidth=dwWidth;
m_dwHeight=dwHeight;
m_pMap=new DWORD[m_dwMaxOffset];
//Bail on allocation failure
if (!m_pMap) return true;
//Class is ok
m_bInitFlag=true;
//Function success
return false;
}
DWORD SetMapValue(DWORD dwOffset,DWORD dwValue)
{
if (!m_bInitFlag) return MAP_ERROR;
if (dwOffset<m_dwMaxOffset)
{
DWORD dwPrevValue=m_pMap[dwOffset];
m_pMap[dwOffset]=dwValue;
return dwPrevValue;
} else return MAP_ERROR;
}
DWORD GetMapValue(DWORD dwOffset)
{
if (!m_bInitFlag) return MAP_ERROR;
if (dwOffset<m_dwMaxOffset)
{
return m_pMap[dwOffset];
} else return MAP_ERROR;
}
bool SaveToHandle(int iHandle)
{
if (!m_bInitFlag) return true;
CMapHeader hdr;
hdr.dwWidth=m_dwWidth;
hdr.dwHeight=m_dwHeight;
hdr.dwChunkSize=m_dwMaxOffset;
//Write out header
_write(iHandle,&hdr,sizeof(CMapHeader));
//Write out map data
_write(iHandle,m_pMap,m_dwMaxOffset);
}
bool LoadFromHandle(int iHandle)
{
CMapHeader hdr;
//Read header
_read(iHandle,&hdr,sizeof(CMapHeader));
//Check for existing map and delete
if (m_pMap) delete [] m_pMap;
//Setup class data
m_dwMaxOffset=hdr.dwChunkSize;
m_dwWidth=hdr.dwWidth;
m_dwHeight=hdr.dwHeight;
//Create map
m_pMap=new DWORD[m_dwMaxOffset];
//Bail on error
if (!m_pMap) return true;
//Class is ok
m_bInitFlag=true;
}
//Read in map data
_read(iHandle,m_pMap,m_dwMaxOffset*sizeof(DWORD))
//Function success
return false;
}
CMap &operator = (const CMap &map)
{
//Assign class data
m_dwMaxOffset=map.m_dwMaxOffset;
m_dwWidth=map.m_dwWidth;
m_dwHeight=map.m_dwHeight;
//Ensure passed object is valid
if (map.m_pMap && map.m_bInitFlag)
{
//Setup pointer to passed object
DWORD *ptrMap=map.m_pMap;
//Check for existing map and delete
if (m_pMap) delete [] m_pMap;
//Create our map
m_pMap=new DWORD[m_dwMaxOffset];
//Only way out is an exception
if (!m_pMap) throw CEngineException("Out of Memory") ;
//Setup pointers and temp data for asm routine
DWORD *ptrClassMap=m_pMap;
DWORD tempMaxOffset=m_dwMaxOffset;
//32-bit copy from [DS]:ESI to ES:EDI
_asm {
mov esi,[ptrMap]
mov edi,[ptrClassMap]
mov ecx,[tempMaxOffset]
rep movsd
}
} else throw CEngineException("NULL pointer assignment");
//Class is ok
m_bInitFlag=true;
}
//Return this object
return *this;
}
};
enum Directions {
UP=0x01,
DOWN=0x02,
WEST=0x04,
EAST=0x08,
NORTH=0x10,
SOUTH=0x20
};
struct CRoomDescripInfo
{
DWORD dwRoomID;
DWORD dwStartIndex;
DWORD dwEndIndex;
};
class CRoom
{
WORD m_dwID;
CRoomDescripInfo m_RoomInfo;
WORD m_dwExits;
WORD m_dwDoors;
....
};
class CRoomContainer
{
std::vector<CRoom *> m_vRooms;
...
public:
...
void ShowDescription(void);
};
This is the very beginning of a text adventure game. The map class would have a map container that controlled all access to and from the map. Each map could be of varying sizes If the player moves up, he moves down in the vector and down is up in the vector. Doors can be coded by using the Directions enum as well as directional data for each room.
I leave the rest to you.
For the message system, think about how Windows does it.
I wrote this code while sitting here so I'm sure there are bugs in it. This system needs to completely implement the following classes.
CMapContainer
CRoom
CRoomContainer
CObject
CObjectContainer
CCharacter
CCharaterContainer
CParser
CMessageSystem
CMessageSystem 'could' send a message to the CObjectContainer and/or CCharacterContainer which would then route the message to the correct MessageProc of the CObject class or CCharacter class. The message is then processed by the class. This means that the central message system is only responsible for sending messages, not for responding to them. The bulk of the work would be done in each class's MessageProc.
You don't have to worry about function pointers because if the MessageProc is inside of the class and is public, then all CMessageSystem needs is a vector or array of pointers to all objects/characters in the game.
CMessageSystem->CObjectContainer->CObject->CObject::MessageProc()
Like that.