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

Создание ролевой компьютерной игры 37) Хочу кушать!


37) Программирование ролевой игры: Хочу кушать!

Хочу кушать!

Еда - одна из оригинальных игровых концепций, которая не всегда реализуется в коммерческих играх, однако по праву считается одной из увлекательных идей в roguelike-играх. Каждый игрок характеризуется таким параметром, как усталость - по мере ее накопления при движении и сражении повышается вероятность снижения здоровья. А когда усталости набирается много, здоровье теряется очень быстро. Снимается же усталость принятием пищи - еще одного типа игровых предметов. С него и начнем. Новый тип назовем itemEat:

  type TGameItemType = (itemHandWeapon, itemArmor,
        itemAmmo, itemRangedWeapon, itemMagik, itemMoney, itemEat,
        itemNone);

Увеличим число игровых типов и дополним массив ItemTypes новым описанием:

  const MaxItemTypes = 9;
         ItemTypes: array[1..MaxItemTypes] of TGameItem =
         (
  ...
  (ID:9;
   x:0; y:0;
   Price:10;
   IType:itemEat;
   Name:STR_EATITEM;
   Ints: (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
   Reals: (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
  );

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

  STR_EATITEM = ' Пищевой паек ' ;

Символ для представления пищи на карте - желтый 'e':

  const ItemRecords: array[TGameItemType] of TTileRecord =
     (
     (C: ' + ' ; Clr:LightCyan),
     (C: ' [ ' ; Clr:LightGreen),
     (C: ' | ' ; Clr:LightCyan),
     (C: ' { ' ; Clr:LightGreen),
     (C: ' ; ' ; Clr:LightCyan),
     (C: ' $ ' ; Clr:Yellow),
     (C: ' e ' ; Clr:Yellow),
     (C: ' ' ; Clr:Black)
     );

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

  ...
  tileMagicShop:
    begin
    ni := 0;
    for i := 1 to MaxItemTypes do
      if ItemTypes[i].IType in [itemMagik, itemEat] then
         inc(ni);

    ni := random(ni)+1;
    for i := 1 to MaxItemTypes do
      if ItemTypes[i].IType in [itemMagik, itemEat] then
      begin
      ...

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

  ...
  if (-n) in [1..MaxHeroItems] then
     begin
     if Heroes[CurHero].Items[-n].IType in [itemHandWeapon, itemArmor,
              itemAmmo, itemRangedWeapon, itemEat, itemMagik] then
        begin
        ...

Теперь введем концепцию усталости. Добавим в тип THero новое поле Fatique (после чего, внимание, старые сэйв-файлы будут неработоспособны!) :

  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;
       x,y,
       HP, MaxHP,
       Mana, MaxMana,
       Exp, MaxExp, Fatique,
       Level, VisLong: Integer;

       Class, Race: Integer;

       Name: String[20];
       end;

В момент генерации героя его значение будет нулевым:

  { ----------------- }
  procedure InitHero(HeroNum: Integer);
  var i: Integer;
  begin

  with Heroes[HeroNum] do
    begin

    Fatigue := 0;

  ...

Возрастать оно должно на единицу при перемещении:

  { ----------------- }
  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
     HeroAttack(Heroes[CurHero], m);
     MonstersStep;
     Exit
     end;

  IncFatigue(1);

  ...

Почему увеличение усталости мы выделили в отдельную процедуру - определим ее в этом же модуле Game:

  { ----------------- }
  procedure IncFatigue(finc: Integer);
  begin

  end;

станет понятно далее.

В ходе схватки увеличение усталости будет более значительным - за каждый удар она возрастет на 10 единиц, а за каждый выстрел из лука и за каждый бросок заклинания - на 5 единиц:

Ручная атака (модуль Combat):

  { --------------------------- }
  procedure HeroAttack( var H: THero; m: Integer );
  var i, d, dam, skin: Integer;
  begin
  if not SkillTest(Heroes[CurHero], skillHandWeapon) then
        begin
        ShowInfo(STR_BAD_ATTACK);
        Exit
        end;

  i := GetHeroWeapon(H);
  if i = 0 then
        begin
        ShowInfo(STR_NONE_WEAPONS);
        Exit
        end;

  IncFatigue(10);

  ...

Стрельба из лука (там же):

  { --------------------------- }
  procedure HeroShot(dx,dy: Integer);
  var d,n,m,x,y,dam: Integer;
  begin
  if Heroes[CurHero].Slots[slotHands].IType <> itemRangedWeapon then
        begin
        ShowInfo(STR_NONE_WEAPONS);
        Exit
        end;

  if Heroes[CurHero].Slots[slotHands].Ints[intRangedAmmo] = 0 then
        begin
        ShowInfo(STR_NONE_AMMO);
        Exit
        end;

  if not SkillTest(Heroes[CurHero], skillRangedWeapon) then
        begin
        ShowInfo(STR_BAD_RANGED_ATTACK);
        Exit
        end;

  IncFatigue(5);

  ...

Бросок заклинания (модуль Magik):

  { ----------------- }
  procedure ThrowSpell;
  var n: Integer;
  begin

  n := GetSpellNo;
  if n=0 then Exit;

  if Heroes[CurHero].Mana < Spells[n].Mana then
     begin
     ShowInfo(STR_MAGIK_BADMANA);
     Exit
     end;

  IncFatigue(5);

  ...

Узнать уровень усталости можно будет из процедуры отображения полной информации о герое:

  { ----------------- }
  procedure ShowHeroFullInfo;
  var i: Integer;
  begin
  ClrScr;
  GoToXY(1,1);
  WriteLn(Heroes[CurHero].Name, ' , ' ,
          RaceName[Heroes[CurHero].Race], ' ' ,
          ClassName[Heroes[CurHero].Class]);
  WriteLn(STR_HEROFI_LEVEL, Heroes[CurHero].Level);
  WriteLn(STR_HEROFI_EXP, Heroes[CurHero].Exp, ' /
  ' ,Heroes[CurHero].MaxExp);
  WriteLn(STR_HEROFI_FAT, Heroes[CurHero].Fatigue, ' / ' ,MAX_FATIGUE);
  ...

Тут мы используем одну строковую константу:

  STR_HEROFI_FAT = ' Усталость ' ;

и одну целочисленную, которая определяет максимально допустимый уровень усталости - она будет описана в модуле Hero:

MAX_FATIGUE = 1000;

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

LIGHT_FATIGUE = 700;
HARD_FATIGUE = 900;

Контролировать превышение этих значений мы будем в процедуре IncFatigue - именно для этого она и создавалась:

  { ----------------- }
  procedure IncFatigue(finc: Integer);
  begin
  inc( Heroes[CurHero].Fatigue, finc );

  if Heroes[CurHero].Fatigue > MAX_FATIGUE then
     begin
     ShowInfo(STR_BIGFATIGUE);
     IncHP( Heroes[CurHero], -1 );
     end else

  if Heroes[CurHero].Fatigue > HARD_FATIGUE then
     begin
     if random(10)=0 then
        begin
        ShowInfo(STR_BIGFATIGUE);
        IncHP( Heroes[CurHero], -1 );
        end
     end else

  if Heroes[CurHero].Fatigue > LIGHT_FATIGUE then
     begin
     if random(30)=0 then
        begin
        ShowInfo(STR_BIGFATIGUE);
        IncHP( Heroes[CurHero], -1 );
        end;
     end;

  end;

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

  STR_BIGFATIGUE = ' Усталость истощает ваши силы! ' ;

Теперь остается дополнить процедуру работы с инвентарем процессом "поедания" предмета-пищи. Допустим, паек будет случайным образом уменьшать на 5-25 процентов текущий уровень усталости. "Поедание" будем осуществлять, когда в инвентаре выбирается предмет-еда:

  { ----------------- }
  procedure ShowHeroItems;
  var fg,i,n,s: Integer;

  ...

  if n > 0 then
     begin

     if Heroes[CurHero].Items[n].IType = itemAmmo then
        begin
        ...
        end else

     if Heroes[CurHero].Items[n].IType = itemEat then
        begin
        fg := Heroes[CurHero].Fatigue;
        fg := round( fg * (95-random(21))/100 );
        Heroes[CurHero].Fatigue := fg;
        Heroes[CurHero].Items[n].IType := itemNone;
        ShowInfo(STR_FATGOOD + IntToStr(fg));
        end else

  ...

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

   STR_FATGOOD = ' Вы перекусили. Усталость снижена до ' ;

Данную идею можно разнообразить увеличением числа доступных блюд (предметов пищи с разными названиями), которые могут по разному влиять на усталость. В некоторых roguelike-играх например употребление героем слишком большого объема пищи приводит к нехорошим последствиям :), итд.


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

тут, 2076.zip, 16082 байта


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

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

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


В избранное