Урок 15. Рюкзак и инвентарь-2
Давайте
снабдим героя в начале игры двумя предметами - легкой броней и недорогим
топором. Положим эти предметы ему в рюкзак с помощью следующих операторов
процедуры InitHero:
procedure InitHero(HeroNum: Integer); var i:
Integer; begin
with Heroes[HeroNum] do begin for
i := 1
to MaxChars do
Chars [i] :=
0; for i :=
1 to MaxSkills do
Skills [i] :=
BaseSkill_Table[i];
For i:=1 to MaxHeroItems do
Items[i].IType:=ItemNone;
Items[i]:=ItemTypes[1];
Items[i]:=ItemTypes[4];
Предметами, хранящимися
в рюкзаке героя, стали топор и броня. Эти предметы нам надо будет перемещать из
рюкзака на тело и в руки героя.
Нам потребуется
еще один массив, который мы назовем Slots, элементы которого будут соответствовать
предметам, непосредственно используемым героем. Это броня или оружие. В
коммерческих играх список носимых предметов обычно сопровождается кольцами,
цепочками, колчаном, оружейным поясом, отдельными артефактами для рук (наручи),
ног (поножи), специализированными видами оружия. Можно и самостоятельно
реализовать в игре такую возможность по описанной структуре ТНего:
Const
slotSlots=2;
slotBody=1;
slotHands=2;
Type
THero=record
Chars:array[1..MaxChars] of integer;
Skills:array[1..MaxSkills] of
integer;
Items:array[1..MaxHeroItems] of TGameItem;
Slots:array[1..MaxSlots] of
TGameItem;
End;
Два слота
(броня на теле и оружие в руках), доступ к которым может производиться с
индексов slotBody и slotHands. Проинициализируем этот массив в
InitHero:
Procedure InitHero (HeroNum: Integer);
Var i:integer;
Begin
For i:=1 to Items[HeroNum] do
Begin
For i:=1 to MaxChars do
Chars[i]:=0;
For i:=1 to MaxSkills do
Skills[i]:=BaseSkill_Table[i];
For i:=1 to MaxHeroItems do
Items[i].IType:=ItemNone;
Items[i]:=ItemTypes[1];
Items[i]:=ItemTypes[4];
For i:=1 to MaxSlots do
Items[i].IType:=ItemNone;
End;
End;
Массив слотов
просто представим набором "пустых" предметов. Для носимых героем
предметов подготовим процедуру ShowHeroSlots (модуль
Procedure SHowHeroSlots;
Var i:integer;
begin
…
…
…_SLOT ITEMS) ;
For i:=1 to MaxSlots do
begin . ..
GoToXY(l,i+2);
Write(SlotName[i], ' :
' );
if
Heroes[CurHero].Slots[i].IType = itemNone then Write(STR_EMPTY_ITEM)
else
Write(Heroes[CurHero].Slots[i].Name)
end;
GoToXY(l,20); ReadLn; ShowGame;
end;
Константа STR_HERO_SLOTITEMS должна быть описана в модуле Texts:
const STR_HERO_SLOTITEMS = ' Используемые героем предметы: '
;
Массив констант SlotName (названия слотов героя) опишем также в модуле Texts:
const SlotName: array[1..MaxSlots] of string[20] = (' Тело
' , '
В Руках ' );
Внесение этой
константы в программу может вызвать определенные трудности. Дело в том, что она
использует другую константу - MaxSlots, которая описана в модуле Hеrо. Попытка добавить ссылку на модуль
Него в interface-заголовок модуля Texts закончится неудачно из-за того, что
возникает замкнутая кольцевая цепочка ссылок interface-разделов друг па друга.
Модуль Texts ссылается на модуль Него, а тот в свою очередь - на модуль
Gameltem, которому снова требуется сслыка на Texts, так как в его интерфейсной
части расположены описания массива типовых предметов ItemTypes, поле Name
которого требует указания константы из модуля Texts.
Сейчас мы
решим эту проблему "в лоб". Переместим описание константы MaxSlots,
из-за которой разгорелся весь сыр-бор, в модуль Texts (хотя это и не совсем
корректно в смысловом плане):
const MaxSlots = 2;
SlotName: array[1..MaxSlots] of string[20] = ('
Тело '
, ' В Руках ' ) ;
Добавим в список
подключенных модулей модуля Него ссылку на модуль Texts, чтобы оставалась доступной константа
MaxSlots: unit Hero;
interface uses
Gameltem, Texts;
Одновременно
убрав ее из раздела реализации.
Теперь
программа соберется без проблем. Однако, как уже говорилось, такой подход, хотя
и достигает цели, но все же не может считаться оптимальным. Ведь модуль Texts
предназначен для хранения только текстовых констант, и подобное смешение
понятий может по мере усложнения программы привести к новым ошибкам подобного
класса. Правильнее спрятать подробное описание массива ItemTypes в раздел
реализации модуля Gameltem, переместив туда же и ссылку на модуль Texts, а
доступ к элементам ItemTypes обеспечить с помощью дополнительной функции -
например, GetltemType, возвращающей элемент массива по его номеру (параметру
функции). Вы сами можете дополнить программу такой возможностью.
Вызов
процедуры ShowHeroSlots введем в обработчик нажатий на клавиши в главном модуле
программы Main. Пусть список надетых героем предметов показывается при нажатии
на клавишу 'e':
While Item do
Begin
SHowItem;
I:ReadKey;
Case i of
‘i’ : ShowHeroSlots;
‘j’ : SHowHeroItems;
Функции расположены
в алфавитном порядке нажимаемых клавиш – это позволяет быстро найти нужный
обработчик определенного нажатия. Артефакты, находящиеся в рюкзаке, и надетые
на тело, надо уметь менять местами (из рук в рюкзак, а из рюкзака - одеть на
тело). Кроме того, предметы иногда приходится и i и поднимать с земли. Разделим
эту задачу на несколько частей.
Ложим
предметы в рюкзак.
Дополним
возможности списка надетых предметов дополнительными возможностями. Позволим
задавать
команды - например, ввод номера слота будет означать команду "предм с этим
номером убрать в рюкзак". Для этого в процедуре ShowHeroSlots добавим
возможность просмотра, а также выделим основную часть процедуры в бесконечный
цикл, который будет зацикливаться когда пользователь введет в качестве номера
ноль.
Procedure ShowHeroSlots;
Var I,n:integer;
Begin
While True do
… (readln)
…(1, 1);
…_Hero_SlotItmes);
For i:=1 to MaxSlots do
Begin
(retaky)…(1,i+2)
Write (i, ‘ ) ‘ , SlotName[i], ‘ : ‘
);
If Heroes [CurHero].Slots[i].IType =
itemNone
Then Write(STR_EMTY_ITEM)
Else Write(Heroes[CurHero].Slots[i].Name);
End;
… (1, 20);
Write (‘>’);
…(0);
If n=0 then break;
End;
…
End;
Значение надо
проверить. Если слот пуст, или в рюкзаке нет места, никаких действий принимать
не надо. В противном случае (в слоте есть предмет и в рюкзаке свободные места)
переместим предмет в рюкзак. Проверка пустоты слота несложна, а проверку
свободного места в рюкзаке желательно реализовать в виде отдельной функции. Заметим,
что данная реализация будет в значительной степени зависеть от способа того
места в рюкзаке. Этот способ может содержать анализ веса груза, местоположения
предмета в рюкзаке и так далее. Поэтому выделим GetFreeBag, возвращающую номер
свободного элемента в рюкзаке или ноль. Запишем функцию в модуле Него:
Procedure GetFreeBag (var H:Hero): Integer;
Var i:integer;
Begin
GetFreeBag:=0;
for
i : = 1 to
MaxHeroItems do
if
H.Items[i].IType = itemNone
then
begin
GetFreeBag :=
i;
Exit
end;
end;
В ней
происходит перебор предметов, хранящихся в рюкзаке, и как только обнаруживается
элемент, у которого значение поля IType соответствует значению типа
"пустого" предмета, функция возвращает его индекс в массиве Items,
входящего в структуру Него. С учетом этих нововведений процедура ShowHeroSlots
запишется так:
procedure ShowHeroSlots;
var
i,n,s: Integer;
begin
while true
do begin ClrScr;
GoToXY(l,1);
Write(STR_HERO_SLOTITEMS) ;
for
i := 1
to MaxSlots do
begin
GoToXY(l,i+2);
Write(i, ■
) ' ,SlotName[i], '
: ' );
if
Heroes[CurHero].Slots[i].IType
= itemNone then
Write(STR_EMPTY_ITEM)
else
Write(Heroes[CurHero].Slots[i].Name)
end;
GoToXY(l,20);Write( '
> ' ); ReadLn(n);
if
n = 0
then
break;
s
:= GetFreeBag(Heroes[CurHero]);
if
(Heroes[CurHero].Slots[n].IType
<> itemNone) and
(s > 0)
then
begin
Heroes[CurHero].Items[s] :=
Heroes[CurHero].Slots[n];
Heroes[CurHero].Slots[n].IType :=
itemNone;
end;
end;
ShowGame;
end;
После того,
как введенный номер слота проверен и выяснено, что он корректен, скопируем
соответствующий элемент массива Slots в найденный незанятый элемент рюкзака
(массив Items), после чего пометим элемент Slots[n] как "пустой".
Далее:
Одеваем предметы из рюкзака.
Источник: delgame.at.ua
|