□ 有关品质管制

所有的 Mud 都有自己的风格、发展方向、跟程式码品质的要求，通常这些要求会
随著主持这个 Mud 的神而异，但是往往由於每个神对 mudlib 的了解程度不一，
所能对品质要求的深度也不同，为了能够让程式码的品质维持在一个水准以上，最
好能够制订出一个最基本的品质要求，以下是东方故事 II 的品质要求：

□ 程式码

(1) 程式码必须缩排整齐，缩排一律用 tab (相当於 4 的空格)，虽然缩不缩排
    无关程式是否能够执行，但是如果你是一个只求程式执行正确，而不管别人
    是否容易阅读你的程式码，在 Mud 这样一个由多人共同发展程式的环境中
    将会十分惹人反感。

(2) 必须没有编译时段错误，这是非常基本的要求，如果你的区域还有无法正确
    被 load 进来的物件程式，别找神 QC ，否则最轻的惩罚是屁股上一个清楚
    的鞋印。

(3) 在 QC 的大巫师测试过程中，必须没有执行时段错误，区域的作者有义务报告
    所有隐藏的动作、□密，并且经由 QC 的大巫师测试无误。

(4) 档案路径名称必须和绝对路径无关，换句话说你的程式必须在整个目录被移动
    到另一路径下之後仍然能够正常动作，你可以用 __DIR__ 这个由 MudOS 提供
    的巨集定义表示这个档案目前所在的目录( __FILE__表示目前这个档案的档名
    )，即使移动之後必须做修正，最好也限於某个 .h 档案。

□ 使用者界面

(1) 所有的讯息必须正确而且适当，所谓「正确」是指基於一般常识所应给予的讯
    息必须无误，例如某人拉下一根拉□，拉拉□的人看到的讯息跟旁观的人看到
    的讯息就必须正确，所谓「适当」是指讯息出现的地方、讯息的长短、标点符
    号、颜色、出现时间必须尽量合乎真实世界的情形，例如人物 chat 的讯息跟
    add_action的讯息应该要能适当地表达出动作者的立场，不能有看起来怪怪的
    感觉。

(2) 景物或物品的设置必须合理，如一株可以爬的树你可以把它写成房间的景物，
    也可以用一个物品来表示树的存在(可以砍下来带走)，但是一个可以钻进去的
    地洞就不应该写成一个物品。

□ 内容

(1) 人物的强度必须合理，东方故事 II 虽然采开放式的属性系统，但是另一个重
    点是：NPC 和玩家是同一个世界的人，NPC 的作用不能作为它具有变态能力的
    理由，换句话说 NPC 的「强」必须有故事背景设定上的理由，因为 quest 或
    任何应用上的牵强理由而设计的强力 NPC 会受到最严格的检验。
    ( 请先参照各门派掌门的强度，作为假设的玩家强度水准上限，NPC 没有特殊
    理由不应该强过这些人，而且这种强度的 NPC 应该是十分「少见」的。)

(2) 武器装备的强度必须合理，和 NPC 相同，强力的装备应该是十分罕见的，如果
    没有适当的故事背景设定，强力的装备会受到最严格的检验，而且这些装备必
    须是极其罕见的。

(3) 区域的大小与其中所含的「□密」必须成正比，如果你的区域只有一堆用房间
    编辑器做出来的空房间，跟一些用 copy 跟改叙述、数字做出来的 NPC、装备
    或物品，超过 20 个房间的这种阳春区域通过 QC 的可能性就非常渺茫了。

□ 风格

(1) 一如你所见到的，东方故事 II 的风格应该十分明显，什麽该有，什麽不该有
    你应该在申请 QC 之前自己再确认几次，不要让 QC 的大巫师在你的区域发现
    一罐可口可乐或黑星手枪。

(2) 我们要求区域的设计需要有相当的「原创性」，也许很多武侠小说中的情节、
    人名、地名、武器跟东方故事 II 的风格十分吻合，但是我们希望巫师能用自
    己想出来的东西(甚至最好回避跟一些太出名的作品雷同的东西)，如果你的区
    域是完全出自某部小说或电影，为了避免智慧财产权上可能的争议与麻烦，这
    种区域开放的可能性非常低。

□ 维修之义务与後续发展

(1) 一但你的区域通过 QC 开放给玩家，这并不表示你的责任就完了，而是更重的
    责任，通常当你的区域开放之後，会将整个目录搬到 /d 下，并且神还可能会
    给你该目录的读写权，这表示你必须负责这一个区域的维修工作，任何这个区
    域产生的错误你都必须负责修护。


□ 继承

    相信有为数不少的巫师对於  LPC 物件的「多重继承」感到困惑，这是因为许
多 mudlib 的继承结构十分混乱，而且没有固定风格，因此这里特别说明一下我们
的继承结构所遵循的风格：

    「任何物件，可以继承最多一个『标准物件』，和接著标准物件的继承叙述之
後，若干个『物件特徵』。」

    其中的『物件特徵』以『标准物件』所未曾继承过的物件特徵为限，换句话说
一个物件特徵不应该在任何物件中被继承超过两次以上。

□ 标准物件 (standard objects)

    也就是位於 /std 下的物件，这些物件如果加上一个适当的 create() 函数就
可以成为一个完整的物件。不过，无论在任何情况下，都不应该对一个标准物件做
clone 的动作，你只能继承它。

    标准物件作为各式物件的主体，如果一个物件除了标准物件外没有另外继承任
何其他的物件特性，而且物件中除了 create() 之外没有第二个函数，如一般的房
间、怪物、物品等，应该在 create() 函数结束之前，用 replace_program()  将
自己的程式用标准物件取代，因为 create() 只有在物件被创造(或 clone)出来时
执行一次，以後再也用不到了，所以乾脆用所继承的标准物件取代，这样可以省下
为数不少的记忆体。

    在某些情形下，一个标准物件只是另一个标准物件与一些物件特性的组合，其
物件本身并不定义其他的函数，如 npc.c，原因是因为我们「常常会用到这样的组
合」，如果将这样的组合定义为一个标准物件，就可以用前面提到的在 create()
中用  replace_program 省下不少记忆体，因为标准物件的程式在记忆体中只存一
份而已。

□ 物件特性 (features)

    也就是 /feature 下的物件，这些物件只是提供单一的属性模组，是纯粹用来
被继承的物件，当然，绝对不应该去 clone 它，你只能继承它。

    定义物件特性的原则是「模组化」，也就是说，要能尽量在独立於标准物件外
的情形下工作，虽然完全独立是不可能的，但「尽量就是」。物件特性，顾名思义
，提供的是一项特性(如 equip.c)、特殊功能(如 alias.c, more.c)、或一些相互
关连密切的函数组成的模组(如 attack.c) ，如果所要描述的特性具有能让许多不
同物件使用的性质，应该优先把它写成一个物件特性，反之，则把它写成一个标准
物件。

    好了，看了这麽多文诌诌的定义，我们用一个例子来解释「标准物件」和「物
件特性」的意义。比方说我们要写一个能够装备的剑之精灵(生物)，用以下的继承
方式定义：

    inherit NPC;        // 标准物件 NPC
    inherit F_EQUIP;    // 物件特性 「可装备」

    聪明的你，到这里应该看得出这种组合的弹性了吧，因为  NPC 并不具有能让
不同物件使用的性质，因此我们把它写成标准物件，而「可装备」因为具有这种性
质，所以写成一个物件特性，如果在传统的 mudlib ，monster.c 与 weapon.c 都
是「标准物件」型的物件，就算有哪位巫师胆敢同时继承这两个档案，後果一定相
当可怕。

    如果你细心的话，其实可以发现分析到最後，一个最基本的标准物件只是几个
物件特性的组合，所谓的标准物件事实上是将一些物件特性「包装」起来，所以理
想中应该「尽量避免」在标准物件中定义函数，但是也不必过分地硬将所有的标准
物件拆开成一些奇怪的物件特性，这些东西可能和游戏系统规划者的思考方式与个
人喜好有关，总而言之，「简单」「合理」「富弹性」应该是设计继承结构的主要
考量。

□ 技能系统

我们使用的技能系统是属於多元成长途径的系统，有别於传统用经验值换取等级的
方式，一个技能由两个数值来描述：「等级」与「学习点数」，平常使用时只看等
级，学习点数完全不影响技能的效力。

由於技能的性质不同，我们将技能分为两大类：基本技能跟特殊技能，基本技能又
可以称为技能种类，所有的特殊技能都必须有至少一种的基本技能为基础，举例来
说，「xx剑法」是一种特殊技能，以「基本剑术」、「拆招卸力之法」为基础，这
个意思是说，这种剑法可以被「使用」在剑术与拆招卸力的用途上，并不是说必须
先学过基本剑术与拆招卸力之法才能学这个剑术，有关技能的学习限制完全由技能
本身定义，系统不预作假设。

通常我们公式中计算技能等级的时候，使用的是一种称为「有效等级」的计算方式
，所谓的有效等级，是以「技能种类」(基本技能)为索引的，特殊技能没有有效等
级，因为有效等级的计算是合并 □ 基本技能、□ 被「致能」的特殊技能， 以及
 □ 装备、法术等其他影响，所计算出来的，其中基本技能的等级以二分之一计算
，特殊技能以原来等级计算，装备及其他法术影响以修正值计算(可正可负)。大概
的公式如下：

  有效等级 = (基本技能 / 2) + 特殊技能(enabled) + 修正值

大多数的公式中所称的「技能等级」指的便是上面的有效等级。

□ 技能定义

所有的技能包括基本技能与特殊技能，我们都用一个物件来定义，定义技能的物件
必须继承标准技能物件 SKILL，并且定义以下几个函数：

string type()

  传回技能的型态，目前有 "knowledge"、"martial" 两种，其中 "martial" 型态
  的技能 learn 指令会自动限制当实战经验小於技能(不是有效技能)三次方的时候
  不能学习。

int valid_learn(object me)

  检查 me 是否有学习这项技能的条件，这里的学习包括 learn, study, practice
  ，但是不包括经由使用而进步的情形。如果合乎条件，传回 1，否则传回 0。

int valid_enable(string usage)

  检查这个技能是否可以用在 usage 的用途上，若是，传回 1，否则传回 0。

int practice_skill(object me)

  让一个人物「练习」一个技能，这个函数是由 practice 指令呼叫，作为技能物件
  的处理函数。

void skill_improved(object me)

  每当一个人物的技能等级提升了，就会呼叫这个函数，你可以在这里面做一些跟等
  级相关的改变或奖励。

□ 物件特性

    物件特性的使用方法大部分是经由特定函数，每个物件特性各不相同，因此
这里也没什麽好说的，只是提供几个以後当你要加入新的物件特性时，最好要遵
守的原则：

(1) 简单

    当你发现一个物件特性写下来将是一个 10K 以上的大档案时，想办法缩短
    或拆成两个特性，尽量维持一个特性所提供的功能单纯化。

(2) 记忆体

    请你记得物件特性是被继承的，而继承这个特性的物件将会 copy 一份所有
    宣告在这个特性中的 global variables ，因此如果一个特性中需要用到许
    多具有常数性质的变数( 即，执行过程中不随继承这个特性的物件而变 )，
    试著用 daemon 的形式以节省记忆体，如 ALIAS_D，SKILL_D 等。

(3) 效率

    采用物件特性的一个原因就是避免「深层继承」，虽然 driver 在执行上并
    没有太大的差异，但是对於大部分巫师而言，要了解或除错一个继承得乱七
    八糟的物件是相当困难的，尽量用「一个标准物件加若干个物件特性」的模
    式去定义所有物件。

□ ACTION

varargs void start_busy(mixed new_busy, mixed new_interrupt)

nomask int is_busy() { return busy != 0; }
判定使用者是否正忙著做某件事.

void continue_action()
如果战斗中使用者有 busy 著个 flag, 则 heart_beat() 会改呼叫这个函
数.

This function will be called in the place of attack() during
combat to indicate a person is in busy statue.

void interrupt_me(object who, string how)
This function interrupts whatever the user is doing.

int start_call_out(function fun, int delay)
开始执行 call_out, 将传入的函数名称及延迟时间传给 call_out() 这个
函数, 并於时间到时执行该函数.

void eval_function(function fun) { evaluate(fun); }
执行某个函数.

Evaluate the function named fun. (a fun function :)

□  ALIAS -- 替代指令

MAX_REPEAT 30  : 最多同一个指令可以重覆的数量.
MAX_ALIASES 40 : 可设定的替代指令数量.
HISTORY_BUFFER_SIZE 10 : 系统记忆指令的数量.

string process_input(string str)
处里输入指令.
如果使用者已输入超过 MAX_REPEAT 所定义的数量的相同指令, 系统会强迫使
用者离开.
接下来系统会判定使用者是否要重覆使用前面用过的指令, 再来会核对使用者
自己所设定的替代指令表, 若有找到符合的则将输入的替代指令扩张为完整的
指令. 传回值为处理过後的输入指令. 若以上条件都不符合则呼叫 ALIAS_D
的 process_global_alias() 函数并将结果做为此函数的传回值.

int set_alias(string verb, string replace)
将新的替代指令加入替代指令列表. 若没有传入第二个参数, 则取消所传入之
替代指令的设定, 并确定使用者没有超过替代指令数量的上限.

mapping query_all_alias()
传回物件所设定的替代指令.

□APPRENTICE

int is_apprentice_of(object ob)
判定指定入物件是否为传入物件的弟子.

void assign_apprentice(string title, int privs)
设定物件在所属门派地位及抬头. -1 表示物件为掌门人, 0 表是物件为弟子.

void create_family(string family_name, int generation, string title)
建立一个 mapping 来储存物件的门派资料, 并设定指定物件为掌门人.

int recruit_apprentice(object ob)
设定传入物件为指定物件的弟子并设定相关资料.

□ F_AUTLOAD

void clean_up_autoload()
将 autoload 阵列清除掉.

void save_autoload()
过虑所有在某物件 inventory 中的物品, 如果 query_autoload() 有传回
值则将物品的档案名称及其传回值存入 autoload 阵列中.

void restore_autoload()
读取一个物件的 autoload 阵列并试图复制所存的物品. 若失败则会显示错
误讯息并将讯息存入 /log/AUTOLOAD 档中.

□ F_CLEAN_UP - 提供继承这项特徵的物件一个标准的 clean_up 函数。

    每当一个物件在隔了一段固定长度时间没有被其他物件参考到，MudOS 会呼
叫该物件的 clean_up 函数以检查这个物件是否有继续存在的必要，也就是说，
如果这个物件有一段时间没有被使用到，也许物件已经没有用了，可以将它从记
忆体中清除以节省空间，例如：一个很久没有人走过的房间。

    clean up 时间的长短是在 MudOS 的 runtime config 档里设定的。所有可
以被 clone 的物件、房间、指令处理物件、以及非长驻型 daemon都应该要继承
这项特徵以便能在物件不被使用到时加以摧毁并释放出所占记忆体。

    当此函数传回 1 时，意思是告诉 MudOS 「我现在暂时要继续留在记忆体中
，但是如果再隔一段固定长度时间这个物件仍然没有被其他物件参考到，请你再
呼叫一次我的 clean_up() 函数」，若传回 0 或是 clean_up  函数没有被定义
，则 MudOS 会假设这个物件不需要 clean up这种服务，以後就不会再呼叫这个
物件的 clean_up()。

标准的 clean_up() 判定程序：

    首先，如果这个物件是一个使用者 (用 interactive() 决定)，此函数传回
 1，因为使用者发呆过久是在 heart beat 里处理的。

    接下来，如果这个物件是在另一个物件里，例如一个房间里的怪物、袋子里
的物品等等，我们改呼叫含有这个物件的房间或袋子的 clean_up() ，这样做有
两个主要理由：如果同一个房间里有不能被 clean up 的物件，如玩家，那麽我
们不希望任何物件在他们面前凭空消失，而且，如果一个存在於另一物件中的物
件能够被清除，容纳它的物件必定也能够被清除，如果容纳它的物件不能被清除
，那麽这个物件也必定不能被清除，如玩家身上的物件，因此我们让容纳这个物
件的物件来决定要不要清除，因为清除一个物件的同时，也会清除这个物件里的
所有物件。

    如果这个物件并不是在另一个物件里，而是一个房间，我们还必须检查是否
有玩家在这个房间里，以免玩家发呆了一会儿就被踢到最後乐园去。

    最後，经过以上各个关卡，我们判定这个物件已经没有存在的必要，所以我
们将这个物件摧毁，传回 0 用以告知 MudOS 这个物件已经「不见了」以後不用
再检查它是否需要 clean up  。

□ F_COMMAND

init_command_hook()
经由 add_action, 将所有 user 输入东西传入 command_hook() 这个函数.
本函数没有传回值.

find_command(string verb)
呼叫 command daemon (COMMAND_D) 看看在指定路径内是否有跟 verb 同名
的档案, 若有, 传回档名.

command_hook(string arg)
一但使用者输入一行文字, driver 会将除了第一 个字以外的文字当成参数
(arg) 并传至此函数. 输入的第一个字就是所谓的动词 (verb). 通常是 一
个指令.接下来我们经由 query_verb()  得到 这个动词, 也就是使用者所输
入的第一个字.然後开始判定使用者输入这行文字的目的. 首先我们将动词传
入 find_command()看看在使用者的路径设定中是否有与这个动词相同的指令
档名, 如果有, 则试著以call_other()  去呼叫 该档名的 main() 函数并传
入参数 arg 及动作人 物件.如果成功, 则结束离开. 不符合上述条件者, 本
函数在此呼叫 EMOTE_D ( emotedaemon), 判别这个动词是不是 emote 之一,
如果不是的话则呼叫管理交谈频道的 daemon, (CHANNEL_D) 看看这个是不是
交谈频道用的指令. 最後, 我们来判定这是不是使用者用来移动的 的指令 (
如: 东, 南, 西, 北等等). 如果以上皆非, 那我们就传回 0, 有可能这个使
用者输入了一个不存在的指令.

如果 PROFILE_COMMANDS 有定义的话, 有关这个指令的使用情形会由
PROFILE_D记录下来并存於指定档案中.

set_path(string *p)
将 *p 指定为路径. *p 为阵列型态.

force_me(string cmd)
强迫动作物件执行一个指令, 呼叫此函数的物件需要有 ROOT UID 才会成功.

□ DAMAGE

int is_ghost()
判断物件是否为鬼魂.

varargs int receive_damage(string type, int damage, object who)
让物件某项状况的数值减少. damage 不得为负数. 本函数会检查伤害种类是
否合法後将物件的状况数值减少, 若最後值低於零则设为 -1. 最後, 打开物
件的心跳 (heart_beat).

varargs int receive_wound(string type, int damage, object who)
同 receive_damage. 但伤害是加在状态值上 (eff_xxx).

varargs int receive_heal(string type, int heal)
同 receive damage, 但作用相反, 本函数是将某个数值加入状况的数值上.

varargs int receive_curing(string type, int heal)
同 receive_wound, 但作用相反, 本函数是将数值加入状态值上, 相当於
医疗.

void unconcious()
把某个物件弄昏, 判别及执行步骤如下: 没有生命的物件不能设, 如果物件
是因为受伤而昏的, 呼叫在 COMBAT_D 中的 winner_reward() 以处理胜力
者的奖赏.. etc. 删除指定物件的敌人表, 送出相关讯息 (让在场的大家都
知道) 将精气神等状态设为0, 关闭指定物件接受讯息的的权力, 最後设定
清醒的时间, 放到 call_out 上.

varargs void revive(int quiet)
把某人弄醒. 如果 call_out 还在, 删掉他. 如果清醒的物件本来是在另一
个使用者的 inventory 中, 将之移到使用者的环境中. 呼叫 enable_player()
以设定玩家的指令和相关设定, 传出对应的讯息并将讯息管道打开. 若传入
值 quiet 为 1, 则清醒的讯息不会送出.

void die()
死了...
如果物件是昏迷不醒的状况, 先把他弄醒. 奖励杀人者 (hmm...) 并制造一
具□体, 把 killer 表删除, 如果死者是玩家, 则把他设为鬼魂并送到阴间
去, 否则直接摧毁该物件.

void reincarnate()
复活...
将物件的 ghost 标记去掉. 身体的各项状态值设为上限.

int heal_up()
update all status (gin, kee, sen, fp, mp, ap.. etc)

□ F_DBASE 物件资料特性

mixed set(string prop, mixed data)
将属性 prop 的内容设为 data 并存入 dbase mapping 中, 如果 dbase
不存在的话则会建立一个新的 dbase.

varargs mixed query(string prop, int raw)
传回属性 prop 的内容, 如果该属性不存在或是 dbase 不存在则传回 0.
第二个传入值可有可无, 如果 raw 值为 1, 则传回值为 prop 所呼叫的
函式名称, 否则传回该函式的传回值.

int delete(string prop)
将属性 prop 从 dbase 中删除.

mixed add(string prop, mixed data)
将 data 加入属性 prop 的内容, 如果该属性不存在, 则呼叫与
set(prop, data) 相同.

int set_temp(string prop, mixed data)
将临时属性 prop 的内容设为 data 并存入 tmp_dbase mapping 中, 如果
tmp_dbase 不存在的话则会建立一个新的 tmp_dbase.
tmp_dbase 为临时性的资料库.

 mixed query_temp(string prop)
传回临时属性 prop 的内容.

int delete_temp(string prop)
将临时属性 prop 从 tmp_dbase 中删除.

mixed add_temp(string prop, mixed data)
将 data 加入临时属性 prop 的内容, 如果该属性不存在, 则与呼叫
set(prop, data) 相同.

mapping query_entire_dbase()
传回整个 dbase 的内容.

void set_default_object(mixed ob)


□ F_EDIT

int edit(function callback)
开始编辑程式。

void input_line(string line, string text, function callback)
由编辑程式呼叫此函数，将使用者所输入的文字或指令作适当的回应，当使用者结
述输入时，传入的函数 callback 将会被执行。

□ EQUIP

int wear()
判断一个物件是否可以装备物品. 物件必需是一个人物 (以 is_character()
判定), 想穿的物件必需是可以穿的... 等等. 系统会检查是否已经穿了同
类型的护具, 判断成功後设定所有穿上护具後的资料.

int wield()
判断一个物件是否可以装备武器. 物件必需是一个人物 (以 is_character()
判定), 想想使用的物件必需是武器... 等等. 系统会检查是否有空出来的手
来拿这样武器. 如果判断成功则设定所有使用武器後的资料.

int unequip()
将穿著的护具或武器脱掉或放下.

□ F_FINANCE

int can_afford(int amount)
这个函数是用来判定一个物件 (this_object()) 是否付得起 amount 这个价钱.
若传回 0 表示这个物件并没有这麽多钱, 传回 1 表示物件有这麽多钱, 而且
有正确的钱币种类. 传回 2 则表示此物件虽然有足够的钱, 却没有正确的钱币
种类.

□ MESSAGE -- 讯息

MAX_MSG_BUFFER 500      最多可以储存的暂存讯息行数.

void receive_message(string msgclass, string msg)
如果收到讯息的物件不是玩家, 则将所有传入的参数传给 relay_message()
函数. 接下来判定这段讯息是否为交谈频道或是有关天气的讯息, 并判定使
用者是否可以收到这些讯息. 再来则判定接受讯息的玩家是否有设定任何阻
档讯息的属性, 并依此决定该玩家是否可以收到该讯息. 最後, 如果玩家正
在编辑档案或输入模式中, 则将讯息送至暂存空间存放. 如果判定使用者可
以接受这段讯息, 则呼叫 receive() 函数并将讯息传入.

void write_prompt()
写入使用者的下一个 prompt, 若使用者有暂存讯息还未输出则先将该讯息
传给使用者.

void receive_snoop()
将监看时对方所看到的讯息前面加上 % 符号以示区别.

□ F_MORE

void more(string cmd, string *text, int line)
将传入的文字阵列一行一行的显示出来，并依照使用者所输入的指令动作。

void start_more(string msg)
开使执行 more，将传入的字串一行一行的分开以方便显示。

□ F_NAME

void set_name(string name, string *id)

将 name 字串设入物件的 name 属性中. 如果该物件还没有设 id 属性,
则取传入之 id 阵列的第一个 (id[0]) 元素为该物件的 id. 并将 my_id
阵列设为传入之 id 阵列的值.

int id(string str)
如果 "apply/id" 这个临时属性存在且不是空字串的话, 表示这个物件正在假冒
别的物件, 我们就用 applied_id □的内容来当这个物件的 id, 如果有找到的
话传回 1. 否则传回 0. 这样可以避免「冒牌货」在 id 指令下见光死.
如果没有 "apply/id", 则以 my_id 的内容来判定. 如果 str 与 my_id 阵列中
的某个元素相同, 则传回 1, 否则传回 0.

string *parse_command_id_list() { return my_id; }
如果 "apply/id" 这个临时属性存在且不是空字串的话, 传回 applied_id 阵列
的所有内容. 否则传回 my_id 阵列的内容.

varargs string name(int raw)
如果物件有 "apply/name" 这个临时属性而且不是空的的话, 则传回值为 mask
这个阵列的倒数第二项元素.
如果物件有 "name" 这个属性, 传回值为 "name" 的内容. 否则传回这个物件的
档名.

varargs string short(int raw)
如果该物件设有 short 这个属性, 则传回字串为 short 的内容加上 id. 否则
传回字串为该物件 name 属性的内容加上 id. 如果该物件不是人物 (由
is_chaaracter() 判定) 则在此传回字串的内容.
如果这个物件有 "apply/short" 这个临时属性且内容不是空的, 则传回字串被
设为 "apply/short" 阵列的倒数第二个元素. 否则传回字串会将物件的 nickname
及 title 等属性加以组合. 最後判定这个物件是否在断线中, 输入文字中, 编
辑档案中或是昏迷不醒, 并将适当的字串加入先前的字串一起传回.

varargs string long(int raw)
如果这个物件有 "apply/long" 这个临时属性且内容不是空的, 则传回 "apply/long"
阵列的倒数第二个元素. 否则传回 "long" 这个属性的内容. 若该物件没有 "long"
这个属性, 则呼叫并传回 short() 函数的内容并加上 "。\n".

□ F_SAVE

int save()
判定物件的存档档名并将该物件存档。

int restore()
将物件的资料由档案内载入。

□ SKILL

mapping query_skills()
传回物件的所有技能.

mapping query_learned()
传回所有技能的学习点数.

void set_skill(string skill, int val)
设定技能. 传入技能名称及等级, 若技能的 mapping 不存在则新建立一个,
否则直接设入技能名称及等级.

int delete_skill(string skill)
删除某项技能. 连带的也会删除该项技能的学习点数.

varargs void map_skill(string skill, string mapped_to)
把某项技能致能, 若第二个参数没有传入, 则取消该项致能.

string query_skill_mapped(string skill)
传回某项技能种类所致能的技能名称.

Returns the name of the skill that is enabled as as skill type
passed as parameter.

varargs int query_skill(string skill, int raw)
传回技能的等级.

mapping query_skill_map()
传回技能致能的列表.


int skill_death_penalty()
人物死後会呼叫这个函数以计算死亡时所造成的技能损失.

varargs void improve_skill(string skill, int amount, int weak_mode)
增家传入技能的学习点数, 并判定是否有进步以增加等级.

□ team

int set_leader(object ob)
将传入物件设为该队伍的领队。

object query_leader()
传回队伍的领队物件。

int follow_path(string dir)
让指定物件执行移动指令，方向以传入值为主。

int follow_me(object ob, string dir)

int add_team_member(object ob)
将指定物件加入队伍中。如果队伍还不存在，则会建立一个新的队伍，并把呼叫此函
数的物件和传入物件纳入队伍之中。

int set_team(object *t)
设定队伍。（注：此函数没有传回值。）(未完)

varargs int dismiss_team(object ob)
解散队伍。若解散人为这个队伍的领队，则整个队伍会被解散。否则仅该成
员离开队伍。

object *query_team()
传回队伍□所有队员。

int is_team_leader()
测试指定物件是否为一个队伍的领队。


□ VENDOR -- 标准贩卖物件

int buy_object(object me, string what)
(为完)

void complete_trade(object me, string what)
完成交易, 将物件交给买主并传出相关讯息.

string price_string(int v)
以对等的币值列出物件的价值.

int do_vendor_list(string arg)
列出所出售的物品及价钱.

□ 人物症状

所谓的症状(condition)就是一个人物因为受伤、中毒、或法术影响所产生的
各种症状，在 ES2 mudlib 里面定义成「需要每次 recover 时变化的症状」
，换句话说，人物症状是一种相当耗费系统 CPU 时间的物件，而且一发生错
误就可能导致 lost heart beat。

将一个症状加到一个人物身上十分简单，人物物件继承的 F_CONDITION 中定
义了一个界面函数 apply_condition() 让你将一个症状加诸於该名人物身上
，这个函数第一个参数是定义这个症状的物件档名，第二个参数则是一个
mixed 型态的任意参数，用来描述这名人物此一症状的资讯。

apply_condition 之所以用这种和 function 比较起来较慢的格式来定义一个
症状，主要是为了让人物的症状状态能被 save_object() 储存下来，因此当
你设计新的症状时，请注意描述这个人物症状的参数中不要使用 object 型态
的栏位，以免在 save 及 restore 之後失去这个栏位的内容。

定义症状的物件十分简单，F_CONDITION 在每次人物 recover 时间到，呼叫
heal_up() 之前，会对人物所有的症状之定义物件呼叫 update_condition()
这个自定函数，呼叫传入两个参数，第一个是 object 型态，也就是带有这个
症状的人物，第二个则是用来记录这名人物该症状资讯的 mixed 型态参数，
你在症状定义物件中宣告第二个参数的时候可以任意指定它的型态，F_CONDITION
并不会对这个变数做任何处理。

update_condition() 的传回值是一个表示多个 flag 的整数，这些 flag 都定
义在 <condition.h> 中，如果传回值为 0，这个症状会在 return 之後被从这
名人物身上移除(这样设计可以让错误的症状定义物件档名在一次呼叫之後立刻
被移除)。

CND_CONTINUE    若传回值的这个 flag 为 on，则下次 recover 时这个症状
                会再次被呼叫。

□ 动作(action)

    这里所谓的「动作」(action)，是指生物战斗时的行为的最小单位，可以是挥出
一拳、踢出一脚、用剑一刺、射出一支飞镖、丢出一个火球、甚至是□试一次逃跑等
等，只要是逻辑上属於「同一时间」所作的行为，都可以归类於一个动作。

    我们假设所有的动作都是在战斗状态下的行为，因为非战斗状态下的动作直接做
出来就可以了，不需要花心思用以下将提到的格式描述它。反过来说，把战斗状态下
的行为统一用「动作」来描述的理由很简单，我们不希望一个人可以藉由任何巫师意
料之外的技巧在敌人挥出一剑的同时，丢出十个火球、捡起地上的三十瓶药剂、等等
情形发生，另一方面，也提供巫师们在设计怪物、武器、物品等物件时，可以用比较
轻松的方式定义出一些变化较大的表现方式。

    在程式中，我们用一个 mapping 来表示一个动作，值得一提的是，由於mapping
这种结构在 LPC 中是以位址传递(call by address)，因此为了安全起见，我们规定
所有处理动作的程式「绝不」修改其他物件所提供的动作 mapping 的内容 ，这个原
则是预防一些未来可能发生的错误出现。

    在定义一个动作的 mapping 中，可能有以下的栏位，其中前面有 * 号的表示这
个栏位是可有可无的：

action

    执行这个动作所出现的讯息，格式是和 message_vision 这个 simul efun 要求
    的相同，例如 "$N对准$n的%s挥出一拳" ，其中的 %s 会用这个动作所攻击的部
    位名称代替。

*post_action

    这是一个 function type 的栏位，如果这个栏位被定义，combatd 会在 action
    讯息印出之後呼叫这个 function，有不少战斗中的特殊效果都是利用这个方式
    完成，如投射武器的消耗、某些战斗用的魔法等。

*damage

    执行这个动作的伤害力修正，单位是百分比，□围是大於 -100。
    default: 0

*damage_type

    攻击命中时所造成的伤害种类，如"割伤"、"瘀伤"、"灼伤"等等，详细的伤害种
    类请参考其他文件。
    default: "伤害"

*dodge

    这个动作对敌人躲避此一攻击的修正百分率。
    default: 0

*parry

    这个动作对敌人招架此一攻击的修正百分率。
    default: 0

□ 人物状态

一个人物具有三项基本的数值：形体(Physical)、能量(Energy)、精神(Mental)。

每一个数值都有一个「目前值」，表示该状态在计算人物能力时所具有的强度，一个
「有效上限」，表示这个状态在人物自然的情况下，能够经由新陈代谢或自我修复的
机能慢慢恢复的上限，跟一个「最大上限」，表示这个人物该项状态的极大值。

这三个值的高低永远是：目前值 ≤ 有效上限 ≤ 最大上限。

□  Phisical 形体

形体的目前值表示一个人物形体(身体机能)的机能状态，形体机能会因为作战时的阻
挡、承受攻击而降低，形体目前值降到零表示这个人物的形体机能完全丧失作用，也
就是死亡状态(dead)。
形体的有效上限表示一个人物形体的完好程度，形体有效上限会随著人物受到实体伤
害( 物理性伤害 )而降低，形体有效上限降到零表示这个人物的形体已经被摧毁、报
废了，也就是毁灭状态(destroyed)。

□ Energy   能量

能量的目前值表示一个人物体力(动力)的目前状态，能量会因为进行攻击、或各种消
耗体力的动作而降低，能量降到零表示这个人物已经体力透支(动力丧失)，而成为瘫
痪状态(exhausted)。
能量的有效上限表示一个人物体能(能源)的状态，体能会因为受到能量型武器、气功
等能量攻击而降低，能量的有效上限降到零表示人物的体能耗竭(能源耗尽)，而成为
衰竭状态(withered)。

□ Mental   精神

精神的目前值表示一个人物的集中力目前的状态，精神力会因为使用魔法、或进行需
要脑力的工作而降低，精神降到零表示这个人物已经累倒了，进入昏睡状态(asleep)
。
精神的有效上限表示一个人的意志力、魔力状态，精神上限会因为受到催眠、药物的
副作用、魔法的精神攻击等影响而降低，当这个有效上限值降到零表示这个人物的精
神已经崩溃，而陷入疯狂状态(insane)。

□ 关於生命型态

并不是所有的生命型态对於上面三个状态都有定义，换句话说，有些状态对某些生命
型态是不具意义的( 这里的生命型态包括机器人在内 )。

□ 形体

   对於精神体的生物如鬼魂(Ghost)而言，形体是没有意义的，因此精神体的生物不
   会受到形体的伤害，因此也不会有死亡或毁灭的状态。

   对於不死生物(Undead)而言，其活动的原理跟形体的机能无关，故形体是无目前值
   的，因此不死生物没有死亡状态，只有毁灭状态。

□ 能量

   对於魔法驱动的生物如 Golem 而言，其能量来自施法者的意志，自身的能量是没
   有意义的，因此魔法驱动的生物没有瘫痪跟衰竭的状态。

   对於非机械体的人造人而言，能量是无目前值的(动力恒定)，因此非机械体人造人
   不会瘫痪，只有衰竭(如魔鬼终结者 T-2000、七龙珠的人造人)。

□ 精神

   对於纯机械人而言，精神是没有意义的，因此纯机械人不会昏睡或疯狂。

   对於不死生物而言，其精神是来自其成为不死生物时的咒文之力量，因此精神没有
   目前值，不会昏睡，只有疯狂(如：Lich 施法术没有法力限制)。

由上面的例子我们可以看出每个状态除了前面定义中描述的行为之外，还有「无定义」
跟「目前值与有效上限恒一致」的情形。

在「无定义」的情况下，该型态的生命无法使用消耗该状态目前值的技能，如纯机器人
不能使用魔法、魔法驱动生物不能使用气功、精神体生物不能使用装备。同时，这种生
物也不会受到降低该状态目前值、有效上限的伤害，如纯机器人对催眠没有反应，魔法
驱动生物不怕气功(能量型武器可能同时造成形体伤害)，精神体生物不怕物理性攻击。

□ 房间属性

"short"                                                         (string)

    房间的短叙述。

"long"                                                          (string)

    房间的长叙述。

"item_desc"                                                     (mapping)

    房间中个别景物的叙述，格式为：([ <景物名称>:<景物叙述>, .... ])。
    其中<景物叙述>可以是字串或 function type。

"exits"                                                         (mapping)

    房间的出口，包括有门的方向，格式为：([  <出口>:<房间档名>, .... ])。

"objects"                                                       (mapping)

    房间中的物品、生物，格式：([ <物品或生物档名>:<数量>, .... ])。

"outdoors"                                                      (string)

    房间是否为「户外」，户外房间可以看到天色变化与气候影响。字串的意义
    表示房间的气候区，通常和该区域的 domain (即 /d 下的目录名称) 同。

"no_fight"                                                      (int)

    房间是为禁止作战区域。

"no_magic"                                                      (int)

    房间是为禁止施法区域。

□ 武器属性

"skill_type"                                                    (string)

    这个武器所属的技能种类，如 "sword"、"blade" 等，要注意在 /d/skill
    下必须有一个定义该武器技能的物件，否则装备这个武器战斗时会有错误
    讯息。

"rigidity"                                                      (int)

    武器的硬度，当使用武器相斗时，硬度、武器的重量、持用者的力量将会影
    响武器受损的机率。
    武器受损机会所用的强度值计算公式如下：

        重量/500 + 硬度 + 力量

"weapon_prop"                                                   (mapping)

    武器对持用者的状态影响，通常武器只影响 "damage"，这些状态影响会在
    武器被装备时用 add_temp() 加到持用者的 apply 上，并於卸除或 dest
    时减回来。

"flag"                                                          (int)

    武器的一些型态旗标.

□ 以下是目前允许的护具种类：

head    头部护具，凡是戴在头上的都算，包括头巾、耳环、头盔、帽子等等。
neck    带在颈子上的护具或饰物，项□、护身符、围巾等等。
cloth   穿在身上的轻便型衣物，包括上衣跟裤子整套。
armor   铠甲、穿在 cloth 外面，也是包括上半身与下半身整套。
surcoat 外套、穿在铠甲外面。
waist   腰部护具，包括腰带、腰巾。
wrists  手腕护具，如手镯、护腕。
shield  盾牌，不用解释了吧。
finger  戒指，只有戴在右手无名指的戒指才能发挥效力，所以只能戴一个。
hands   手部护具。
boots   足部护具，鞋类。

□ 物品属性

"name"                                                          (string)

    物品的中文名称。

"id"                                                            (string)

    物品的英文名称。

"long"                                                          (string)

    物品的详细描述。

"value"                                                         (int)

    物品的价值，单位是「钱」(coin)。

"unit"                                                          (string)

    物品的单位，如：个、把、枝.....

"no_get"                                                        (int)

    表示物品是否可被捡起来。

"no_drop"                                                       (int string)

    表示物品是否可被丢弃，如果型态是 string, 则当有人企图丢弃这个物品时
    会将该字串用 notify_fail 传回去。

□ 定义在人物物件中的附加函数(apply functions)

void defeated_enemy(object victim)

当这名人物打昏一个敌人的时候会呼叫这个附加函数，victim 即是被打昏的的
敌人物件。
呼叫者: COMBAT_D
有预设定义此一函数的系统物件: none

void killed_enemy(object victim)

当这名人物杀死一个敌人的时候会呼叫这个附加函数，victim 是即将要被杀死
的敌人物件。
呼叫者: COMBAT_D
有预设定义此一函数的系统物件: none

int accept_fight(object who)

当有其他生物对这个人物下 fight 指令的时候，会呼叫这个附加函数，who 是
下 fight 指令的生物，如果是对 player 下，"fight" 指令会直接显示讯息徵
求被挑战一方的同意，如果是对 NPC 下这个指令，则当这个附加函数传回 1
时才会接受挑战，否则显示某某不想跟你较量的讯息。
呼叫者: "fight" 指令
有预设定义此一函数的系统物件: NPC

int accept_object(object who, object item)

当有人用 give 指令给这个非玩家人物东西时，会呼叫这个附加函数，传回 1
表示愿意接受这个东西，传回 0 表示不接受。
呼叫者: "give" 指令
有预设定义此一函数的系统物件: none

void recruit_apprentice(objct apprentice)

当一个人物收了另一个人物做弟子时会呼叫这个附加函数，你可以在这个函数里
修改弟子的 rank 或其他东西。
呼叫者: "apprentice" 指令
有预设定义此一函数的系统物件: none

□ 评价系统

假设在一个集合中有个数为 N 的相异个体( N 种 NPC 或 player )，每一个
个体具有「评价」与「能力」两种数值属性。

评价一开始的初始值一律是 0，能力的数值则不在这个系统的关心□围。

假设这个系统从 t = 0 开始，每单位时间内会发生若干个「战斗」，战斗的
发生为一机率密度固定的事件，即，不考虑瞬间发生大量战斗的情形。

战斗: 任取系统中两个相异个体，战斗过程不考虑，只取「产生一胜一负」的
      情形为有效样本，并作为一次评价变动的输入，任一个体得胜的机率和
      其「能力」成正比，和敌方的「能力」成反比。

评价变动: 得胜的一方视双方「目前的评价」的比值获得若干评价，负方的评
          价不变动。

试求一合适之评价变动公式，使得在经过一段时间後，该集合中个体的「评价」
和「能力」能成正比(以能力和评价为 x, y 轴，每个个体描一个点，会呈现线
性分布)。

□  如何建造一个房间

    房间是构成这整个世界的要素之一，在此我们提供了一个房间的标准物件来让
所有的房间继承。而如同其他的物件一般，你需要写一个 create() 来设定房间中
的叙述、出口、物品、生物等等。这里，我喜欢说你用 create() 这个函式来赋予
这个房间的属性。一般来说，要建造一个简单的房间，你只要赋予它基本的属性即
可。当然，我们不认为一个区域中几十个房间没有任何的机关或秘密，是个会吸引
玩家一游的好地方。

    下面，提到了一些建造房间所需要留意的事项，也会配合一些例子来说明。

  一、基本篇

    一个基本的房间，要有 short <短叙述> 、 long <长叙述>、 exits <出口>

□  当你在写一个房间的 long <长叙述>时，其格式为：

        set("long", @LONG

房间的叙述.......

LONG
        );

    其中 @LONG 和 LONG  是互相对应的，你可以用任何字接在 @  後面，但是前
後两个字一定要一样，这样系统才能判别，而房间的叙述写完时，一定要换行後再
接第二个 LONG ，且同一行不能再有其他任何的字元，不然系统无法判定叙述是否
该结束了，会造成编译时的错误。

    而为求区域看起来外观上整齐、统一，房间的长叙述中每一行的长度必须一样
，而一行的长度建议为 29 到 32 个中文字，约占萤幕的三分之二。并且一个房间
的叙述最好不要低於三行，区域各个房间的叙述重复性降到越低越好，这样你的区
域看起来才不会太过阳春。当然，有时候为了某些目的，比如一个迷宫，你可能会
相邻的几个房间都用到一样的叙述，那自然不在此限。

□  一个房间的出口则以下列格式赋予：

        set("exits", ([
                "方向"          : "连接到的房间之档名",
                ...........
        ]);

    在这里，为了一个以後区域开放後搬移目录的便利性，建议采用__DIR__ 这个
由系统提供的巨集来写路径，比如说：

                "west"          : __DIR__"path3",
        和
                "west"          : "/u/d/davidoff/goathill/path3",

是完全一样的。但前者显然在以後目录的搬移上方便的多。而在下面会提到设定房
间中的物品或生物时，也建议采用这种方式写作。

□  item_desc 这是用来设定个别景物的描述，当玩家用 look 这个指令时就会作
    用。其格式为：

        set("item_desc", ([
                "景物名称"      : "景物叙述",
                ...........
        ]);

    其中景物叙述可以是字串或是一个 function ，所以你可以利用这个功能加以
变化，当玩家 look 一个景物时，可能看到叙述，也可能发生一些特殊的事件，而
你就可以在被呼叫的函式中写下这些事件。

□  objects 可以让这个房间在每次 reset时载入某些生物或某些物品：

        set("objects", ([
                "物品或生物的档名"      : 数量,
                ...........
        ]);

    如同前面所提到的，建议采用 __DIR__来编写你的路径，而数量则要用整数。

□  要为这个房间添上门户时，记得前面必须先 #include <room.h>。而格式为：

        create_door("出口方向", "门的名称", "进入方向", 预设状态);

    比如说，这里明显的出口有 west、east 和 up。 而你要让西边有一个关上的
红木门，你可以这样写：

        create_door("west", "红木门", "east", DOOR_CLOSED);

    当玩家进入这个房间时，他会看到：

        这里明显的出口有 east 和 up。

    而当他 look west 时，会看到：这个红木门是关上的。


    其他的一些属性，你可以参考 /doc/build/room_prop 或是读一下标准物件的
room.c。也建议你可以多用 more here来观看一间特殊的 room。
  二、进阶篇

    要让你的区域中富有变化，生动有趣，除了文字叙述的丰富度以外，你更可以
利用 init() 这个函式为你的房间增加一些「机关」或「秘密」。

    这里，先让我们了解一下 init() 的用途为何，和为什麽要用到它。每一个房
间的 create() 只有当 reset时才会被呼叫到，而 init() 则是在 B物件进入到 A
物件时都会呼叫到 A物件的 init() 。看到这，你应该可以看出差别了，我们希望
当一个物件（此处较多是玩家）进到一个房间时，能够经由某个动作启动这个房间
的机关的话，自然是利用 init() 来编写。

    一般的使用方式，是在 init() 中利用 add_action() 来呼叫你写的函式，其
格式为：
        add_action("function type", "action");

    function type 即是被呼叫的函式名    action 是启动的动作

    而你就可以将被 action 启动後要发生的事，都写在被呼叫的函式里面。理论
上来说，利用这个方式我们可以做到任何事，当然，能不能达成就看写程式的功力
了。下面举个简单的例子：

void init()
{
        add_action("do_pick", "pick");
}

int do_pick(string arg)
{
        object me;

        me = this_player();
        if ( !arg || ( arg != "flower" ) ) return notify_fail("你要摘什麽？\n");

        else if ( random((int)me->query("kar")) < 7 )
                message_vision("$N将花摘了下来，但一不小心被刺了一下。\n", me);

        else
                message_vision("$N摘下一朵美丽的血红色鲜花。\n", me);

        return 1;
}

    当玩家利用 pick 这个指令时就会呼叫到 do_pick() 这个 function，而启动了
这个房间的机关。

    这里特别提到一点，一个简单的 room 我们为了使记忆体的使用量降到最低，会
在 create() 最後加上一行 replace_program(ROOM); 。这是因为在房间的标准物件
中有定义了如 init() 等其他的函式，而一个简单的房间根本没有用到，所以我们用
replace_program() 来将原本的被继承的标准物件「重置」（或说取代）掉，但是一
旦房间中用到了 init() 来编写时，就绝对不可以用 replace_program()，因为系统
届时找不到 init() 便会随便呼叫一个记忆体中的位址而随便传进一些乱七八糟的东
西，情况严重时，甚至可以让整个 mud crash。但是，我们自不可因噎废食，该用的
时候还是要用，这些应该是一个好的程式写作人员自己必须留意的，发生状况要自己
负责。

  三、建议

    这里我们提供了一个工具来让巫师们可以方便的编写一个房间，那就是房间编辑
器(Roommaker) ，你可以 clone /obj/roommaker 来使用它。一般的步骤是，先利用
mkroom来造一个空房间，然後利用 goto 这个指令到房间里去，再用 rset short 和
connect 来设定这个房间的短叙述及出口，而像其他的一些属性例如 outdoors 等等
也都可以利用他来做到，接著用 to rset long 来设定这个房间的长叙述，最後再用
saveroom将这个房间存档。要是这只是一个基本的房间，那到这里就大功告成了，要
是□想让这个房间富有变化，那就再用线上编辑器 edit 或将这个房间的档案 ftp
回去继续修改。

□ 如何写一个非玩者人物(NPC)

一.年龄:
   由於系统将会依年龄而计算出, 该怪物之天赋及基本状态。
   故请务必设定怪物的年龄。

   设定方法: set( "age", 数值 );

二.天赋: (请参考 help gift)
   str = 膂力, cor = 胆识, int = 悟性, spi = 灵性,
   cps = 定力, per = 容貌, con = 根骨, kar = 福缘。

   设定方式: set( "属性", 数值 );
   数值□围: 10 - 30

三.状态: (请参考 help stats)
   gin = 精, kee = 气, sen = 神。
   atman = 灵力, force = 内力, mana = 法力。

   设定方式: 例如, set( "gin", 200 );
                   set( "max_gin", 200 );
                   set( "force_factor", 3 );

四.经验:
   对一般没有特殊技能的怪物而言, 战斗经验(combat_exp)
   几乎决定了怪物的强度。而综合评价(score)则决定了玩家
   击败此怪物後, 是否能大举提高自己的综合评价。

   设定方式: set( "combat_exp", 数值 );
             set( "score", 数值 );

五.技能:
   如果想做些较厉害的怪物或导师(让玩家学技能)时, 最好的方
   式即是设定技能。

   设定方式:
            1.怪物学过的技能: set( "技巧名", 数值 );
            2.怪物施展的技能: map_skill( "技巧种类", "技巧名" );

   当使用 map_skill() 时, 请务必确定技巧种类是否相符。目前,
   技巧种类如下:
   刀法(blade): spring-blade
   轻功(dodge): chaos-steps pyrobat-steps nine-moon-steps stormdance
   内功(essence): fonxanforce celestial iceforce gouyee nine-moon-force
   修行(evolution):
   法术(magic):
   招架(parry): 各种高级持械功夫
   棍法(stick):
   剑法(sword): fonxansword six-chaos-sword nine-moon-sword
   暗器(throwing):
   拳脚(unarmed): liuh-ken celestrike nine-moon-claw tenderzhi ...
   咒术(spells): necromancy

//希望能把所有技能列於其後//
六.种族:
   如果未指定种族, 系统将会自动选定人类。

   设定方式: set( "race", "种族名" );

七.性别:
   依种族之不同, 有两种系统: 1.男性, 女性。
                             2.雄性, 雌性, 中性。

   设定方式: set( "gender", "性别" );

八.攻击:
   攻击模式包括攻击方式及受功部位。如果种族是人类, 将直接
   继承标准模式, 不须额外设定; 其馀种族则一定要加以设定。
   设定方式: set( "verbs", ({ "攻击方式1", "攻击方式2", ...}) );
             set( "limbs", ({ "受功部位1", "受功部位2", ...}) );

   目前之种族如下表:

   种族    性别    攻击方式        受功部位
  ------------------------------------------
   人类     1      系统给定        系统给定
  .........................................
   野兽     2      hoof, bite,     自由设定
                   claw, poke
  ------------------------------------------

九.暗语:
   我们可以在怪物上设定某些暗语, 使得当玩家询问(ask)特定之
   关键字时, 怪物能自动做我们预设之动作。

   设定方式: set( "inquiry", ([
                  "暗语1" : "回应之语\n",
                  "暗语2" : (: this_object(), "呼叫副程式之名" :)
                ]) );

十.其它:
   其馀之设定如: set_name(), set_short(), set_long(), create_family(),
                 set( "nickname", "?" );
                另文叙述!

□ 制作 emote:

角色伴演游戏中除了谈话外还常常会用到一些动作, 表情等来辅助
或用以表达一些不容易「说」出来的感觉. 这类指令通常叫作 emote.
虽然说中文的 emote 指令是可能的, 但为了方便起见, emote 都以
使用英文动词为主.

emote 由 /adm/daemons/emoted.c 管理.

有关 emote 的指令:
edemote -- 修改, 删除, 显示一个 emote 的内容.
semote  -- 显示目前 emote daemon 所知道的 emote.

emote 分为三种: 有目标, 没有目标和对自己.

在制作有目标的 emote 时, 你要考虑三个「版本」的讯息:
1. 执行者 - 你自己, 执行这个 emote 的人.
2. 对象   - 你用 emote 的目标. 比方说 'smile A' 时, A 就是对象.
3. 其他人 - 除了你和目标外所有在场的人.

对於没有目标或对自己的 emote, 则只要考虑 1 跟 3 两种讯息.

任何讯息都可以有很多行.

讯息的代换参数:
$N -- 你的中文名字.
$n -- 对像的中文名字.
$P -- 自己的人称代名词, 如你, □, 他, 她...
$p -- 对象的人称代名词, 同上.

□例:
假设我们要做一个 'bore' emote:
>edemote bore

编辑 emote：bore
讯息可以有好几行，用 . 表示结束。
讯息中可使用的参数有以下几种：
  $N  自己的名字。
  $n  使用对象的名字。
  $P  自己的人称代名词，如你、□、他、她、它、它。
  $p  使用对象的人称代名词，如你、□、他、她、它、它。
————————————————————————————————————
不指定对象使用这个 emote 时，你自己看到的讯息：
->$P觉得这里很无聊.
->.
不指定对象使用这个 emote 时，其他人看到的讯息：
->$N觉得这里很无聊.
->.
对自己使用这个 emote 时，自己看到的讯息：
->$P觉得$P自己很无聊.
->.
对自己使用这个 emote 时，其他人看到的讯息：
->$N觉得$P自己很无聊.
->.
对别人使用这个 emote 时，自己看到的讯息：
->$P觉得$n很无聊.
->.
对别人使用这个 emote 时，使用对象看到的讯息：
->$N觉得$p很无聊.
->.
对别人使用这个 emote 时，除你自己和使用对象外，其他人看到的讯息：
->$N觉得$n很无聊.
->.
Emote 编辑结束。

当你 (某甲) 使用这个 emote 时:
> bore
[你看到]   : 你觉得这里很无聊.
[别人看到] : 某甲觉得这里很无聊.
> bore 某甲  (这里也可以使用某甲的英文 ID)
[你看到]   : 你觉得你自己很无聊.
[别人看到] : 某甲觉得他自己很无聊.
> bore 某乙
[你看到]   : 你觉得某乙很无聊.
[某乙看到] : 某甲觉得你很无聊.
[别人看到] : 某甲觉得某乙很无聊.

□ 如何架设 ES2类 mudlib

(1) MudOS 部份

  首先，你要有一份 MudOS (最好是 v21c2 版)，而且因为中文的关系，在
  compile 之前需要做一些修改。为了节省您的时间，建议您从 comm.c 的
  telnet_neg() 函数与 ed.c 的 prntln() 函数下手，对 C 语言稍有基础
  的人应该很容易就能看出该如何修改，在此就不赘言了。

  其次，您必须将 MudOS 正确无误地 compile 出可执行档，在开始 compile
  之前，您必须先设定好 options.h 的内容，ES2 mudlib 的 options.h
  也可以从我们提供的 ftp site 得到，如果您无法确定一些选项要如何设定
  ，请参考该档案。

  接下来，在正常的情况下只要在 src 子目录下打 make 就可以作出两个可
  执行档：driver 跟 addr_server，如果出了其他编译上的问题，请您请教
  SYSOP。

(2) Install

  当您有了 MudOS 执行档之後，就可以开始架设 ES2 mudlib，在 es2目录下
  /adm/etc/config.ES2 是用来当作 MudOS driver 参数的 runtime config
  file，请您将这个档案拷贝到 driver 的目录下，然後用文书编辑器修改里
  面 mudlib directory 跟 binary directory 这两个路径为您存放 ES2
  mudlib 以及 driver 可执行档的绝对路径，这个档案中除了前面所说的两
  个路径以外，其他有关路径的设定都是相对於 mudlib 的路径，如果您对
  mudlib 还不是很熟悉，请勿更改。

  到这里如果一切都正常，您只要在 driver 的目录下用：

  driver config.ES2 &

  就可以启动了，当您看到 Setting up IPC on port XXXX (XXXX 是您在
  config.ES2 中设定要让 mud 使用的 port 号码)，就表示安装成功了。

(3) 如何创造第一个人物

  当您拿到这份 mudlib 的时候，会发现并没有任何人物资料档，请您在 es2
  目录下, adm/etc/wizlist 中加上您的名字，以及一个 (admin)，(admin)
  表示具有最高的权限等级，wizlist 这个档案是用来储存所有巫师的等级，
  而且不管人物存在与否，所以请您只留下自己，将其他不存在的人物删掉。

  接下来您重新启动 MudOS，然後连线进去创造您刚才设定的人物，您会发现
  这名人物的状态一开始就是 (admin)，接下来您就可以用 promote 指令将
  其他人擢升为巫师，promote 会自动将这些人物的等级写入 wizlist。
