從零開始學習 NPC
Hello, and welcome to my attempt to teach everyone NPC scripts better. Regardless of the countless tutorials, and countless Help threads that have been posted and solved, people still seem to have issues with NPCs. Hopefully, with this tutorial, people will learn something. Before we start, let me tell you a bit about myself. I joined RageZone in October of 2008. I started just like most of you guys, not knowing a god damn thing. I’ve had my share of nub questions asked, but I’ve also learned from the help others have given me. I read tutorial after tutorial to learn more, so I didn’t have to rely on others to answer my questions. Sure, there are still quite a few things I don’t know, no one knows everything. Anyone who says otherwise is a damn fool. The main point I’m trying to make is, if you truly want to be good at something, it takes work. You can’t sit back, have others do the work, and expect to become a pro at whatever it is you wish to do. You have to show initiative, and want to learn. I hope my tutorial teaches you something, even if it’s just one thing.
第一級: 初心者
第一課: 實用的腳本編輯器
諸多程式都能編寫程式及腳本,有些還支援語法與高亮,如此一來就能看到花括號是從哪裡開始與到哪裡結束,整理腳本時給予不少幫助。以下是幾款推薦的軟體:
NotePad++
PsPad
第二課: Types of NPCs
NPC 主要有兩種:需要status
的,以及不需要status
的。最簡單的區分方法就是…顯然地…status
。編寫 NPC 時,時時刻刻捫心自問:我的 NPC 會需要兩個以上的對話窗嗎?如果答案為是,那你絕大多數時候會需要用到status
;若答案為非,那status
可能用不太到。請見以下範例:
不須status
的NPC
1 | function start() { |
另一個不須status
的NPC
1 | function start() { |
須要status
的NPC
1 | var status; |
第三課: 與你和其他腳本作者溝通
編寫腳本的重點之一就是能夠了解你到底在做什麼或是能夠了解其他作者在幹嘛。不作紀錄還能有什麼好方法幫你了解腳本?你可曾看過//
出現在腳本某一行的後面?他們叫做「註解」。以下是兩種註解:
1 | // 這是一種註解 |
註解會被腳本解釋器忽略,意味著任何「被註解」的東西都是給人讀的。註解在你編寫很長的腳本時能幫你紀錄你正在做什麼而十分有用,還能讓你的程式碼讀者在不懂程式時能瞭解你想做什麼。
第四課: 學習專門術語
該來的還是會來,你不可能不知道正確的專門術語就能編寫 NPC。以下是幾個小抄:
NPC 顏色代碼、道具圖片、其他
1 | #b = 藍色文本 |
cm.[指令]
dispose
結束與 NPC 的對話,讓你可以與其他 NPC 對話。
用法: cm.dispose();
sendNext
顯示一個帶有「下一個」按鈕的對話窗。
用法: cm.sendNext(“[文本]”);
sendPrev
顯示一個帶有「上一個」按鈕的對話窗。
How to use: cm.sendPrev(“[文本]”);
sendNextPrev
顯示一個帶有「上一個」、「下一個」按鈕的對話窗。
用法: cm.sendNextPrev(“[文本]”);
sendOk
顯示一個帶有「確認」按鈕的對話窗。
用法: cm.sendOk(“[文本]”);
sendYesNo
顯示一個帶有「是」、「否」按鈕的對話窗,「否」將結束對話,除非額外改寫。
用法: cm.sendYesNo(“[文本]”);
sendAcceptDecline
顯示一個帶有「接受」、「拒絕」按鈕的對話窗,「拒絕」將結束對話,除非額外改寫。
用法: cm.sendAcceptDecline(“[文本]”);
sendSimple
顯示一個不帶有任何按鈕的對話框。
用法: cm.sendSimple(“[文本]”);
sendStyle
顯示一個選擇造型的對話框。
用法: cm.sendStyle(“[文本]”, [變數]); // 你需要宣告該變數
warp
傳送腳本到地圖。
用法: cm.warp([地圖ID], [傳送點]); // 預設則設定 [傳送點] 為 0
openShop
開啟商店視窗。
用法: cm.openShop([商店ID]);
haveItem
檢查角色是否有道具 (背包或身上)。
用法: cm.haveItem([道具ID]);
gainItem
給予或收回角色道具
用法: cm.gainItem([道具ID], [數量]); // 設定 [數量] 為負數來收回道具
changeJob
修改角色的職業。
用法: cm.changeJob([職業ID]);
getJob
取得角色的職業。
用法: cm.getJob();
startQuest
開始任務。
用法: cm.startQuest([任務ID]);
completeQuest
完成任務。
用法: cm.completeQuest([任務ID]);
forfeitQuest
放棄任務。
用法: cm.forfeitQuest([任務ID]);
getMeso
取得角色楓幣。
用法: cm.getMeso();
gainMeso
給予或收回角色楓幣。
用法: cm.gainMeso([數量]); // 設定 [數量] 為負數來收回楓幣
gainExp
給予或收回角色經驗值。
用法: cm.gainExp([數量]); // 設定 [數量] 為負數來收回經驗值
getLevel
取得角色的等級。
用法: cm.getLevel();
teachSkill
教角色技能。
用法: cm.teachSkill([技能ID], [技能等級], [技能最大等級]);
get[狀態]
取得角色的 [狀態]。[狀態] 可以是: HP, MP, STR, DEX, INT, LUK.
用法: cm.get狀態;
modifyNX
Gives/Takes the player nx
How to use: cm.gainNX([amount]);
Make it negative to make it take away.
// 外流端專屬
modifyCSPoint
給予或收回點數。
用法:cm.modifyCSPoint([點數], [種類]); // [種類]:1 = GASH 點數,2 = 楓葉點數
檢查楓幣、道具、GM、性別
1 | if (cm.getPlayer().isGM()) { // 檢查是否為 GM |
職業代碼
1 | BEGINNER - 0 |
Keep in mind that some repacks use a different Terminology than this, so it’s best to browse your repack/source folder to get familiar with yours.
第二級: 中堅份子
第一課: Learning to code an NPC
編寫 NPC 時最好對自己在幹嘛有個概念,簡單的方法是打開記事本,把你想要 NPC 做的事情寫下來。例如:
1 | 道具交換 |
以上只是個範例,但這樣子做紀錄,你就能事先想好最佳的佈局,甚至能提醒你當初的想法,假如你沒法一次寫好腳本的話。
現在,在你開始之前,你要先思考你需要什麼類型的 NPC。如果你需要參考,請回到第一級第二課。在這個範例中,我會使用需要 status 的 NPC,請閱讀註解(第一級第三課)來瞭解我在做什麼。
1 | var status; |
這裡是一些能幫你弄懂 NPC 的東西:
sendNext(); & sendOk();
Type = 0
如果點了停止 - mode = -1
如果點了下一個/確認 - mode = 1
sendNextPrev();
Type = 0
如果點了停止 - mode = -1
如果點了下一個 - mode = 1
如果點了上一個 - mode = 0
sendYesNo();
Type = 1
如果點了停止 - mode = -1
如果點了是 - mode = 1
如果點了否 - mode = 0
sendAcceptDecline();
Type = 12
如果點了停止 - mode = -1
如果點了接受 - mode = 1
如果點了拒絕 - mode = 0
sendGetText();
沒事兒
sendGetNumber();
Type = 3
如果點了停止 - mode = 0
如果點了確認 - mode = 1
sendSimple();
Type = 4
如果點了停止 - mode = 0
如果點了選擇 - mode = 1
Credits: BENG
括號會是決定 NPC 能不能運作的關鍵。讓我們看看上面 NPC 腳本的一個小片段:
1 | if (cm.haveItem(4001129, 10)) { // 檢查道具 |
看看第一行…
1 | if (cm.haveItem(4001129, 10)) { |
在第一行的尾端有一個左括號,這代表當條件為真時,其後所接的事情將會發生。以該行來說,如果你有 10 個 4001129,下一行就會執行。
看看此片段的最後一行,注意到右括號了嗎?
1 | } |
右括號結束條件,所以任何介於左括號與右括號之間的事情都將會在指定的條件成立後發生。
右括號又經常伴隨著 else,也就是當你沒有足夠的道具時,else 之後的片段便會執行。
第二課: 使用運算子
在這一課,你會學到幾種運算子以及如何用他們。運算子可以幫你決定某A是大於、小於還是等於某B。以下是你最有可能會用到的運算子清單。
運算子 | 名稱 | 類型 | 描述 |
---|---|---|---|
! | 非 | 一元 | Returns true if 右運算元 evaluates to false. Returns false If the 右運算元 is true. |
&& | 條件且 | 二元 | If the operand on the left returns false, returns false without evaluating the operand on the right. |
|| | 條件或 | 二元 | If the operand on the left returns true, returns true without evaluating the operand on the right. |
以上運算子常用在條件句之間。什麼是條件句?那是 NPC 腳本常看到的東西。
1 | if (cm.haveItem(itemid, amount)) { |
以上都是條件句。因此,在其中運用運算子,可以讓你指定某種條件。例如:
1 | if (cm.haveItem(4001129, 50) && cm.getMeso() >= 1000) { // 此處需要角色擁有 50 黃金楓葉標誌 且 擁有 1000 楓幣 |
Using this is a good way to limit players from saving up loads of an item to cash in at once. 接著,我們還有關係運算子,他們很常被用在條件當中。列舉其中一部分:
運算子 | 名稱 | 描述 |
---|---|---|
== | 等於 | Returns true if the expression on the left evaluates to the same value as the expression on the right. |
< | 小於 | Returns true if the expression on the left evaluates to a value that is less than the value of the expression on the right. |
<= | 小於或等於 | Returns true if the expression on the left evaluates to a value that is less than or equal to the expression on the right. |
> | 大於 | Returns true if the expression on the left evaluates to a value that is greater than the value of the expression on the right. |
>= | 大於或等於 | Returns true if the expression on the left evaluates to a value that is greater than or equal to the expression on the right. |
我已經給過範例了,如果你沒注意到的話,我再列幾個給你:
1 | if (cm.getMeso() >= 1000) { // 如果角色的楓幣「大於或等於」1000 |
第三課: 學習使用變數
在你學習編寫時,你一定有發現一些以var
起頭的東西出現在腳本開頭,那就是變數。簡單來說,變數是用來「取代」東西的。你可以用你命名的變數來「取代」數字或文字等。一般來說,你會想要用變數來縮短腳本,即使只是幾個字而已。範例:
1 | var gl = 4000313; |
As you can see from the example, the item id 4000313 is being replaced with the variable “gl”. Even though it’s only a minor difference, I shortend the script by 1 character. Now imagine having hundreds of places on a script where 4000313 was replaced by “gl”. It adds up on how much space you save by using variables. That was an example on how to use a variable for a number. Here is one on how to use a variable for a string, or text sentence.
1 | var yes = "太好了,你帶齊了黃金楓葉!"; |
如你清楚地看見,使用這些變數省下了很大的空間。我只用了 111 個字就打完了腳本,還包含開頭的變數,而不用打長長的 202 個字。未來如果要編輯腳本,有使用變數的話就更加簡單了。
第三級: 高手過招
第一課: 學習陣列
好玩的來了!「高手」都會強調陣列,如果能用就用。使用陣列不僅讓腳本更簡潔,還能提高腳本整體的表現。陣列十分容易修改,一旦你學會如何使用,你就能瞭解腳本作者在做什麼。以下是幾個陣列的範例:
1 | var item = [4000313, 4001129, 4001126]; |
As you can see, the item ids are placed in the order they are shown in the sendSimple. The [selection] in the cm.gainItem method, calls the Array to determine which item to give the player. If they chose the second selection, it would call the second number in the Array. By placing the items in an Array, it shortens the script by a lot. Here is what the script would look like without an Array.
1 | function start() { |
There is a clear difference between the two in which is longer. One of the most common ways to use Arrays, is with the infamous for loop, which I will explain in a later lesson.
第二課: 學習多維陣列與亂數
本節我會教你多維陣列和亂數。Now, I’m not an expert at this, so I’ll only be able to show you what I know. 什麼是多維陣列?多維陣列就是由陣列組成的陣列。Basically, you can have more than one array, and simplify them further by making them into a multi-dimentional array. Unfortunately, I only know how to use them with randoms, so that is what I’ll be teaching you. First, lets’ set up a simple multi-dimentional array NPC.
1 | var status; |
Alright, time to explain. This is the multi-dimentional array.
1 | var item = [[4001129, 4001129], [1082025, 1102023], [4000313, 1002085]]; |
Each separete array is colored, so you can see the 3 different arrays within the multi-dimentional array. Next is the random part, or this little snippet.
1 | var rand2; |
Ok, at the top of the script, you see this line.
1 | var rand = Math.floor(Math.random()*100); |
Think of this as if it were a dice. The number symbolizes the sides of a dice. So on this dice, there are 100 sides. Continue reading the random part as you read this. If the dice lands on side 1 - 50, give an item in the first array. If the dice lands on side 51 - 90, give an item in the second array. If it lands on any other side, give an item in the third array. This line here…
1 | cm.gainItem([rand >= 1 && rand <= 50 ? item[0][rand2] : rand >= 51 && rand <= 90 ? item[1][rand2] : item[2][rand2]]); |
Basically follows through with the action. That is all I know about these types of arrays.
第三課: Learning the Infamous For Loop
你可曾在腳本看見這樣的東西?
1 | for (var i = 0; i < options.length; i++) |
This is the infamous for loop. It simplifies almost anything it is used with. It was created to specifically deal with Arrays. Here is the entire part of the code, so I can explain what it means.
1 | var text = "#e#k What region have you trained in?#b"; |
Ok so, i = 0. While 0 is less than the options array, do the code. After executing the code, do i++. i++ basically tells the program running the script to increase i until it reaches the length of the array. So since the options array has a length of 8, it will keep increasing until it displays all 8 options.
Contributions from other members
Spoiler:
Quote Originally Posted by .:LastBreath:. View Post
Insert In NpcConversatonManager:
1 | public void changeKeyBinding (int key, int type, int action) { |
How to use
1 | var status = 0; |
CM command:
Code:
changeKeyBinding
allows you to place a skill anywhere in your key config.
How to use: cm.changeKeyBinding(selection, 1, SKILLID);
(THINKS THATS HOW TO USE?)
ALL CREDITS GOES TO “made4forum” FROM HIS RELEASE
LINK:Skill teaching NPC
….
Further Explanation of the For Loop
Quote Originally Posted by Rice
Final Words
This concludes the tutorial. There is still quite a bit I don’t know, even with NPCs. If anyone has something they wish to add/contribute, feel free to post it and I’ll include it with credits. I hope you enjoyed my wall of text and learned something valuable. Please leave credits if this is posted anywhere else. Lastly, if you need help with an NPC script or have trouble understanding something, feel free to ask HERE. Do not quote the entire tutorial. If you do, I will ask a Mod to remove your post, because it’s an inconvenience for others.
Credits:
Shawn aka bboy242 aka DevonsDaddy
Special Thanks:
Moogra and Osiris
.:LastBreath:.
made4forum
Alcohol
Rice