《使用MUDCORE框架从零开发LPMUD游戏》系列内容,通过教程既能熟悉 MUDCORE 框架的使用,也能学习LPCMUD游戏的开发,版权所有 mud.ren。
上一节我们实现了游戏系统的职业和等级模块,本节我们来开发基础的NPC功能,增加一些天使到天使界,让天使界不再空无一物。
请注意,本节内容一样是游戏功能,非MUDCORE框架提供,所以需要自己独立实现。
NPC是生物,所以要继承我们前面实现的生物公共模块LIVING
,但是NPC也会有一些自己特别的公共功能,比如NPC的战斗、行动等,为此我们单独实现一个NPC公共模块NPC
,对应代码文件inherit/npc.c
,目前内容如下:
/*****************************************************************************
Copyright: 2019, Mud.Ren
File name: npc.c
Description: 非玩家角色公共接口:NPC/魔物等
Author: xuefeng
Version: v1.0
Date: 2019-03-14
History:
*****************************************************************************/
inherit CLEAN_UP;
inherit LIVING;
int is_npc() { return 1; }
/**
* reset时回到起始环境
*/
int return_home(object home)
{
if (!environment() || environment() == home)
return 1;
if (!living(this_object()) || !mapp(environment()->query("exits")))
return 0;
message("vision", this_object()->name() + "急急忙忙地离开了。\n",
environment(), this_object());
return move(home);
}
/**
* 非战斗状态的行为
*/
// 说话
void chat(string msg)
{
command("say " + msg);
}
// 随机移动
void random_move()
{
mapping exits;
string *dirs;
if (!mapp(exits = environment()->query("exits")) || !sizeof(exits))
return 0;
dirs = keys(exits);
command("go " + dirs[element_of(dirs)]);
}
/**
* 战斗状态的行为
*/
// 使用咒文
void cast_spell(string spell)
{
command("cast " + spell);
}
// 使用特技
void exert_skill(string skill)
{
command("exert " + skill);
}
/**
* NPC行为控制
*/
void action()
{
// todo 行为控制,如战斗行为
}
// 触发任务提示
varargs void greeting(object ob, object me)
{
ob = ob || this_player();
me = me || this_object();
if ((environment(me)->is_area() && !area_environment(ob, me)) || environment(ob) != environment(me))
return;
msg("info", "$ME对$YOU说到:你好呀,旅行者,我这里需要你的帮助。(提示:ask " + me->query("id") + ")", me, ob);
}
void init()
{
object ob = this_player(), me = this_object();
// debug_message(sprintf("init: %O -> %O", ob, me));
if (interactive(ob) && QUEST_D->hasQuest(ob, me))
{
remove_call_out("greeting");
call_out("greeting", 1, ob, me);
}
}
注意:这里实现的功能比较多,实际开发中刚建的模块应该没什么能力,只有在开发到需要的功能时再完善,这里只是演示,所以把后面计划开发的功能直接都一次预留了,其中包括了任务和战斗相关的接口。
然后在include/inherits.h
中增加NPC模块的宏定义NPC
:
diff --git a/include/inherits.h b/include/inherits.h
index 232bdf9..1dc4526 100644
--- a/include/inherits.h
+++ b/include/inherits.h
@@ -12,6 +12,7 @@
#define MESSAGE "/inherit/message"
#define MOVE "/inherit/move"
#define NAME "/inherit/name"
+#define NPC "/inherit/npc"
#define ROOM "/inherit/room"
#define SAVE "/inherit/save"
#define USER "/inherit/user"
注意:这里使用git diff
功能生成的补丁,显示改变的代码,方便演示,学习时可以使用git apply
直接把diff更新自己的代码,如把以上代码复制到你项目下inherits.diff
文件中,然后指令git apply inherits.diff
可自动修改代码,后面对项目代码的改变也都用diff的方式显示。
下面我们更新以前实现的标准NPC对象std/living/npc.c
,完善代码如下:
diff --git a/std/living/npc.c b/std/living/npc.c
index 667e9c3..34388e4 100644
--- a/std/living/npc.c
+++ b/std/living/npc.c
@@ -1 +1,48 @@
// 标准NPC对象模板
+
+inherit NPC;
+
+varargs void create(string vocation, int lvl)
+{
+ mixed level_info;
+ if (!stringp(vocation))
+ {
+ vocation = "common";
+ }
+ if (lvl < 1)
+ {
+ lvl = 1;
+ }
+ if (lvl > 99)
+ {
+ lvl = 1 + random(99);
+ }
+
+ level_info = LEVEL_D->level_info(vocation);
+
+ set_name("小女孩", ({"girl"}));
+ set("long", "这是一个小女孩,一直梦想长大后成为托拉佩塔城公主的侍女。");
+ set("gender", "女性");
+ set("vocation", vocation);
+ set("lv", lvl);
+ set_attr("str", level_info[lvl][0]);
+ set_attr("agi", level_info[lvl][1]);
+ set_attr("vit", level_info[lvl][2]);
+ set_attr("luk", level_info[lvl][3]);
+ set_attr("charm", level_info[lvl][4]);
+ set_attr("mend", level_info[lvl][5]);
+ set_attr("int", level_info[lvl][6]);
+ set_attr("max_hp", level_info[lvl][7]);
+ set_attr("max_mp", level_info[lvl][8]);
+ // set("exp", 10);
+ // set("coin", 2);
+ set("no_get", "你想绑架啊?\n");
+ set("no_fight", 1);
+ set("action", ([ // 随机行为
+ "spell" : ({ }), // 可用咒文列表
+ "skill" : ({ }), // 可用特技列表
+ "msg" : ({ }), // 随机说话内容
+ "chance" : 0, // 行为机率,数值越大机率越小
+ ]));
+ setup();
+}
标准对象是基本功能完善的对象,可直接继承使用并根据需要扩展功能,所有具体对象都应该继承自标准对象。这个STD_NPC
会根据职业和等级自动取对应的属性并设置给NPC,同时示例了action
属性,将来在NPC模块中实现相关功能。
下面我们实现具体的天使代码,在world
目录中新建npc/angel.c
,代码如下:
inherit STD_NPC;
private create()
{
string *vocation_list = ({
"warrior",
"priest",
"mage",
"martial-artist",
"thief",
"minstrel",
"gladiator",
"paladin",
"armament-alist",
"ranger",
"sage",
"luminary",
});
::create(element_of(vocation_list), 100);
set_name("天使", ({"angel"}));
set("long", "这是一位守护世界的天使。");
set("gender", random(2) ? "男性" : "女性");
}
这里代码比较简单,因为具体功能在继承对象中已实现,代码只是列出了可用职业和描述,随机生成不同职业和等级的NPC。
下面修改出生地代码,在游戏中增加NPC:
diff --git a/world/start_room.c b/world/start_room.c
index d73085d..841629b 100644
--- a/world/start_room.c
+++ b/world/start_room.c
@@ -26,3 +26,15 @@ varargs private void create(int x, int y, int z)
setArea(0, x, y, z);
}
+
+void virtual_start()
+{
+ if (!random(3))
+ {
+ set("objects", ([
+ WORLD_DIR "npc/angel":random(3),
+ ]));
+ }
+
+ setup();
+}
现在我们进入游戏应该在天使界能随机看到一些天使了,不过因为look
指令没完善,看不到细节,现在完善一下指令,可以看NPC的职业和等级,代码更新如下:
diff --git a/cmds/std/look.c b/cmds/std/look.c
index 5917fae..83b6912 100644
--- a/cmds/std/look.c
+++ b/cmds/std/look.c
@@ -1,5 +1,7 @@
#include <ansi.h>
+#define SC_CMD "/cmds/std/score"
+
int look_room(object me, object env);
int look_item(object me, object obj);
int look_living(object me, object obj);
@@ -72,20 +74,118 @@ int look_item(object me, object obj)
return 1;
}
+string look_equiped(object me, object ob)
+{
+ mixed *inv = all_inventory(ob);
+ string str = "", subs = "";
+ string pronoun;
+ mapping equip_info;
+ int i, n = 0;
+
+ for (i = 0; i < sizeof(inv); i++)
+ {
+ if (inv[i]->query_temp("equipped"))
+ {
+ equip_info = inv[i]->query("equip_info");
+ switch (equip_info["type"])
+ {
+ case "accessory":
+ n++;
+ subs += HIC " [饰品]" NOR + inv[i]->short() + "\n";
+ break;
+ case "armor":
+ n++;
+ subs += HIC " [上身]" NOR + inv[i]->short() + "\n";
+ break;
+ case "bracer":
+ n++;
+ subs += HIC " [护腕]" NOR + inv[i]->short() + "\n";
+ break;
+ case "helmet":
+ n++;
+ subs += HIC " [头部]" NOR + inv[i]->short() + "\n";
+ break;
+ case "shield":
+ n++;
+ subs += HIC " [盾牌]" NOR + inv[i]->short() + "\n";
+ break;
+ case "shoe":
+ n++;
+ subs += HIC " [脚部]" NOR + inv[i]->short() + "\n";
+ break;
+ case "trouser":
+ n++;
+ subs += HIC " [腿部]" NOR + inv[i]->short() + "\n";
+ break;
+ case "weapon":
+ n++;
+ subs += HIC " [武器]" NOR + inv[i]->short() + "\n";
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if (me == ob)
+ {
+ pronoun = pronoun(2, me);
+ }
+ else
+ {
+ pronoun = pronoun(3, ob);
+ }
+ str += pronoun + "是一位 " + ob->query("lv") + " 级的" + chinese(ob->query("vocation")) + ",";
+ if (n)
+ {
+ str += pronoun + "装备着:\n" + subs;
+ }
+ else if (ob->is_npc())
+ {
+ str += pronoun + "的装备你看不出来什么属性。";
+ }
+ else
+ {
+ str += pronoun + "没有装备任何物品。\n";
+ }
+ return str;
+}
+
int look_living(object me, object ob)
{
- // 先简单实现,后期扩展功能
- string msg;
- if (ob == me)
+ string msg, equip_msg;
+
+ msg = "名称:" + ob->short() + "\n";
+ msg += "说明:" + ob->query("long") + "\n";
+
+ if (ob->is_mob())
{
+ tell_object(me, sort_string(msg, 72, 6));
return 1;
}
+
+ equip_msg = look_equiped(me, ob);
+
+ if (ob->is_npc())
+ {
+ msg += " " + equip_msg;
+ tell_object(me, sort_string(msg, 72, 6));
+ return 1;
+ }
+
+ if (ob == me)
+ {
+ tell_object(me, equip_msg);
+ }
else
{
msg = "$ME看了看$YOU,好像对$YOU很感兴趣对样子。";
-
+ msg("vision", msg, me, ob);
+ tell_object(me, equip_msg);
+ if (wizardp(me))
+ {
+ tell_object(me, SC_CMD->score(ob));
+ }
}
- msg("vision", msg, me, ob);
return 1;
}
至此,代码功能完善,提示一下,因为mudcore更新,移除了look指令别名,请自己增加内容为look
的别名cmds/std/l.alias
。
下面,在游戏中看看NPC,是不是显示了职业和等级?
天使界 - /world/start_room/-5,0,0
这里是位于星空深处的天使界,是守护世界的天使们居住的地方,据说女神就在天使界的
某地沉睡。
这里明显的出口是 south、north、east、west 和 up。
2个天使(Angel)
l a
名称:天使(Angel)
说明:这是一位守护世界的天使。
他是一位 82 级的圣骑士,他的装备你看不出来什么属性。
l a 2
名称:天使(Angel)
说明:这是一位守护世界的天使。
她是一位 67 级的魔法战士,她的装备你看不出来什么属性。
因为score
指令未完善,没有看NPC属性的能力,不过我们可以使用data
指令看看不同NPC的属性,如:
data a
Object: /world/npc/angel.c
action : ([ /* sizeof() == 4 */
"chance" : 0,
"spell" : ({ }),
"msg" : ({ }),
"skill" : ({ }),
])
attr : ([ /* sizeof() == 11 */
"mend" : 137,
"luk" : 8,
"mp" : 146,
"str" : 296,
"max_mp" : 146,
"agi" : 61,
"vit" : 301,
"hp" : 502,
"max_hp" : 502,
"int" : 0,
"charm" : 97,
])
gender : "男性"
home : "/world/start_room/-5,0,0"
id : "angel"
long : "这是一位守护世界的天使。"
lv : 82
name : "天使"
no_fight : 1
no_get : "你想绑架啊?
"
race : "人类"
vocation : "paladin"
总共有 12 个储存的资料。
TEMP DATA :
living : 1
总共有 1 个暂存的资料。
data a 2
Object: /world/npc/angel.c
action : ([ /* sizeof() == 4 */
"chance" : 0,
"spell" : ({ }),
"msg" : ({ }),
"skill" : ({ }),
])
attr : ([ /* sizeof() == 11 */
"mend" : 0,
"luk" : 76,
"mp" : 157,
"str" : 243,
"max_mp" : 157,
"agi" : 120,
"vit" : 168,
"hp" : 374,
"max_hp" : 374,
"int" : 195,
"charm" : 128,
])
gender : "女性"
home : "/world/start_room/-5,0,0"
id : "angel"
long : "这是一位守护世界的天使。"
lv : 67
name : "天使"
no_fight : 1
no_get : "你想绑架啊?
"
race : "人类"
vocation : "armament-alist"
总共有 12 个储存的资料。
TEMP DATA :
living : 1
总共有 1 个暂存的资料。
最后 commit 我们当前版本:
git add .
git commit -m 'v3.2'
本节代码
附本节版本源码下载地址,如果下载使用,请在项目目录执行 git submodule update --init
。