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

Создание ролевой компьютерной игры 42) Квесты-2


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

42) Программирование ролевой игры: Квесты-2

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

В первом приближении структура квеста получится у нас такой:

  type TQType = (qKillTarget);
       TQuest = record
       QType:TQType;
       MainInd, TargetId: Integer;
       Money, XP: Integer;
       end;

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

Определим теперь массив квестов (пускай их будет 10 на игровой уровень):

  const MaxQuests = 10;
  var Quests: array[1..MaxQuests] of TQuest;

Автоматическую генерацию квеста у нас будет выполнять процедура GenerateQuest:

  { ----------------- }
  procedure GenerateQuest(qi, hn: Integer; qt: TQType);
  begin

  end;

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

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

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

  MaxNPCNum = 3;

Вот как запишется расширенный код:

  { ----------------- }
  procedure GenerateMonsters;
  var i,j,x,y: Integer;
  label Break2;
  begin
  for i := 1 to MaxMonsters-MaxNPCNum do
    begin
    while true do
      for j := 1 to MaxMonsterTypes do
        if (MonsterTypes[j].Level = CurMap) and (random(6)=0) then
           begin
           Monsters[i] := MonsterTypes[j];
           FreeMapPoint(x,y);
           Monsters[i].x := x;
           Monsters[i].y := y;
           goto Break2
           end;
   Break2:
    end;

  for i := MaxMonsters-MaxNPCNum+1 to MaxMonsters do
    begin
    Monsters[i] := MonsterTypes[8];
    FreeMapPoint(x,y);
    Monsters[i].x := x;
    Monsters[i].y := y;
    end;

  end;

Первый цикл расстановки "злых" монстров сокращен на MaxNPCNum единиц, а свободные позиции заполняются вторым циклом - непосредственно таким типом "монстров", как "мирный житель". Для удобства будем их также показывать на глобальной карте, чтобы помнить, куда возвращаться после выполнения квеста. Дополним список значений, заносимых в локальный массив gm процедуры ShowGlobalMap, числом 55, обозначающим мирного жителя:

  ...
  n := IsMonsterOnTile( LOCAL_MAP_WIDTH+x, LOCAL_MAP_HEIGHT+y );
  if n > 0 then
     if Monsters[n].ID in GoodMonsterSet
        then gm[x,(y+1) div 2] := 55
        else gm[x,(y+1) div 2] := 5;
  ...

Символ мирного жителя пусть совпадет с символом монстра, а вот цвет будет не красным, а зеленым:

   case gm[x,y] of
  ...

  55: begin
      TextColor(LightGreen);
      Write( '#' );
      end;
  ...

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

  { ----------------- }
  procedure MoveHero( dx,dy: Integer );
  var m, dam: Integer;
  begin
  if not FreeTile( GameMap[CurMap].Cells[
            Heroes[CurHero].x+dx,Heroes[CurHero].y+dy].Tile ) then
     Exit;

  m := IsMonsterOnTile(Heroes[CurHero].x+dx, Heroes[CurHero].y+dy);
  if m > 0 then
     begin
     if Monsters[m].ID in GoodMonsterSet
       then CallQuest(CurHero, m)
     else
       begin
       HeroAttack(Heroes[CurHero], m);
       MonstersStep;
       end;
     Exit
     end;
  ...

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

Желательно, чтобы один и тот же мирный персонаж не раздавал слишком много квестов налево и направо, иначе они могут потерять актуальность. Как минимум, введем запрет на выдачу квеста, если данный ранее NPC уже предлагал квест, и тот пока не завершился. Определить это можно по полю MainInd (условимся, что оно равно нулю, если соответствующий элемент массива Quests свободен - то есть в нем на данный момент не хранится действующий квест). В переменной n будем хранить такой свободный элемент (его индекс), который можно задействовать для создания нового квеста.

Если выбранный "мудрец" еще не задействован в выдаче партийцам заданий, его можно привлечь к такому процессу. Для этого сначала найдем подходящего для уничтожения монстра, а потом сделаем из него оригинальный, разнящийся от других объект. Ведь пока все монстры одного типа в игре неотличимы друг от друга!

  { ----------------- }
  procedure CallQuest(hn, mn: Integer);
  var i,n: Integer;
  begin
  n := 0;
  for i := 1 to MaxQuests do
    if Quests[i].MainInd = mn then
       begin
       ShowInfo(STR_QIS);
       Exit
       end;

  for i := 1 to MaxQuests do
    if Quests[i].MainInd = 0 then
       begin
       n := i;
       break
       end;

  if n = 0 then
     begin
     ShowInfo(STR_QMANY);
     Exit
     end;

  GenerateQuest(n,mn,qKillTarget);

  end;

Текстовые константы:

  STR_QIS = ' Мудрец уже выдавал квест, и он еще не завершен! ' ;
  STR_QMANY = ' Вы уже выполняете максимально возможное число
  квестов! ' ;

В следующем выпуске приступим к формированию конкретного квеста убийства монстра.

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

тут, 1116.zip, 17775 байтов


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

Школа программирования с нуля
Все предыдущие выпуски базового курса всегда тут:
http://www.infiltration.ru/p/


Вышел мой учебный курс "Технологии Delphi. Разработка приложений для бизнеса".
http://shop.piter.com/book/978591180282/

В книге описаны десятки технологий, каждой из которых посвящены отдельные книги. Таким образом, купив одну мою книгу, вы существенно сэкономите :) В книге полностью описан язык Delphi (версия 2006, полностью совместимая с Turbo Delphi) для обеих платформ - Win32 и .NET. Охвачены также темы работы с файлами на этих платформах, создания файл-серверных, клиент-серверных, распределенных приложений, веб-программ (Indy, ASP.NET, веб-сервисы). Описаны языки SQL и OCL. Немало глав посвящены истории программирования и различных технологий. Особое внимание уделено созданию программ с помощью технологии ECO и языка моделирования UML - программы фактически рисуются, и теперь даже для создания корпоративных приложений и их переноса в Интернет не обязательно знать программирование!
Отдельная часть отведена технологиям организации групповой работы, управления требованиями, контроля версий, локализации и тестирования.
Тут подробнее про книгу.

Другие мои книги, которые пока доступны в продаже:


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


В избранное