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