Digs - Персональная территория

Авторский проект Артема Глазкова
? 
        Версия для печати (цвет)  

Использование материалов
Заметка #36
30 апреля 2012

Комнаты



    Комнаты будут простые, прямоугольные.

    Способ отображения лабиринта практически такой же, как и в предыдущих заметках. Карта сначала вычищается от стен (за исключением границ карты), а затем строятся комнаты. Но не только внутренняя часть, но и окружающие комнату стены. Не тронутое пространство, для наглядности, отображается зеленым узором.

    Для собственного удобства написал небольшую библиотеку функций, на основе которой делалась генерация. Поэтому сначала небольшое введение в работу библиотеки, а потом сам код построения.

    Для работы с картой введена структура, называемая курсор (TmgCursor). Курсор характеризуется координатой на карте, а также вектором направления. Через свойство Value курсора можно устанавливать или запрашивать значение в ячейке карты. Курсор сам по себе не перемещается. Но с помощью его методов можно создавать новые курсоры. Есть четыре пары методов. Первые (Ahead, Back, Left, Right) создают новый курсор, путем сдвига текущего в одном из четырех направлений и при этом направление становится вектором для нового курсора. Другие четыре (CAhead, CBack, CLeft, CRight) также сдвигают, но направление остается как у породившего курсора.

    Помимо этого курсор имеет функцию (rndDist), возвращающую случайную дистанцию в заданном интервале в направлении вектора курсора и с учетом не пересечения границ карты.

    Объект управления картой умеет создавать новый курсор (NewCursor), заливать область карты некоторым кодом (Fill), и проверять, свободна ли заданная область (isFilled).

    Основной список (p) будет хранить курсоры, которые будут определять положение дверного проема и направление, в котором относительно проема будет располагаться комната.

    Вначале определяется три значения: ширина комнаты влево от проема (w1), ширина вправо от проема (w2) и длина комнаты (L). Далее делается проверка, можно ли поместить такую комнату. Есть да, то рисуется комната, в точке курсора устанавливается проем (если это не первая точка). После этого на трех оставшихся стенах случайно выбираются точки для следующих проемом, и помещаются в новый список. Этот список (np), после работы цикла, станет основным. Создавать или не создавать проемы, также подвержено случайности.

    Помимо комнат будем делать коридоры. Коридор отличается от комнаты тем, что имеет ширину, равную проему (т.е. параметры w1 и w2 равны нулю) и не имеет проемов на правой и левой стенке.

   Процедура добавления проема:

  procedure add_np(C: TmgCursor);
  begin
    //cor – флаг, обозначающий, что сейчас делается коридор
    if cor then C.Tag := 1 else C.Tag := 0;
    if (c.x>1)and(c.x<map.SizeX*2-1)and(c.y>1)and(C.y<map.SizeY*2-1) then begin
      //Если создается коридор, или комнат пока меньше 10,
      //то проем добавляется всегда. Иначе с вероятностью 0.5
      if cor or (Random(10)>5) or (Rooms<10) then begin
        np.Add(C);
        Inc(Rooms);
      end;
    end;
  end;

    Сама процедура генерации:

    First := map.NewCursor((map.SizeX and $FFFE)+1,(map.SizeY and $FFFE),0,1);
    First.tag := 1;
    p.Add(First);
    //до тех пор, пока проемы не кончились
    while p.Count>0 do begin
      for i:=0 to p.Count-1 do begin
        c := p[i];
        //на основе tag и с помощью random определяем
        //будем рисовать коридор или комнату
        if C.tag=0 then cor := Random(10)>8 else cor := false;
        if Cor then begin w1 := 0; w2 := 0; c.RndDist(1,3,L); end
        else begin
          //находим направление влево и получаем случайную ширину
          c.Left.RndDist(1,4,w1);
          //тоже самое вправо
          c.Right.RndDist(1,4,w2);
          //длина комнаты от 1 до 3
          c.RndDist(0,2,L);
        end;
        //клетка может иметь стену, если хотя бы одна кооридината нечетная
        //поэтому ширину увеличиваем в два раза.
        //тем самым в c1 и c2 получаем координаты внутренних углов комнаты.
        c1 := c.CLeft(w1*2).AHead;
        c2 := c.CRight(w2*2).AHead(L*2+1);
        //проверяем, есть ли пространство для комнаты
        if map.IsFilled(c1,c2,0) then begin
          //заливаем внешнее пространство комнаты стенами
          map.Fill(c1.CBack.CLeft,c2.CRight.CAhead,1);
          //потом вычищаем внутреннее
          map.Fill(c1,c2,2);
          //если не первая точка, то ставим проем
          if not c.cmp(First) then c.Value := 2;
          //добавляем в новый список будущие проемы
          if not cor then begin
            add_np(c1.Left.CRight(Random(L+1)*2));
            add_np(c2.Right.CRight(Random(L+1)*2));
          end;
          add_np(c2.Ahead.CLeft(Random(w1+w2)*2));
        end;
      end;
      //подготавливаем для работы новый список
      p.Assign(np);
      np.Count := 0;
    end;


© 2005-16, Powered By Digs (Написать письмо, vk)