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

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


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

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

22) Раса и класс героя

Надо еще добавить герою характеристики мудрости chrWIS и харизмы chrCHA:

  const chrSTR = 1;
        chrDEX = 2;
        chrCON = 3;
        chrIQ = 4;
        chrWIS = 5;
        chrCHA = 6;

Также, впрок - определим класс Мага:

          classWarrior = 1;
          classRanger = 2;
          classMage = 3;

(в архиве игры учтены всевозможные мелкие модификации, связанные с добавлением нового класса и новых характеристик)

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

Раса будет влиять на распределение базовых параметров героя. Самый простой способ - это в зависимости от расы определить диапазон значений каждого навыка, после чего случайно его генерировать. Для этого в модуле Tables подготовим массив, в котором для каждого из базовых параметров каждой из рас буде храниться пара чисел - начальное значение навыка и максимальная величина, на которую он может случайно вырасти. "Внутри" этой величины распределение вероятности будем считать равномерным.

Такой массив будет трехмерным и может быть описан так:

  const Race_Table: array[ raceHuman..raceHobbit, chrSTR..chrCHA, 1..2 ]
        of Integer =

Вот возможный набор его значений:

  (
  ((12,4),(12,4),(12,4),(12,4),(12,4),(10,4)),
  ((11,3),(16,2),(11,3),(14,3),(13,3),(13,2)),
  ((16,2),(12,3),(16,2),(9,1),(10,2),(7,3)),
  ((10,2),(13,1),(9,2),(16,2),(15,3),(15,3))
  );

Что за числа выбраны здесь в качестве значений? Мы воспользовались классическим подходом к величинам параметров, где число 1 представляет собой наихудшее значение, а число 18 - наилучшее.

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

Вот как запишется код заполнения значений базовых параметров на основе данного массива (процедура GenerateHero):

  for i := chrSTR to chrCHA do
    Heroes[CurHero].Chars[i] :=
        Race_Table[ Heroes[CurHero].Race, i, 1 ] +
        random(Race_Table[ Heroes[CurHero].Race, i, 2 ])+1;

Чтобы он работал, в разделе реализации модуля LowLevel укажем модуль Tables:

implementation uses Hero, Texts, Game, Tables,

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

  const
  ...
  skillMin = 1;
  skillHandWeapon = 1;
  skillTrapSearch = 2;
  skillDefence = 3;
  skillMax = 3;

Мы ввели две новые константы, skillMin и skillMax, значение первой останется неизменным, а вот при внесении новых навыков значение второй константы (значение максимального навыка, фактически - число навыков) надо будет скорректировать. Чтобы после этой корректировки не забыть внести изменение в массив связи классов и навыков, опишем этот массив в модуле Tables так:

  const ClassSkill_Table:
        array[ skillMin..skillMax, classWarrior..classMage, 1..2 ] of Integer =

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

  (
  ((80,15),(50,20),(25,10)),
  ((20,20),(80,15),(60,20)),
  ((70,20),(50,20),(15,10))
  );

Их воплощение в герое произойдет способом, схожим с процессом формирования базовых параметров:

  for i := skillMin to skillMax do
    Heroes[CurHero].Skills[i] :=
        ClassSkill_Table[ Heroes[CurHero].Class, i, 1 ] +
        random(ClassSkill_Table[ Heroes[CurHero].Class, i, 2 ])+1;

Ранее мы готовили массив BaseSkill_Table для задания начальных значений независимо от класса и расы, теперь будем пользоваться новым, более гибким подходом.

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

Но что мы будем с этими значениями параметров и навыков героя делать? Где и как их применять?

Реализация навыков уже существует - в частности, в процедуре SkillTest. Будем дополнительно начислять различные бонусы в зависимости от уровня базовых параметров.

Сила влияет, допустим, на величину поражения, наносимого ручным оружием. Эта величина рассчитывается в функции WeaponDamage (модуль Combat), мы будем ее увеличивать в процедуре HeroAttack - например, на 50%, если "испытание" параметра STR закончится успешно:

  dam := WeaponDamage( H.Slots[i] );

  d := RollDice(3,6);
  if d <= H.Chars[chrSTR] then
     dam := dam + dam div 2;

  skin := RollDice( Monsters[m].dd1, Monsters[m].dd2 );
  ...

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

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

  { ----------------- }
  function SkillTest( var H: THero; skl: Integer ): Boolean;
  var d, ss, xp: Integer;
  begin
  SkillTest := false;

  ss := H.Skills[skl];
  if skl = skillDefence then
     begin
     d := RollDice(3,6);
     if d <= H.Chars[chrDEX] then
        ss := ss + ss div 3;
     end;

  if random(100)+1 > ss then Exit;
  SkillTest := true;
  ...

В раздел реализации текущего модуля Hero надо добавить ссылку на модуль Game, чтобы стала доступной функция броска костей RollDice:

implementation uses Tables, Map, LowLevel, Game;

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

  if skl = skillDefence then
     begin
     d := RollDice(3,6);
     if d <= H.Chars[chrDEX] then
        ss := ss + ss div 3;

     d := RollDice(3,6);
     if d <= H.Chars[chrCON] then
        ss := ss - ss div 5;

     end;

Эта цепочка расчетов, если быть до конца пунктуальными, не совсем точна, потому что мы можем сначала увеличить значение навыка на 33%, и лишь затем это увеличенное значение снизить на 20%.Исправить этот недочет предоставляем возможность читателю.

Ум может влиять на качество обнаружения ловушек:

  if skl = skillTrapSearch then
     begin
     d := RollDice(3,6);
     if d <= H.Chars[chrIQ] then
        ss := ss + ss div 3;
     end;

Мудрость оставим до создания магии, а вот существо с высокой харизмой монстры могут не трогать, а уважать и не нападать. Для этого можно модифицировать функцию CanTrace (модуль Monster), которая проверяет, находится ли герой в зоне видимости конкретного монстра. Пусть с вероятностью, зависящей от харизмы, монстр будет "не замечать" жертву с этим развитым качеством:

  { ----------------- }
  function CanTrace(mi, hi: Integer): Boolean;
  var d, dd: Integer;
  begin
  d := Distance( Monsters[mi].x,Monsters[mi].y,
                 Heroes[hi].x,Heroes[hi].y );

  CanTrace := false;
  dd := RollDice(3,6);
  if dd <= Heroes[hi].Chars[chrCHA] then Exit;

  CanTrace := (d <= Monsters[mi].ViewZone) and (d > 1);
  end;

Мы принимаем значение, возвращаемое функцией, за false (отслеживать героя монстр не сможет), если "испытание" харизмы закончилось успешно.

Не забудем добавить в раздел реализации модуля Monster ссылку на модуль Game:

implementation uses Map, LowLevel, Hero, Combat, Game;

Разумеется, каждый базовый параметр может влиять на самые разные факторы и критерии поведения персонажа. Так, ловкость может сильно повышать вероятность попадания при стрельбе из лука.

Далее - выводим статистику по параметрам героя.


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

http://russianenterprisesolutions.com/sbo/download/22125.zip 10643 байта


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

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

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


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

В избранное