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

Программирование с нуля - это совсем просто! 74) Выявляем ловушки, повышаем навыки


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

Школа программирования

Разработка ролевой игры

74) Выявляем ловушки, повышаем навыки

Выявляем ловушки

По мере своего развития герой должен постепенно повышать свои навыки и умения. Пока мы не занимались реализацией концепции навыков, и сейчас настала пора это сделать. А начнем мы с мирного и полезного навыка обнаружения ловушек. Ранее мы уже подготовили для этого навыка константу skillTrapSearch, определяющую индекс текущего значения умения обнаруживать ловушки в массиве Skills. В качестве начального значения данного навыка был занесен ноль (процедура InitHero). Правильно ли это? Не совсем. Ведь в момент подготовки процедуры InitHero мы еще ничего не знали о навыках и просто проинициализировали все поля структуры THero - инициализацию любых данных всегда лучше делать явно, даже если неизвестно, как и где будут они применяться. Теперь же нам надо заполнить подходящие элементы массива Skills более осмысленно.

Что будет в программе означать некоторое умение? Как правило, уровень умения в игровых системах связан с вероятностью успешно осуществить связанное с умением действие. Чем выше уровень умения, тем выше вероятность того, что попытка выполнить соответствующее действие закончится удачей. В нашем случае мы непосредственно отразим в массиве Skills вероятности реализуемых навыков - они будут равняться величинам от нуля до ста и станут означать вероятность или процент удачи. Так, значение 30 в элементе Skills[skillTrapSearch] говорит о том, что с вероятностью 30% попытка обнаружить скрытую ловушку будет успешна - и соответственно с вероятностью 70% завершится провалом.

Принципиально важно определить начальные значения навыков героя. В игровых системах для этого используются весьма сложные алгоритмы, связывающие профиль всех начальных параметров с автоматически генерируемой (или создаваемой сценаристами) предысторией героя, а также его характеристиками (чем умнее герой, тем легче ему даются навыки владения магией, а чем он сильнее, тем проще ему управляться с оружием). Большое значение имеет и общая направленность героя, или так называемый класс. В популярной системе Dungeons & Dragons каждый игрок должен обязательно принадлежать одному из жестко заданных классов (воин, маг, стрелок, вор, бард), а классы в значительной степени отличаются распределением начальных значений всевозможных навыков и умений. Не менее сильное влияние на это распределение оказывает и раса героя. Так, гномы традиционно сильны в рукопашном бою, мастерски владеют мечами и топорами, эльфы отлично стреляют, зорко выслеживают врага и возможные ловушки, из них получаются неплохие волшебники. Люди обычно универсальны - их характеристики усреднены и поэтому могут совершенствоваться в самых разных направлениях. Столь гибкие и богатые выборы начальных характеристик героя позволяют прежде всего отыгрывать каждую партию в неповторимом режиме, прикладывая и развивая самые разные комбинации навыков. Это придает игре завлекательную глубину и красочность.

Мы пока не стали вводить в нашу программу концепции классов и рас, хотя сделать это не сложно. Достаточно расширить запись THero новыми полями (например, HeroRace и HeroClass), позволить играющему самостоятельно выбрать начальные значения расы и класса, а потом программно задать подходящий профиль навыков. Остановимся на принципах начальной инициализации характеристик героя, а развитие программы в направлении полноценной ролевой игры оставим на потом.

Изменения, связанные с начальными значениями массива Skils, внесем в процедуру InitHero. Все константы мы договорились хранить в одном месте - модуле Tables. Добавим туда новый массив, в который поместим начальные значения навыков героя:

  const BaseSkill_Table: array[1..MaxSkills] of Integer =
    (
    30, 20
    );

Значение 30 (процентов) соответствует навыку рукопашного боя с оружием, а значение 20% - навыку обнаружения ловушек. Тогда оператор инициализации массива Skills в процедуре InitHero перепишется так:

  ...
    for i := 1 to MaxSkills do
      Skills[i] := BaseSkill_Table[i];
  ...

В какой момент надо применять данное умение? Оно будет полезно, когда герой вступил на тайл, где имеется скрытая ловушка. Такая проверка выполняется в процедуре MoveHero (модуль Game). Возможный вариант тестирования навыка может быть запрограммирован следующим образом:

  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;
     if not SkillTest(Heroes[CurHero], skillTrapSearch) then
        begin
        dam := random( round(Heroes[CurHero].MaxHP * 1.1) ) + 1;
        ShowInfo(STR_TRAP + IntToStr(abs(dam)));
        IncHP( Heroes[CurHero], -dam );
        end;
     end;

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

Теперь рассмотрим, как устроена функция SkillTest. Мы поместим ее в модуль Hero. В SkillTest будет реализовано сразу несколько важнейших игровых принципов. Если компилятор выдаст ошибку, проверьте, добавлены ли в Implementation-разделы ссылки на нужные подключаемые модули!

  function SkillTest( var H: THero; skl: Integer ): Boolean;
  var xp: Integer;
  begin
  SkillTest := false;
  if random(100)+1 > H.Skills[skl] then Exit;
  SkillTest := true;

  case skl of

     skillTrapSearch:
         begin
         ShowInfo(STR_TRAPOK);
         IncXP(H, H.Level + random(H.Level));
         SuccessSkillTest(H, skillTrapSearch);
         end;

  end;
  end;

Если случайное значение, выпавшее в диапазоне 1..100, будет больше значения нашего навыка, значит, его проверка неудачна, и функция завершает свою работу. Если же достигнут успех, то будут выполнены следующие действия: пользователь увидит сообщение об обезвреженной ловушке; повысится количество опыта героя (при этом возможен переход на новый уровень); повысится значение успешно примененного навыка.

Сообщение STR_TRAPOK разместим, как обычно, в модуле Texts:

  const STR_TRAPOK = ' Вы обезвредили ловушку! ' ;

Процедуру IncXP (увеличить опыт) реализуем в текущем модуле Hero.

  procedure IncXP( var H: THero; axp: Integer );
  begin
  inc( H.Exp, axp );
  ShowInfo(STR_ADD_EXP + IntToStr(axp));
  if H.Exp > H.MaxExp then
     begin
     H.Exp := 0;
     end;
  end;

При этом момент, когда набранное количество опыта при повышении уровня героя обнуляется, не совсем корректен. Более точным будет подход, когда излишек (превышение значения XP над текущим максимальным значением MaxXP) не исчезнет, а просто откорректируется в соответствии с пропорцией старого и нового значений MaxXP. Оставляем эту задачку читателям.

Повышение уровня мастерства реализуется в процедуре SuccessSkillTest. Она записывается в модуле Hero и должна повышать уровень определенного навыка. Однко реализация этого повышения подразумевает увеличение значения соответствующего элемента массива Skills. Это значение нельзя увеличивать каждый раз после успешной попытки реализации навыка, потому что оно быстро вырастет выше 100 (процентов), и совершенствование потеряет смысл. Можно сделать так: задать тип элементов Skills не Integer, а Real, и повышать значение не на 1 (минимально допустимое целое положительное значение в Integer), а на любую небольшую величину - например, 0.001, подобрав с его помощью оптимальный темп роста навыка. Хотя этот вариант во всех отношениях неплохой, мы поступим по другому - просто для того, чтобы продемонстрировать еще один способ. Увеличение навыка на 1 (один процент) будет происходить не каждый раз, а случайно. Главное - правильно подобрать вероятность повышения, чтобы рост мастерства происходил не слишком быстро и не слишком медленно. Прикинем, каким может быть такая вероятность. На карте у нас в среднем 32*32 / 100 = 10 ловушек, так как одна ловушка размещается на участке 10 на 10 тайлов. Глубину лабиринта мы выбрали равной четырем локациям (константа MaxDungeonLevel), таким образом герой теоретически может выполнить 10*4 = 40 успешных тестов навыка обнаружения ловушек. В принципе начальное значение 20 навыка skillTrapSearch допускает повышение на один процент, так как даже абсолютный мастер поиска ловушек в таком случае сможет повысить свой навык (вероятность успешного обнаружения) до 60%, что не так уж и много. Однако если глубина пещеры увеличится, например, до 40 уровней (такой порядок типичен для популярных бродилок), герой сможет 400 раз выполнять проверку. Поэтому в демонстрационных целях ограничимся предельным значением навыка, равным 40. То есть 40 тестов должны будут принести рост навыка на 20 единиц, или вероятность такого увеличения составит 20/40 = 0,5 или 50%. Итак, будем увеличивать навык обнаружения ловушек на 1 случайным образом, с вероятностью 50%:

  procedure SuccessSkillTest( var H: THero; skl: Integer);
  var rnd: Integer;
  begin
  case skl of

     skillTrapSearch:
         begin
         rnd := round(20 / MaxDungeonLevel*100);
         if random(100)+1 <= rnd then
            begin
            ShowInfo(STR_TRAPSKILL_OK);
            inc(H.Skills[skillTrapSearch]);
            end;
         end;
  end;
  end;

Это текстовая константа (модуль Texts):

  const STR_TRAPSKILL_OK = ' Навык обезвреживания ловушек
  повышен. ' ;

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

Далее - наконец, :) добавляем герою рюкзак и инвентарь.

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

http://russianenterprisesolutions.com/sbo/download/18105.zip 6699 байтов


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

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

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


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

В избранное