作者laechan (小太保)
看板mud
标题[闲聊] 传统物件及虚拟物件
时间Wed Dec 12 21:50:01 2012
传统物件的特色是
void create()
{
里面呼叫一些设定函数,将资料设定在 data 变数里;
}
void init()
{
定义一些动作;
}
动作函数()
{
bla bla
}
然後这样的物件通常被玩家携带在身上,取出的方式是
if( ob = present("物件id", 玩家) )
或者说该物件本身为玩家所存在的环境,取出的方式是
if( ob=environment(玩家) )
或者其它的「实体物件型式」,例如以 find_living、
find_player、find_object、....等取出的物件。
因为资料都在物件里头,所以取出时通常是这样..
ob -> query("xxx")
ob -> query_xxx()
.
.
================================================
传统物件及虚拟物件的差异,可以用一个例子来说明。
比方某物件叫做 pill,它 add_action 了一个 eat 动
作,可以让玩家 eat pill。
int eat_pill()
{
玩家吃了药;
this_object()->remove(); // 让药消失
return 1;
}
假设我们这个 mud 有很多可以 eat 的东西,那麽最直
觉的想法就是,eat 不能写成像 chat、look 等等的「
全域指令」吗?
於是有了第一种变型写法,就是 eat 是全域指令
int cmd_eat(string str)
{
if(ob=present(str,this_player()))
{
if(ob->query("这东西可被 eat 的参数"))
{
玩家吃了药;
ob->remove();
这样写的好处,就是「所有的药」都不需要再定义 eat
动作以及相关的函数,只要写一个 eat 指令,然後所有
可 eat 的药都设定可被 eat 的参数,则有设参数的药
就可被 eat,其效果如同在物件里面定义 eat 动作一样
=================================================
那麽,进一步来想,「pill」这个 物件 真的需要存在
吗?例如改写上面的东西..
int cmd_eat(string str)
{
// 比方 eat str pill(力量药丸)
if(this_player()->query("obj/"+str)>0)
{
玩家吃了药;
this_player()->add("obj/"+str,-1);
.
.
换言之,玩家取得了药或买了药:
this_player()->add("obj/"+str,1 或 n);
玩家吃了药或卖出了药或将药给人:
this_player()->add("obj/"+str,-1 或 -n);
if(this_player()->query("obj/"+str)<1)
this_player()->delete("obj/"+str);
这就是第二种变型写法,也就是「药」这个物件实际
上已经不存在了,它变成只是一个「资料stream」,
只存在着被加减、设定及删除等特性,我称此为虚拟
物件。
当我们的 mud,很多的实体物件都能被虚拟物件所取
代时,它达到的最直接效果就是物件减量,我们不仅
可以省去「创造」物件的时间,还能更快速地处理一
些动作(如喝药水)。
==============================================
假设我们把 str pill(力量药丸) 给虚拟化了,那麽
我们可能会写一个指令来列出我们身上到底有哪些这
类的物品..
> obj
力量药丸 138颗 生命药水 144瓶
内力药水 50瓶 苹果 12颗
.
.
今天我们有了一个 eat 指令可以吃力量药丸了,进一
步来说,我们或许也想 view 看看力量药丸究竟系啥
米碗糕。
写法有非常多种,这里只举一种,就是定义「资料集
物件」,例如..
mapping obj_data=
([
"str pill":(["name" : "力量药丸",
"unit" : "颗",
"desc" : "这是一颗力量药丸,吃了(eat)之後力量会增加喔!",
"value" : 5000,
.
. ]),
"hp pot" :(["name" : "生命药水",
"unit" : "瓶",
"desc" : "这是一瓶生命药水,喝了(drink)它可以回复生命值。",
"value" : 1000,
.
. ]),
.
.
]);
那麽,当我们 view str pill 时,就去呼叫上述的物件
取得 obj_data["str pill"] 的所有资料,自然就能得到
下列结果
> view str pill
obj_data["str pill"]["name"]
==========================================
英文: str pill
单位: obj_data["str pill"]["unit"]
价钱: obj_data["str pill"]["value"]
叙述: obj_data["str pill"]["desc"]
==========================================
这就是第三种变型写法,也就是所有虚拟物件的资料,其
实都集中存放於一个物件的 mapping 资料里头,有需要
的时候就去读出即可。
这样既可确实减少物件的数量,但同时又能令虚拟物品具
有「实体感」。
==================================================
再来的话,既然可以有「虚拟物品」,那能否有「虚拟村
民」呢?比方说我们要列出房间所有的物品时是这样做的
obs = all_inventory(room);
foreach(ob in obs)
{
if(userp(ob))
msg += ob->query("short")+"正站在这里。\n";
else
msg += ob->query("short")+"\n";
}
那假设我们让虚拟村民的列表排在前头,那麽我们就可以
这样干..
vobs = room->query("vobs"); // 读取房间的虚拟资料
foreach(vob in vobs)
msg += 虚拟资料物件->query_vob_name(vob); // 读取该虚拟物件的名称
// 然後才做实体物件的列表
obs = all_inventory(room);
foreach(ob in obs)
{
if(userp(ob))
msg += ob->query("short")+"正站在这里。\n";
else
msg += ob->query("short")+"\n";
}
这样村民就会先被列在前头。而所谓的 look 村民的动作
就套用上面读取村民 desc 的应用即可。
这个适用於「村民通常没啥用途只是摆着好看」的 mud,
它可以确实减少村民物件的数量。
甚至,如果想写的更完善一点的话,还可以实现村民的「
loop speak」、「random moving」、「cound ask」....
这些都可以用虚拟物件来实现,而且都存在着多种写法。
此即虚拟物件的变通应用。例如圣殿目前使用的 quest 任
务系统(以任务脚本为主体)即是其变通应用的一种。
虚拟物件的好处在於可以省去许多 ob->query/set/add/..
或是 ob->query_xxx/set_xxx/add_xxx 的物件资料存取动
作,直接做本地式的资料存取,「有需要」时才去读取该
物件的其它资料即可,例如:
view 时 -> 才去读 desc
sell 时 -> 才去读 value
.
.
以上,一点心得跟大家分享。
Laechan@Sanc
--
※ 发信站: 批踢踢实业坊(ptt.cc)
※ 编辑: laechan 来自: 210.61.157.53 (12/12 22:06)
1F:推 kyoe:我还以为是 virtual object= = 12/12 22:39
2F:→ laechan:嗯? 12/12 23:33
3F:推 bnn:推 12/13 03:16
4F:推 fishsquare:push 12/13 04:27
5F:推 Jate:这种方式在给其他低阶GM撰写区域时, 不会造成困扰跟限制吗? 12/19 10:07
我们假设该 GM 已经写好区域及怪物,只差「掉落物品」没有
设定,以我自己在圣殿为例
/u/l/laechan/area/newsnake/room // 区域档存放位置
/u/l/laechan/area/newsnake/mob // 怪物档存放位置
> areadata short
snake1.c 一群啪啦啪啦蛇(Snake) [13条蛇]
snake2.c 一群唏咻唏咻蛇(Snake) [13条蛇]
snake3.c 一群波米波米蛇(Snake) [13条蛇]
目录下共有三种怪物,我想设计让它们都会掉蛇鳞、蛇牙以及
蛇尾,那我就先设计好三种虚拟物品。
> vobjs -filter name = 毒蛇
编号 名称 设定者 单位 性 质 携带 价钱 卖店 交易
============================================================================
m003 毒蛇的鳞片 laechan 片 怪掉落 99 100
m004 毒蛇的牙齿 laechan 颗 怪掉落 99 300
m005 毒蛇的尾巴 laechan 条 怪掉落 99 500
============================================================================
设定好之後,再设定「怪物掉落虚拟物品」,如下..
/u/l/laechan/area/newsnake/mob: list
([ "snake1" : ([ "m003" : 500, "m005" : 200, "m004" : 300 ]),
"snake3" : ([ "m003" : 500, "m005" : 200, "m004" : 300 ]),
"snake2" : ([ "m003" : 500, "m005" : 200, "m004" : 300 ]) ])
其中 500 代表掉落机率 50%。(这时 1 就代表 0.1%),
"m003" : 500 就代表鳞片掉落率 50%。
从这时起,玩家到这个区域打这三种怪物,就会掉鳞片、
牙齿及尾巴,而且不需要更动到怪物档。
而某一天例如我们想调整牙齿的价格时,使用 vobjs 指
令调整即可,价格可马上套用;又某一天想变更 snake1
的牙齿掉落率为 40% 时,用 vobjs 指令同样可马上变更
并套用。
它的好处就是摆脱传统「掉落物」的设定方式,将物品改
为虚拟(即实际上并没有鳞片这个物件存在),好更新、好
管理、而且其更动具有即时性。
> call snake;die
一群啪啦啪啦蛇发出一声惨叫!!
一群啪啦啪啦蛇慢慢的倒在地上死了...你得到 5346点的经验值。
你取得了一个毒蛇的鳞片。
再使用圣殿的 ob 指令即可进行观看及商店贩售
> ob
你的物品栏带着 4/99 种物品:
─────────────────────────────────────
1.毒蛇的鳞片 ( 36) 2.测试物品 ( 99) 3.毒蛇的尾巴 ( 2)
4.毒蛇的牙齿 ( 15)
> ob sell 36 毒蛇的鳞片
你卖掉 36 片毒蛇的鳞片获得 3600 影特币。
换言之,可依自己 mud 的需要,看是只写出虚拟物品用
来读值就好,或是搭配怪物掉落的设计、掉落物贩售交易
的设计都可。
然後其最终目的,都是为了让 wiz 「只需透过设定而非
撰写 code」的方式来配置怪物掉落物。
※ 编辑: laechan 来自: 210.61.157.53 (12/19 11:42)
6F:→ laechan:圣殿目前从区域绘图到区域完成,通通只需做设定不需写code 12/19 11:44