引言
昨天完成了玩家的 生命、攻擊、防禦 等設定,
那接下來就是戰鬥啦!
還記得昨天引言中提到魔塔的全自動戰鬥機制,
今天就要利用該機制來設計我們自己的戰鬥。
以下是今天敵人的規格表,底下將會使用到它們:
敵人 | 生命 | 攻擊 | 防禦 |
---|---|---|---|
1 | 35 | 18 | 1 |
2 | 45 | 20 | 2 |
3 | 35 | 38 | 3 |
4 | 60 | 32 | 8 |
5 | 50 | 42 | 6 |
然後其實這些資料是參考魔塔中前五個會遇到的小怪...
Monster 結構
我們必須先定義出什麼是「怪」(Monster),這部分跟「玩家」差不多,
只是少了幾個屬性:
/* File: PlayGround.h */
typedef struct monster
{
char skin; // 共5種怪,skin分別是'1', '2', '3', '4', '5',一目瞭然對吧XD
int hp; // 以下三個分別是 生命、攻擊、防禦
int atk;
int def;
}Monster;
Monster m1, m2, m3, m4, m5; // 宣告五種怪
Monster初始化、設定數值
建立好結構後需要初始化它們,並幫五種怪根據規格表設定數值:
/* File: PlayGround.h */
void initMonster();
/* File: PlayGround.c */
void initMonster() // 根據規格表替五種怪設定數值
{
m1.skin = '1';
m1.atk = 18;
m1.def = 1;
m1.hp = 35;
m2.skin = '2';
m2.atk = 20;
m2.def = 2;
m2.hp = 45;
m3.skin = '3';
m3.atk = 38;
m3.def = 3;
m3.hp = 35;
m4.skin = '4';
m4.atk = 32;
m4.def = 8;
m4.hp = 60;
m5.skin = '5';
m5.atk = 42;
m5.def = 6;
m5.hp = 50;
}
在main函數中呼叫初始化函數:
/* File: main.c */
.
..
...
initPlayerStatus();
// ------------新增部分-------------------
initMonster();
// --------------------------------------
playGround(LEVEL0_MAP, 28, 4);
playGround(LEVEL1_MAP, 28, 27);
return 0;
地圖上加入五種Monster
我們先嘗試著在level0中加入5種怪,並在movePlayer中加上條件:
/* File: PlayGround.c */
// 寫在movePlayer中
if(game_map[y][x] == '1') // 如果遇到1號怪
{
if(atkMonster('1') == 0) // 如果打不贏 (atkMonster等等要定義了)
{
printf("怪太強,練練後再來吧!\n"); // 打不贏就勸退玩家
waitForUser(500);
}
else // 如果運算起來,打贏了(atkMonster會把血量扣好)
{
game_map[p.y][p.x] = ' ';
game_map[y][x] = p.skin;
p.x = x;
p.y = y;
printf("打贏1號怪!\n"); // 就告知打贏了
playMusic(SYSTEM_OK, SE_MODE);
waitForUser(500);
}
}
if(game_map[y][x] == '2')
{
if(atkMonster('2') == 0) // 如果打不贏
{
printf("怪太強,練練後再來吧!\n");
waitForUser(500);
}
else
{
game_map[p.y][p.x] = ' ';
game_map[y][x] = p.skin;
p.x = x;
p.y = y;
printf("打贏2號怪!\n");
playMusic(SYSTEM_OK, SE_MODE);
waitForUser(500);
}
}
if(game_map[y][x] == '3')
{
if(atkMonster('3') == 0) // 如果打不贏
{
printf("怪太強,練練後再來吧!\n");
waitForUser(500);
}
else
{
game_map[p.y][p.x] = ' ';
game_map[y][x] = p.skin;
p.x = x;
p.y = y;
printf("打贏3號怪!\n");
playMusic(SYSTEM_OK, SE_MODE);
waitForUser(500);
}
}
if(game_map[y][x] == '4')
{
if(atkMonster('4') == 0) // 如果打不贏
{
printf("怪太強,練練後再來吧!\n");
waitForUser(500);
}
else
{
game_map[p.y][p.x] = ' ';
game_map[y][x] = p.skin;
p.x = x;
p.y = y;
printf("打贏4號怪!\n");
playMusic(SYSTEM_OK, SE_MODE);
waitForUser(500);
}
}
if(game_map[y][x] == '5')
{
if(atkMonster('5') == 0) // 如果打不贏
{
printf("怪太強,練練後再來吧!\n");
waitForUser(500);
}
else
{
game_map[p.y][p.x] = ' ';
game_map[y][x] = p.skin;
p.x = x;
p.y = y;
printf("打贏5號怪!\n");
playMusic(SYSTEM_OK, SE_MODE);
waitForUser(500);
}
}
與怪物戰鬥
再來就要把上頭的atkMonster實作了,它會負責計算好血量,並在玩家打贏後扣掉血量:
/* File: PlayGround.h */
int atkMonster(char monsterSkin); // 傳入怪物skin,進行戰鬥判定,傳回成功或失敗
/* File: PlayGround.c */
int atkMonster(char monsterSkin)
{
int monsterCurrentHp; // 用來裝怪獸目前的血量(不動到原本的血量,這個拿來計算用)
int playerCurrentHp; // 用來裝玩家目前的血量(不動到原本的血量,這個拿來計算用,
// 打贏後才更動原本的)
playerCurrentHp = p.hp; // 把值設為原來的血量
Monster *monsterChooser; // 由monsterSkin判斷受操作者是誰,用指標來指向要操作的Monster類型
if(monsterSkin == '1') // 如果收到的skin是1,把指標指向m1
{
monsterChooser = &m1;
}
else if(monsterSkin == '2') // 以此類推至下面
{
monsterChooser = &m2;
}
else if(monsterSkin == '3')
{
monsterChooser = &m3;
}
else if(monsterSkin == '4')
{
monsterChooser = &m4;
}
else if(monsterSkin == '5')
{
monsterChooser = &m5;
}
else
{
monsterChooser = &m1;
}
if(monsterChooser->def >= p.atk) return 0; // 一定輸,當你的攻擊力比怪的防禦小,根本打不穿
if(p.def >= monsterChooser->atk)
{
printf("本次玩家毫髮無傷!\n");
return 1; // 一定贏,當你防禦比怪的攻擊力大,你根本不會被打到
}
monsterCurrentHp = monsterChooser->hp; // 設定怪目前的血量
while((playerCurrentHp > 0) && (monsterCurrentHp > 0)) // 只要雙方都沒人死,就不斷戰鬥
{
monsterCurrentHp -= (p.atk - monsterChooser->def); // 怪被玩家打
if(monsterCurrentHp <= 0) break; // 怪如果死了就離開戰鬥
playerCurrentHp -= (monsterChooser->atk - p.def); // 玩家被怪打
}
if(playerCurrentHp <= 0) return 0; // 玩家和怪如果一起死,算輸
if(monsterCurrentHp <= 0) // 玩家沒死,怪死,就贏了
{
printf("本次玩家所受傷害: %d\n", p.hp - playerCurrentHp); // 計算本次玩家被打血量
if(((double)(p.hp - playerCurrentHp)) / ((double)(p.hp)) > 0.9)
{
printf("差點死掉(汗\n"); // 如果被扣的血佔原本血量90%,就印出「差點死掉」
}
else if(((double)(p.hp - playerCurrentHp)) / ((double)(p.hp)) > 0.5)
{
printf("滿痛的...\n"); // 如果被扣的血佔原本血量50%,就印出「滿痛的」
}
else if(((double)(p.hp - playerCurrentHp)) / ((double)(p.hp)) > 0.3)
{
printf("有點痛...\n"); // 如果被扣的血佔原本血量30%,就印出「有點痛」
}
p.hp = playerCurrentHp; // 贏了的話,血量要真的算給玩家
return 1;
}
}
執行
這次重點在atkMonster函數上,希望大家有看懂魔塔的自動戰鬥邏輯,以下是執行測試:
這樣就實作出簡易的戰鬥系統囉!