iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 25
2

引言

昨天完成了玩家的 生命攻擊防禦 等設定,
那接下來就是戰鬥啦!

還記得昨天引言中提到魔塔全自動戰鬥機制
今天就要利用該機制來設計我們自己的戰鬥。

以下是今天敵人的規格表,底下將會使用到它們:

敵人 生命 攻擊 防禦
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中加上條件:
https://ithelp.ithome.com.tw/upload/images/20191009/20111429SgZN92Z9I2.png

/* 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函數上,希望大家有看懂魔塔的自動戰鬥邏輯,以下是執行測試:

https://ithelp.ithome.com.tw/upload/images/20191009/20111429xIEvjqgS1t.png
https://ithelp.ithome.com.tw/upload/images/20191009/20111429ATEeRxacON.png
https://ithelp.ithome.com.tw/upload/images/20191009/20111429naRoKfjDKc.png

大家可以看到1, 2, 3號怪都輕鬆解決了~


https://ithelp.ithome.com.tw/upload/images/20191009/20111429xdXaLTq4UH.png

到4號怪直接被打了638滴血,超過了原來的50%,系統印出「滿痛的」

https://ithelp.ithome.com.tw/upload/images/20191009/20111429Qa2xiGcXSM.png

到5號的時候血量就不夠打了,系統提示「打不過」


這樣就實作出簡易的戰鬥系統囉!


上一篇
[11屆鐵人賽Day24] 2D遊戲—生命、攻擊、防禦
下一篇
[11屆鐵人賽Day26] 2D遊戲—錢$$、商店
系列文
若沒有遊戲引擎、合作夥伴...做得出遊戲嗎? 不試試看不知道吧? [使用C語言]30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言