![]() |
![]() |
#1 |
高级会员
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() 注册: 08年04月11日
来自: 盘丝洞
帖子: 311
声望力: 20
声望:
50
![]() 现金:29两梁山币
资产:1245两梁山币
致谢数: 0
获感谢文章数:0
获会员感谢数:0 |
CONDITION系统分析 作者:叮当
condition 是利用系统的心跳来解决关于在一个不算太长的时间里,定时触发种现象的解决方法。在MUD中,为了解决定时触发某种现象,一般有两种方法,一种是通过 call_out()延时呼叫,另一种就是通过心跳。在spock翻译的LPC及教材中曾对这两种方法进行了比较,好象是说:如果时间较长的话采用心跳比 较好,时间短的话采用call_out()。紧接着又说了句,其个人看不出这两者有什么必要的区别,反正稀里糊涂的。不过,在实际运用中,两者都有不同的 效果,大家看明白了本文之后,可以有自己的理解,并进行正确的选择。 call_out(other_fun,t)这个外部函数就是起一种延时呼叫的作用,后面必须要加第一个参数,指定延时呼叫的函数名,第二个参数表示 延时几秒,再之后加上的参数可以作为呼叫的那个函数的参数。象这一个也就是设定在t秒钟后,呼叫other_fun这个事先指定的函数,你可以在这个函数 里设定好应该进行的事情。比如,你在一个玩家进入监狱后,要做牢五分钟,如果一定要用call_out()进行的话,你可以设定成: call_out("out_jianyu",300,ob); 意思就是在300秒后呼叫out_jianyu()这个函数,ob作为这个玩家的变量传递过去,然后在这个函数里进行将玩家从牢中放出来,告诉他做牢 结束等等处理。但是,有的巫师看到这里,就会问了,如果五分钟没到,这个玩家退出游戏了怎么办?对,如果这个玩家退出游戏,这个call_out()一到 时间就会找不到这个操作对象,如果程序写得不好就容易出错,即使是不会出错,那个玩家再次进入游戏后也就无法继续刚才的延时过程,也就不能够出牢。于是, 对于要跨起离线前后的象做牢这类的事,大多都是采用condition。 condition的处理方法,就是在开始的时候在玩家身上设定一定的点数的记号,这些记号会通过save()保存进玩家的档案中。然后通过系统的心 跳,每一次心跳就执行一次这个固定的condition的函数,函数每被执行一次就减一点记号,直至这个记号为0后,就可以触发某种效果。比如做牢,就在 进牢时设定一定点数,每一次心跳减一点,减为0时,将玩家放出。由于这个记号在玩家身上,因此,在玩家离线时不会发生到有关函数对这个玩家的操作。 在ES系列MUD中。一个完整的condition系统包括三个部分: 一、首先就是调用或者说是触发它的程序,也就是/inherit/char/char.c这个 文件,这是所有玩家与NPC都共同继承的文件,在里面的heart_beat()函数,就是每一次心跳时调用执行的函数。里面有这么几句: if( tick-- ) return; else tick = 5 + random(10); cnd_flag = update_condition(); 解释:tick是这个文件里的一个全局变量,假设它只要>1,那么tick--就不会等于0,那么tick值就会减1,并且立即return;中止这个函 数向下执行。假想如此这样经过几次减1之后,终有一次tick--就会为0,那么这时,程序就不会中止而是执行下一句的else。这时,tick被重新赋 值为5+random(10)。并执行update_condition()函数,update_condition()是一个什么函数呢?在 char.c里怎么也找不到。 那么我们再回头看看char.c这个文件的开头,就会看到,这个文件已经继承了/feature/condition.c文件,update_condition()这个函数正是在这个文件里面。所以下面我们就开看这个文件。 附:由于大多数MUD里的心跳是每两秒调一次,5+random(10)是5至14次,因此可以看出每一个condition被调用的时间是平均19秒。知道了这此,你才能更加有数地设定一些毒的发作时间,一些做牢的时间长短了。 二、下面就是主程序,feature目录下的condition.c文件。 程序详解: #include "condition.h"//继承一些宏定义 mapping conditions;//定义一个映射集 /*更新函数 update_condition() 这个函数首先要检查玩家身上的每一个condition是否有效,如果出现了无效的condition,它会记录进/log /condition.err这个文件里,经常性地检查一下这个文件,可以发现并排除相当多的错误,因为一旦有一个错误,会在每一次的心跳中经常性地发 生。然后它会按照condition的名字,也就是str这个变量去/kungfu/condition(某些MUDLIB下的路径是/daemon /condition)目录下去 寻找同名的.c文件进行执行。*/ nomask int update_condition() { mixed *cnd, err; int i, flag, update_flag; object cnd_d; if( !mapp(conditions) || !(i=sizeof(conditions)) ) return 0; //判断玩家有无condition的映射,没有就中止,毕竟不会每人都会有 cnd = keys(conditions); //从这个映射中把关键词取出组成一个数组,实际上就是不同condition的名字 update_flag = 0;//初始化这个变量 while(i--) //如果有1个以上的condition,就会一个个地循环执行 { cnd_d = find_object(CONDITION_D(cnd[i])); //到放condition的目录下寻找这个文件名,这个目录路径有宏定义的文件指定,一般在XKX风格里大多是kungfu/condition/下,xyj等放在/daemons/condition/下 if( !cnd_d )//如果没有的话,再尝试 { err = catch(call_other(CONDITION_D(cnd[i]), "???"));//强制检验 cnd_d = find_object(CONDITION_D(cnd[i]));//再次寻找 if( err || !cnd_d )//如果强制检验与再次寻找中仍找不到,表示不存在这个condition { log_file("condition.err",sprintf("Failed to load condition daemon %s, removed from %O\nError: %s\n",CONDITION_D(cnd[i]), this_object(), err) ); //记录下来 map_delete(conditions, cnd[i]); //删除玩家身上的这个condition continue;//继续循环下一个 } } flag = call_other(cnd_d, "update_condition", this_object(), conditions[cnd[i]]); //这个就开始执行这个conditon的设定文件里的update_condition()函数了,flag就是返回值,这个要看下面的具体设定文件的详解 if( !( flag & CND_CONTINUE ) ) map_delete(conditions, cnd[i]); //返回值为0就表示这个conditon完毕了,就删掉 update_flag |= flag; } if( !sizeof(conditions) ) conditions = 0; //检查一个也没有了,就清零 return update_flag; } /*改变大小函数 apply_codition(cnd,info),通过这个函数将玩家身上名叫的cnd的condition的值设为info,info可以为0,所以可以得用这个函数对玩家身上的condition进行增减。 */ nomask void apply_condition(string cnd, mixed info) { if( !mapp(conditions) ) conditions = ([ cnd : info ]); //如果没有的话,就添加上,以cnd为键名,info为内容值 else conditions[cnd] = info; //有的话,直接改变内容值 } /*取值函数 query_codition(cnd)通过这个函数,可以调出某个玩家身上名叫cnd的这种condition的值有多少。*/ nomask mixed query_condition(string cnd) { if( !mapp(conditions)||undefinedp(conditions[cnd]) ) return 0; //如果没有conditions或者没有这个cnd的condition,就返回为0 return conditions[cnd];//否则返回具体值 } /*清除函数 clear_condition()这个主要是在/feature/damage.c里的die()函数里调用,意思是一旦死亡,死者就会被清除所有的comdition。*/ nomask void clear_condition() { conditions = 0; } //END 三、下面就是上面说的与cnd同名.c文件--具体设定文件。一般每一种condition种类都要对应一个文件。里面只有一个 update_condition()函数,通过/feature/condition.c这个程序来调用它的这个函数,可以添加一些效果或信息,比如, 中毒的就会减精减气。但最主要是就每调用一次,将键名为这个cnd的内容值减少1点或多点。然后到了设定的点数,一般是到了0之后,会出现一些特殊的现 象。象做牢的到了0就会被放出来,等等。 下面看一个例子:少林的做牢的condition的详解: //kungfu/condition/bonze_jial.c #include <ansi.h> #include <login.h> /*参照前面调用这个函数的/feature/condition.c文件,就可以发现,调用它的时候会传递一个玩家与他的名为bonze_jia1的这个condition的值*/ int update_condition(object me, int duration) { if (duration < 1) //如果这个点数参数小于1,就表示做牢时间够了 { me->move("/d/shaolin/guangchang1"); //从牢房里直接移到少林大门口 message("vision","只听乒地一声,你吓了一跳,定睛一看,\n" "原来是一个昏昏沉沉的家伙从大门里被扔了出来!\n",environment(me), me); tell_object(me, HIY "只觉一阵腾云驾雾般,你昏昏沉沉地被扔出了少林寺!\n" NOR); //出一些信息 me->set("startroom", START_ROOM); return 0; } //如果不小于1,则设定减去一点,返回1,下次还会再执行 me->apply_condition("bonze_jail", duration - 1); return 1; } 到这里,condition的三大部分介绍完了,我们就以这少林寺的做牢为例,看一看condition执行调用的流程。 程序大约是在松林里被僧兵抓住后,进了戒律院,这个房间文件会通过apply_condition()这个函数在玩家身上被加上了名为bonze_jail的condition。于是玩家身上会多了这样的一个映射: condition([({"bonze_jial":35},.....)]) 那么通过前面的文件,就会看到,由于我们玩家是继承了char.c文件,每一个心跳中,就会检查tick,如果tick为1时,那么就会开始调用update_conditon()函数了。 这个函数首先取出玩家身上conditions映射中的关键词组成一个数组cnd: cnd = ({"bonze_jial",......}); 首先发现了bonze_jia1,于是开始到/kungfu/condition/目录下寻找名为bonze_jial.c的文件,也就是上面帖出的 第三部分的文件,如果没有找到这个文件就会被记录进/log/condition.err文件里,有的话,开始传递时这个玩家与这个conditon的 值,开始执行bonze_jial.c里的update_condition()函数 。第一次执行时duration也就是35,只要它大于或等1,就会被减出一点,返回值是1。会在下次tick为1的心跳是再次呼叫。这样经过若干次调用 后,duration已经等于0了,那么也就是durtion < 1,于是me被move到少林的广场,出现信息:只听乒地一声,你......me又被覆盖了startroom然后返回值为0。玩家身上就会被删除这个 condition。由于返回是0,那么/feature/condition.c文件里的update_condition()函数就会删除玩家身上的 这个bonze_jia1的内容。 除了做牢之外,通过这样的随机调用,可以实现象毒这样不定时发作的特殊效果,每调用一次就让玩家减一些精呀气之类的,却出现一些恐怖的中毒信息症状之 类的。其实,你还以设计一些特殊的毒,不但在发作的过程中伤害人体。而且要求必须在这段时间找到解毒方法或解药,否则,一旦到了最后一两点还没有彻底解 毒,这个人就OVER死翘翘。呵呵,看起来有点恐怖呀,实际上我觉得这样才是最符合实际情况的呢! 采用condition处理的好处就在于,这个属性是加在玩家身上的,通过在游戏中的心跳进行调用。如果玩家离了游戏就不会调用得到。一旦玩家进入游 戏中又会开始继续执行。这比call_out()一旦主呼叫者或呼叫中的参数失也就无法继续执行的缺点要灵活得多。此外,它由心跳调用,不管玩家在做什么 事情,它都可以即时地主动反馈情况。 而如果你无需即时反映变化,比如说去领工资,只需要规定玩家在一定时间的间隔之内不能领两次工资的话。可以在第一次领的时候,在玩家身上记下领的时 间,第二次再去领时,将当前时间与记录时间进行上减,看间隔够不够就行。象这样子就很简单,也几乎没有任何系统负责,比采用condition林节约得 多。而如果你需要有时间一到就立即通知玩家去领下一次工资的话。那就必须要采用conditon。 它的缺点也显而易见,放在每一次的心跳里呼叫。如果一个MUD系统里的生物与玩家身上的condition过多过长的话,系统的负担也是不小的。 最后谈一谈有关的BUG。许多新巫师没有正确或完全理解condition的用法,就开始大范围地使用。在一些不应该用的地方也使用 condition。举例,在某一个可以使玩家得到金钱等东西地方,设定任何玩家都必须间隔在线一定的时间才能去拿一次。如果这个通过condition 来实现。就有可能利用死亡后清除所有condition的特点,让一个玩家反复去死亡再立即重新去拿钱拿东西。象这种情况,你或者要修改死亡后会清除所有 condition,或者就不能采用它来做那些拿钱等东西的效果。 其二,象上面所讲的少林监狱的condition文件中,就隐藏着一个BUG。因为在XKX的文件里,少林的监狱并非是一定要等时间到了才会被放出 来,可以通过贿赂狱卒,走五行洞先出来。而这样的话,一旦这个conditon到了最后的时候,不论这个玩家在哪里,都会被一下子Move到少林广场,然 后出现你被扔出监狱等的字样。仔细想一想:如果MUD有不让你随便带东西或正常出来的地方,那么你就可以通过这个BUG跑到这种地方,把那些不能带出的东 西放在身上,专心等时间一到,就会象乘飞机一样,一下子从那个地方飞到少林的广场。(例如谁与争锋里演武场) 解决方法很简单,只要在if(duration < 1)下面再加一个条件: if(environment(me)->query("short")=="监狱") 才会move玩家出现信息。否则直接reuturn 0;就解决了。 最后,举一个毒的例子进行详解作为结束吧: // /kungfu/condition/ice_poison.c #include <ansi.h> #include <condition.h> inherit F_CLEAN_UP; int update_condition(object me, int duration) { if( duration < 1 ) return 0; if( !living(me) ) //如果对象不是清醒着,出的信息 { message("vision", me->name() + "浑身颤抖,痛苦地哼了一声。\n", environment(me), me); } else//否则就清醒着 { tell_object(me, HIB "忽然一阵奇寒从丹田升起,沁入四肢百骸,你中的寒冰绵掌发作了!\n" NOR ); message("vision", me->name() + "的身子突然晃了两晃,牙关格格地响了起来。\n",environment(me), me); } me->receive_wound("qi",15 + random(10)); me->receive_wound("jing", 10); //伤一定的精与气 me->apply_condition("ice_poison", duration - 1); //这个值减一点 if ( (int)me->query_temp("powerup") ) { me->add_temp("apply/attack", -(int)(me->query_skill("force")/3)); me->add_temp("apply/dodge", -(int)(me->query_skill("force")/3)); //降低他的攻击力与躲避力 me->delete_temp("powerup"); //结束他的powerup } if( duration < 1 ) return 0; //如果到了0,就返回,什么也不做,表示毒发结束,好了 return CND_CONTINUE; } |
![]() |
![]() |
![]() ![]() |
添加到书签 |
|
|
![]() |
||||
主题 | 主题作者 | 论坛 | 回复 | 最后发表 |
马上秋 [作者:孟婆(kop) ] | fengyue_xyj | 『 泥巴原创 』 | 17 | 2012-02-07 15:57 |
系统刷新与内存清除分析 作者:叮当 | Odysseus | 『 巫师天下 』 | 0 | 2011-12-03 23:02 |
叮当出车祸拉! | milky lotus | 『 超酷贴图区』 | 2 | 2011-02-27 19:40 |
现实中的叮当猫 | spell | 『 超酷贴图区』 | 1 | 2011-02-25 20:06 |
真人版的小叮当 | forget | 『 开心一刻 』 | 0 | 2003-08-25 18:16 |