Урок 18.
Монстры наносят ответный удар.
Займемся монстром, когда он все
же получил повреждение? Его здоровье проверяется, когда оно становится меньше
или равным нулю, считается, что он убит и герой получит причитающийся ему за
победу опыт:
Procedure HeroAttack(var H:THero; m:integer);
Var …:integer;
Begin
if not SkillTest(Heroes[CurHero],
skillHandWeapon) then
begin
ShowInfo(STR_BAD_ATTACK);
Exit;
End;
…Weapon[n]:
if … then
begin
ShowInfo(STR_NONE_WEAPONS);
Exit;
End;
dam : =
. WeaponDamage ( H, Slots [i] ) ;
skin
:= RollDice( Monsters[m].ddl, Monsters[m]-dd2 );
if
skin >= dam then
begin
ShowInfo(STR_BIG_SKIN);
Exit
end;
dec(
Monsters[m].HP, dam-skin );
Showlnfo(STR_ATTACK +
IntToStr(dam-skin) );
if Monsters[m].HP <=
0 then
begin
Showlnfo(Monsters[m].Name +
STR_MON_KILL);
IncXP(
H,Monsters[m].XP );
end;
end;
Текстовые константы могут быть
описаны в модуле Texts так:
const STR_ATTACK =
' Герой наносит
повреждение ' ;
const
STR_MON_KILL = ' завален! ' ;
Попробуйте походить по локации,
нападая на встречающихся монстров. После перерисовки карты погибший монстр
исчезнет с нее автоматически. Пока сражения окажутся несложными - ведь чудовища
пока не умеют давать сдачи. Монстры наносят ответный удар
Где будет находится процедура
ответных ударов окружающих героя монстров? Она будет вызываться сразу после
процедуры HeroAltack. Назовем ее MonstersAttack:
procedure MoveHero( dx,dy:
Integer );
var m,
dam: Integer;
begin
if not
FreeTile( GameMap[CurMap].Cells[
Heroes [CurHero] .x+dx,
Heroes [CurHero.1 .y+dy] .Tile )
then
Exit;
m :
= IsMonsterOnTile(Heroes[CurHero].x+dx,
Heroes[CurHero].y+dy); if m > 0
then
begin
HeroAttack(Heroes[CurHero] , m);
MonstersAttack;
Exit
end;
Отметим, что в этой процедуре мы
не 01раничимся ответным ударом одного атакованного монстра. Ведь, хотя наш
положительный персонаж один, нападающих па пего монстров может быть много, и
каждый из них имеет право на свою атаку. При этом в свою очередь хода герой
может напасть только на одного монстра, точно также, как и его противники.
Каждый из них имеет право ровно на одну атаку, но ведь их несколько! Поэтому
персонажу периодически придется после одного нападения отражать несколько
вражеских атак. Реализуем процедуру MonstersAttack в модуле Combat.
procedure
MonstersAttack;
var i:
Integer;
begin
for
i := 1
to MaxMonsters do
if
(Monsters[i].HP > 0)
and CanAttack(i,CurHero) then
begin
MonsterAttack[I, Heroes[CurHero]);
End;
End;
Проверка монстра жив ли он и
здоров, а также находится ли герой в зоне видимости монстра проверку вынесем в
отельную функцию. Параметрами ее выступают индекс персонажа в массиве Heroes в
массиве (модуле) Monsters:
Function CanAttack(mi, hi: Integer): Boolean;
begin
… Distance( Heroes[hi].x,Heroes[hi].y,
Monsters[mi].x,Monsters[mi].у ) = 1;
End;
Проверка выполняется просто - достаточно
проверить, находится ли монстр на расстоянии одной клетки от персонажа. Расстояние
между объектами локации измеряется, клетками по вертикали и горизонтали. Это
расстояние можно вычислить с помощью … Distance
Function …Distance (xf, yf, yi, xt, yt:
Integer): Integer;
Begin
…Distance := Abs(xf-yf) +
abs(yf-yt);
end;
Зрением наделен монстр,
способный напасть на героя, выполняется расчет его атаки. Эта атака может быть
запрограммирована так:
Procedure MonsterAttack (m: Integer; var H: THero );
Var
…:integer;
Begin
… (Monsters[m].Name+STR_MON_STOP);
… SkillDefence(H);
… :
Defence (Monsters[m].Adl, Monsters[m].Ad2 );
SkillDefence(H);
If … then
begin
…(Monsters[m].Name + STR_MON_DEF);
…
… :
… (Monsters[m].Name + STR_MON_ATTACK) ;
… (H … def) ) ;
Вначале происходит проверка умения
героя уклоняться от ударов. Умения skillDefence нет. Поэтому выполним процедуру
добавления. В модуле Неrо
увеличиваем значение константы MaxSkills i константу skillDefence:
Const …Skills=3;
Const …Weapon=1;
Const …Search=2;
Const …Defence=3;
Во время компиляции программы возникнет
ошибка в модуле Tables
- ведь мы … BaseSkill_Table, в котором храняncя базовые значения навыков.
Заменим новым значением:
const BaseSkill__Table:. array[1. .MaxSkills]
of Integer =
(
30, 20,
25
);
Величина 25% для защиты выглядит
не очень высокой. Она означает, что герой сможет отбивать каждый четвертый
удар. Однако далее мы сделаем так, чтобы со временем этот процент быстро рос -
до разумных значений, конечно. Далее исправим процедуру SkillTest:
function
SkillTest( var H: THero;
ski: Integer ):
Boolean;
var xp:
Integer;
begin
SkillTest
:= false;
if
random(100)+l > H.Skills[ski] then
Exit;
SkillTest
:= true;
case ski
of
skillHandWeapon, skillDefence:
begin
SuccessSkillTest(H, ski);
end;
skillTrapSearch:
begin
Showlnf о (STR__TRAPOK) ;
IncXP(H,
H.Level + random(H.Level));
SuccessSkillTest (H, skillTrapSearch.) ;
end;
end;
end;
Здесь проверки навыков, не
требующих дополнительных действий, объединены в один обработчик.
А рост навыка обороны добавим в
процедуру SucccssSkillTesl. Как часто нам надо будет повышать этот навык?
Вспомним аналогичные подсчеты, связанные с умением наносить удары
(.skillHandWeapon).
За игру персонаж, как мы
подсчитали, может атаковать монстров около двух тысяч раз. Количество ответных
нападений монстров будет, конечно, большим. Ведь для героя мы считали только
успешные атаки, а обороняться ему придется от всех нападений противника.
Поэтому выберем приближенное значение - например, пять тысяч. Пусть навык
возрастет с 25 до 75%. Большее, чем 75, значение брать неразумно - ведь тогда
герой станет слишком неуязвимым и способным отражать все без исключения атаки
противника. Теперь мы можем рассчитать вероятность повышения этого навыка на
единицу - она будет равна 5000 / (75 - 25) или такое повышение должно
происходить в одном случае из ста.
procedure SuccessSkillTest( var H:
THero; ski: Integer);
var rnd:
Integer;
begin
case ski of
skillDefence:
begin
if random(100)
= 0 then
begin
ShowInfo(STR_DEFENCESKILL_OK) ;
inc(H.Skills[skillDefence]);
End;
End;
Если герою не удалось уклониться
от нападения, выполняется проверка поражения, которое наносит монстр. Эта
величина зависит от характеристик монстра (поля Adl, Ad2). Наконец, вычисляется
броня (величина, аналогичная шкуре монстра). Такая бронезащита связана с количеством
надетой на героя брони. Расчет данного параметра производится в GetHeroDefence:
Function GetHeroDefence( var Н: ТНего ): Integer;
Var ..:integer;
begin
if H.Slots[SlotBody].IТуре <> itemNone then
…:=H.Slots[SlotBody].Ints[intArmorDefence];
…I=d;
Здесь действия вполне можно было вынести в один
оператор, но мы выбрали структуру функции с учетом того, что число слотов,
связанных с персонажем может быть увеличено. Если в дальнейшем в программу
будет добавлен слот, то данную процедуру надо видоизменить следующим образом:
Function GetHeroDefence( var Н: ТНего ): Integer;
Var ..:integer;
begin
if H.Slots[SlotBody].IТуре <> itemNone then
D:=H.Slots[SlotBody].Ints[intArmorDefence];
if H.Slots[SlotHelm].IТуре <> itemNone then
D:=H.Slots[SlotHelm].Ints[intArmorDefence];
Defence I-d;
Добавим два слота шлема:
Const SlotBody=1;
SlotHans=2;
slotHelm=3;
В модуле Texts:
Const HeadSlots=3;
SlotName: array[1..MaxSlots] of string[20] =
(
‘ Тело’ , '
В Руках '
, ' На
Голове '
);
В MonsterAttack к происходит снижение уровня здоровья героя -
при попадании. В упомянутых процедурах выглядят так:
STR_DEFENCESKILL_OK
= '
Вы отбили удар! '
;
STR_NON_STOP=' атака отбита!
' ;
STR_NON_DEF=‘ наносит удар, но
не пробивает вашу’;
STR_NON_ATTACK=' наносит удар!
' ;
Продолжение следует...
Источник: Delgame.at.ua
|