╭═════════════╮
║ ║
║ LPC講座⑴ ║
║ ║
║ Elon 主講 ║
║ 1994.3.12 ║
╰═════════════╯
⊙首先,是介紹MUD的歷史⊙
In the beginning someone created Tinymud. So Tinymud is the first MUD (formally)
it uses database. the whole world is saved in a single file.
diku was created that way too, but with some modification.
LPmud was first created by some guy name Lars (he's from swden I believe).
based on codes from Ambermud, which is a Tinymud alike.
Lars get tired of single database, that's why he made LP the way it is right now
LPmud continues to grow, but stayed at 2.4.5 for a long time (nobody knows why)
I played 2.4.5 for at least 2 years without seeing new modifications
(major ones)
then Lars released ver 3.0, but it's so hard to work with that people
decide to stick with the old one.
until the people at TMI (the mud institue) decide to start a project
on creating MUDOS.
so MudOS is what ES is using now (as driver) and it was based on
3.1.2 driver code.
Lars give up on LPmud after 3.1.2, but the people who used
to work with Lars continue to make changes.
So the CD driver was created, different than MudOS driver.
Other version of drivers exist too.
But not as popular as CD and MusOS.
CD driver were used mostly in European muds and MudOS in US muds.
⊙ Well, 從這裡開始,我們正式進入LP的內容 ⊙
how lp works:
☆下面簡單的介紹Functions and 繼承的概念☆
LP consists of two parts: driver and mudlib
driver is the binary code, and the interpreter,
which will be used to "run" the mudlib.
Mudlib is the definition of the game
besides the different types of drivers, there's also different types of mudlib.
most popular ones: nitemare, TMI and discworld.
A LOT of muds use basic TMI mudlib to create their game. (like ES :)
because the original mudlib.n (from ver 3.0) is too hard to work with.
some mud created their custom mudlib from the basic 2.4.5 mudlib (ancient :)
so coding in a lpmud can be confusing sometimes because the difference
between mudlibs.
You'll get different function calls,
but the syntax/structure is pretty much the same.
as I was saying.. functions in different mudlib can be different,
but *some* will slways be the same as long as the driver is the same.
why? because there is so few name to use for function names :)
for example: set_name() will probably be used everywhere.
Q : set_name?...ES中有嗎?
A : yes. set_name() in all objects.
and you should set that (both chinese and English)
btw- it's also as known as set("name",xxx)
set_name() is just easier to use then set() function.
hat's why they have set_skills(), set_level().. etc
just remember, most functions act differtly under different mudlib,
so always read the help file before coding on a new lib.
working with CDlib is different than MudOS too..
since I doubt anyone here seen CD, we'll skip that :)
In LP, everything is an object. player is an object,
mob is object, room is object ..etc.
so I guess that's why they call LPC an OOP language.
dunno, I never learn C or C++.. I only know LPC.
each object consist the defination on setting up something.
for example: in room file, you define how to treat exits.
how to treat room description .. etc.
anyway, in room file, u also need to define how to handle players
entering and staying.. etc.
this will cause some major memory problem as you are loading
dupilcates and they are wasting space.
That's how inherit came along..
with inherit, there is a central defination for common objects.,
like rooms, mobs..
so when you create a room, you "inherit" the generic room file
so you don't have to defind all those stuff yourself.
which makes creating objects a lot easier.
what inherit does is, when mud loaded,
the generic file was loaded into memory.
and when your code refers to an inherited obj, it refers to that
section of memory. rather than load a new copy of every defination.
which saves a lot of memory.
ok. questions before we start specific codes.. (room, mob, .. etc)
the rest will be pretty much lib dependent, means mostly for ES coding
☆從這裡開始教導如何寫一個房間☆
to create a room, first you inherit the generic room.
(now you got a brand new room to modify)
major part (must have) in room file : create()
in create() you'll need to include the defination
(ie, name, desc) for your room file.
(<Wind> To 學過 C++ 的人...create()就等於是C++ 中的 main())
but first, since you only told the driver to "inherit" a copy of room,
you also need to tell it to call the original create() the ones at
generic room file.
so within your create() you'll do :: create(),
means to call the create() in "generic room"
(this is sometimes confusing.. but just remember,
you need to create a copy of generic room before u can do anything to it,
so :: create() is necessary.
also whenever you need to refer to the function used in the inherit file,
you always use :: to call.
after create, we'll need to set room description and a lot of stuff..
a basic room consist of short, long description and exits.
if u toggle to brief mode, you'll only see short desc when you moved
into a room.
in ES, since it supports both chinese and english mode,
the function to set long and short desc would be like:
set_short(english,chinese);
╭═════════════════════════════════╮
║ (Wind 舉例: set_short( "Adventurer Guild", "冒險者公會" );) ║
╰═════════════════════════════════╯
set_short CAN NOT have \n tho..
it only means to be one line anyway..
set_long has the same syntax..
╭════════════════════════════════════╮
║舉例: ║
║"You are in the Adventurer Guild of Farwind. You are welcome to here\n" ║
╰════════════════════════════════════╯
but with long description, you need \n to wrap lines..
now we have long and short desc, we need exit,
unless you are building a prison..
Q : Scorpion_ 問:要不要管ascii 控制碼?
A : scor: yes.. u need to put escape char before control char..
(and some weird Chinese char too)
╭═══════════════════════╮
║ 舉例: ║
║ <Wind> set("exits", ([ ║
║ <Wind> "east" : FARWIND"smain", ║
║ <Wind> "west" : "/d/wiz/wiz_hall", ║
║ <Wind> "up" : FARWIND"adv_storage", ║
║ <Wind> "down":FARWIND"quest_room", ║
║ <Wind> ]) ) ; ║
╰═══════════════════════╯
and one last thing: lights. since I found ES's generic room default
lights to be 0.. (unusual)
or set_light(1); I believe this works as well..
<Wind> Ya...初學者要小心,不然會寫出一堆的黑暗地區...
(pay special notice to this function, you can use it to create some
funny stuff, like light switch :)
infrasion doesn't work at ES, so light is only 1 and 0,
1 means light and 0 means dark.
IF you are working with libs support infrasion, light can be more than 0 and 1.
Thus we have a basic room. But it's boring to create 100 roms like this.
sometimes we need to add furnitures into a room (sign, plaque)
Q : Scorpion_ 問:那在室外有白天和晚上呢?
A : <Wind> 在ES中時間不影響light...
<elon> scor: there is a special mark for room that's outside.
╭═══════════════════════════════════╮
║ 舉例囉... ║
║ set("c_item_desc", ([ ║
║ "sign" : @C_SIGN ║
║ 這張告示寫著: ║
║ ────────────────────────────────── ║
║ 歡迎來到冒險者公會: ║
║ 冒險者公會是屬於所有在這個世界上冒險的人﹐不管你是否加入了其它的 ║
║ 公會﹐這裡的大門永遠為你而敞開。你可以在這裡訓練(train) 技能或提昇 ( ║
║ advance)等級與屬性﹐輸入 help guild 可以得到更詳細的說明。 ║
║ 有關本公會的詳細情形﹐請用 help adventurers。 ║
║ ────────────────────────────────── ║
║ C_SIGN ║
║ ]) ); ║
║ ║
╰═══════════════════════════════════╯
from the example, we see that an iten "sign" has been define.
now since ES operates in both language,
we need to define item_desc and c_item_desc
so when a player typed look sign in that room,
he/she will get the desc u define for sign.
this saves the disk space and meory to load an actual sign object.
╭═════════════════════════════════════╮
║<Wind> @AA .... AA 的用法可以幫助妳寫較大的敘述,不然就要像這樣寫: ║
║ ║
║"你現在位於遠風鎮的冒險者公會﹐這裏隨時歡迎疲倦或需要休息的冒險者\n" ║
║+"光臨。在屋子的一角有座樓梯通往二樓的倉庫﹐在出口附近你可以看到一張告\n" ║
║+"示(sign)﹐西邊的牆上有一個閃著奇幻光芒的力場。以及有一個很大的階梯通\n" ║
║"往下面的一個大廳.\n" ║
║ ║
║<Wind> 這樣就很累了... ║
╰═════════════════════════════════════╯
actually.. depends on the taste.. I like \n better :P
but there's nothing wrong with both method.
and remember, always check lib doc.. some doesn't support @start start..
Q : Scorpion_ 也就是說,@aa xx aa 或"xx\n"任選其一?
A : Yes
☆再下來是在分別 ([...]) 與 ({...}) 的不同☆ (p.s 這段很累...)
<Wind> ([ ....]) 代表一種mapping
<Wind> 例如:
<Wind> set("exits", ([
<Wind> "east" : FARWIND"smain",
<Wind> "west" : "/d/wiz/wiz_hall",
<Wind> "up" : FARWIND"adv_storage",
<Wind> "down":FARWIND"quest_room",
<Wind> ]) ) ;
<Wind> 中,exits有很多個...就用 ([]) 將他們括起來...
<Wind> 其中每一個出口對應一個檔案... 中間用冒號隔開
<Wind> 注意,在 ([]) 中,一定是「一個東西」 : 「一個東西」...
<Wind> 而 ({....}) 呢...我們來看下一個例子
<Wind> add( "id" , ({ "wine","xo" }) );
<Wind> 這是xo的檔案中的一行...
<Wind> 我們都知道,一個東西不只一個名字...你可以get xo, 也可以get wine...
<Wind> 這個叫陣列...
<Wind> 擺在陣列中的東西是一堆資料,並沒有map的動作...
<Wind> 而 mapping 就是把一個東東對應到另一個東東...
<Wind> 例如 "east" 對應到一個 file...
<Wind> 其中冒號來代表map的動作..
<Wind> 這樣大概可以分別了..
☆下面在教導一些較省事的參數設定...寫一個自己的 XXXX.h ☆
sometimes it's wasting time to type out the whole path.
so we #define a keyword, in this case, FARWIND,
to "/d/noden/farwind/"
which is the exact location of the file.
so FARWIND"smain" means /d/noden/farwind/smain
╭═══════════════════════╮
║ <Wind> #define FARWIND "/d/noden/farwind/" ║
║ <Wind> #include <mudlib.h> ║
║ <Wind> 這是farwind.h的內容... ║
╰═══════════════════════╯
this is to make life easier if someone decide to move farwind
directory to somewhere else..
so wizards won't ended up editing 100+ files to change pathname.
best way to do is to have a defination file called farwind.h
and all farwind files includes it.
╭══════════════════════════════╮
║ <Wind> 這是 wind.h ...以下: ║
║ <Wind> #define WROOM "/u/w/wind/rooms/" ║
║ <Wind> #define WWEAPON "/u/w/wind/weapon/" ║
║ <Wind> #define WARMOR "/u/w/wind/armor/" ║
║ <Wind> #define WMONSTER "/u/w/k/wind/monster/" ║
║ <Wind> #define NAME this_player()->query("cap_name") ║
║ <Wind> #define C_NAME this_player()->query("c_cap_name") ║
║ <Wind> #define F_NAME C_NAME+"( "+NAME+" )" ║
╰══════════════════════════════╯
ok.. a room is basicly complete now..
☆ Wind 舉例不當,開始牽扯到一些物件導向的問題... ☆
Q : Scorpion_ 問:this_player()->query("c_cap_name")是什麼意思?
A : this_player() : the player calling the function
-> calling , query("c_cap_name) name of the calling obj
sometimes we want to add some "fun" thing in a room..
like if you look at something, something will smile at you (ie, painting ..)
Scorpion : 也就是player做了某些動做就call this_player 這個function ?
for example, in a room with painting, if you look at it, it will smiles at u.
then we need to use the item_func property.
(btw- all the set(xxx) where xxx are properties.
這應該算是一個物件(object)而不是function
with item_func you don't need to create an actual object painting
to do this effect.
<Wind> this_player()->query("c_cap_name") 表示去call
this_player()中的query這個function...
btw- those of you who reads the propertiy files under
/doc/properties/room.properties..
there's an error in syntax of item_func
<DragFire> sorry what is btw ?
<Wind> by the way
this_player() is the calling object.
this_player() 是去 call 的. query() 是被 call 的.
<Wind> 需要一點物件導向(OOP)的觀念,有人弄不懂嗎?
每個player是一段程式(一個物件)
每個物品(EX.龍血鎗)也是
<Wind> this_player() 這個function會去call這個玩家的這段程式...
<Wind> this_player()->query()
<Wind> 就是去call這個玩家(的程式)中的query這個函數...
☆ 下面Elon開始教item_func的用法 ☆
基本形式
set("item_func",(["obj":"function_to_call"]));
╭═════════════════════════════╮
║<Wind> 舉個例子(抱歉,找不到什麼好例子,所以用我自己的) ║
║<Wind> set( "item_func", ([ ║
║<Wind> "pipe" : "touch_pipe" ]) ); ║
║<Wind> 注意到喔,這個也是用mapping ║
╰═════════════════════════════╯
so when you "look" at pipe. the function touch_pipe is called.
then you can write a function touch_pipe() in your code to do
whatever you want to do.
let's not go into the detail on how to write that function.
we just want to know it's possible to create "fake" object that does something.
╭════════════════════════════════════╮
║int touch_pipe() ║
║{ ║
║ .....[QUOTE DELETE].... (p.s. 抱歉,因為這算機密,所以我把他刪掉了...)║
║ return 1; ║
║} ║
╰════════════════════════════════════╯
"item_func" and "item_desc" are both useful and save a lot of memory.
they are the new addition of LPC3, LPC2 doesn't have this.
since not all of them you will use in every room you create.
and there are quiet a few of them .. nobody can remember them all anyway.
let's go over simple mobs real quick.
(btw- we can have a lot of fun with rooms, but I guess it's another class
monster is a lot like players. in fact, they are players of some sort.
as some of you might see Yamor or Rashudi running around.. <grin>
so monster shares a lot of properties with player.
names, levels, hp,sp, stats.. etc just to name a few.
☆ 下面開始教那個MOB的寫法 ... ☆
<Wind> : 傳說中的醉漢來了
╭═════════════════════════════════════╮
║ #include <mudlib.h> ║
║ inherit MONSTER; ║
║ void create() ║
║ { ║
║ object ob1, ob2; ║
║ ::create(); ║
║ set_level(2); ║
║ set_name( "drunk", "醉漢" ); ║
║ set_short( "a drunk", "醉漢" ); ║
║ set_long( ║
║ "You see a smelly drunk. He looks sad and down.\n", ║
║ "一個渾身酒臭味的醉漢﹐看他失意潦倒的樣子﹐不知道是遭到什麼不幸" ║
║ "還是自甘墮落。\n" ║
║ ); ║
║ set_perm_stat( "str", 3 ); ║
║ set_skill( "dodge", 10 ); ║
║ set( "gender", "male" ); ║
║ set( "race", "human" ); ║
║ set( "alignment", -100 ); ║
║ ob1 = new( "/d/noden/farwind/items/coat" ); ║
║ ob1->move( this_object() ); ║
║ equip_armor( ob1 ); ║
║ ob2 = new( "/d/noden/farwind/items/bottle" ); ║
║ ob2->move( this_object() ); ║
║ } ║
╰═════════════════════════════════════╯
inherit MONSTER; -> to tell the driver info about inherit
:: create(); is important.
set_level() 是一個 function.. 很容意看出來用途..
<drag> sorry ask a problem, How can you know how much function can call?
<Wind> 有DOC檔...可是看起來會吐血...
<Wind> 最好的方法還是「看files,寫files...」
<elon> yeah... so we guess, and study other files..
<elon> Oh.. and there's a command called: apropos
this is a handy command once you know it's usefulness.
it does keyword scan on all(?) function man page.
and returns any function related to the search word.
for example: apropos cat will come up with at least two function:
cat() and more()
<elon> apropos file will return all function related to file handling..
<elon> so that's another way to find about functions.
back to set_level() --
<Wind> 還有那個doc/properties/ 下的東東也要看..蠻重要的
<elon> properties 下的東西很多.. 但新增的 function 都沒人要寫進去.
so.. read bulletin board is important too.
<elon> set_level(2) means we set this mob to level 2.
with monster, you have a choice of setting it's level or hp only, or both.
each level has it's defauly hp, so if u only set level,
it'll auto set hp.
remmeber tho. u can't set a mob's hp lower than the level default.
higher is ok :)
set_name() this is important for all object other than rooms.
because except room file, player need to refer to it somehow..
here we set the name of mob to drunk, so when people type look drunk,
the driver will know that ppl want to see drunk.
so return the desc for drunk.
name will also be one of the ids.. if you didn't set additional id,
then name will be the only id for the mob.
set_long & set_short are the same usage as room.
then we need to set some basic stuff for the mob,
like gender, race, alignment
hese are not necessary but they adds varities into the game.
basic mob will work with only name, long & short desc.
so in the example, we set the gender to be male,
race to be human and lignment to be -100
alignment: if u kill this person, ur alignment will -100
alignment: if u kill this person, ur alignment will -100
the mob's alignment is -1000 (negative means bad guy)
so u actually gain 1000 when killing it. BY default.
I'm not sure if there's any alignment modification built into ES..
after we set the generic stuff, we'll go on to set the special stuff.
for stats, as usual, if you didn't set it,
it'll be set according to the default level value.
but if you want to set it directly, set_perm_stat() is the one.
and set_skills() for setting skills.
there's no need to memorize all these functions,
since you can always find reference of it on other files..
(like it's dumb to memorize all C functions)
ok. after setting up mob data, we need to give mob something.. :)
noticed at the beginning section of the mob code, object ob1, ob2;
two reference of other object are made.
ob1=new(...); means we assign ob1 to the newly created obj .
so we create both ob1 and ob2, now move it to the mob.
using the move() function.
<Scor> why need 'move' ?
scor: because the obj was created in the environment,
not in the mob's inventory. it needed to be moved to the mob's inv.
as you can see, most of the mobs are classified as simple mob.
which doesn't do particular anything besides taking role
which doesn't do particular anything besides taking role
at a room and let player kill them.
<Wind> 對了,補充一下,現在的ES程式已經改過了...妳可以直接
<Wind> wield_weapon("/u/w/wind/weapon/seven.c");
<Wind> 不需要再new,move
<Wind> 要在room裡加東西也只要
<Wind> set( "objects", ([
<Wind> "shop owner" : "/u/w/wind/shops/wine.owner",
<Wind> ]) );
<Wind> 不需要new...
.....[QUOTE DELETED]....後面就開始閒聊了...
☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
☆ 今天的課程就到這裡了,有什麼問題的話,請找Elon老師, ☆
☆ 或是助教Wind... ☆
☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆