Archive for 5月 2018
這裡教大家編寫SourceMod插件的基本介紹,SourceMod他是使用SourcePawn中使用的Pawn的版本,Pawn是一種“腳本”語言,用於在其他程序中嵌入功能。這意味著它不是獨立的語言,如C ++或Java,其細節將根據應用程序而有所不同。
您可以使用SPEdit,Crimson Editor,PSPad,UltraEdit,Notepad ++,TextPad,Pawn Studio,BasicPawn或任何其他您喜歡編寫插件的程式編輯器。
從頭開始
打開你最喜歡的程式編輯器,並創建一個新的空文件。當你有一個空文件時,你可以開始用核心語言編寫代碼,但是,你將無法使用任何的SourceMod功能,因為編輯器不知道它們。
這是故意做的,所以可以在SourceMod之外使用SourcePawn。但是由於我們正在編寫一個SourceMod插件,因此首先啟用對SourceMod功能的訪問是一個好主意。
這是使用#include指令完成的。它告訴編譯器將來自另一個文件的代碼“複製貼上”到你的代碼中。
#include <sourcemod>
這個怎麼用?
首先,請注意,我們將文件名包含在<>括號中。
<>括號告訴編譯器查看默認的include目錄。
默認情況下,它會在遊戲路徑\left4dead2\addons\sourcemod\scripting\include。
你現在可以打開它並在那裡看到很多inc文件。
這些是SourceMod包含文件(要先安裝SourceMod平台),用於描述可用於SourceMod插件的各種功能,標記和其他功能。
這些文件是純文字的,如果時間允許可以閱讀它們。
但是,您會注意到,那裡沒有太多的代碼,當然不足以實現SourceMod的所有優點
它們是在用C ++編寫的SourceMod內核中實現的,並且被編譯為以bin結尾的二進製文件目錄。
那麼如果編譯器不知道後者的存在,你的SourcePawn代碼和SM核心如何鏈接在一起?
SourceMod包含文件是專門編寫的,所以他們說功能的實現在別的地方。編譯器知道這一點,並生成一個特殊的代碼,說明這個函數調用正在外面。當SourceMod加載您的插件時,它會檢查這些代碼並替換它自己的內部函數。這被稱為動態鏈接。
設置插件信息
現在我們可以訪問SourceMod功能,現在可以設置通過sm plugins list命令顯示的信息了。我們可以查看sourcemod.inc文件,並查看應該聲明信息的格式。查看SM包含文件以查找您不知道的信息總是有幫助的。
還有一個API文檔,但它可能已過時,並且它只有SM核心文件,所以如果您的插件要使用任何第三方擴展或其他插件,則必須學習inc文件。所以,打開sourcemod.inc並向下滾動一下,直到看到:
/**
* Plugin public information.
*/
struct Plugin
{
public const char[] name; /**< 插件名稱 */
public const char[] description; /**< 插件說明 */
public const char[] author; /**< 插件作者 */
public const char[] version; /**< 插件版本 */
public const char[] url; /**< 插件網址 */
};
和這個
/**
* Declare this as a struct in your plugin to expose its information.
* Example:
*
* public Plugin myinfo =
* {
* name = "My Plugin",
* //etc
* };
*/
public Plugin myinfo;
它告訴我們,我們需要創建一個全局公共變量myinfo,它必須是Plugin類型,它是一個包含5個字段的結構體,它們本身就是字符串。
對初學者來說聽起來很複雜,但很簡單。讓我們繼續創建一個:
public Plugin myinfo =
{
name = "我的第一個插件",
author = "作者",
description = "我的第一個插件說明",
version = "1.0",
url = "http://www.sourcemod.net/"
};
該公共關鍵字意味著SourceMod將能夠直接訪問我們的變量。
插件:定義我們的變量的類型。
myinfo顯然是SourceMod所需的我們變量的名稱。
這是填寫插件信息的最佳方式。
之後,你的插件的完整代碼應該是這樣的:
#include <sourcemod>
public Plugin myinfo =
{
name = "我的第一個插件",
author = "作者",
description = "我的第一個插件說明",
version = "1.0",
url = "http://www.sourcemod.net/"
};
獲取代碼運行
我們已經包含了SourceMod功能並且已經設定插件信息。我們現在有一個完美格式的插件,可以通過SourceMod進行編譯和加載。
但是,有一個問題 - 它什麼功能都沒有。
您可能會試圖在myinfo聲明之後開始編寫代碼,以確保它不會編譯。
與其他腳本語言如Lua不同,SourcePawn不允許代碼在函數之外。
閱讀完之後,你可能想要定義一些函數,可能主要命名它,編譯並加載一個插件,看看你的代碼永遠不會被調用。
那麼我們如何讓SourceMod調用我們的代碼呢?
可以由另一方作為回調來實現。
當第一方開始前轉呼叫時,具有匹配回叫的所有方接收該呼叫。
SourceMod聲明了我們可以實現的大量有趣的內容。
正如你所看到的,轉發是讓我們的代碼執行的唯一方法,牢記這一點。
所以讓我們先實現OnPluginStart。
正如您可能已經猜到的那樣,我們的插件啟動時會調用它。
為此,我們必須查看OnPluginStart的聲明。
它在sourcemod.inc裡面聲明,我們已經熟悉這個文件,讓我們來找到它:
/**
*當插件完全初始化並調用所有已知的外部引用時調用
*已解決。 這只在插件的生命週期中調用一次,並且是
*與OnPluginEnd()配對。
*
*如果在此回調期間發生任何運行時錯誤,則插件將被標記
*失敗。
*
*不需要關閉此功能中的任何手柄或刪除掛鉤。
* SourceMod保證插件自動關閉並正確釋放
*所有資源。
*
* @noreturn
*/
forward void OnPluginStart();
空括號告訴我們,在這個轉發中沒有任何參數被傳遞,@noreturn文檔裡面告訴我們,我們不需要返回任何東西,很簡單。
那麼如何為它寫一個正確的回調呢?首先,我們的回調必須具有相同的名稱,所以它的OnPluginStart,其次,我們的回調應該有相同數量的參數,在這種情況下,最後,SourceMod需要能夠調用我們的回調,因此它需要公開。所以實現如下所示:
public void OnPluginStart ()
{
}
現在我們可以在大括號內編寫代碼,並且它會在我們的插件啟動時執行。
我們輸出“Hello world!” 到服務器控制台。為此,我們將使用PrintToServer函數。它在console.inc中聲明,
但是,我們不需要手動包含console.inc,因為它是作為sourcemod.inc的一部分自動包含的。
/**
* 將消息發送到服務器控制台.
*
* @param format 格式化規則.
* @param ... 可變數量的格式參數.
* @noreturn
*/
native int PrintToServer(const char[] format, any ...);
正如你所看到的,這是一個本地函數。它在SM內核中實現。從它的論點來看,我們可以看到它是一個格式類函數。
但是,我們現在不需要任何格式,所以讓我們通過“Hello world!” 字符串作為唯一參數:
public void OnPluginStart ()
{
PrintToServer (“Hello world!” ) ;
}
完成!你的插件的完整代碼應該是下面這樣的:
#include <sourcemod>
public Plugin myinfo =
{
name = "我的第一個插件", author = "作者",
description = "我的第一個插件說明",
version = "1.0",
url = "http://www.sourcemod.net/"
};
public void OnPluginStart()
{
PrintToServer("Hello world!");
}
在您的服務器上編譯並加載您的插件,並親自看到該消息顯示在服務器控制台中。
参考資料
https://wiki.alliedmods.net/Introduction_to_SourcePawn_1.7
https://wiki.alliedmods.net/Introduction_to_SourceMod_Plugins
SourceMod插件的基本介紹#1
一般只要使用冰動相關技能,都會有一個爆風效果,但這會導致把玩家震開
這常常會讓玩家飛到場外,造成不必要的紛爭
這技能都會分別設定友傷跟敵傷,所以我們要把爆風的攻能放在敵傷那裏面
爆風功能語法是PointPush
搜尋PointPush可以找到8個地方有使用,1個爆風主程式
語法大概是下面這樣
PointPush(Client, pos, 1000, IceBallRadius[Client], 0.5);
將這些放在特感的判斷理面,例如下面
if(GetClientTeam(i) == 3 && IsPlayerAlive(i) && !IsPlayerGhost(i))
{
GetEntPropVector(i, Prop_Send, "m_vecOrigin", entpos);
SubtractVectors(entpos, pos, distance);
if(GetVectorLength(distance) <= Radius)
{
DealDamage(Client, i, IceBallDamage[Client], 0 , "chain_lightning");
FreezePlayer(i, entpos, IceBallDuration[Client]);
}
}
要記得把PointPush放在攻擊範圍Radius判斷裡面