第 二 章

上 手 篇


  怎么開始工作,有人說先做起來再說,有人說先看起來再說,我們的意見是,從簡單的房間和人物在編寫開始,最簡單的起步就是找一些相近的自己也能看得懂的程序來改,這樣產生錯誤的可能最小。待涉及到機關與秘密的制作后,再開始全面地讀看MUDLIB的程序,實在看不懂,先跳過去,一直看完,結合我們的中級工作手冊(編纂中),我想接下去的自學歷程應該是十分順利的了吧?
  為了使這本手冊最大限度地通俗易懂,我盡量做到決不過早地向大家提及一大堆專業朮語,以免嚇跑我們的新巫師朋友,但是......這里,我先提一個,就一個......好不好?我們來理解一個概念--繼承(inherit)。在程序中,繼承的意義就在于把很多東東中間的共性提出來,設置成一些標准物。然后,別的程序就沒有必要再一個個地重復設置,直接繼承它就有了這些特性。繼承共分兩種:一是標准物件,就是具體的東東,比如說:npc(人物)、item(物品)、room(房間)﹔第二種就是標准特征,比如說:F_MASTER(可收徒的)、F_EQUIP(可裝備的)。多用繼承可以大大減輕系統的記憶量,也對我們的工作帶來很大的便利。好,下面我們開始寫程序了。

  我們新巫師不會一上手就去開發新的系統程序,先起手寫的大都是一些簡單的房間、物品、人物,這類程序首先必需要繼承一個標准物件,如:房間要 inherit ROOM; 物品要 inherit ITEM; 人物要 inherit NPC; 道理前面已經講清楚了。
  接下來可能要碰到一大堆令人頭疼的函數了,不過沒關系,我們先不要去理會它們,做一些簡單的房間呀、人物之類的只要用到一個最基本的函數:create()。create()的作用就是在你寫的這個程序,不管是房間還是東西,當它被制造出來,它就會立刻啟用這一函數,對自己進行一些最基本的設定。函數里的語句格式也很簡單,就是:
  set("屬性",某個值);
  到底有哪些屬性可以set呢?你可以去找一些程序多看看,也可以問問較資深的巫師。下面為新巫師們列出了一些基本屬性:()里的表示這個屬性的類性。string表示是字符串,int表示是數字型。
  房間屬性
"short"(string)      房間的短敘述。
"long" (string)      房間的長敘述。
"item_desc"(string或函數) 房間中個別景物的敘述,格式為:([ <景物名稱>:<景物敘述>, .... ])。其中<景物敘述>可以是字串或 function type。
"exits" (mapping)     房間的出口,包括有門的方向,格式為:([ <出口>:<房間檔名>, .... ])。
"objects" (mapping)    房間中的物品、生物,格式:([ <物品或生物檔名>:<數量>, .... ])。
"outdoors" (string)    房間是否為「戶外」,戶外房間可以看到天色變化與氣候影響。字串的意義表示房間的氣候區,通常和該區域的 domain (即 /d 下的目錄名稱) 同。
"no_fight"(int)      房間是為禁止作戰區域。
"no_magic"(int)      房間是為禁止施法區域。
  人物屬性
"id" (string)   人物英文名,這個字可以用來識別玩家,通常 "id" 跟 "name" 都不直接用set() 設定,而是用 F_NAME 中的 set_name()。
"title",
"nickname",
"name" (string)  人物的稱號、綽號、與中文姓名。
"age" (int)    人物的年齡。
"age_modify"(int) 這個數值會在 update_age() 中被加在人物的年齡上,可以是負數。
"jing",
"eff_jing",
"max_jing"(int)  精,當前精,最大精
"jingli",
"eff_jingli",
"max_jingli"(int) 精力,當前精力,最大精力
"neili",
"eff_neili",
"max_neili" (int) 內力,當前內力,最大內力
"qi",
"eff_qi",
"max_qi"(int)   氣,當前氣,最大氣
"shen_type", (int) 神的類型,0是負,1是正
"str",
"int",
"con",
"dex" (int)    人物的天賦,依序分別為膂力、悟性、根骨、身法
"combat_exp"(int) 人物的實戰經驗
"jiali"(int)   表示人物打中別人并加的內力點數。
"family"(mapping) 人物的師承門派等記錄
"skill"(string)  人物的武功
  物品屬性
"name" (string)  物品的中文名稱。
"id" (string)   物品的英文名稱。
"long" (string)  物品的詳細描述。
"value" (int)   物品的價值,單位是「錢」(coin)。
"unit" (string)  物品的單位,如:個、把、枝.....
"no_get"(int)   表示物品是否可被撿起來。
"no_drop"(int string)  表示物品是否可被丟棄,如果型態是 string, 則當有人企圖丟棄這個物品時會將該字串用 notify_fail 傳回去。
  武器屬性
"skill_type"(string) 這個武器所屬的技能種類,如 "sword"、"blade" 等,要注意在 /d/skill下必須有一個定義該武器技能的物件,否則裝備這個武器戰斗時會有錯誤訊息。
"rigidity" (int)   武器的硬度,當使用武器相斗時,硬度、武器的重量、持用者的力量將會影響武器受損的機率。
"weapon_prop"(mapping)  武器對持用者的狀態影響,通常武器只影響 "damage",這些狀態影響會在武器被裝備時用 add_temp() 加到持用者的 apply 上,并於卸除或 dest時減回來。

  一個基本的房間,只要有 short <短敘述> 、 long <長敘述>、 exits <出口>就行了。就象:
void create()
{
  set("long", @LONG
    房間的敘述.......
  LONG
  );
  set("exits",([
  "west" : __DIR__"path3",
  ]);
在這里__DIR__是什么意思?假如說我們寫的這個房間的文件在/d/city目錄下,那么這個__DIR__就是"/d/city/"也就是“"west":"/d/city/path3"”的意思。在上一章的品質要求里講過,我們提倡采取這樣的寫法,因為這樣寫了以后,不管你這個區域的目錄放在哪里、或者是更名都不會受到影響。接下來,我們可以給這個房間里添一點生動一些的東西,比方說,讓玩家在這里能夠look到什么:
  set("item_desc", ([
  "景物名稱1" : "景物敘述",
  "景物名稱2" : "景物敘述",
  ...........(可以有很多)
  ]);
  其中景物敘述可以是一句、几句話,或是一個自定義的函數,這個函數你就寫在后面的某個地方,以根據不同的情況顯示look景物名稱后而出現的信息與效果。所以你可以利用這個功能加以變化,當玩家 look 一個景物時,可能看到敘述,也可能發生一些特殊的事件。
  然后下面就再給這個房間加點東西。
  set("objects", ([
  "物品或生物的檔名" : 數量,
  ...........
  ]);
  如同前面所提到的,建議采用 __DIR__來編寫你的路徑,而數量則要用整數。
  寫到這里,有的巫師有點不滿足,說,我看見有的房間有時某一方向的門是開的,有時又看不到,要使用open door指令打開門才行,這是怎么做呢?這其實也簡單,只不過首先要復習一下前面的繼承概念。因為這個門是房間里的一個特征繼承,需要用到一些在/include/room.h里定義的一些變量,所以要有文件頭記得必須先 #include <room.h>。然后在文件中寫上:
  create_door("出口方向", "門的名稱", "進入方向", 預設狀態);
比如說,這里明顯的出口有 west、east 和 up。 而你要讓西邊有一個關上的紅木門,你可以這樣寫:
  create_door("west", "紅木門", "east", DOOR_CLOSED);
當玩家進入這個房間時,他會看到:
  這里明顯的出口有 east 和 up。
而當他 look west 時,會看到:這個紅木門是關上的。
  有關create_door()是如何讓你的房間表現出這樣的特性的,那就要去仔細看看學習它所繼承的/inherit/room/room.c文件了。不過對于新巫師來說,還不必急著去看,只要知道要加上這些語句就可以實現,也就行了。

  簡單的人物原則上只要有 set_name<名字> 、 combat_exp <經驗>就行了,當然我們總得稍微多添一點了。
inherit NPC;
void create()
{
  set_name(<中文名>, ({ <英文id> }) );
  set("title", <頭銜>);
  set("gender",<男性、女性或是無性>);
  set("age", <年齡>);
  set("long", <人物的長相描述>);
  set("combat_exp", <人物的實戰經驗>);
  set("attitude", <好戰態度>);
  set("neili", <內力值>);
  set("max_force", <最大內力>);
你想讓這個人物能夠時不時說點什么或動點什么,就要
  set("chat_chance", <隨機動作概率的百分比>);
  set("chat_msg", ({
"隨機的動作或語言的描述"
........
  }) );
這時,如果還想讓對玩家ask它時有些特殊的反應的話,就得:
  set("inquiry", ([
  "ask的關鍵詞": "回答的話",
.......
  ]) );

  物品要比上面兩類更要簡單一點,看懂了這些,再去調几個物品的文件,自己看看也就明白了。好了,現在你可以自己對自己寫一個工作室,也可以在里面放一個傻乎乎的書童了。接下來我們就要深一步,了解文件中更富有變化。更有意思的基本函數之二:
  init() 函數
  init()函數就是在有玩家或 npc等的活物進入房間時(可以是走進來,扔進來或clone 進來)被觸發,從而實現函數功能。也就是說,如果你要對進入房間的玩家做一些動作,比如弄暈他或給他中毒、或者向他問好、顯示出一些特殊的信息等等,那么就在init()函數里對那個玩家進行操作。
  在 init() 中最常見的的函數莫過於add_action("function", "action")了,它的作用是在進來的生物身上添加上一個指令 (注意, 系統只認指令的第一個單詞), 并在玩家下達這個指令時去呼叫那個名稱的函數:舉例而言, 如果我們寫了這樣的 init():
  init()
  {
    add_action("do_climb", "climb");
  }
  就是說,當玩家走進這個房間時, 系統會幫他多出 climb 這個指令,當他下達了climb tree 這個指令時, 系統會去尋找 do_climb() 這個函數, do_climb()這個函數當然是你去寫了。同時, 系統會將玩家所輸入的 "climb"這個指令后面的所有文字,作為一個檢查的變量(是不是有些深了?沒關系,看不懂就跳過去,知道是這么個意思就行了)傳給 do_climb()。你可以將 do_climb這個函數宣告為
int do_climb(string arg)
  這樣一來,當玩家下達 climb tree,或是 climb the red wall這種指令時,"tree"或是 "the red wall"就會被存進變量 arg 之中供do_climb進行檢查處理。如果判斷后面的變量與我們設計的無關,也就是說,比如我們希望玩家應該是climb red tree,那么就應該寫:
  if(arg!="red tree") return 0;
  “!=”就是不等于的意思,“!”代表不、否定的意思。return 傳回的是 1,就表示通過﹔是0,則表示函數處理中止。所以,這句判斷名就是當后面跟的那個變量不是“red tree”時,函數的處理就終止。反過來,你就可以設計并設置,條件符合時會發生的事,然后別忘了,在最后加上一句return 1;

  在這里,我們給有一些基礎的巫師開一個小灶,看不懂的就跳過去看下一段。有時有些新巫師會發現一個有點費些思量的事,就是當我們所加的add_action()的指令同時也是系統提供的cmds指令時,一般add_action的指令將優先于cmds進行執行。比如kill是一個cmds指令,但在你現在寫的房間程序中將要進行某種程度的限制。一般的做法就是加add_action("do_kill","kill"),那么,玩家一旦在這里發出kill指令,將首先尋找do_kill()這個函數,進行變量的檢查。比如,發現玩家的某些條件不符合就給出類似“這里不允許對殺”的信息后return 1;就表示判斷通過了,也就意味著這個指令已被你寫的這個函數處理掉了。如果你在某個條件后給出“你心中一動,殺機已起,小小的一個地方立刻激蕩出無邊的戰氣”后return 0; 這就是告訴系統,我這個kill指令并沒有完,剛才只是增加kill更多的信息,此后系統就會尋找本來的那個kill的cmds指令,也就是正式執行,這被稱之為重載。(有點繞人?多想想,想通了后很有用的)
  在你的函數檢查到玩家輸入的變量有問題時 (例如你要他們climb red tree, 但他們卻輸入了一些錯誤的指令如 climb three 之類的),你可以象上面一樣直接return 0;終止這個函數,系統就會顯現出“什么?”的錯誤信息。而如果你想給他們一些特別的、有些意思或提示性的錯誤訊息時, 你可以用 notify_fail(".....")來代替0寫這個訊息, 比如:
  return notify_fail("你想爬什么?\n");
  return notify_fail()就是用來取代return 0的東西,這就是想讓返回的錯誤信息更加丰富、或者有效而已。所以我們最常用的寫法是:
  if (條件不合)
    return notify_fail(錯誤訊息);
  if (另一個條件不合)
    return notify_fail(另一個錯誤訊息);
  .............................
  (所有可能導致錯誤的輸入都過濾光了  開始真正干活的部份.... )
  ..............................
  return 1;

  人物的、物品的init()與 ROOM 中的init()函數類似, 但物品中被呼叫的機會多了許多, 主要有下列的几種情況:
  1,物品擺在房間中, 有一個玩家走進來,這很好理解﹔
  2,一個物品突然出現在某個玩家所在的房間中,這就是象別人丟下的,機關觸發出來的,或者是巫師變出來的﹔
  3,一個物品突然出現在某個玩家的物品欄中,象別人給你的,你買到的東西,通過機關直接觸發到身上的﹔
  這些 action 會生效的場合歸結起來很簡單, 就是: 「玩家用 look 或是 i 指令看得到這個物品的時候」,但是同一個房間中他人或 npc身上的東西時不算,裝在袋子的東西不算。
  廢話少說,下面還是拿出例子進行逐句的解釋,這也是新巫師最需要的。首先說明:在文件中“//”后面跟的就是注釋語句,凡是這一行“//”后的東東,程序在執行時概不理會。而“/*”也是表示后面是注釋名,只不過它表示后面所有行的東東都是程序不必理會執行的語句,一直到“*/” 結束。用在注釋行較多的地方。這里為了區別,我們把教材中注釋用淡一些的顏色標出。

// Room: /d/wuxi/ximen.c //無錫西城門 程序開頭注明一下 文件類型和絕對路徑和中文名
// llm by 99/05/21     //繼續注釋 誰寫的和編寫時間 有時是修改時間
#include <ansi.h>     //表明它繼承了定義顏色的文件,后面不要分號
#include <room.h>     //表明它繼承了定義房間的文件,因為門在這個文件里
string look_pai(object me); //聲明一下這個文件里有一個函數的原型定義
inherit ROOM;     //表明它是繼承 ROOM 類
void create()     //開始創建函數void create() 在里面定義各種屬性
{
  set("short", "梁溪門"); //short是指房間的短描述 系統會自動定義成亮青色
  set("long", @LONG    //房間的長描述即場景描述
這是無錫城的西城門,緣由城門外的一條梁溪河而得名為梁溪
注意:系統會自動在開頭一行空兩格,所以你不要多事自己加空格
門,看城官兵比較懈怠,向西出城是一條塵土飛揚的大驛道,往東
便可進入熱鬧的無錫城了。
LONG
/*在長描述中,前后要加入 @LONG 和 LONG,它們是互相對應的,你可以用任何字接在 @ 後面,但是前后兩個單詞一定要一樣,也就是說你也可以:set("long",@TXTE
*******
TXTX
);
這樣系統才能判別,而房間的敘述寫完時,一定要換行后再接第二個 LONG ,并且LONG或TXTE這一行不能再有其他任何的字元,不然系統無法判定敘述是否該結束了,會造成編譯時的錯誤。而如果不加 @LONG和LONG時,字符串前后必須要加上" ",中間也必須手動添上“\n”的換行符號。
*/
);
  set("valid_startroom", 1);   //它的作用是如果在該房間退出游戲的話,就可以成為下一次進來的地方
  set("no_fight",1);   //表明這是一個不允許戰斗的房間,同理還有“no_beg、no_steal”
  set("item_desc", ([   //這用在某些可以用look看出機關或特殊描寫的地方
    "men":"一座高大的城門,用上好的紅木拼釘而成,十分威武",
    "pai": (: look_pai :),
  ]) );
/*這里放了兩個常用的用法,共同之處,就是“:”的左邊是物品名(其實不是物品,也就是一個記號而已)或者叫可以讓玩家用look指令看的字符,一旦look men后,就會由右邊的來顯現信息。第一種情況:右邊直接是描述的語句,比如"look men"就會出現“一座高大的城門......”。第二種情況后面是(: 函數名 :),就調用這個函數。而這個函數名必須在文件頭聲明了,比如我們這里的"pai"就是調用(: look_pai :),在后面要對該個函數進行設置*/
  set("exits", ([   //設這個房間的出口
    "east" : __DIR__"dajie1", //該方向要連到的房間文件名,注意不需加.c后綴
    "west" : __DIR__"yidao1", //__DIR__的意思就是這個文件所在的當前目錄
    "northeast":__DIR__"qiangdong",
  ]));
  set("objects", ([     //設這個房間里東東,npc和物品
    __DIR__"npc/bing":2,  //后面的2,也可是1、3 表示數量
  ]));
  create_door("northeast", "小門", "southwest", DOOR_CLOSED);
/*這是定義門,注意它的格式是:
create_door("入口方向","門的名稱","出口方向","預設狀態")
預設狀態有兩種,也就是DOOR_CLOSED和DOOR_OPENED,分別表示初始狀時是關著或開著。因為這些都是寫在/include/room.h文件里,所以我們一定在這個文件頭加上inherit <room.h>。*/

  set("outdoors", "wuxi");   //指出它是室外,并在wuxi(區域目錄名)這一區域
  setup();           //設置結束
}
/*注意:關于“replace_program(ROOM);”的用法,由于在房間的標准物件中有定義了如 init() 等其他的函式,而一個簡單的沒有機關的房間根本沒有用到,所以就用replace_program() 來將原本的被繼承的標准物件「重置」(或說取代)掉,以便最大限度地節約系統內存的耗用。但是一旦房間中用到了 init() 來編寫時,就絕對不可以用 replace_program(),因為如果你寫的是一個復雜的房間,就會在很多觸發函數的地方,而這個文件加了這行后,又會把那些多于簡單房間定義的函數清除掉了。于是一旦這些地方開始觸發了,系統到時就會找不到那些觸發的函數。一般地情況下,系統就隨便呼叫一個記憶體中的位址而隨便傳進一些亂七八糟的東西,而情況嚴重時,可以讓整個 mud宕機。我們現在這的這個例子中需要用到一些其它的函數,那當然不能用它嘍。對于新巫師來說,這一行在不能確定是是否要加的情況下,還是選擇不加為好,畢竟,浪費些空間與當機的比較是很明顯的。
接下來我們來定義前面提到了look_pai的函數了*/
string look_pai(object me) /*me是一個對象,指作動作人,也就是this_player(),如果在這里不定義,那么就要在函數里用 object me; me = this_player();進行定義*/
{
  if( wizardp(me) ) //wizardp(me)是一個efun函數,判斷me是否是巫師
  return "大木牌寫著:無錫城門。正在建設中,叮當留。\n";/*根據上面的條件,這句話只有me是巫師時才能看到*/
  else        //如果不是巫師
  return "大木牌寫著:無錫城。\n";
}

這個程序到此結束。

  接下來是一個較為復雜的人物例子
// /kungfu/class/baituo/ouyang-feng.c 白駝開山祖師歐陽鋒
#include <ansi.h>     //表明這個文件要用到顏色
#inherit NPC;       //繼承NPC的屬性
inherit F_MASTER;     //繼承可收徒的NPC屬性
int check_self();     //聲明,文件中有戰斗行為函數的定義
int learn_message(object ob,string skill);//聲明:文件中有learn_message()函數的定義
string ask_zhang(); //聲明:文件中有謎題函數的定義
void create()
{
  set_name("歐陽鋒", ({ "ouyang feng", "ouyang", "feng" }));
//注意,不要用set("name","")直接set_name
  set("long", "他是白駝山庄主,號稱"HIW"“西毒”"NOR"的歐陽鋒。\n"
    +"雖然由于習練「九陰真經」走火入魔,變得精神錯亂,但\n"
  +"是他那額頭上的層層紫暈,令人不得不服他是一代高手!\n");
//這就是不用@LONG&LONG的例子,所以就必須在每句尾加上“\n”的換行標志
  set("nickname", HIW"西毒"NOR); //外號,用到了顏色,所以開頭沒有include <ansi.h>這里會出錯
  set("gender", "男性"); //性別,太監是無性
  set("age", 53);     //年齡
  set("shen_type",-1); //神的正負,如果沒有set默認是1,用這個乘以exp/10得到神值
  set("attitude", "peaceful"); //指這個人物的好戰態度
  set("str", 30); //膂力,
  set("int", 29); //悟性
  set("con", 30); //根骨
  set("dex", 28); //身法,這些先天屬性,可設可不設,但要符合原著精神
  set("qi", 2500); //當前氣
  set("max_qi", 2500); //最大氣,就是恢復滿時
  set("jing", 900); //當前精
  set("max_jing", 900); //最大精
  set("neili", 2000); //當前內力
  set("max_neili", 2000); //最大內力
  set("jiali", 50); //相當于玩家的加力jiali *
  set("combat_exp", 1500000); //經驗
  set_skill("force", 200); //設置武功,這是基本內功
  set_skill("unarmed", 170); //反正一項項設,略.......
    ......
  set_skill("nilian-shengong", 200);
    ......
  map_skill("force", "nilian-shengong"); //相當于玩家的jifa
    ......
  create_family("白駝山派",1, "開山祖師");//門派頭銜
  set("inquiry" ,([
    "歐陽克":"歐陽鋒嘿嘿一笑:“那是我的乖侄子,你見過他了嗎?”\n",
    "蛇杖":(:ask_zhang:),
  ]));
/*這個是設置當玩家ask sb about sth時的信息,“:”前就是sth,后面
則是返回的信息。這有點與房間里的set("desc_item")相似。所以也有關
于 (:ask_zhang:)的函數調用,這個函數我們在文件頭已經定義過了,后
面將會有具體的內容。*/
  set("chat_chance",2); //設置隨機動作的機率,這是指2%
  set("chat_msg",({ //設置隨機動動作
    "歐陽鋒自言自語道:“我白駝山派神功一成,定能重霸江湖!!”\n",
    "歐陽鋒道:“我兒歐陽克聰慧過人,必能夠重振白駝山派雄風!”\n",
    "歐陽鋒道:“江湖險惡,困難重重,我才是天下第一!”\n",
  }));
  set("chat_chance_combat", 100); //這是指戰斗中的隨機行為,注意區別。
  set("chat_msg_combat", ({
    (: command("wield zhang") :),
    (: command("wield zhang") :), //裝備武器
    (: perform_action, "staff.shewu" :),
    (: perform_action, "staff.shewu" :),//使用絕招
    (: command("unwield zhang") :),
    (: check_self :), //這是我們自定義的一個函數,在后面寫著
  }) );
  setup();
  carry_object("/d/baituo/obj/shezhang");//身上的東西,加上“->wield”就是裝備好了,如果沒有這個,就會在身上,但沒裝備起來
  carry_object("/clone/misc/cloth")->wear();//這件衣服就是穿上的,也可不穿
  add_money("silver",50); //設置他身上的錢,可以gold,coin
}
void init()
{
  ::init();
  add_action("do_skills","skills");
  add_action("do_skills","cha"); //兩個動作調用同一個函數do_skills
}
int do_skills(string arg)//定義do_skills函數,并表明其類型
{
  object ob ;
  ob = this_player () ; //定義ob是指的執行這個動作的人
  if( !arg && arg!="ouyang feng"&& arg!="ouyang"&& arg!="feng" )
    return 0; //如果對象不是歐陽鋒,則返回調用skills的cmds指令
  if(wizardp(ob)) //return 0; 是巫師的話返回調用skills的cmds指令
  if (ob->query("family/master_name")!="歐陽鋒")
    return 0; //師父不是歐陽鋒的話返回調用skills的cmds指令
  if(!ob->query_skill("nilian-shengong",1))//如果沒學過逆練神功
  {
    write("歐陽鋒目前所學過的技能:\n"+
" 基本內功 (force) - 深不可測 200/ 0\n"+
"□蛤蟆功 (hamagong) - 深不可測 200/ 0\n"+
......\n"); //略
    return 1; //參考前面的懂了這里retrun 0和return 1的意思嗎?
  }
  else //相反則是學過
  {
    write("歐陽鋒目前所學過的技能:\n"+
" 基本內功 (force) - 深不可測 200/ 0\n"+
"□逆練神功 (nilian-shengong) - 深不可測 200/ 0\n"+
.......
\n"); //這時才讓他的徒弟能查看歐的逆練神功級別
    return 1;
  }
}

void attempt_apprentice(object ob)//這個函數的原型就是在前面定義的inherit F_MASTER里
{
  if((int)ob->query("combat_exp")<100000) //條件一,經驗要大于100000
  {
    message_vision("歐陽鋒冷冷地對$N道:“這點經驗就想來拜師?!”\n",ob);
    return; //因為這是一個void函數,直接return,面不是return 1之類的
  }
  if((int)ob->query_skill("hamagong",1)<60) //條件二,蛤蟆功大于60級
  {
    message_vision("歐陽鋒冷冷地對$N道:“武功不錯,先跟我的弟子學些的入門武功吧!”\n",ob);
    return;
  }
  message_vision("歐陽鋒拍拍$N的頭,微微點了點頭。\n",ob); //過濾完了就通過
  command("recruit " + ob->query("id"));
  return;
}
int check_self() //即我們自定義戰斗中行為
{
  int max_qi,eff_qi,qi; //命名三個變量
  object me;
  me = this_object(); //不多說了吧
  max_qi = me->query("max_qi");
  eff_qi = me->query("eff_qi");
  qi = me->query("qi"); //對三個變量進行初始定義
  if((int)(qi*100/max_qi)<30 || (int)(eff_qi*100/max_qi)<50)//就是說歐受傷到一定程度
  {
    if((int)me->query_temp("powerup")) //如果已經在提升戰斗力狀態
    {
      ....... //將干什么什么
      return 1;
    }
    ......; //否則就怎么怎么
    return 1;
  }
}

string ask_zhang()//定義解謎中的關于蛇杖的函數
{   //string與int都可以,區別在于string函數return的是字符串
  object me,weapon,obj,obn;
  mapping fam;
  me = this_player();
  if(!(fam = me->query("family"))|| fam["family_name"] != "白駝山派") //不是白駝弟子
    return "\n歐陽鋒沖你陰陰地一笑:“你是不是想嘗嘗我西域靈蛇毒的厲害?”\n";
  if((int)me->query_skill("xunshe-shu",1)<50) //馴蛇朮太低
    return "\n歐陽鋒拍拍你的頭說:“你的馴蛇朮還不到家,現在用蛇杖太危險!”\n";
  if( !me->query("weapon")||(string)me->query("weapon/type")!= "杖") //沒有自鑄的杖
    return "\n歐陽鋒說道:“要想煉蛇杖,必須要有一根自鑄的杖,你先找歐冶子鑄杖吧!\n";
  if( me->query("weapon/she")) //已經有了蛇杖
    return "\n歐陽鋒怒道:“小子也敢戲弄老夫,明明已有蛇杖,還要問什么?滾!!!\n";
  if( me->query_temp("dixi-wan")) //已經要了藥丸
    return "\n歐陽鋒大怒:“貪得無厭的家伙,拿了還想拿,再這樣,老夫一杖叫你上西天!\n";
//到此為止,一切不符合解謎的條件都過濾完了,則開始執行
  obn=new("/d/baituo/obj/dixi-wan"); //要取出的東西的路徑文件名,相當于clone
  obn->set("sign",me->query("id")); //東西上設上給的人的記號
  obn->move(me); //這個東西放進問的人的身上
  me->set_temp("dixi-wan",1); //問的人做記號,以防他再去要,看前面
return "\n歐陽峰仰天哈哈一笑后,緩緩說道:“我靈蛇杖法奇妙無比,配合蛇杖上靈蛇\n"
"的攻擊,可使對手防不勝防,我這有一顆通靈地犀丸,你拿去找蛇奴,他會知道的!”\n"
HIC"說完歐陽鋒遞過來一顆鴿蛋大小的藥丸。\n"NOR;
}

int learn_message(object ob,string skill) //定義學武功的條件
{
  if((skill=="nilian-shengong")&&(!ob->query_skill("nilian-shengong",1)))
  {
    message_vision("歐陽鋒陰陰地對$N說道:“你如何能從我這學會這種沒有一點功基的武功?”\n",ob);
    return 0;
  }
  else return 1;
}

  人物也講完了,其實根據這兩個文件,你可只選其中一兩點就可以改出很多你所需要的人物來,在初級階段,把現成的文件改會避免太多的BUG的出現。但是我所要說的是:重在理解。理解了之后,什么都好辦了。

  以下列出一些定義在人物里的一些附加函數,以供參考。

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 指令的生物,只有當這個附加函數傳回 1時才會接受挑戰,否則顯示某某不想
  跟你較量的訊息。
  呼叫者: "fight" 指令
  有預設定義此一函數的系統物件: NPC
int accept_object(object who, object item)
  當有人用 give 指令給這個非玩家人物東西時,會呼叫這個附加函數,傳回 1
  表示愿意接受這個東西,傳回 0 表示不接受。
  呼叫者: "give" 指令
  有預設定義此一函數的系統物件: none
void recruit_apprentice(objct apprentice)
  當一個人物收了另一個人物做弟子時會呼叫這個附加函數,你可以在這個函數里
  修改弟子的 rank 或其他東西。
  呼叫者: "apprentice" 指令
  有預設定義此一函數的系統物件: none

  有關房間、人物和物品這三種類型只是我們人為的划分,對于系統來說,它們應該都是一回事。為了實現某一種效果,既可以寫在房間里進行執行,也可以寫在人身上進行執行。看了這一章后,你可以嘗試著寫一些程序了。我們希望你的感性認識是建立在自己寫了超過百個的編譯通過的程序以后,下一章,我們就可以學習LPC的概念了。