作者laechan (挥泪斩马云)
看板mud_sanc
标题[wizs] 副本(instance)简易使用手册
时间Thu Oct 13 10:18:28 2022
为了避免自己再忘记後不晓得怎麽使用,所以写这个手册。
/std/new_ob/instance_room.c 副本区域房间样本档
/std/new_ob/instance/ 主目录
/std/new_ob/instance/satin.c 单纯的 npc,里面备注了日後的星际传送门 npc
/open/cmds/cmd_instance.o 指令 instance 的储存档
/std/new_ob/instance/laechan/ 我所写的所有副本(包含一些范例)
// instance_ob.c
// 副本物件继承用样本档
// instance.c
// 副本区域房间样本档
// instance_rooms.c
// 副本房间暂存处,本物件不 update
上面三个都不用看,因为 simul_efun questing("instance" 已写好
// _instance.c
// 副本管理指令
只需要看这个指令,以及 laechan/ 目录下的 005.c 副本范例。
/open/cmds/cmd_instance.o 资料结构
"副本所在房间" : ([ "归类/副本" : ({ ({"开放的难度":0=单/1=多人,}),
({"开放的难度":0=单/1=多人,}), ... }),
.
.
]),
instance(副本管理指令)说明:
============================================================
instance -list 列出副本管理员所在地的副本情报
instance -enter [副本编号] 进入所选择的副本
也就是说在 instance 指令内就包含了如何从副本 A 跳去副本 B
的程式码。
1.部分副本进入前设有等级种族职业等限制。
2.部分副本进入前须通过防机测试。
这些都可以在跳过去之前写在 questing 之外。
管理者指令区
instance -export 汇出目前设定总表
instance -export here/房间完整路径档名 汇出指定资料设定
instance -add 房间完整路径档名 = 副本识别id 难度 单人/多人
instance -del 房间完整路径档名 = 副本识别id 难度 单人/多人
范例
instance -add /d/wiz/room/disc = laechan/001 normal single
instance -del /d/wiz/room/disc = laechan/001 normal single
instance -add /d/wiz/room/disc = justinj/002 hard1 multi
============================================================
> instance -export
副本设定总表:
副本所在房间 归类 副本名 难度 单/多人
instance -add /d/ppl/map/n6e4 = ppl_area/ina_tiger hard1 single
instance -add /d/ppl/map/n6e4 = ppl_area/ina_tiger hard1 multi
instance -add /d/ppl/map/n6e4 = ppl_area/ina_tiger normal single
.
.
指令内容,enter 的部份:
单人副本
questing("instance","create",tmps[n3*2-2],({files,({me}),
tmps[n3*2-1][0],tmps[n3*2-1][1]}));
多人副本
questing("instance","create",tmps[n3*2-2],({files,
tmps2,
tmps[n3*2-1][0],tmps[n3*2-1][1]}));
亦即进入副本只需呼叫 questing("instance","create",
tmps 资料结构即 ({"难度",0/1,"难度",0/1,...})
所以玩家假设选第一个,tmps[n3*2-2] = tmps[1*2-2] = tmps[0] = "归类/副本"
tmps[n3*2-1] = tmps[1*2-1] = tmps[1] = ({"难度",0/1})
files = 副本所在房间,即主 key
也就是说,如果要从我的工作室直接进入副本:
> l
[/u/l/laechan/workroom ]
= 天上界 =
明显出口有: 无
Lv255.副本管理员─绫儿(Instance manager, Satin)
> instance -list
绫儿: 这里可 enter 的副本如下:
==========================================
1.白瓦镇黑熊讨伐任务副本(普通单人模式)
2.测试串接副本(普通单人模式)
3.测试串接副本(普通多人模式)
==========================================
> instance -export
副本设定总表:
instance -add /u/l/laechan/workroom = laechan/001 normal single
instance -add /u/l/laechan/workroom = laechan/006 normal single
instance -add /u/l/laechan/workroom = laechan/006 normal multi
则 running code 可以这样写
questing("instance", // 呼叫副本处理
"create", // 呼叫创建(进入)副本
"laechan/001", // 副本名称
({
"/u/l/laechan/workroom", // 副本所在房间
({me}), // 传自己进去
"normal", // 难度
0, // 单人
}));
me->force_me("look");
========== 程式执行区 ==========
副本载入中......ok!
[/std/new_ob/instance_room#652352 ]
[副本]山林小径
你拨开草丛, 隐约地可以看见埋在杂草堆里面的道路, 由此可以
想见这条小径平时应该没有什麽人在走动. 据说小径可以通往这
座山的深处, 可是蛮危险的.
明显出口有: north.
========== 程式执行区 ==========
这样就可以实现从副本 A 跳到副本 B 的设计,不过这是以後的
事,重点是接了某任务,在解任务过程中需要把玩家传进副本,
这时候 files 的部份就很微妙,关键在於有些副本是不允许玩
家任意进入,只有在解任务解到某一步骤时才能进去。
== 分隔线 ==
再来需要副本的房间循环判断部份
:::::::::::::: /std/new_ob/instance/laechan/005.c ::::::::::::::
// 005.c
// Laechan@Sanc add in 2014/04/26
// 模拟幻想○域的地狱裂痕副本
instance_data=([
"002":(["instance_check":1,
"cant_go":(["north":1]),
]),
即需要循环判断就增设 instance_data 并给 instance_check = 1
int instance_check(string files,object room)
{
int flags,t;
object ob;
flags=(int)room->query("instance_flags");
t=time();
这个 flags 并不需要事前设定,最初值都是 0(即无)
switch(files)
{
// 西尔克交待任务
case "002":
switch(flags)
{
case 0:
birth_npcs(room,INSTANCE_NPC,"sealker");
instance_set(room,
({
"instance_data/enter_msgs",
({HIW"西尔克:你就是...来帮助我的人吧!"NOR"\n",
HIW"西尔克:请帮助我打倒矿坑内的魔物吧!"NOR"\n",}),
"instance_next_times",12+t,"instance_flags",1,
}));
break;
case 1:
if(t>room->query("instance_next_times"))
{
instance_del(room,({"cant_go/north"}));
instance_set(room,({"instance_flags",2,"already_ended",1}));
}
break;
}
break; // end of 002
采双层 switch 设计,针对不同的房间、不同的 flags 阶段,
来产生不同的结果。
刚进入房间时,它会产生 西尔克 这只 npc:
instance_set(room,
({
"instance_data/enter_msgs",
({HIW"西尔克:你就是...来帮助我的人吧!"NOR"\n",
HIW"西尔克:请帮助我打倒矿坑内的魔物吧!"NOR"\n",}),
"instance_next_times",12+t,"instance_flags",1,
}));
并跑出对话,同时设定触发下一 flags 阶段所需的时间 12 秒,
并把阶段参数 flags 改成 1。
则 12 秒过後
case 1:
if(t>room->query("instance_next_times"))
{
instance_del(room,({"cant_go/north"}));
instance_set(room,({"instance_flags",2,"already_ended",1}));
}
移除不能往北走的设定,并把 flags 改成 2(实际上无 case 2),
并加上 already_ended 参数,则 times_check 看到该参数就不会
做动作。
嘛,这样就没问题。我刚在从头看了一次,重点在於
1.副本档要先写好
2.然後决定要把它设定在哪个房间
3.然後就能让玩家进去玩了
我最近若有空,会拿毒竹城区域(即影子传说区域)来测试,测试若
成功,毒城城会改为副本区域,好处是
1.这会是一个在其它 mud 没看过的新设计
2.它可以模拟任天堂的影子传说,设定难度关卡
3.如果这个能成功放出来,那吞食天地二也可以具现化
==== 分隔线 ====
副本档案的 instance_check 函数无作用,原因不明。
检查 /open/cmds/quest/quest_data/quest_data.c 690 行起
if(s>=4)
ob->create_instance(vars[0],vars[1],vars[2],vars[3]);
else if(s==3)
ob->create_instance(vars[0],vars[1],vars[2]);
else
ob->create_instance(vars[0],vars[1]);
参数给足四个,因此会呼叫副本档案的 create_instance 函数
检查 /std/new_ob/instance/instance_ob.c
它会呼叫底下
// 最後再把房间资料设到 /std/new_ob/instance/instance_rooms.c
ob->ppl_instance("set",ppl_name,setting_data);
检查 /std/new_ob/instance/instance_rooms.c 的 ppl_instance 函数
case "set":
ppl_instance[ppl_name]=setting_data;
tmps=keys(ppl_instance[ppl_name]);
heart_beat_obs-=({0});
foreach(tmp in tmps)
if(tmp[0..0]!="#" && ob=ppl_instance[ppl_name][tmp])
heart_beat_obs+=({ob});
break;
我刚检查的结果,副本房间都有被丢进 heart_beat_obs 里头
int heart_beat()
{
int i,j;
object ob;
j=sizeof(heart_beat_obs);
for(i=0;i<j;i++)
{
if(!ob=heart_beat_obs[i]) continue;
else if(!ob->query("already_init")) continue;
else if(ob->query("already_ended"))
{
heart_beat_obs[i]=0;
continue;
}
// 有 instance_check 才做 check_instance
// if(ob->query("instance_check"))
ob->check_instance();
}
所以我搞错了,必须要有 already_init 但是不能有 already_ended
,一但出现 already_ended,instance_rooms 就会将其移除。
这时一种可行的方法,就是只要重新把它加回去即可,即
// 确定 room 有 already_init
INSTANCE_ROOMS->heart_beat_obs_func("add",room);
但基本上还是没用,因为不明原因 already_ended 都会被加上去,
以 grep 搜寻的结果,这东西出现在以下几个地方
/std/new_ob/instance_room.c
int init()
{
if(!query("already_init"))
{
string instance_ob,msg;
if(instance_ob=query("instance_ob"))
{
set("already_init",1);
// laechan add in 2015/02/22
// 没有 instance_check 的情况直接加上 instance_ended
if(undefinedp(query("instance_data")) ||
undefinedp(query("instance_data/instance_check")))
set("already_ended",1);
if(msg=catch(call_other(instance_ob,"init_instance",this_object(),ppl)))
write(msg+"\n");
}
}
这意思是,虽然一开始全部房间都被加进 heart_beat_ob,但是
进入某房间後,如果该房间没有 instance_data 资料或是没有
instance_data/instance_check 资料,那就设定 instance_ended
,接着 heart_beat 发现它有 instance_ended 时就会将其移出
心跳判断。
对 instance_room.c 除错的结果:
> instance -enter 2
副本载入中......ok!
debug:
already_init=0
already_ended=0
instance_data=UNDEFINED <= 这里是有问题的
ppl=玩家(laechan /std/user)
将 init 内 catch 的段落挪到前面:
debug:
already_init=1
already_ended=0
instance_data=([ "instance_check" : 1 ])
ppl=玩家(laechan /std/user)
已可正常识别出资料,bug fixed。
Laechan
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 59.126.145.135 (台湾)
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/mud_sanc/M.1665627510.A.BCE.html
※ 编辑: laechan (59.126.145.135 台湾), 10/13/2022 17:31:05