編程環境
通常我們所見到的Mud大多是LpMud。LpMuds使用Unix的指令和文件結構。如果你對Unix有所了解,那么LpMud中的一些指令和它的文件結構與普通的Unix基本一樣。如果你從未使用過Unix,那么它與Dos不同的是在文件的路徑用"/",而不是Dos的"/".一個典型的LpMud的文件是這樣的: /clone/player/player.c 其中"/clone/player/"是路徑,player.c是文件名。
在多數的LpMud中,下面這些的基本的Unix指令是可以使用的:
pwd: 顯示當前目錄
cd: 改換你當前的工作目錄,和Dos的cd一樣。
ls: 列出指定目錄下的所有文件,如果沒有指定任何目錄,那就列出當前目錄底
下的文件。和Dos的dir一樣。
rm: 刪除一個文件 和Dos的rmdir一樣
mv: 從命名一個文件 和Dos的move一樣
cp: 復制一個文件 和Dos的copy一樣
mkdir: 創建一個目錄
LPC入門 LPC就是我們用來寫MUD的語言啦,它的語法和C 基本一樣。它獨特之處在於有簡單的 OOP特性(簡單但很有用:PP),還有一個C里面沒有的HASH表的類型:MAPPING LPC和C還有一個不同是其主函數是CREATE()而不是MAIN()。create()別寫錯哦:PP LPC里面主要的(也是寫MUD所足夠的)數據類型有int,string,mapping,object, mixed。下面主要講一下這些類型了。 因為整數類型對於寫MUD已經足夠,所以不用FLOAT了。同樣的,因為我們的 漢字是雙字節的,所以CHAR類型其實也沒用,只需要STRING 類型就可以了。 STRING的定義,這里要提一下:string常量的賦值,假如是常量的話可以只用 “連接”的辦法代替string,例如: string str = "我" "們" ;那么結果是str == "我們" 同樣的str = "我" "們";也是一樣,空格與換行在LPC編譯時是被忽略的,所以我們 寫MUD的時侯不仿多些TAB和換行,這樣程序容易看些。 當然,除了直接連接之外還可以用 + 連接。在有變量的時侯就要用了。例如: string str0 = "我們"; string str1 = "和"+str0+"大家"; 結果是str1 == "和我們大家 mixed是一個比較特別的類型。mixed 類型的變量可以賦任何其他類型的值。 這在未知變量類型的時侯非常有用。不過一般情況下很少會用到mixed。 LPC里面沒有“指針”的概念。在變量名前面加 * 的定義表示數組。如int *a 表示a 是整數數組。一般來說我們定義數組時是未知其大小的。所以定義時不用象 C 那樣給定大小。數組和MAPPING有些類似,所以將在下面和MAPPING一起講它們 的操作。 LPC里面沒有“指針”的概念。在變量名前面加 * 的定義表示數組。如int *a 表示a 是整數數組。一般來說我們定義數組時是未知其大小的。所以定義時不用象 C 那樣給定大小。數組和MAPPING有些類似,所以將在下面和MAPPING一起講它們 的操作。 object, 是OOP概念了,在LPC里面好象有CREATE()的都可以做object 類型變量 了。object 我們稱為“對象”,在MUD里就是一件物品,一個房間或任何一個“具 體”的東西,都是OBJECT。“對象”,在OOP中是一些數據與基於這些數據的函數的 集合(好象文謅謅的? :PP)嗯,object 中的數據一般不能直接操作(至少在LPC 里面不能的 :PP),所以對object的操作只有賦值(對object類型的賦直 )和執行 object的函數( private函數不能被外部調用 )。 函數調? 式: eg. object ob; ob-$#@62;test(1,2); 基本格式: object類型變量名-$#@62;函數名(函數參數列); 其中若該object 中未定義該函數名的函數則返回 0 值(呵呵,不會有出錯信息的哦, 所以千萬別寫錯名字了)。 寫了這么多終於寫到LPC 最有特色的兩個類型了,mapping和數組。 mapping和數組在“外觀”上有些類似,所以在一起寫了。前面提到過:mapping是 散列表,具體如何這里不詳述了,只希望大家一定要記住mapping的格式!!( 實際 上這格式只在給變量賦初值時用到 )mapping 格式如下: eg. mapping a = ([ "ab" : 1 , "cd": 2 , ]) 標准格式: mapping 變量名 = ([ key1 : value1 , key2 : value2 , .......... .......... ]); 其中key可以是除了int以外的任何值!! (好象char也不行吧 :P ),包括任何數組 甚至mapping也可以做key ,而value則可以是任意所有值。mapping的應用在於: 可以用key來index得到相應的value值。如上面eg的,我們有:a["cd"] == 2,.... 因為要用key來index的關系,在mapping中key值不能相等,若相等則覆蓋掉原來該 key對應的value。寫的有些亂了,SORRY :P 那么一個mapping怎么改變它的值呢?有兩個方法(下面設mapping map )。 1. map[key] = value; 2. map += ([key : value ]); 請大家留意第二種賦值方法,它跟我們將要提到的數組的方法一樣的 。 在mapping中“刪除”一個key (其相應value當然也沒了)方法是: map_delete(map,key); lpc里面的系統函數(efun)的命名一般按參數順序來取名( map_delete的參數就是 map在前,被delete的key在后的 ),希望大家注意。 另外mapping的efun還有values,keys,undefinedp 大家可以直接在MUD里 help efunname 來看幫助,也可以參閱 /doc/lpc/types/mapping 這一文件 數組: 即ARRAY,前面說過,LPC中的數組只要在類型后面加 * 就可以了 一般來說數組不用預先定大小 但若有需要可以用allocate(size)來固定大小。如:a = allocate(10); 在固定了SIZE之后好處是可以任意用下標定位來對數組元素操作。 我的習慣是不用固定SIZE的數組,因為那樣更自由些。數組的操作在LPC里是相當強的。 最常見的是 +, - 操作,也是最useful的。 +就是通常的“連接”操作, -是集合的“差”操作,如果沒有這兩個概念請看下面例子: a == ({ 1,2,2,3, }) 則a + ({2,3,4}) == ({ 1,2,2,3,2,3,4}) a - ({ 2,1}) == ({3}) 請注意 - 的時候會把所有相同的元素都消掉的. 結合上面例子可以知道ARRAY常量的表示方法了,就是 ({元素,元素,....}) 當然這只是一維數組(事實上我們用一維數組就足夠了,我想) 在對數組的元素改變的時候,一般可以用直接賦值,如a[3] = 2; 在對數組的元素改變的時候,一般可以用直接賦值,如a[3] = 2; 如何刪除一個元素?假如你確定該元素的值唯一的話可以用 - ({元素值 }) 但要是有同值元素的話就會把它也刪掉了,那不是我們所要的。 一般常用的方法是把該元素賦一個“不可能值”,或者說“無用值”, 然后再用 - 把該元素刪除掉,比如說 我們確定數組內所有元素都為正,則我們可把那個元素賦值為0然后把數組 -= ({0}) 關於array的一個很有用的函數是member_array 用法是 member_array(元素值,數組,起始下標(可選) ); 函數返回數組中從下標開始第一個元素值為所搜索值的元素下標。若無則返回-1; 嗯,LPC的數組是跟C一樣的,下標從0開始到size-1 ,請注意。 member_array的參數中起始下標一般可缺省。 eg: a = ({1,2,2,3}) 則 member_array(2,a) == 1 member_array(2,a,2) == 2 member_array(4,a) == -1 ARRAY和MAPPING可以說是LPC里面最有用和最直接用的結構了。 再加上兩個功能強大的函數:filter_array 和filter_mapping。 我們有了filter就可以實現對ARRAY,MAPPING里各元素的過濾操作。 這樣一來我們就可以實現數據庫的操作了,這樣我們的LPC程序所擁有的功能 是非常強大了 最后說一下多維數組的定義方法。 多維數組一般各維SIZE固定的,有兩種方法定義: eg1: 定義多維數組變量 a = allocate(10); a[0] = allocate(10); a[1] = allocate(10); ...etc... 也就定義了a [10][10] eg2: 用mixed 類型 = 多維數組常量 mixed a; a = ({ ({ 1, 2, 3 }), ({ 1, 2, 3 }) });
rmdir: 刪除一個目錄
more: 按頁顯示一個文件在你的當前屏幕。
cat: 顯示整個文件。和Dos的type一樣。
tail: 顯示一個文件的結尾幾行。
ed: 允許你使用Mud自帶的編輯器,編輯一個文件。
Lpc程序
Lpc的程序看起來和一般的C區別不斷大,語法基本一樣,但是Lpc和一般的語言有着根本的不同,Lpc程序是編寫一個一個的"Object"。這有什么區別呢?一般的程序是在執行過程中,通常有一個明顯的開始和和結束。程序從一個地方開始,然后順序執行下去,到了結束的地方就中斷了。Lpc的Object不是這樣的。所謂的不同的Mud,實際上是一些不同的Lpc的Object在一個Driver的上的各種不同的表現。也就說,Lpc的Object是運行在一個Driver上的。這些Object組成了LpMud的豐富多彩的世界。Driver幾乎不參與創建你所接觸到的世界,它所完成的工作只是讓那些Lpc的Object活動起來。Lpc的Object可能沒有明顯的開始和結束的標志,它可能永遠在工作。
和一般的程序一樣,Lpc“程序”也是由一個或多個文件組成。一個Lpc的Object是按如下方式被執行的:Driver把和這個Object相關的文件讀入內存,然后解釋執行。但是要記住的是,讀入內存,並不是說,它就開始按順序執行。
Driver和Mudlib關系
在有些游戲中,整個游戲包括Driver和游戲世界都用C寫好,這樣能快一些,但是游戲的擴充性很差,巫師們不可能在游戲進行中添加任何東西。LpMud則相反。Driver理論上應該和玩家所接觸的世界幾乎沒有任何直接的聯系。游戲世界應該是自己獨立的,而且是“即玩即加”的。這就是為什么LpMud使用Lpc作為編程語言的原因。它允許你創建一個游戲世界,再由Driver在需要時讀入解釋執行。Lpc甚至比C更簡單,更容易明白,但是它可以創建一個可以讓許多人在上面游戲的世界。在你寫完一個Lpc的文件時,它存在於主機的硬盤上。在游戲進行中,當需要整個Object時,這份文件將被調入內存,一個特殊的函數被調用來初始化這個Object的一些變量。現在你不用關心什么是變量,什么是函數以及游戲本身怎樣來調用這個object,你只要記住Driver從硬盤中讀入一個文件,然后放在內存中,如果沒有任何錯誤的話。
Object載入內存
一個Object不會也不必有一個特點的地方讓Driver去執行它,通常Drvier會在Object中找一個地方去初始化它。一般都是這個函數叫做create()。Lpc的Object是一些變量(它的值能變化)和函數(函數通常是用來操縱那些變量的一段程序)的組合。函數操縱變量的方式有:調用其他函數,使用Driver內部定義的函數(efun),基本的Lpc表達式以及流控制。我們來看個變量的例子: wiz_level。這個變量記錄你的巫師等級,如果是0呢,通常是普普通通的玩家了。這個值如果越大就表示你的巫師等級越高。這個也同時控制了你能不能執行一些巫師指令。基本上來說,一個Object就是一些變量“堆”在一起的東西。一個Object改變了,也就是某一個或者一些變量改變了。總的來說,一個Object如果要被內存中的另一個Object調用,Driver會去找這個Object的那堆變量放在哪里,如果這些變量沒有值,那么Driver會調用一個特定的函數create來初始化這些變量。但是create()不是Lpc代碼開始執行的地方,只是大多數的Object從這里開始。事實上,create()可以不存在。如果這個Object不需要對變量初始化,那么create()可以不存在。那么這樣的Object開始執行的地方就完全的不同於一般的Object,可以從任何地方開始。那么究竟什么是Lpc的Object?Lpc的Object就是一堆變量的集合,它帶有一個或者更多的函數來操縱控制這些變量,函數的排列順序是無所謂的,隨便那個排在前面對這個Object的特性沒有影響。
小結
關於Lpc程序和編程環境,就介紹到這里。看完這一章,我想大家要記住的是LpMud是采用Lpc做為編程語言, Unix文件結構作為文件組織形式。Lpc是編寫Object的一種語言,它的程序沒有特殊的開始和結束的標志。如果Object被使用到,那么它被調入內存,如果這個Object有一個叫create()的函數,首先被執行,來初始化一些變量。Lpc的Object是一堆變量的集合,同時帶有一些能操縱改變這些變量的函數。Lpc的代碼風格,我想一個Mud最好有一個統一的風格,特別的XO有自己的特別的要求。
LPC Tutorial
從lpc-tutorial下載tutorial,通過閱讀教程來學習LPC。
關於LPC
LPC被發明出來是一個用於LPMUD的解釋性語言。LPMUD其實就是一個游戲服務器,那么就很清楚了LPC是一個用來寫游戲服務器的腳本語言。
LPC這個名字暗示了和C語言的聯系。當然兩者之間是有區別的,后面會漸次展開來講。
LPC游戲解析
一個LPC游戲可以划分為三個部分:游戲驅動、mudlib、domain code。
游戲驅動:運行於主機上的程序。基本的對象管理核心和解釋器。它被用來理解LPC語言並執行這些指令。
mudlib:LPC對象的集合。其中包含了基本的游戲環境。mulib里面的對象是最基本的游戲元素,比如玩家、怪物、房子等等。
domain code:???
語法入門
啥都別說了,看代碼。
while語句 while (test) statement; if語句 if (this) { statement; } else if (that) { another_statement; } else { default_statement; } 定義變量 int a, b, c; for循環: for (a = 0; a < 10; a++) { b = function_call(a, b * 2); c = b * 3 / 4; } 空語句循環: while (!(var = func(var))) ; for循環: for (i = 0; i < 100; i++); { <code that gets executed only once, but always> } 定義方法: public void my_function(int a, int b) { < code > } 文件頭注釋: /* * <filename> * * <Short description of what the file does, no more than 5-7 lines. * ... * ... > * * Copyright (C): <your name and year> * */ 函數注釋: /* * * * Arguments: <A list of all arguments, one per line * arg1 - description no longer than the line. * arg2 - next argument, etc. > * Returns: <What the function returns> */
LPC基本語言概念
LPC不是編譯型的,而是解釋型的語言。
每次運行都被會重新解釋為機器語言。
其實這意味我們寫的是一種間接語言,通過特定的解釋器執行特定的機器語言。
LPC語言的文件都是以.c為后綴的。文件名全部小寫,如果文件里面含有多個單詞,用下划線_把單詞隔開。
LPC基本語法
注釋
// This is a comment stretching to the end of the line. /* This is an enclosed comment */
數據類型
void:nothing int:the range -2147483648 to 2147483647. float:range 1.17549435e-38 to 3.40282347e+38. string:such as "hallo world!" mapping:key value pair. object:references to LPC programs loaded into memory. function:LPC functions. array:all of things mixed:all of type
變量聲明
int counter; float height, weight; mapping age_map; int counter = 8; float height = 3.0, weight = 1.2; mapping age_map = ([]); object *monsters = ({});
基本上語法和pike是差不多的,如果還沒入門最好先去看看pike。pike學習筆記
如果沒有為變量賦初值,那么變量會被賦值為0,相當於其他語言的null,一般來說都不是我們希望看到的,所以哪怕賦值為空都好過沒有。
方法聲明
/* * Compute the diameter of a circle given the circumference. * * Variables: surf_area - the surface area * name - the name given the circle * Returns: The circumference. */ float compute_diam(float surf_area, string name) { float rval; // Circumference = pie * diameter rval = surf_area / 3.141592643; write("The diameter of " + name + " is " + ftoa(rval) + "\n"); return rval; }
基本上對照上面的例子就知道怎么去聲明和定義一個方法了。
語句和表達式
就是一些算數、布爾、條件、比較操作符。跟pike差不多,不贅述了。
比較特別的是:The statement 'a = 1, 2, 3;' will set 'a' to contain '1'.
一般我們寫if語句都這樣寫:
if (name == "fatty") { nat = "se"; desc = "blimp"; } else if (name == "plugh") { nat = "no"; desc = "warlock"; } else if (name == "olorin") { nat = "de"; desc = "bloodshot"; } else { nat = "x"; desc = "unknown"; }
更好的選擇其實是使用switch語句:
switch (name) { case "fatty": nat = "se"; desc = "blimp"; break; case "plugh": nat = "no"; desc = "warlock"; break; case "olorin": nat = "de"; desc = "bloodshot"; break; default: nat = "x"; desc = "unknown"; }
省了很多括號,而且更加清晰明了。
多用三元符號代替if-else語句:
int max(int a, int b) { if(a > b) return a; else return b; } int max(int a, int b) { a > b ? a:b; }
優先級可以去查表:lpc優先級查找
普通的循環語句就不再贅述了。
array
可以通過下面的方式聲明array:
int *my_arr, *your_arr; float *another_arr; object *ob_arr; my_arr = ({})
雖然我覺得這種方式不太好。
可以聲明一個固定大小的array:
you_arr = allocate(3); // => your_arr = ({ 0, 0, 0 });
此外,如何想要在array后面或者前面添加元素,可以這樣:
int a = 3; int *b = ({1,2}); b = b + ({a});
甚至還能切片,切片始終返回一個array:
my_arr = ({ 9, 3, 5, 10, 3 }); my_arr = my_arr[0..0] + my_arr[2..4]; // => ({ 9, 5, 10, 3 })
mapping
mapping就是鍵值對序列。
聲明一個mapping:
mapping my_map;
使用mapping的方法和pike一致。
比較特別的是,如果想刪除mapping內的數據,可以用這個:
my_map = m_delete(my_map, "bertil"); my_map = m_delete(my_map, "david");
此外,如果查找一個不存在的鍵值對,不會報錯,而是返回0.
預處理
預處理並不屬於LPC語言的一部分。在編譯為可執行程序之前,預處理會將替換好所有的特定字符串。
導入源文件
當我們需要一些其他源代碼文件提供的函數時,我們可以通過下面的方式來導入:
#include <standard_file> #include "special_file" #include <stdproperties.h> #include <adverbs.h> #include "/d/Genesis/login/login.h" #include "my_defs.h" #include "/sys/adverbs.h" // Same as the shorter one above
基本上和C語言導入源文件是一樣的。
宏定義
偶爾我們會需要用字符串來代替數字或者表達式,比如說:
#define MAX_LOGIN 100 /* Max logged on players */ #define LOGIN_OB "/std/login" /* The login object */ #define GREET_TEXT "Welcome!" /* The login message */
一般來說,不建議寫宏。因為宏是無類型的,而且會在異常時無法確定到底是哪個地方出了問題。建議使用常量來代替宏,記得宏之所以還存在完全是為了向下兼容。
#if, #ifdef, #ifndef, #else and #elseif
直接看代碼吧:
#if CODE_VAR == 2 <code that will be kept only if CODE_VAR == 2> #else <code that will be kept only if CODE_VAR != 2> #endif #define CODE_VAR /* This defines the existence of CODE_VAR */ #ifdef CODE_VAR <code that will be kept only if CODE_VAR is defined> #else <code that will be kept only if CODE_VAR isn't defined> #endif #ifndef CODE_VAR <code that will be kept only if CODE_VAR isn't defined> #else <code that will be kept only if CODE_VAR is defined> #endif
感覺用這些有硬編碼的感覺,會增加理解代碼的難度,所以不推薦使用。
進階篇
打印
1、write:自然不用在贅述了,相當於printf。
2、dump_array:打印一個array所有值,調試的時候挺有用的。注意,pike里沒有這個函數。
函數調用
各種外部函數調用方式:
pie = math_ob->compute_pie(1.0); pie = "/d/Mydom/thewiz/math_ob"->compute_pie(1.0); pie = call_other(math_ob, "compute_pie", 1.0); pie = call_other("/d/Mydom/thewiz/math_ob", "compute_pie", 1.0);
雖然后面三種也能調用函數,但是這種代碼的可讀性太低了,完全應該忘掉。
再來看看實際應用時的情況:
object *people, *names;
mapping hp_map;
// Get a list of all players. people = users(); // Get their names. names = people->query_real_name(); // Make a mapping to call with. Item = name:pointer hp_map = mkmapping(names, people) // Replace the pointers with hit point values. hp_map = hp_map->query_hp(); // All this could also have been done simpler as: hp_map = mkmapping(users()->query_real_name(), users()->query_hp());
如何繼承一個對象類?
inherit "<file path>"; // 比如說 inherit "/std/door"; inherit "/std/room.c"; //栗子 void my_func() { /* * This function exists in the parent, and I need to * call it from here. */ ::my_func(); // Call my_func() in the parent. }
檢測變量類型
由於變量可能是0或者任意類型的東西,往往需要自己對變量做類型檢查。
@bullet{int intp(mixed)} Test if given value is an integer @bullet{int floatp(mixed)} Test if given value is a float @bullet{functionp(mixed)} Test if given value is a function pointer @bullet{int stringp(mixed)} Test if given value is a string @bullet{int objectp(mixed)} Test if given value is an object pointer @bullet{int mappingp(mixed)} Test if given value is a mapping @bullet{int pointerp(mixed)} Test if given value is an array
類型限定符
static 變量:靜態的全局變量,聲明一次之后一直存在
static 函數:只能內部訪問,外部是不可見的
private 變量或函數:不被繼承,只能對象內部訪問
normal 變量或函數:can not be mask?
public 變量或函數:默認的限定符,任何成員都可訪問內部對象
varargs 函數:可變參數數量的,按順序對參數賦值,如果沒有則默認賦值為0。
函數類型
函數也可以作為一個變量。
function my_func, *func_array;
my_func = allocate;
my_func = &allocate();
int *i_arr; i_arr = allocate(5); // Is the same as... i_arr = my_func(5); // ... using the function assignment above.
通過這種方式給函數重命名。
switch case
LPC的switch case支持int范圍:
switch (i) { case 0..4: write("Try again, sucker!\n"); break; case 5..6: write("Congrats, third prize!\n"); break; case 7..8: write("Yes! Second prize!\n"); break; case 9: write("WOOOOPS! You did it!\n"); break; default: write("Someone has tinkered with the wheel... Call 911!\n"); break; }
catch throw
LPC和普通語言的try-catch方式捕獲異常是不一樣的:
int catch(function) e.g. //0-fail 1-true if (catch(tail("/d/Relic/fatty/hidden_donut_map"))) { write("Sorry, not possible to read that file.\n"); return; } throw(mixed info) e.g. if (test < 5) throw("The variable 'test' is less than 5\n");
mapping、array 引用
LPC的mapping、array與pike一樣是引用類型:
object *arr, *copy_arr;
arr = ({ 1, 2, 3, 4 }); // An array copy_arr = arr; // Assume (wrongly) that a copy_arr becomes // a copy of arr. // Change the first value (1) into 5. copy_arr[0] = 5; //如果想要一份拷貝怎么做? copy_arr = ({ }) + arr;
LPC/Mudlib接口
感覺到這里就是要開始學習如何實際使用LPC來編程了。前面的都只是基本的語法知識。
首先介紹:/std/object.c。游戲里所有的對象都會繼承這個基本類型。
其他類型有:
`/std/armour.c'
Armour of any kind
`/std/board.c'
Bulletin boards
`/std/book.c'
A book with pages you can open, turn and read
`/std/coins.c'
The base of all kinds of money
`/std/container.c'
Any object that can contain another
`/std/corpse.c'
Corpse of dead monsters/players/npcs
`/std/creature.c'
Simple living creatures, basically a mobile that can fight
`/std/domain_link.c'
Use this as a base to preload things in domains `/std/door.c' A door that connects two rooms `/std/drink.c' Any type of drink `/std/food.c' Any type of food `/std/guild (directory)' Guild related objects (the guild and the shadows) `/std/heap.c' Any kind of object that can be put in heaps `/std/herb.c' Herbs `/std/key.c' Keys for doors `/std/leftover.c' Remains from decayed corpses `/std/living.c' Living objects `/std/mobile.c' Mobile living objects `/std/monster.c' Monsters of any kind `/std/npc.c' A creature which can use 'tools', i.e. weapons. `/std/object.c' The base object class `/std/poison_effect.c' Handle effects in poison of any kind `/std/potion.c' Potions `/std/receptacle.c' Any kind of closable/lockable container `/std/resistance.c' Handle resistance against various kinds of things `/std/room.c' Any kind of room `/std/rope.c' Rope objects `/std/scroll.c' Scrolls `/std/shadow.c' Used as base when creating shadows `/std/spells.c' Spell objects, tomes etc `/std/torch.c' Torches/lamps etc `/std/weapon.c' Any kind of weapons
對象的使用
一個對象總是能夠得到自己的引用:
ob = this_object()
這個就類似於C++的this指針。
對象的函數能夠往前去查找調用此函數的對象(好神奇的感覺):
p_ob = previous_object(); // The object calling this function. pp_ob = previous_object(-2); // The object calling the object // calling this function.
甚至還能往前找指定層數的對象。
不過這個函數只能去找外部調用,如果我們想要更牛掰的話,用這個:
object calling_object(void|int step)
用法是一樣的,但是能夠找內部也能找外部。
怎么去判斷找到的是一個合法的東西呢?(不是一個0)用objectp(something)
就好了:
if (objectp(calling_object(-2))) write("Yes, an ob calling an ob calling this object exists!\n"); else write("No such luck.\n");
函數類型
在LPC里面,函數function也是一種對象,或者說變量類型。
可以像這樣定義一個函數指針:
function f = (: local_func :);
上面的 f 可以用於其他程序流程或外部函數中, 如同普通的變量值:
foo(f); map_array( ({ 1, 2 }), f);
或者可以直接執行:
x = evaluate(f, "hi"); 等同於: x = local_func("hi");
甚至於,定義函數指針時還能指定參數:
function f = (: write, "Hello, world!\n" :); evaluate(f); 顯然,會輸出: Hello, world!
如果想要調用外部對象的函數:
f = (: this_player(), ({ "query", "short" }) :) 等同於: f = (: call_other, this_player(), "query", "short" :) /* 一個外部函數指針, 使用 call_other */ f = (: this_player()->query("short") :) // 有效的運算式; 請見下文.
特殊的,運算式函數指針:
evaluate( (: $1 + $2 :), 3, 4) // 返回 7. 這可以用於 sort_array, 范例如下: top_ten = sort_array( player_list,(: $2->query_level() - $1->query_level :) )[0..9];
不知名函數(函數內部的函數):
void create() { function f = function(int x) { int y; switch(x) { case 1: y = 3; case 2: y = 5; } return y - 2; }; printf("%i %i %i\n", (*f)(1), (*f)(2), (*f)(3)); }