Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Создание ролевой компьютерной игры 11) Программируем ловушки и источники жизни


Информационный Канал Subscribe.Ru

Создание ролевой компьютерной игры


11) Программируем ловушки и источники жизни

Надо ли выделять ловушки в отдельный класс игровых объектов, или же удастся обойтись уже имеющимися наработками? Механизм ловушек достаточно прост - герой встает на некоторый тайл, и здоровье его либо ухудшает, либо растет. Воплотить этот механизм в игре можно, например, добавлением новых тайлов - тайла ловушки и тайла источника, например, "оздоравливающей маны". Срабатывание ловушек будет происходить в момент перемещения героя на эти тайлы.

Добавим в модуль Map две новые константы:

         tileGrass = 1;
         tileGround = 2;
         tileStairsUp = 3;
         tileStairsDown = 4;
         tileTrap = 5;
         tileLive = 6;

         tileFirstStopTile = 7;
         tileTree = tileFirstStopTile;
         tileStone = tileFirstStopTile+1;
         tileLast = tileFirstStopTile+1;

Теперь при попытке компиляции система выдаст сообщение об ошибке, указав на неверную длину массива TileRecords. Это естественно, так как число тайлов в игре изменилось. Мы как раз планировали, чтобы подобные сообщения компилятора выдавались в нужных местах исходного текста при расширении характеристик игры.

Опишем представление новых тайлов:

  const TileRecords: array[1..tileLast] of TTileRecord =
     (
     (C: ' . ' ; Clr:Green),
     (C: ' _ ' ; Clr:Brown),
     (C: ' < ' ; Clr:LightGray),
     (C: ' > ' ; Clr:LightGray),
     (C: ' . ' ; Clr:Green),
     (C: ' * ' ; Clr:LightCyan),
     (C: ' ! ' ; Clr:LightGreen),
     (C: ' ^ ' ; Clr:LightGray)
     );

Здесь символ * будет соответствовать источнику жизни. Обратите внимание, что описание ловушки в точности совпадает с одним из описаний проходимых тайлов. Это необходимо для того, чтобы герой исходно не мог определить, где находится ловушка. Выявление опасных мест будет выполняться с помощью особого умения героя, которое мы реализуем позже, когда займемся концепцией навыков персонажа.

Осталось только распределить их на карте достаточно разумным способом. Как часто мы хотим видеть ловушки на карте? Предположим, желательно, чтобы одна ловушка располагалась на участке 10*10 тайлов. Тогда вероятность появления ловушки на этом участке равна 1/100. Из этого соотношения и будем исходить.

Добавление ловушек на карту будем выполнять в процедуре MapGeneration. В блоке, ответственном за фиксацию проходимого тайла (tileGrass или tileGround), допишем оператор, размещающий с вероятностью 0,01 в текущей точке карты ловушку или источник жизни:

  ...

       begin
       if random(100) < 35
          then GameMap[CurMap].Cells[x,y].Tile := tileTree else
          if random(2) = 0
             then GameMap[CurMap].Cells[x,y].Tile := tileGrass
             else GameMap[CurMap].Cells[x,y].Tile := tileGround;

       if random(100) = 0 then
          if random(2) = 0
             then GameMap[CurMap].Cells[x,y].Tile := tileTrap
             else GameMap[CurMap].Cells[x,y].Tile := tileLive;
       end;

  ...

Теперь запрограммируем момент срабатывания ловушек. Это должно происходить, очевидно, тогда, когда герой наступает на соответствующий тайл. Процесс перемещения героя реализован в процедуре MoveHero. Добавим в конец этой процедуры дополнительную проверку вида тайла, на котором находится персонаж. Если это ловушка, нанесем герою случайное поражение; если это источник, восстановим утраченное здоровье. Чтобы ловушка могла наносить поражение, соответствующее реальному уровню здоровья героя, будем вычислять это поражение как функцию от максимального значения здоровья. Ведь если это поражение будет абсолютным (например, жестко заданные три или пять условных единиц), то по мере совершенствования героя и увеличения его параметров опасность такой ловушки станет уменьшаться. Договоримся, что ловушка может причинить вред, случайно определяемый в диапазоне от единицы до величины максимального здоровья (MaxHP), умноженной на коэффициент 1,1 (он чуть больше единицы, поэтому ловушка потенциально может убить героя).

  if GameMap[CurMap].Cells[ Heroes[CurHero].x,Heroes[CurHero].y].Tile in
  TrapTileSet then
     begin
     GameMap[CurMap].Cells[ Heroes[CurHero].x,Heroes[CurHero].y].Tile :=
  tileGround;
     dam := random( round(Heroes[CurHero].MaxHP * 1.1) ) + 1;
     ShowInfo(STR_TRAP + IntToStr(abs(dam)));
     IncHP( Heroes[CurHero], -dam );
     end;

Мы специально выделили константу-множество тайлов-ловушек TrapTileSet, описанную в модуле Map:

  const TrapTileSet = [tileTrap];

Такой подход удообен тем, что позволяет в дальнейшем свободно добавлять новые тайлы ловушек, специально не запоминая глубоко запрятанное место в программном коде, требующее дополнительного уточнения списка таких тайлов. Все изменения вносятся только в область определения констант.

На место сработавшей ловушки записывается обычный тайл "земли". Далее играющему выдается информационное сообщение о том, что он наступил на ловушку и получил повреждение. Режим информирования пользователя о текущих событиях в виртуальном мире очень популярен. Это неотъемлемая "фича", удобная и ставшая стандартной особенностью всех коммерческих игр. Обычно она реализуется в виде так называемого журнала, причем описания событий в нем могут представлять собой логически связанные и литературно обработанные тексты. Обычно часть из них готовится заранее сценаристами, а какая-то информация генерируется автоматически, причем алгоритмы такой генерации подчас весьма сложны и изощренны. Рекомендую попробовать поэкспериментировать с режимом выдачи человеку сообщений игры.

Сообщение выводится на экран с помощью зависимой от платформы процедуры ShowInfo, единственным параметром которой и является сообщение. Константа STR_TRAP (модуль Texts), составляющая текстовую информацию, может выглядеть так:

  const STR_TRAP = ' Ловушка сработала и наносит поражение ' ;

Величина поражения переводится в текстовый вид с помощью функции IntToStr. Эта функция стандартна в Delphi, однако в TurboPascal такой возможности еще не было, поэтому запрограммируем ее сами, добавив в модуль LowLevel. Отметим, что даже ее описание в интерфейсной части этого модуля должно быть привязано к выбранной целевой платформе, так как в Delphi функция IntToStr уже существует:

  {$I DEFINES.INC}

  unit LowLevel;

  interface uses Map, Monster;

  {$IFDEF DOS_GAME}
  function IntToStr( v: Integer ): string;
  {$ENDIF}

  ...

Это ее ДОС-реализация:

  function IntToStr( v: Integer ): string;
  var s: string;
  begin
  Str(v, s);
  IntToStr := s;
  end;

Процедура вывода сообщения может выглядеть следующим образом:

  procedure ShowInfo(Msg: string) ;
  begin
  GoToXY(1,1);
  TextColor(LightGray);
  Write(Msg,STR_INFO);
  Readln;
  end;

В первой строке печатается текст сообщения. Строка STR_INFO содержит служебную информацию о том, что для продолжения игры надо нажать Enter:

  const STR_INFO = ' (нажмите Enter) ' ;

Дополнительные пробелы в конце нужны, чтобы стереть возможный хвост старого, более длинного сообщения. Нажатие клавиши требуется для того, чтобы человек успел прочитать текст, в противном случае выведенная на экран информация может быть затерта новыми сообщениями.

В заключение продолжим корректировку процедуры MoveHero - вычислим величину поражения героя и вызовем процедуру IncHP, изменяющую текущий уровень здоровья героя. Ее мы поместим в модуль Hero:

  procedure IncHP( var H: THero; dam: Integer );
  begin
  H.HP := H.HP + dam;
  if H.HP <= 0 then HeroDied;
  if H.HP > H.MaxHP then H.HP := H.MaxHP;
  end;

Рассмотрим данный код более подробно. Вначале здоровье героя (соответствующий объект передается по ссылке) изменяется на величину, хранящуюся в переменной dam. Если результирующее здоровье героя меньше или равно нулю, что означает, что он мертв, вызовем процедуру HeroDied, которая должна сообщить играющему о гибели персонажа и завершить работу программы. В случае, если здоровье выше допустимого для текущего уровня героя значения, исправим его на более реалистичное значение.

Чтобы программа после внесения всех изменений вновь стала работоспособной, нам остается написать текст процедуры HeroDied. Так как в ней должно происходить завершение программы, вынесем ее в модуль LowLevel:

  procedure HeroDied;
  begin
  ShowInfo(STR_HERO_DIED);
  Readln;
  ClrScr;
  Halt;
  end;

Играющий получает сообщение о смерти героя, нажимает на Enter, после чего программа завершает работу. Константа STR_HERO_DIED описывается, как и другие текстовые константы, в модуле Texts:

  const STR_HERO_DIED = ' Герой погиб! ' ;

Откомпилируем и запустим программу. Побродив по игровому пространству, рано или поздно можно натолкнуться на ловушку и получить повреждение.

Но помимо ловушек мы разместили на карте еще и источники жизни. Когда герой встает на такой тайл, его здоровье увеличивается. Пусть оно увеличивается до максимума, потому что мы не хотим, чтобы источник жизни пропадал после использования. Если же он остается на месте, то не имеет смысла ограничивать величину восстановления - ведь ничто не мешает герою заходить на тайл источника повторно, до полного оздоровления. Допишем проверку тайла, на который вступил герой, следующим образом:

  if GameMap[CurMap].Cells[ Heroes[CurHero].x,Heroes[CurHero].y].Tile in
  LiveTileSet then
     begin
     ShowInfo(STR_LIVE);
     IncHP( Heroes[CurHero], Heroes[CurHero].MaxHP );
     end;

Список тайлов-источников выглядит так (модуль Map):

  const LiveTileSet = [tileLive];

Сообщение об оздоровлении (модуль Texts) - так:

  const STR_LIVE = ' Вы восстановили здоровье в Источнике. ' ;

Здоровье увеличивается на максимальное значение - с запасом. Оно будет исправлено на допустимое значение в процедуре IncHP автоматически. Потестируйте программу - походите по карте, подрываясь на ловушках и восстанавливая здоровье в источниках жизни.

Исходный код текущей версии (всегда проверен и работоспособен, главный файл- main.pas):

http://russianenterprisesolutions.com/sbo/download/4105.zip 5420 байтов

Далее - программируем инвентарь и игровые предметы.


(c) 2004-2005 Сергей Бобровский bobrovsky@russianenterprisesolutions.com

Школа программирования с нуля
Все предыдущие выпуски базового курса тут:
http://russianenterprisesolutions.com/sbo/

Дизайн рассылки: Алексей Голубев - Web-дизайн и web-программирование


Subscribe.Ru
Поддержка подписчиков
Другие рассылки этой тематики
Другие рассылки этого автора
Подписан адрес:
Код этой рассылки: comp.soft.prog.prognull.game
Архив рассылки
Отписаться
Вспомнить пароль

В избранное