mud 板


LINE

看板 mud  RSS
这篇说明也会同步丢到 tmi2_v3_改 的 document 资料夹。 efun 可以想成是「先天的全域函数」,simul_efun 可以想成是 「後天的全域函数」,前者就可以随时取用的如 time(),後者则 定义在 /adm/simul_efun/ 目录下的各个函数物件内,然後再用 /adm/obj/simul_efun.c 将它们一一 include 进来,如 atoi.c 的 atoi 函数就是透过这样的方式才能成为全域函数。 我这次会在 tmi2_v3_改 资料夹里面放一个 func_spec.c 档,这 里面所列的函数就是 efun 函数,简单举几个常用的: unknown call_other 比方 ppl->set("level",10) 相当於 目标 函数 这个函数所接的参数们 call_other(ppl, "set", "level", 10); object this_object 这东西也是 efun,相同的还有 this_player() 等等,因为很 直觉就不做说明。 object clone_object _new(string, ...); 从这里可看出 clone_object 的动作实际上就跟做 new() 的动 作是差不多的。 int sizeof(mixed); 计算阵列的 size,如 tmps=({"a","b","c"}), sizeof(tmps) = 3 int strlen sizeof(string); 从上面可以发现 string 就跟「阵列」的概念是类似的,也就是 说如果一 string = "abcde", 它就类似({"a","b","c","d",e"}) 这样的阵列排在一起的结果。 strlen 就是计算字串的长度。strwidth 也相当於 strlen 只是 一般都用 strlen。 void destruct(object default: F__THIS_OBJECT); 简单的说 ob->remove() 一般就相当於 destruct(ob)。 destruct 因为是 efun,所以它就是很单纯的把 ob 给 destruct 掉而已,ob->remove() 我们还可以对 remove() 函数动一些手脚 ,这就是两者的差异。(remove 一般最终也是把 ob destruct) string file_name(object default: F__THIS_OBJECT); file_name 跟 base_name 读出来的东西差异如下 base_name(me) = "/std/user" file_name(me) = "/std/user#10" 其中 #10 就是「已载入的物件编号」,它的编号方式是采流水号 的做法,确保每一个已被载入的物件都有独特的编号。 (因为在编辑档案时档名不能带 #,所以它用#来当编号开头) string capitalize(string); 首字大写。 比方 name="laechan", 则 capitalize(name) = "Laechan" string *explode(string, string); 比方 tmp="1,2,3,4,5" 那 mixed tmps; tmps=explode(tmp,","); 则 tmps = ({"1","2","3","4","5"}) 也就是说 explode 就是对一个字串依特定的子字串去做拆解,将 拆解後的结果一一存进阵列,相当於其它程式语言常见的 split 常见的 explode 如下 string tmp=read_file("/x/x/xxx"); // 读入一个档案的内容 mixed tmps=explode(tmp,"\n"); // 依分行符号做拆解 则 tmps 里面每一个元素就相当於该档案的「每一行」。 mixed implode(mixed *, string | function, void | mixed); 这东西就是 explode 的相反,把阵列变成字串,并在阵列的元素 之间塞进指定的分隔字串。 例如 tmps=({"1","2","3","4","5"}) string tmp=implode(tmps,","); ↑指定的分隔字串 则 tmp = "1,2,3,4,5" ↑implode 出来的字串就会依指定的分隔字串分隔 所以 explode 与 implode 是相对的。 int call_out(string | function, int,...); call_out("哪个函数",几秒,要带过去的参数们); 比方一程式执行到最底下 write("你躺在床上开始休息,Z z z...\n"); call_out("rest_over",2,ppl); return 1; } // 两秒後这个函数才被呼叫 int rest_over(object ppl) { if(!ppl) return 1; ppl->set("hp",ppl->query("hp_src")); write("你睡饱了, 感觉体力全部回来了!\n"); return 1; } 但是建议 call_out 能免则免,因为它的执行类似底下 // t=目标时间 while(time()<t) { } 有学过程式的都知道上面的 loading 是很重的。 int member_array int strsrch 这两个就跟 sizeof 及 strlen 一样是可以放在一起讲的,就是 一个是针对阵列做处理,一个是针对字串做处理。 对阵列来说它是 第0个 第1个 ↓ ↓ 例如 mixed tmps=({"a","b"}); member_array("a",tmps); 传回的结果是 0 member_array("b",tmps); 传回的结果是 1 member_array("c",tmps); 传回的结果是 -1 代表没找到 又例如 string tmp="ab"; strsrch(tmp,"a"); 传回的结果是 0 strsrch(tmp,"b"); 传回的结果是 1 strsrch(tmp,"c"); 传回的结果是 -1 代表没找到 所以要注意的是,要判断一个 sub element 是不是存在於一个集 合里,用的做法不是 if(!strsrch(tmp,"a")) 或 if(strsrch(tmp,"a")) 而是 if(strsrch(tmp,"a")==-1) 或 if(strsrch(tmp,"a")!=-1) int input_to 这东西要讲可以讲好几页,所以只简单讲。 write("请输入 "); 输入完按enter後呼叫的函数 输入模式 参数群 你输入了什麽被存在这 input_to("input_over", 0, ...., str); 一般来说输入模式用 0 即可(也就是一般输入),像如果是输入密 码的情况会用 3 居多。 参数群就是指要带过去给 input_over 函数用的,str 就是储存你 所输入的东西,input_over 大概长这样 你输入的东西 带过来的参数群 int input_over(string str, ..............) 所以要注意的就是「你输入的东西」要放在 input_over 函数的最 前面,而在它之後所接的,才是你原先要让 input_to 带过来的参 数群,底下是例子 write("请输入: "); input_to("input_over",0,ppl,n,str); return 1; } int input_over(string str,object ppl,int n) { // 使用者没输入东西就按 enter if(!str || str=="") { write("请输入: "); input_to("input_over",0,ppl,n,str); return 1; } write("你输入的东西是: "+str+".\n"); return 1; } 然後以上面的用法为例,它也可以简略如下 input_to("input_over",0,ppl,n); // 不需要有 str 则这时候 ↓它同样会是你输入的东西 int input_over(string str,object ppl,int n) int random 简单的说 random(10) 跑出来的数字有可能 0~9。 object environment 简单的说 environment(ppl) 能传回 ppl 所在的空间,它不一定 是房间,比方某个东西 ob 放在你的身上,那 environmnt(ob)传 回的就是你,因为 ob 在你身上。 object *all_inventory 简单的说如果你所在的空间是 env mixed obs=all_inventory(env); // 传回在这个空间的所有物件 mixed obs=all_inventory(me); // 传回在 你身上 的所有物件 object *deep_inventory object first_inventory object next_inventory 这三个非常非常少用,有兴趣可自行用 running code 测试。 void say void tell_room 简单的说 say("test.\n"),呼叫主体本身看不到,呼叫主体以外 的同房间物件收得到,所以一般常看到的写法就是 write("你说道: test.\n"); // 给自己看的 say(me->query("cap_name")+"说道: test.\n"); // 给其它人看 而 tell_room(environment(me),"......") 就是给房间里所有的 人看的讯息。 然後它可以用来模拟 say,例如 write("你说道: test.\n"); tell_room(environment(me),me->query("cap_name")+ "说道: test.\n",({me}) ); ↑哪些物件排除於 tell_room 之外 object present 这个函数非常非常的常用。 ob=present("laechan",environment(me)); 它的意思就是去找 environment(me) 这个空间里面有没有 id 有 "laechan" 的物件存在,有的话 ob 就是这个叫 laechan 的物件 请注意是 "id",所以如果有两个以上的物件拥有相同的 id,例 如 大蚂蚁(big ant) id = ({"big ant","ant"}) 小蚂蚁(small ant) id = ({"small ant","ant"}) 当同一房间 env 的两个物件其 id 都有 "ant" 时 ob=present("ant",env) 这时候 ob = 大蚂蚁(big ant),因为它是同 id 里面排第一个的 ob=present("small ant",env) 这时候 ob = 小蚂蚁(small ant),因为只有一只叫 "small ant" ob=present("ant 2",env) 这时候 ob 也是 小蚂蚁(small ant),因为它是第二只 ant。 ob=present("xxx",env) 这时候 ob = 空(UNDEFINED),因为 env 没有 id 叫 xxx 的物件 void move_object(object | string); 这个很少用,所以我也不晓得它是干嘛的,有兴趣的可以自己试。 void add_action 这个最常见於 void init() 函数内,它可以定义一个动作指令, 当触发 init 函数的使用者执行了这个动作指令时,就可以指定 此时要呼叫哪一个函数来做处理: add_action("要做处理的函数","动作指令"); 或者也有这种用法 ↓执行哪些指令会呼叫同一函数 add_action("要做处理的函数",({"指令1","指令2",..})); 例如 add_action("openup_box",({"openup","打开"})); 则玩家下 openup 指令或是 打开 指令,都会呼叫 openup_box。 常见的做法如下 void init() { add_action("drink_water","drink"); } ↓使用者在 drink 後面接了什麽 int drink_water(string str) { if(!str || str=="") return notify_fail("你要喝什麽?\n"); 当触发者与触发主体已经不在同一 env 时,add_action 的指令就 自动失效,最常见的就是「当玩家离开了有 add_action 的房间後 」,比方 a 房间可 drink water,当你离开 a 房间时自然就不能 在 drink water。 string query_verb(); 以上面为例 int drink_water(string str) { string verb; verb=query_verb(this_player()); 这时 verb 就是 "drink " + str,也就是说它可以截取刚刚使用 者下了什麽指令,比方使用者下 drink water <= 这就是 verb。 int command(string); 它可以令呼叫主体执行一道命令,例如 command("say hi"); command("quit"); command("suicide"); 请注意是「呼叫主体」,非呼叫主体是不能令它人 command 的。 int remove_action(string, string); 既然有 add_action 当然就有 remove_action add_action("drink_water","drink"); // 增加触发者可下的指令 remove_action("drink_water","drink"); // 将该指令移除掉 int living 这东西是用来判断一个 ob 是不是「生物」,比方 if(living(ob)) 大概就是这样用,是生物的话(包含玩家与怪物)就传回 1。 mixed *commands(); 这东西很少用。 void disable_commands(); void enable_commands(); 一般在 mob 档案内会看到 enable_commands(),它的用意就是要 让该 mob 处於可执行指令的状态。 反过来说 disable_commands() 就是要 disable 掉该 mob 能下 指令的状态。 void set_living_name(string); 这东西跟 set("id") 的差异,可以想成是是否有登录为「全域id 」,比方: set("id",({"small ant","ant"})); set_living_name("ant"); 当一只怪物有 set_living_name 并被载入时,你 chat *hi ant 就有可能透过 find_living("ant") 找到这只 ant。 若没有 set_living_name 的话 find_living 就找不到。 一般 mob 都是会 set_living_name 的,但是反过来说,如果这只 怪物只是做一般用途,不 set_living_name 反而是比较好的。 (因为 living 判断不会因有无 set_living_name 而改变,只是没 有 set_living_name 的话 find_living 会找不到而已) object *livings(); object *users(); object *objects(); 这三个可以一起讲 livings 传回的就是所有被 set_living_name 且被载入的生物 users 传回的就是所有线上的玩家 objects 传回的就是所有已被载入的物件 所以其包含范围是 objects > livings > users 要注意的就是 livings 包含 users 这一点,因为玩家也是生物, 也同样有被 set_living_name。 object find_living(string); object find_player(string); find_living 就上面提过的。find_player 则将目标锁定在玩家。 object ppl=find_player("laechan"); 当 "laechan" 这个玩家有在线上时,ppl 就是 laechan 这个玩家 void notify_fail 这个现在也很常用,可以把它跟 write 放在一起。 write("test.\n"); return 1; 与 notify_fail("test.\n"); return 0; 或写成 return notify_fail("test.\n"); 为什麽要有 return 1 跟非 return 1 的区别呢,比方说我们下了一 个指令 10 n,结果你往北 5 格就会撞墙,在 _go.c 里面会这样写 if(!room->query("exits/"+dir)) return notify_fail("往 "+dir+" 这个方向没路喔.\n"); me->move(room->query("exits/"+dir)); return 1; 而 10 n 的回圈判断就是这样写 for(i=0;i<10;i++) if(command("go north")<1) break; 也就是说,如果我们在执行 10 n 的过程中遇到失败(return 0), 那剩下的就不需要再做(break),因为再做也是失败,所以我们才 需要有一个用来判断成功或者是失败的回传值,以上面的例子为例 它定义的方式就是 return 1: 成功 return 0: 失败 string lower_case(string) 它可以把一个字串里面的全部字母通通小写 string tmp1="LaeChAn"; string tmp2=lower_case(tmp1); 则此时 tmp2 = "laechan" 全部都会小写。 string replace_string 简单的说例如 string tmp1="我 是 一 只 小小 小 小 鸟" tmp2=replace_string(tmp," ",""); 上面的意思就是说我要把 tmp1 这个字串里面的 " " 空格,全部替 换成 "",则这时候 tmp2="我是一只小小小小鸟"; // 空格全部消失 也就是说要找寻的目标字串放前面,要用来替换的字串放後面。 int restore_object(string, void | int); mixed save_object(string | int | void, void | int); 这东西在有储存资料的系统很常见。 if(file_exists("/data/xxx.o")) restore_object("/data/xxx"); 上面的意思就是说,如果资料档 xxx.o 存在的话,就 restore 它 save_object("/data/xxx"); 然後如果要储存资料到 /data/xxx.o 的话其呼叫就如上。 这个可自行观看有使用这两个呼叫的系统物件,会比较清楚。 string save_variable(mixed); mixed restore_variable(string); 这两个很少用。 mixed *get_dir(string, int default: 0); 这东西主要用来读取一个目录下有哪些「档名以及目录名」,例如 说 > ls /d/area/test Path: [/d/area/test] 1 boat.c* 1 port1.c* 1 port2.c* tool/ 可以看到有三个 .c 档以及一个 tool 目录,则 mixed tmps=get_dir("/d/area/test/"); 则 tmps=({ "boat.c", "port1.c", "port2.c", "tool" }) 请注意传回的是「档名」而不是「完整档案路径名称」;传回的是 「目录名」而不是「完整目录路径名称」。 则通常要用来判断传回的是目录还是档案的做法就如下 string tmp,paths="/d/area/test/"; mixed tmps=get_dir(paths); foreach(tmp in tmps) { // 要用完整的路径档名去做判断 if(file_size(paths+tmp)==-2) write(paths+" 目录下的 "+tmp+" 是一个目录.\n"); else write(paths+" 目录下的 "+tmp+" 是一个档案.\n"); } void message(mixed, mixed, string | string * | object | object *, void | object | object *); 这东西在 simul_efun 还算蛮常见的,但是实际用的情况很少,因 为多半都有自订一些跑讯息用的函数了,这个「源头函数」就很少 用。 理论上要做简繁转换可以靠修改它来做。 mixed *values(mapping); mixed *keys(mapping); void map_delete(mapping, mixed); (allocate_mapping 很少用) 这几个就是用在 mapping 变数的操作上的,例如今天宣告一个 :号前面叫 key :号後面叫 value ↓ ↓ mapping data=([ "laechan" : "小宝", "spock" : "小强", ]); mapping 资料简单的说就是 ([key:value, key:value, ...]) mixed tmps=keys(data); 则 tmps = ({"laechan","spock"}) 就是所有 key 的集合 mixed tmps=values(data); 则 tmps = ({"小宝","小强"}) 就是所有 value 的集合 map_delete(data,"laechan"); 则 data 就剩下 (["spock":"小强"]), 也就是把 "laechan" 这个 key 及它所接的 value 给 delete 掉的意思。 int clonep(mixed default: F__THIS_OBJECT); int intp(mixed); int undefinedp(mixed); int nullp undefinedp(mixed); int floatp(mixed); int stringp(mixed); int virtualp(object default: F__THIS_OBJECT); int functionp(mixed); int pointerp(mixed); int arrayp pointerp(mixed); int objectp(mixed); int classp(mixed); string typeof(mixed); 这些都很直觉,例如说 mixed data=(["laechan":"小宝"]); if(mapp(data)) 就是用来做 data 是不是一个 mapping 的判断 if(arrayp(data)) 就是用来做 data 是不是一个阵列的判断 if(undefinedp(data["spock"])) 就是用来做 data 是不是有内含 一笔 key 为 "spock" 的资料,当没有这笔资料时,undefinedp就 会传回 1(因为是 undefinedp, "un"), 代表没有. 其它 intp、stringp、floatp、functionp、objectp 这些都很直 觉,pointerp、virtualp 很少用,nullp 也不常用。 typeof 则可以传回一个变数它到底是什麽型态。 其它下一篇介绍。(吃饭时间到了) --



※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 210.61.157.53
※ 文章网址: http://webptt.com/cn.aspx?n=bbs/mud/M.1403756049.A.761.html
1F:推 dannielz :推L大的每日MUD课堂 14.216.36.224 06/26 12:36
※ 编辑: laechan (210.61.157.53), 06/26/2014 13:31:38







like.gif 您可能会有兴趣的文章
icon.png[问题/行为] 猫晚上进房间会不会有憋尿问题
icon.pngRe: [闲聊] 选了错误的女孩成为魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一张
icon.png[心得] EMS高领长版毛衣.墨小楼MC1002
icon.png[分享] 丹龙隔热纸GE55+33+22
icon.png[问题] 清洗洗衣机
icon.png[寻物] 窗台下的空间
icon.png[闲聊] 双极の女神1 木魔爵
icon.png[售车] 新竹 1997 march 1297cc 白色 四门
icon.png[讨论] 能从照片感受到摄影者心情吗
icon.png[狂贺] 贺贺贺贺 贺!岛村卯月!总选举NO.1
icon.png[难过] 羡慕白皮肤的女生
icon.png阅读文章
icon.png[黑特]
icon.png[问题] SBK S1安装於安全帽位置
icon.png[分享] 旧woo100绝版开箱!!
icon.pngRe: [无言] 关於小包卫生纸
icon.png[开箱] E5-2683V3 RX480Strix 快睿C1 简单测试
icon.png[心得] 苍の海贼龙 地狱 执行者16PT
icon.png[售车] 1999年Virage iO 1.8EXi
icon.png[心得] 挑战33 LV10 狮子座pt solo
icon.png[闲聊] 手把手教你不被桶之新手主购教学
icon.png[分享] Civic Type R 量产版官方照无预警流出
icon.png[售车] Golf 4 2.0 银色 自排
icon.png[出售] Graco提篮汽座(有底座)2000元诚可议
icon.png[问题] 请问补牙材质掉了还能再补吗?(台中半年内
icon.png[问题] 44th 单曲 生写竟然都给重复的啊啊!
icon.png[心得] 华南红卡/icash 核卡
icon.png[问题] 拔牙矫正这样正常吗
icon.png[赠送] 老莫高业 初业 102年版
icon.png[情报] 三大行动支付 本季掀战火
icon.png[宝宝] 博客来Amos水蜡笔5/1特价五折
icon.pngRe: [心得] 新鲜人一些面试分享
icon.png[心得] 苍の海贼龙 地狱 麒麟25PT
icon.pngRe: [闲聊] (君の名は。雷慎入) 君名二创漫画翻译
icon.pngRe: [闲聊] OGN中场影片:失踪人口局 (英文字幕)
icon.png[问题] 台湾大哥大4G讯号差
icon.png[出售] [全国]全新千寻侘草LED灯, 水草

请输入看板名称,例如:Tech_Job站内搜寻

TOP