Let's put the source code here. It's not a complete one, but hopefully easy to figure out missing part which should be Coords (class with x, y variables) and K_Rect (class with x, y, w, h) and rng.
header:
#include "classes/coords.h"
class Level;
struct K_Rect;
class Drunkard
{
private:
Level *cave;
Coords location;
int direction;
int floor_tile;
bool sober;
int steps;
int Get_Edge_Direction();
bool Sober_Up();
void Think();
void Turn();
public:
Drunkard(Level *c, int percentage, int ft);
};
source:
#include "classes/kdir.h"
#include "drunkard.h"
#include "level.h"
#include "teemu.h"
#include "terrtype.h"
Drunkard::Drunkard(Level *c, int percentage, int ft)
{
cave=c;
floor_tile=ft;
int cleared_tiles=0;
const int bt=cave->lvltheme.Get_Base_Tile(); //level's wall tile
const int width=cave->Get_Width();
const int height=cave->Get_Height();
//count amount of tiles needed to clear
const int sz=width*height;
const double p=(double)percentage/100.0;
const int required_tiles=(int)(sz*p);
//set a safety limit after which the generation stops
const int max_steps=required_tiles*10;
//set start location at center of the level +/- 5 tiles
location=cave->Get_Random_Center(5);
direction=-1;
Turn(); //get first random direction
steps=-1;
Think(); //select initial walking mode
//walk until required number of tiles are cleared
int panic_steps=0;
while (cleared_tiles<required_tiles)
{
if (cave->Get_Terrain(location)==bt)
{
cave->Put_Terrain(location, floor_tile);
cleared_tiles++;
}
Think();
if (sober)
{
if (Get_Edge_Direction()!=-1) sober=false;
}
if (sober==false) Turn();
location.Move_Direction(direction);
//use hard limit check to preserve edges of level
if (location.x<1) location.x++;
else if (location.x>=width-1) location.x--;
if (location.y<1) location.y++;
else if (location.y>=height-1) location.y--;
//count all steps and panic exit if max reached
panic_steps++;
if (panic_steps>max_steps) break;
}
}
int Drunkard::Get_Edge_Direction()
{
//turn around when reaching edge area of level
static const int Edge_Size=5;
int sd=-1;
if (location.y<Edge_Size) sd=D_South;
else if (location.y>cave->Get_Height()-Edge_Size) sd=D_North;
if (location.x<Edge_Size) sd=D_East;
else if (location.x>cave->Get_Width()-Edge_Size) sd=D_West;
return sd;
}
bool Drunkard::Sober_Up()
{
//don't sober up at edges of the level
if (Get_Edge_Direction()!=-1) return false;
//start by centering a rectangle on the location
K_Rect r(location.x-4, location.y-4, 9, 9);
//move the rectange from the origin and extend to current direction
switch (direction)
{
case D_North: r.y-=4; break;
case D_South: r.y+=4; break;
case D_West: r.x-=4; break;
case D_East: r.x+=4; break;
default: break;
}
//max 25% of tiles can be floor tiles
const int min_amount=r.Get_Size()/4;
int a=0;
//check the area ahead if it's empty enough to create a corridor
Coords c;
for (c.y=r.y; c.y<=r.y+r.h; c.y++)
{
for (c.x=r.x; c.x<=r.x+r.h; c.x++)
{
int tt=cave->Get_Terrain(c);
//outside level, don't sober up
if (tt==0) return false;
if (tt==floor_tile) a++;
//too many floor tiles, don't sober up
if (a>min_amount) return false;
}
}
return true;
}
void Drunkard::Think()
{
if (steps<=0)
{
if (Sober_Up() && rng->Sometimes())
{
sober=true;
//corridor steps
steps=rng->Get_Min_Max(5, 10);
}
else
{
sober=false;
//cavern steps
steps=rng->Get_Min_Max(20, 30);
}
}
else steps--;
}
void Drunkard::Turn()
{
int d=direction;
//select another direction until it's different than original
while (d==direction)
{
d=rng->Get_Random_Cardinal_Direction();
}
//try to limit the creation to edges of the level
int sd=Get_Edge_Direction();
//make it slightly random to avoid too blunt limit
if (sd!=-1 && rng->Flip_Coin()) d=sd;
direction=d;
}