LPC學習筆記

LPC Tutorial

lpc-tutorial下載tutorial,通過閱讀教程來學習LPC。

關(guān)于LPC

LPC被發(fā)明出來是一個用于LPMUD的解釋性語言。LPMUD其實就是一個游戲服務(wù)器,那么就很清楚了LPC是一個用來寫游戲服務(wù)器的腳本語言。

LPC這個名字暗示了和C語言的聯(lián)系。當然兩者之間是有區(qū)別的,后面會漸次展開來講。

LPC游戲解析

一個LPC游戲可以劃分為三個部分:游戲驅(qū)動、mudlib、domain code。

游戲驅(qū)動:運行于主機上的程序?;镜膶ο蠊芾砗诵暮徒忉屍?。它被用來理解LPC語言并執(zhí)行這些指令。

mudlib:LPC對象的集合。其中包含了基本的游戲環(huán)境。mulib里面的對象是最基本的游戲元素,比如玩家、怪物、房子等等。

domain code:???

語法入門

啥都別說了,看代碼。

while語句
while (test) 
    statement;

if語句
if (this)
{
    statement;
}
else if (that)
{
    another_statement;
}
else
{
    default_statement;
}

定義變量
int a, b, c;

for循環(huán):
for (a = 0; a < 10; a++)
{
    b = function_call(a, b * 2);
    c = b * 3 / 4;
}

空語句循環(huán):
while (!(var = func(var)))
    ;

for循環(huán):
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>
 *
 */

函數(shù)注釋:    
/* 
 * 
 *
 * 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不是編譯型的,而是解釋型的語言。

每次運行都被會重新解釋為機器語言。

其實這意味我們寫的是一種間接語言,通過特定的解釋器執(zhí)行特定的機器語言。

LPC語言的文件都是以.c為后綴的。文件名全部小寫,如果文件里面含有多個單詞,用下劃線_把單詞隔開。

LPC基本語法

注釋

// This is a comment stretching to the end of the line.

/* This is an enclosed comment */ 

數(shù)據(jù)類型

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;
}

基本上對照上面的例子就知道怎么去聲明和定義一個方法了。

語句和表達式

就是一些算數(shù)、布爾、條件、比較操作符。跟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;
}

優(yōu)先級可以去查表:lpc優(yōu)先級查找

普通的循環(huán)語句就不再贅述了。

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內(nèi)的數(shù)據(jù),可以用這個:

my_map = m_delete(my_map, "bertil");
my_map = m_delete(my_map, "david");

此外,如果查找一個不存在的鍵值對,不會報錯,而是返回0.

預(yù)處理

預(yù)處理并不屬于LPC語言的一部分。在編譯為可執(zhí)行程序之前,預(yù)處理會將替換好所有的特定字符串。

導(dǎo)入源文件

當我們需要一些其他源代碼文件提供的函數(shù)時,我們可以通過下面的方式來導(dǎo)入:

#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語言導(dǎo)入源文件是一樣的。

宏定義

偶爾我們會需要用字符串來代替數(shù)字或者表達式,比如說:

#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所有值,調(diào)試的時候挺有用的。注意,pike里沒有這個函數(shù)。

函數(shù)調(diào)用

各種外部函數(shù)調(diào)用方式:

    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);

雖然后面三種也能調(diào)用函數(shù),但是這種代碼的可讀性太低了,完全應(yīng)該忘掉。

再來看看實際應(yīng)用時的情況:

    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 變量:靜態(tài)的全局變量,聲明一次之后一直存在

static 函數(shù):只能內(nèi)部訪問,外部是不可見的

private 變量或函數(shù):不被繼承,只能對象內(nèi)部訪問

normal 變量或函數(shù):can not be mask?

public 變量或函數(shù):默認的限定符,任何成員都可訪問內(nèi)部對象

varargs 函數(shù):可變參數(shù)數(shù)量的,按順序?qū)?shù)賦值,如果沒有則默認賦值為0。

函數(shù)類型

函數(shù)也可以作為一個變量。

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.

通過這種方式給函數(shù)重命名。

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指針。

對象的函數(shù)能夠往前去查找調(diào)用此函數(shù)的對象(好神奇的感覺):

p_ob = previous_object();     // The object calling this function.
pp_ob = previous_object(-2);  // The object calling the object
                                  // calling this function.

甚至還能往前找指定層數(shù)的對象。

不過這個函數(shù)只能去找外部調(diào)用,如果我們想要更牛掰的話,用這個:

object calling_object(void|int step)

用法是一樣的,但是能夠找內(nèi)部也能找外部。

怎么去判斷找到的是一個合法的東西呢?(不是一個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");

函數(shù)類型

在LPC里面,函數(shù)function也是一種對象,或者說變量類型。

可以像這樣定義一個函數(shù)指針:

function f = (: local_func :);

上面的 f  可以用于其他程序流程或外部函數(shù)中, 如同普通的變量值:

foo(f);  map_array( ({ 1, 2 }), f);

或者可以直接執(zhí)行:

x = evaluate(f, "hi");
等同于:
x = local_func("hi");

甚至于,定義函數(shù)指針時還能指定參數(shù):

function f = (: write, "Hello, world!\n" :); 

evaluate(f); 

顯然,會輸出:
Hello, world! 

如果想要調(diào)用外部對象的函數(shù):

f = (: this_player(), ({ "query", "short" }) :)

等同于:

f = (: call_other, this_player(), "query", "short" :)        /* 一個外部函數(shù)指針, 使用 call_other */ 
f = (: this_player()->query("short") :)        // 有效的運算式; 請見下文.

特殊的,運算式函數(shù)指針:

evaluate( (: $1 + $2 :), 3, 4)        // 返回 7.

這可以用于 sort_array, 范例如下: 
top_ten = sort_array( player_list,(: $2->query_level() - $1->query_level :) )[0..9];

不知名函數(shù)(函數(shù)內(nèi)部的函數(shù)):

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));

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Pike Tutorial Study Pike是一門解釋型的、面向?qū)ο蟮木幊陶Z言,與C和C++有點像,但是更加容...
    1angxi閱讀 3,946評論 2 5
  • 前言 人生苦多,快來 Kotlin ,快速學習Kotlin! 什么是Kotlin? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,686評論 9 118
  • 從小語文老師就讓我們多閱讀并養(yǎng)成做讀書筆記的習慣。從而將書中精華吸收為自己的語言提高寫作能力。那么如何做閱讀筆記?...
    idoixiu閱讀 339評論 0 4
  • 給全世界球迷帶來了極度狂歡的世界杯已經(jīng)開幕了。我不是球迷,不會半夜三更爬起來看比賽,也很少會為某個隊的勝敗而歡呼或...
    成崖余閱讀 342評論 0 2
  • 有個七八歲左右的女生,在接受到兒子的挖沙邀請后,并不是快樂地接受他的小鏟子,而是小心翼翼地走到奶奶面前去請示,我兒...

友情鏈接更多精彩內(nèi)容