[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2.4.5 Maze Class

The Maze class is responsible for managing the game world. To do that it keeps track of a three dimensional grid of sectors. Every sector represents a node in the maze (a room). Nodes are connected using portals. Here is the class declaration:

 
class Maze
{
private:
  AppMazing* app;
  iSector* rooms[MAZE_DIMENSION][MAZE_DIMENSION][MAZE_DIMENSION];
  csArray<RoomCoordinate> connections[MAZE_DIMENSION][MAZE_DIMENSION]
  	[MAZE_DIMENSION];
  bool occupied[MAZE_DIMENSION][MAZE_DIMENSION][MAZE_DIMENSION];

public:
  Maze (AppMazing* app);

  iSector* GetSector (int x, int y, int z) const
  {
    return rooms[x][y][z];
  }
  iSector* GetSector (const RoomCoordinate& rc) const
  {
    return rooms[rc.x][rc.y][rc.z];
  }
  bool CreateSector (int x, int y, int z);

  const csArray<RoomCoordinate>& GetConnections (const RoomCoordinate& rc) const
  {
    return connections[rc.x][rc.y][rc.z];
  }

  void MakeConnection (const RoomCoordinate& from, const RoomCoordinate& to);

  void FreeSpace (const RoomCoordinate& rc);
  void OccupySpace (const RoomCoordinate& rc);
  bool IsSpaceFree (const RoomCoordinate& rc) const;

  bool CreateGeometry ();
  bool CreateWallOrPortal (iGeneralFactoryState* factory_state,
  	const csVector3& v1, const csVector3& v2,
  	const csVector3& v3, const csVector3& v4,
	CS::Geometry::TextureMapper* mapper,
	bool do_portal,
	const RoomCoordinate& source,
	const RoomCoordinate& dest);
  bool CreateRoom (iMaterialWrapper* wall_material,
  	int x, int y, int z,
	char* portals);
  bool CreateLight (const csColor& color,
  	int x, int y, int z);
};

The important data structures here are:

The most important setup function in this class is CreateGeometry(). That one will do the actual creation of the maze. It is called from within the main applications SetupGame() method.

I will show here the code of the Maze class in parts.

 
Maze::Maze (AppMazing* app)
{
  Maze::app = app;
  int x, y, z;
  for (x = 0 ; x < MAZE_DIMENSION ; x++)
    for (y = 0 ; y < MAZE_DIMENSION ; y++)
      for (z = 0 ; z < MAZE_DIMENSION ; z++)
        occupied[x][y][z] = false;
}

void Maze::MakeConnection (const RoomCoordinate& from, const RoomCoordinate& to)
{
  CS_ASSERT (from.IsValid ());
  CS_ASSERT (to.IsValid ());
  connections[from.x][from.y][from.z].Push (to);
}

void Maze::FreeSpace (const RoomCoordinate& rc)
{
  CS_ASSERT (rc.IsValid ());
  occupied[rc.x][rc.y][rc.z] = false;
}

void Maze::OccupySpace (const RoomCoordinate& rc)
{
  CS_ASSERT (rc.IsValid ());
  occupied[rc.x][rc.y][rc.z] = true;
}

bool Maze::IsSpaceFree (const RoomCoordinate& rc) const
{
  CS_ASSERT (rc.IsValid ());
  return !occupied[rc.x][rc.y][rc.z];
}

First there is the constructor which simply initializes the ‘occupied’ table so that all nodes are free. MakeConnection() will make a connection between two nodes. It is called during the creation of the world. The other functions are for managing the occupied space used by adversaries.

The use of the ‘CS_ASSERT’ macro is interesting. This macro will evalulate the expression and cause the program to abort (even print out a callstack) at runtime when the expression is not true. That means that you can use this to validate that things are as they should be. This macro only operates in debug mode. If your program is compiled in optimize/release mode it will do nothing. So feel free to use it as much as you can. It may help find bugs when the program is compiled in debug mode.

 
bool Maze::CreateSector (int x, int y, int z)
{
  CS_ASSERT (RoomCoordinate::IsValid (x, y, z));
  char sector_name[100];
  sprintf (sector_name, "room_%d_%d_%d", x, y, z);
  rooms[x][y][z] = app->GetEngine ()->CreateSector (sector_name);
  return rooms[x][y][z] != 0;
}

bool Maze::CreateWallOrPortal (iGeneralFactoryState* factory_state,
  	const csVector3& v1, const csVector3& v2,
  	const csVector3& v3, const csVector3& v4,
	CS::Geometry::TextureMapper* mapper,
	bool do_portal,
	const RoomCoordinate& source,
	const RoomCoordinate& dest)
{
  if (do_portal)
  {
    iPortal* portal;
    csVector3 verts[4];
    verts[0] = v1;
    verts[1] = v2;
    verts[2] = v3;
    verts[3] = v4;
    csRef<iMeshWrapper> portal_mesh = app->GetEngine ()->CreatePortal (0,
    	GetSector (source), csVector3 (0, 0, 0),
	GetSector (dest),
    	verts, 4, portal);
    MakeConnection (source, dest);
    if (!portal_mesh)
      return app->ReportError ("Error creating portal mesh!");
  }
  else
  {
    CS::Geometry::TesselatedQuad (v1, v2, v4);
    quad.SetLevel (5);
    quad.SetMapper (mapper);
    quad.Append (factory_state);
  }
  return true;
}

bool Maze::CreateRoom (iMaterialWrapper* wall_material,
	int x, int y, int z, char* portals)
{
  iSector* room = GetSector (x, y, z);

  using namespace CS::Geometry;
  csRef<iMeshFactoryWrapper> walls_factory = GeneralMeshBuilder::
    CreateFactory (app->GetEngine (), "walls_factory");
  csRef<iMeshWrapper> walls = GeneralMeshBuilder::CreateMesh (
      app->GetEngine (), room, "walls", walls_factory);
  if (!walls)
    return app->ReportError ("Couldn't create the walls for the room!");

  csRef<iGeneralMeshState> object_state = scfQueryInterface<
    iGeneralMeshState> (walls->GetMeshObject ());
  object_state->SetShadowReceiving (true);
  walls->GetMeshObject ()->SetMaterialWrapper (wall_material);
  csRef<iGeneralFactoryState> factory_state = scfQueryInterface<
    iGeneralFactoryState> (walls_factory->GetMeshObjectFactory ());

  DensityTextureMapper mapper (.3);

  float sx = float (x) * ROOM_DIMENSION;
  float sy = float (y) * ROOM_DIMENSION;
  float sz = float (z) * ROOM_DIMENSION;
  float rd = ROOM_DIMENSION / 2.0;
  csBox3 box (
  	csVector3 (sx-rd, sy-rd, sz-rd),
  	csVector3 (sx+rd, sy+rd, sz+rd));

  RoomCoordinate start_rc (x, y, z);
  if (!CreateWallOrPortal (factory_state, 
    	box.GetCorner (CS_BOX_CORNER_xYz),
    	box.GetCorner (CS_BOX_CORNER_XYz),
    	box.GetCorner (CS_BOX_CORNER_XYZ),
    	box.GetCorner (CS_BOX_CORNER_xYZ),
	&mapper,
	portals[PORTAL_UP] == '#',
	start_rc, RoomCoordinate (x+0, y+1, z+0)))
    return false;
  if (!CreateWallOrPortal (factory_state,
    	box.GetCorner (CS_BOX_CORNER_xyZ),
    	box.GetCorner (CS_BOX_CORNER_XyZ),
    	box.GetCorner (CS_BOX_CORNER_Xyz),
    	box.GetCorner (CS_BOX_CORNER_xyz),
	&mapper,
	portals[PORTAL_DOWN] == '#',
	start_rc, RoomCoordinate (x+0, y-1, z+0)))
    return false;
  if (!CreateWallOrPortal (factory_state,
    	box.GetCorner (CS_BOX_CORNER_XyZ),
    	box.GetCorner (CS_BOX_CORNER_xyZ),
    	box.GetCorner (CS_BOX_CORNER_xYZ),
    	box.GetCorner (CS_BOX_CORNER_XYZ),
	&mapper,
	portals[PORTAL_FRONT] == '#',
	start_rc, RoomCoordinate (x+0, y+0, z+1)))
    return false;
  if (!CreateWallOrPortal (factory_state,
    	box.GetCorner (CS_BOX_CORNER_xyz),
    	box.GetCorner (CS_BOX_CORNER_Xyz),
    	box.GetCorner (CS_BOX_CORNER_XYz),
    	box.GetCorner (CS_BOX_CORNER_xYz),
	&mapper,
	portals[PORTAL_BACK] == '#',
	start_rc, RoomCoordinate (x+0, y+0, z-1)))
    return false;
  if (!CreateWallOrPortal (factory_state,
    	box.GetCorner (CS_BOX_CORNER_xyZ),
    	box.GetCorner (CS_BOX_CORNER_xyz),
    	box.GetCorner (CS_BOX_CORNER_xYz),
    	box.GetCorner (CS_BOX_CORNER_xYZ),
	&mapper,
	portals[PORTAL_LEFT] == '#',
	start_rc, RoomCoordinate (x-1, y+0, z+0)))
    return false;
  if (!CreateWallOrPortal (factory_state,
    	box.GetCorner (CS_BOX_CORNER_Xyz),
    	box.GetCorner (CS_BOX_CORNER_XyZ),
    	box.GetCorner (CS_BOX_CORNER_XYZ),
    	box.GetCorner (CS_BOX_CORNER_XYz),
	&mapper,
	portals[PORTAL_RIGHT] == '#',
	start_rc, RoomCoordinate (x+1, y+0, z+0)))
    return false;

  return true;
}

bool Maze::CreateLight (const csColor& color,
  	int x, int y, int z)
{
  float sx = float (x) * ROOM_DIMENSION;
  float sy = float (y) * ROOM_DIMENSION;
  float sz = float (z) * ROOM_DIMENSION;
  csRef<iLight> light = app->GetEngine ()
  	->CreateLight (0, csVector3 (sx, sy, sz), ROOM_DIMENSION * 1.5, color);
  if (!light) return false;
  GetSector (x, y, z)->GetLights ()->Add (light);
  return true;
}

bool Maze::CreateGeometry ()
{
  // We don't need a lighting cache. Disable it.
  app->GetEngine ()->SetLightingCacheMode (0);

  // Load the texture we are going to use for all walls.
  if (!app->GetLoader ()->LoadTexture ("wall_texture", "/lib/std/stone4.gif"))
    return app->ReportError ("Error loading 'stone4' texture!");

  iMaterialWrapper* wall_material = app->GetEngine ()->GetMaterialList ()
  	->FindByName ("wall_texture");

  int x, y, z;
  for (x = 0 ; x < MAZE_DIMENSION ; x++)
    for (y = 0 ; y < MAZE_DIMENSION ; y++)
      for (z = 0 ; z < MAZE_DIMENSION ; z++)
        if (!CreateSector (x, y, z))
	  return false;

  if (!CreateRoom (wall_material, 0, 0, 0, "....#.")) return false;
  if (!CreateRoom (wall_material, 0, 0, 1, "....##")) return false;
  if (!CreateRoom (wall_material, 0, 0, 2, "#..#.#")) return false;
  if (!CreateRoom (wall_material, 1, 0, 0, "...##.")) return false;
  if (!CreateRoom (wall_material, 1, 0, 1, "....##")) return false;
  if (!CreateRoom (wall_material, 1, 0, 2, "..#..#")) return false;
  if (!CreateRoom (wall_material, 2, 0, 0, "#.#.#.")) return false;
  if (!CreateRoom (wall_material, 2, 0, 1, "....##")) return false;
  if (!CreateRoom (wall_material, 2, 0, 2, ".....#")) return false;

  if (!CreateRoom (wall_material, 0, 1, 0, "#..#..")) return false;
  if (!CreateRoom (wall_material, 0, 1, 1, "...##.")) return false;
  if (!CreateRoom (wall_material, 0, 1, 2, ".#.#.#")) return false;
  if (!CreateRoom (wall_material, 1, 1, 0, "..#.#.")) return false;
  if (!CreateRoom (wall_material, 1, 1, 1, "#.#..#")) return false;
  if (!CreateRoom (wall_material, 1, 1, 2, "..##..")) return false;
  if (!CreateRoom (wall_material, 2, 1, 0, ".#..#.")) return false;
  if (!CreateRoom (wall_material, 2, 1, 1, "....##")) return false;
  if (!CreateRoom (wall_material, 2, 1, 2, "#.#..#")) return false;

  if (!CreateRoom (wall_material, 0, 2, 0, ".#.#..")) return false;
  if (!CreateRoom (wall_material, 0, 2, 1, "...#..")) return false;
  if (!CreateRoom (wall_material, 0, 2, 2, "...#..")) return false;
  if (!CreateRoom (wall_material, 1, 2, 0, "..##..")) return false;
  if (!CreateRoom (wall_material, 1, 2, 1, ".###..")) return false;
  if (!CreateRoom (wall_material, 1, 2, 2, "..##..")) return false;
  if (!CreateRoom (wall_material, 2, 2, 0, "..#...")) return false;
  if (!CreateRoom (wall_material, 2, 2, 1, "..#...")) return false;
  if (!CreateRoom (wall_material, 2, 2, 2, ".##...")) return false;

  if (!CreateLight (csColor (1, 0, 0), 0, 0, 0)) return false;
  if (!CreateLight (csColor (0, 0, 1), 0, 0, 2)) return false;
  if (!CreateLight (csColor (0, 1, 0), 1, 0, 1)) return false;
  if (!CreateLight (csColor (1, 1, 0), 1, 1, 1)) return false;
  if (!CreateLight (csColor (0, 1, 1), 0, 1, 1)) return false;
  if (!CreateLight (csColor (1, 1, 1), 2, 1, 2)) return false;
  if (!CreateLight (csColor (1, 0, 0), 1, 2, 1)) return false;
  if (!CreateLight (csColor (0, 0, 1), 0, 2, 0)) return false;
  if (!CreateLight (csColor (0, 1, 0), 2, 2, 0)) return false;

  return true;
}

These functions create the actual geometry of the world. The main function here is CreateGeometry(). This function will first load the wall texture and then it will create all the sectors for the maze. After that it will call CreateRoom() to create one of the nodes in the maze. The final parameter of CreateRoom() is a string describing the connections to neighbour nodes. Finally this function will also create a few lights using the CreateLight() method.

The CreateRoom() function will create one genmesh mesh (see section Genmesh Mesh Object) for the solid walls and then it will use iEngine::CreatePortal() to create the individual portals to neighbour nodes wherever there needs to be a connection. This is done in the CreateWallOrPortal() function. This function will either add a wall polygon to the wall genmesh or else it will create a portal.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

This document was generated using texi2html 1.76.