Crystal Space 2.0.0: 4.2.4.5 Maze Class

[ < ] | [ > ] |  | [ << ] | [ 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:

- `‘rooms’`: this is a three dimensional arrays of pointers to `iSector`. Every sector is a node of the three dimensional maze.
- `‘connections’`: this is a three dimensional array of arrays of connections. Basically this means that for every node in the maze we keep an array (represented using a dynamic `csArray`) that holds coordinates of other nodes that are connected to this one.
- `‘occupied’`: to make things simple we avoid having more then one adversary in a single node. This three dimensional array tells us if a certain node is occupied or not.

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 ()
    {
      // 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.