使用MUDCORE框架从零开发LPMUD游戏:荒野之息(虚拟对象世界地图)

《使用MUDCORE框架从零开发LPMUD游戏》系列内容,通过教程既能熟悉 MUDCORE 框架的使用,也能学习LPCMUD游戏的开发,版权所有 mud.ren。

游戏环境除了普通房间模式和上一节介绍的区域模式外,还有利用虚拟对象特性的自动环境模式和迷宫模式,适合打造荒野地型和地牢环境。

虚拟环境模式

虚拟环境模块和普通房间模式类似,只是使用坐标来处理不***间,world/start_room.c已经做了演示,我们明明只有一个文件代码,却生成了无尽的环境地区。

目前项目中world/dq9下面各编号的荒野地图也都是使用这种方式实现,代码示例:

#include <ansi.h>

inherit STD_WILD_9;

varargs void create(int x, int y, int z)
{
    ::create();
    set("short", "贝伦海岸");
    set("long", "这里是等待冒险者征服的地区,无数的魔物等待清理。");
    set("exits", ([
        "north" : __DIR__ "wild/" + x + "," + (y + 1) + "," + z,
        "south" : __DIR__ "wild/" + x + "," + (y - 1) + "," + z,
        "west" : __DIR__ "wild/" + (x - 1) + "," + y + "," + z,
        "east" : __DIR__ "wild/" + (x + 1) + "," + y + "," + z,
    ]));

    setArea(13, x, y, z);
    setMob(random(3), random(4), random(7));
}

这里我们没有使用复杂的代码,但也实现了魔物的自动生成,我们进不同地区会发现有不同的魔物生成,关键代码为:

    setMob(random(3), random(4), random(7));

具体功能在房间模块inherit/room.c实现,主要是把部分设置简化:

// 设置环境区域怪物种类、每种数量上限、等级范围(0~7)
varargs void setMob(int mob, int max, int range)
{
    set("mob", mob);
    set("mob_max", max);
    set("mob_range", range);
}

关键代码是在标准地牢对象STD_DUNGEON所在文件std/room/dungeon.cinit()方法:

/*****************************************************************************
Copyright: 2019, Mud.Ren
File name: dungeon.c
Description: 游戏地牢环境(危险区)继承对象 STD_DUNGEON
Author: xuefeng
Version: v1.0
Date: 2019-03-12
History:
*****************************************************************************/
#include <status.h>

inherit STD_ROOM;

void create()
{
    ::create();
    set("short", "地牢");
    set("long", "这里是废弃地牢,四周雾蒙蒙一片,什么也看不清。");
    set("no_fight", 0);
}

/**
 * 游戏刷新魔物设置
 */

void init()
{
    object me = this_player();
    object ob = next_inventory(me);
    if ( interactive(me) && !me->query_temp(STA_INVISIBLE)
        && query("area") && query("mob")
        && random(me->query_charm()) < random(200)
        && (!objectp(ob) || !living(ob)) )
    {
        set("objects", AREA_D->mob(query("dq"), query("area"),
            query("mob"), query("mob_max"), query("mob_range")));
        setup();
    }
    // 毒气陷阱
    if (query("pitfall"))
    {
        me->start_condition(CD_POISON);
    }
}

当玩家进入环境时触发init方法,调用地区守护进程ATEA_D加载魔物,代码如下:

/*****************************************************************************
Copyright: 2019, Mud.Ren
File name: area_d.c
Description: 游戏区域怪物守护进程
Author: xuefeng
Version: v1.0   Date: 2019-03-12
*****************************************************************************/
int *slice(int *mob, int range);
nosave mixed area_mob = ({
    ({}),
    ({1, 2, 3, 4, 5, 6, 7}),
    ({1, 6, 7, 8, 9, 10, 11}),
    ({12, 13, 14, 15, 16, 17, 21, 23}),
    ({5, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25}),
    ({20, 21, 22, 24, 25, 26, 27, 28}),
    ({27, 28, 29, 30, 31, 32}),
    ({30, 33, 34, 35, 36, 37, 38, 39}),
    ({29, 34, 35, 36, 37, 38, 39, 40, 41, 42, 101}),
    ({42, 43, 44, 45, 46}),
    ({43, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56}),
    ({48, 50, 51, 53, 54, 55, 56, 57, 204}),
    ({41, 52, 58, 59, 60, 61, 62}),
    ({58, 59, 60, 62, 63, 64, 65, 66, 67, 107}),
    ({57, 63, 64, 66, 67, 68, 69, 74}),
    ({68, 69, 70, 71, 72, 74}),
    ({71, 73, 75, 76, 77, 78}),
    ({72, 78, 79, 80, 81, 82, 86, 90, 93}),
    ({83, 84, 85, 86, 87, 88, 89, 204}),
    ({77, 81, 89, 91, 95, 102, 119, 137, 138, 154, 161, 169}),
    ({76, 92, 93, 94, 95, 96, 97, 98, 108}),
    ({98, 99, 100, 101, 102, 103, 111}),
    ({103, 104, 105, 106}),
    ({104, 106, 107, 108, 109, 110, 111}),
    ({110, 112, 113, 114, 115, 116, 117, 151}),
    ({112, 113, 114, 115, 116, 117, 118}),
    ({119, 124}),
    ({120, 121, 122, 123, 124}),
    ({125, 126, 127, 128, 129, 130, 132}),
    ({131, 133, 134, 135, 136}),
    ({139, 140, 141, 142, 143, 144}),
    ({139, 141, 143, 144, 145, 147}),
    ({145, 146, 147, 148, 149, 150, 151, 152, 153, 205}),
    ({138, 149, 150, 152, 154, 155, 156, 157, 158, 159, 160, 161, 163, 168, 169, 170}),
    ({89, 160, 164, 165, 166, 167, 168, 169}),
    ({170, 171, 172,173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 205}),
    ({185, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203}),
    ({202, 220, 230, 231, 232, 233, 235, 236, 237}),
    ({206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218}),
    ({207, 208, 214, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229}),
    ({209, 214, 238, 239, 240, 241, 242, 243, 244, 245, 246}),
    ({214, 221, 226, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256}),
    ({46, 87, 90, 105, 109, 137, 172}),
    ({88, 91, 92, 138, 162, 184})
});

/**
 * @brief 返回魔物列表
 *
 * @param dq    指定所属世界,现在暂未用到,仅支持9,query("dq")
 * @param area  指定所属地区,确定魔物列表,query("area")
 * @param kind  指定加载的魔物种类上限,query("mob")
 * @param max   指定加载种类魔物数量上限,总数最大值为kind*max,query("mob_max")
 * @param range 指定魔物分组范围,query("mob_range")
 * @return varargs
 */
varargs mapping mob(int dq, int area, int kind, int max, int range)
{
    int *mob, i;
    mapping mob_list = ([]);
    if (!dq)
    {
        dq = 9;
    }

    if (area < 1 || kind < 1 ||
        sizeof(area_mob) <= area ||
        !sizeof(mob = area_mob[area]))
    {
        return mob_list;
    }

    if (range)
        mob = slice(mob, range);

    if (max < 1)
        max = 1;

    for (i = 0; i < kind; i++)
    {
        mob_list += ([MOB_DIR + dq + "/" + element_of(mob):1 + random(max),]);
    }
    return mob_list;
}

void create()
{
    // debug_message();
}

// 数组切分3部分,方便控制地区怪物范围
// 0 0 0 : 高4 中2 初1
int *slice(int *mob, int range)
{
    int i;
    int *mob1, *mob2, *mob3;
    if (!range || range > 6)
        return mob;
    i = sizeof(mob) / 3;
    mob1 = mob[0..i];
    mob2 = mob[i..i * 2];
    mob3 = mob[i * 2..];

    return (range & 1 ? mob1 : ({})) + (range & 2 ? mob2 : ({})) + (range & 4 ? mob3 : ({}));
}

varargs int *mob_list(int area, int range)
{
    if (area < 1 || area >= sizeof(area_mob))
    {
        area = 1 + random(sizeof(area_mob) - 1);
    }

    return slice(area_mob[area], range);
}

守护进程统一管理了所有地区可以生成哪些魔物,这样我们不需要复杂的代码,就能满足多变的需求。

迷宫环境

虚拟环境模式可以用一个文件生成无尽的地图,但有时我们需要地图不大,但线路复杂,mudcore提供的迷宫模式则很好的满足了此需求。

我们看看world/dq9/2/maze1.c的代码:

// 迷宫
#include <ansi.h>
inherit CORE_VRM;

void create()
{
    //迷宫房间所继承的对象的档案名称
    set_inherit_room( STD_DUNGEON_9 );

    //迷宫房间里的怪物
    // set_maze_npcs(({MOB_DIR "9/11", MOB_DIR "9/28", MOB_DIR "9/38", MOB_DIR "9/44", MOB_DIR "9/55"}));

    //迷宫的单边长
    set_maze_long(10);

    //入口方向(出口在对面)
    set_entry_dir("south");

    //入口与区域的连接方向
    set_link_entry_dir("out");

    //入口与区域的连接档案名
    set_link_entry_room(__DIR__"entrance");

    //出口与区域的连接方向
    set_link_exit_dir("enter");

    //出口与区域的连接档案名
    set_link_exit_room(__DIR__"dungeon/-55,-5,6");

    //入口房间短描述
    set_entry_short(HIC "基萨格纳遗迹" NOR);

    //入口房间描述
    set_entry_desc(HIB @LONG
这里是基萨格纳遗迹,到处都是断壁残垣,魔物潜藏在里面等待着猎物,这里有离开遗迹的唯一出口,如果要深入探索千万不要迷路。
LONG NOR);

    //出口房间短描述
    set_exit_short(HIC "基萨格纳遗迹" NOR);

    //出口房间描述
    set_exit_desc(HIB @LONG
这里是基萨格纳遗迹,到处都是断壁残垣,向北方向的建筑风格很特殊,而且灯火通明,好像有谁住在里面。
LONG NOR);

    //迷宫房间的短描述
    set_maze_room_short(HIB "基萨格纳遗迹" NOR);

    //迷宫房间的描述,如果有多条描述,制造每个房间的时候会从中随机选择一个。
    set_maze_room_desc(HIB @LONG
这里是基萨格纳遗迹,到处都是断壁残垣,魔物潜藏在里面等待着猎物。
LONG NOR);

    // 迷宫房间是否为户外房间?
    // set_outdoors(1);

    // 是否绘制迷宫地图?
    set_maze_map(1);

    // 迷宫的额外参数
    set_extra_info(([
        "area" : 2,
        "zone/z" : 6,
        "mob" : 3,
        "mob_max" : 3,
        "mob_range" : 3,
    ]));
}

直接继承了mudcore框架提供的CORE_VRM模块,通过固定的配置生成动态的迷宫,具体功能可进入游戏中基萨格纳遗迹体验吧。

京ICP备13031296号-4