Создание ролевой компьютерной игры 37) Хочу кушать!
37) Программирование ролевой игры: Хочу кушать!
Хочу кушать!
Еда - одна из оригинальных игровых концепций, которая не всегда реализуется в коммерческих играх, однако по праву считается одной из увлекательных идей в roguelike-играх. Каждый игрок характеризуется таким параметром, как усталость - по мере ее накопления при движении и сражении повышается вероятность снижения здоровья. А когда усталости набирается много, здоровье теряется очень быстро. Снимается же усталость принятием пищи - еще одного типа игровых предметов. С него и начнем. Новый тип назовем itemEat:
Процедуру генерации предмета нам менять не надо - новый тип "еда" в ней будет учтен автоматически, а каких-то других параметров этого типа мы вводить не будем. Все остальные механизмы использования пищи (взятие в инвентарь и продажа) также будут работать автоматически. Только в процедуре 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);
...
Узнать уровень усталости можно будет из процедуры отображения полной информации о герое:
и одну целочисленную, которая определяет максимально допустимый уровень усталости - она будет описана в модуле Hero:
MAX_FATIGUE = 1000;
При превышении этого уровня герой будет терять единицу здоровья на каждом шаге. Определим также еще два порога - легкая усталость, когда здоровье героя будет ухудшаться раз в 30 шагов, и тяжелая усталость, когда здоровье будет убывать со скоростью один пункт в 10 шагов:
LIGHT_FATIGUE = 700;
HARD_FATIGUE = 900;
Контролировать превышение этих значений мы будем в процедуре IncFatigue - именно для этого она и создавалась:
Теперь остается дополнить процедуру работы с инвентарем процессом "поедания" предмета-пищи. Допустим, паек будет случайным образом уменьшать на 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):