Программирование с нуля - это совсем просто! 91) Что у монстра внутри
Школа программирования
Разработка ролевой игры
91) Что у монстра внутри
Теперь, когда мы полностью определились с перечнем и структурой типов предметов в игре, реализуем упомянутый режим, когда из убитого монстра выпадает некий предмет. Для этого нам потребуется нечто, схожее с кодом процедуры расстановки предметов на генерируемой карте. Ведь в этом коде предметы формируются случайно, и было бы неплохо этим кодом воспользоваться и в текущем случае. Для этого давайте выделим соответствующую часть процедуры MapGeneration в отдельную процедуру GenerateRandomItem (в модуле GameItem),
которая будет выдавать случайно созданный предмет. Правильнее оформить ее в виде функции, но к сожалению версия Borland Pascal еще не умела поддерживать функции, которые возвращают значения сложных типов данных. В число параметров также добавим начальные координаты предмета на земле и текущий уровень генерации карты (MapLevel) - он нужен нам для подсчета случайно разбрасываемой суммы.
{ ----------------- } procedure GenerateRandomItem( var gi: TGameItem; x,y, ml: Integer ); begin
gi := ItemTypes[random(MaxItemTypes)+1];
gi.x := x;
gi.y := y;
if gi.ID = 7 then begin
gi.Ints[intMagikType] := random(mintMax)+1;
case gi.Ints[intMagikType] of
mintStormStf : gi.Ints[intMagikNum] := 5;
mintHealingStf: gi.Ints[intMagikNum] := 10;
else gi.Ints[intMagikNum] := 1;
end;
end else if gi.ID = 8 then begin
gi.Ints[intMoney] := random(ml*10)+1; end
end;
А в процедуре MapGenerate код сократится:
if random(100) = 0 then begin
inc(n); if n <= MaxItems then begin
GenerateRandomItem(Items[n],x,y,MapLevel); end; end;
Теперь воспользуемся новой процедурой для создания предмета, который выпадает из монстра после его убийства. Для этого дополним в конце процедуру HeroAttackFin, которая вызывается всегда, когда уничтожается противник:
... if Monsters[m].HP <= 0 then begin
ShowInfo(Monsters[m].Name + STR_MON_KILL);
IncXP( H,Monsters[m].XP );
n := GetFreeItemNum; if n>0 then
GenerateRandomItem(Items[n],Monsters[m].x,Monsters[m].y,CurMap); end;
end;
Теперь после уничтожения любого монстра на его клетке останется какой-то предмет. Его можно подобрать и, например, продать. А где?
Магазины
Пусть магазины будут особым типом тайлов (tileShop), при вступлении на которые герой будет оказываться внутри магазина, в соответствующем интерфейсе:
Типов магазинов для повышения интереса к игре обычно бывает несколько. Мы введем два типа магазинов - торгующих обычным оружием и магическими предметами. А в полноценной игре можно дополнительно создать магазины, в которых торгуют только зельями, жезлами, луками или топорами.
Как различать типы магазинов? Во-первых, можно ввести дополнительные тайлы для каждого из таких типов. Во-вторых, можно расширить описание структуры TMapCell, дополнив ее значением, уточняющим "смысл" текущего тайла (для магазина - его тип; для горы - степень кривизны и т. д.). В третьих, тип магазина можно запрятать непосредственно в сам программный код, договорившись, что например магазины магических предметов будут находиться на четных вертикалях координатной сетки, а магазины обычного оружия - на нечетных.
Для крупных проектов лучше подойдет второй вариант с информационной избыточностью, а мы реализуем первый принцип, менее простой в плане реализации, но более простой в плане наглядности. Уточним название констант-тайлов магазинов, и их цвета:
Теперь нам надо запрограммировать реакцию программы на посещение магазина. Для этого объединим все тайлы магазинов в одну константу-множество ShopTileSet:
а в процедуре перемещения героя MoveHero добавим проверку вступления на это множество:
if GameMap[CurMap].Cells[ Heroes[CurHero].x,Heroes[CurHero].y].Tile in
ShopTileSet then begin
end;
Пусть в таком случае будет вызываться подпрограмма из модуля LowLevel (так как начнется непосредственная работа с интерфейсом магазина), назовем ее GoToShop. В качестве параметра такой процедуры передадим тайл, чтобы знать, какой магазин создавать:
with GameMap[CurMap].Cells[ Heroes[CurHero].x,Heroes[CurHero].y] do if Tile in ShopTileSet then begin
GoToShop(Tile); end;
Подготовим шаблон процедуры в модуле LowLevel:
{ ----------------- } procedure GoToShop(til: Integer); begin
end;
Мы должны сформировать для каждого из магазинов предлагаемую ими номенклатуру товаров. Как это сделать?
Определим внутри данной процедуры локальный массив предметов магазина:
procedure GoToShop(til: Integer); const MaxShopItems = 5; var ShopItems: array[1..MaxShopItems] of TGameItem; begin
end;
Он должен заполняться случайно и в зависимости от типа продаваемых предметов. Чтобы упростить процесс подбора товаров, выполним предварительно подсчет числа профильных предметов в массиве ItemTypes.
for n := 1 to MaxShopItems do case til of
tileWeaponShop: begin
ni := 0; for i := 1 to MaxItemTypes do if ItemTypes[i].IType in
[itemHandWeapon, itemArmor, itemAmmo, itemRangedWeapon] then
inc(ni); end;
tileMagicShop: begin
ni := 0; for i := 1 to MaxItemTypes do if ItemTypes[i].IType in [itemMagik] then
inc(ni); end;
end;
Далее мы выбираем какой-то случайный номер в диапазоне от 1 до ni, который и станет номером очередного n-го предмета (типа), предлагаемого в магазине.
tileWeaponShop: begin
ni := 0; for i := 1 to MaxItemTypes do if ItemTypes[i].IType in
[itemHandWeapon, itemArmor, itemAmmo, itemRangedWeapon] then
inc(ni);
ni := random(ni)+1; for i := 1 to MaxItemTypes do if ItemTypes[i].IType in
[itemHandWeapon, itemArmor, itemAmmo, itemRangedWeapon] then begin
dec(ni); if ni = 0 then begin
ShopItems[n] := ItemTypes[i];
break end; end;
tileMagicShop: begin
ni := 0; for i := 1 to MaxItemTypes do if ItemTypes[i].IType in [itemMagik] then
inc(ni);
ni := random(ni)+1; for i := 1 to MaxItemTypes do if ItemTypes[i].IType in [itemMagik] then begin
dec(ni); if ni = 0 then begin
ShopItems[n] := ItemTypes[i];
break end; end;
Массив товаров у нас сформирован. Правда, пока в товарах не хватает главного параметра - цены.
Далее: Расширяем возможности магазинов.
Исходный код текущей версии для Turbo Pascal (всегда проверен и работоспособен, главный файл - main.pas):