zoukankan      html  css  js  c++  java
  • LPC基础教程-Lpc程序和编程环境 mudos 加载原理

    编程环境

          通常我们所见到的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) + "
    ");
    
        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!
    ");
            break;
    
        case 5..6:
            write("Congrats, third prize!
    ");
            break;
    
        case 7..8:
            write("Yes! Second prize!
    ");
            break;
    
        case 9:
            write("WOOOOPS! You did it!
    ");
            break;
    
        default:
            write("Someone has tinkered with the wheel... Call 911!
    ");
            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.
    ");
            return;
        }
    
    throw(mixed info)
    e.g.
        if (test < 5)
            throw("The variable 'test' is less than 5
    ");

    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!
    ");
        else
            write("No such luck.
    ");

    函数类型

    在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!
    " :); 
    
    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
    ", (*f)(1), (*f)(2), (*f)(3));
    
    }

     

  • 相关阅读:
    操作系统学习五部曲
    由实模式进入保护模式
    extends && implements
    <mvc:annotation-driven>
    集合类关系
    Servlet8
    SprigMVC基础测试
    (转载)synchronized代码块
    jetty与tomcat
    输入输出流总结
  • 原文地址:https://www.cnblogs.com/cfas/p/5877842.html
Copyright © 2011-2022 走看看