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

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


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

47) Программирование ролевой игры: Завершаем с квестами

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

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

    if (Quests[i].QType = qSendItem) and (Quests[i].TargetId = mn) then
       begin
       for j := 1 to MaxItems do
         if Heroes[hn].Items[j].Ints[MaxItemInt] = Quests[i].MainInd then
            begin
            Heroes[hn].Items[j].IType := itemNone;
            Quests[i].TargetId := 0;
            ShowInfo(STR_QRECEIVE);
            Break
            end;

       Exit
       end;

  ...

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

  qSendItem:
    begin
    if Quests[qi].TargetId = 0
       then s := STR_QSENDOK
       else s :=
               IntToStr(Monsters[ Quests[qi].TargetId ].x) + ' , ' +
               IntToStr(Monsters[ Quests[qi].TargetId ].y) + ' ' ;

    s := STR_QSEND + Monsters[ Quests[qi].TargetId ].Name +
    ' ' +s+ ' ; ' +
         STR_QRETURN +
               IntToStr(Monsters[ Quests[qi].MainInd ].x) + ' , ' +
               IntToStr(Monsters[ Quests[qi].MainInd].y) + ' . ' ;

    end;

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

   STR_QSENDOK = ' (передан) ' ;

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

  { ----------------- }
  function QuestFinished(i: Integer): Boolean;
  var n: Integer;
  begin
  QuestFinished := false;

  case Quests[i].QType of

   qKillTarget:
     if Monsters[ Quests[i].TargetId ].HP > 0 then Exit;

   qSendItem:
     if Quests[i].TargetId > 0 then Exit;

  end;

   QuestFinished := true;
   Quests[i].MainInd := 0;
   Quests[i].TargetId := 0;

  n := GetMoneyNo(Heroes[CurHero]);
  if n > 0 then
     inc( Heroes[CurHero].Items[n].Ints[intMoney],
          Quests[i].Money )

  else if n < 0 then
     begin
     Heroes[CurHero].Items[-n] := ItemTypes[8];
     Heroes[CurHero].Items[-n].Ints[intMoney] :=
        Quests[i].Money;
     end;

  IncXP( Heroes[CurHero],Quests[i].XP );

  end;

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

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

    if (Quests[i].QType = qSendItem) and (Quests[i].TargetId = mn) then
       begin
       for j := 1 to MaxItems do
         if Heroes[hn].Items[j].Ints[MaxItemInt] = Quests[i].MainInd then
           begin
           Heroes[hn].Items[j].IType := itemNone;
           Quests[i].TargetId := 0;
           ShowInfo(STR_QRECEIVE);
           Exit
           end;

      end else

   if (Quests[i].QType = qSendItem) and (Quests[i].MainInd = mn) and
      (Quests[i].TargetId = 0) then
      begin
      if QuestFinished(i) then
         begin
         ShowInfo(STR_QFIN);
         Exit
          end
       end;

  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;

  if random(2) = 0
     then GenerateQuest(n,mn,qKillTarget)
     else GenerateQuest(n,mn,qSendItem);

  end;

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

  { ----------------- }
  procedure GoToNextLevel(dl: Integer);
  var i,x,y: Integer;
  begin
  for i := 1 to MaxHeroes do
    if (i <> CurHero) and
       (Heroes[i].HP > 0) and
       ((Heroes[i].x <> Heroes[CurHero].x) or
        (Heroes[i].y <> Heroes[CurHero].y)) then
        begin
        ShowInfo(STR_NOPARTY);
        Exit
        end;

  MapGeneration(CurMap+dl);
  GenerateMonsters;

  FreeMapPoint(x,y);
  for i := 1 to MaxHeroes do
    begin
    Heroes[i].x := x;
    Heroes[i].y := y;
    end;

  for i := 1 to MaxQuests do
    Quests[i].MainInd := 0;

  end;

На этом программирование квестов будем считать завершенным - а вместе с ними считаем завершенной и создание текстовой версии игры. Хотя данный блок квестов поулчился не очень объемным, он все же достаточно сложен для реализации, так как требует памятования множества особенностей и нюансов работы программы. Основные сложности возникают на этапах стыковки данного модуля с другими элементами игры, прежде всего потому, что понятие квеста охватывает не просто статическую смысловую единицу, а некоторый последовательный процесс, состоящий из ряда шагов (уничтожить/передать - вернуться). Такие вещи программируются обычно весьма сложно, особенно если не продумывались исходно.

Тему квестов можно еще многократно расширять. В них можно ввести наличие глобальных квестов, общей сюжетной линейки заданий, которая сохраняется независимо от местонахождения персонажа на уровнях пещеры. Финалом игры может стать задание убить некоего специального монстра, подготовленного в массиве типов монстров MonsterTypes, но размещаемого в пещере не автоматически и тиражно, а "вручную" и в единственном экземпляре - с помощью специально подготовленных команд. Выдавать же этот квест может пленник данного монстра, которые создается также только в последней, самой глубокой пещере. Когда этот квест выполнен, некоторая глобальная переменная-флажок (ее надо сохранять в файле игры вместе с другими данными) принимает значение true. И когда персонаж поднимеся на самый первый, верхний уровень, там должен будет появиться тайл-выход, использование которого приведет к завершению игры.

Далее - начинаем перенос нашего кода в Windows и ДЕЛЬФИ !

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

тут, 2627.zip, 19284 байта


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

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

Неофициальный сайт поддержки (со срочными вопросами - сюда):
www.prog-begin.net.ru.


Мои учебные курсы "Технологии Delphi / C++ / C#. Разработка приложений для бизнеса".
http://shop.piter.com/display.phtml?a_id=17681&web_ok=all

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

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


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


В избранное