搬运论坛帖子,没有进行翻译,但是也不难懂
Today I worked out some codes to start this integration(I am using NLua.), below is my demo:
First we change the definition in NPCScript.cs:
public class NPCScript
=>
public partial class NPCScript
Then we create a new file next to NPCScript.cs called NPCScriptLua.cs with the following codes:
C#:
using System.Drawing;
using Server.MirDatabase;
using Server.MirEnvir;
using System.Text.RegularExpressions;
using S = ServerPackets;
using NLua;
namespace Server.MirObjects
{
public partial class NPCScript
{
public string LuaFileName = null;
Lua lua = new Lua();
public void LoadLua()
{
lua.RegisterFunction("ADDGOLD", null, typeof(NPCScript).GetMethod("AddGold"));
lua.DoFile(LuaFileName);
}
public void CallLua(string key)
{
key = key.Remove(0,3);
key = key.Remove(key.Length-1);
if (key.EndsWith(')'))
lua.DoString(key);
else
lua.DoString(key+"()");
}
public void CallLua(MonsterObject monster, string key)
{
lua["monster"] = monster;
CallLua(key);
lua["monster"] = null;
}
public void CallLua(PlayerObject player, uint objectID, string key)
{
lua["player"] = player;
lua["objectID"] = objectID;
CallLua(key);
lua["player"] = null;
lua["objectID"] = null;
}
public static void AddGold(PlayerObject player,uint gold)
{
player.GainGold(gold);
}
}
}
Finally we have to modify several functions in NPCScript.cs:
First is the constructor of NPCScript class. I added
C#:
private NPCScript(uint loadedObjectID, string fileName, NPCScriptType type)
{
ScriptID = ++Envir.ScriptIndex;
LoadedObjectID = loadedObjectID;
FileName = fileName;
Type = type;
LuaFileName = Path.Combine(Settings.NPCPath, fileName + ".lua");
if (File.Exists(LuaFileName))
LoadLua();
else
LuaFileName = null;
Load();
Envir.Scripts.Add(ScriptID, this);
}
In this way the lua infos will be loaded when original scripts get loaded. Note that only the Lua file name is needed because we can use lua.DoFile to process the lua file(yes you can manage to do hotfix with this figure)
Next is to modify these Call functions:
C#:
public void Call(MonsterObject monster, string key)
{
key = key.ToUpper();
if (this.LuaFileName!=null) CallLua(monster,key);
for (int i = 0; i < NPCPages.Count; i++)
{
NPCPage page = NPCPages[i];
if (!String.Equals(page.Key, key, StringComparison.CurrentCultureIgnoreCase)) continue;
foreach (NPCSegment segment in page.SegmentList)
{
if (page.BreakFromSegments)
{
page.BreakFromSegments = false;
break;
}
ProcessSegment(monster, page, segment);
}
}
}
public void Call(string key)
{
key = key.ToUpper();
if (LuaFileName!=null) CallLua(key);
for (int i = 0; i < NPCPages.Count; i++)
{
NPCPage page = NPCPages[i];
if (!String.Equals(page.Key, key, StringComparison.CurrentCultureIgnoreCase)) continue;
foreach (NPCSegment segment in page.SegmentList)
{
if (page.BreakFromSegments)
{
page.BreakFromSegments = false;
break;
}
ProcessSegment(page, segment);
}
}
}
public void Call(PlayerObject player, uint objectID, string key)
{
key = key.ToUpper();
if (LuaFileName!=null) CallLua(player,objectID,key);
if (!player.NPCDelayed)
{
if (key != MainKey)
{
if (player.NPCObjectID != objectID) return;
bool found = false;
foreach (NPCSegment segment in player.NPCPage.SegmentList)
{
if (!player.NPCSuccess.TryGetValue(segment, out bool result)) break; //no result for segment ?
if ((result ? segment.Buttons : segment.ElseButtons).Any(s => s.ToUpper() == key))
{
found = true;
}
}
if (!found)
{
MessageQueue.Enqueue(string.Format("Player: {0} was prevented access to NPC key: '{1}' ", player.Name, key));
return;
}
}
}
else
{
player.NPCDelayed = false;
}
if (key.StartsWith("[@@") && !player.NPCData.TryGetValue("NPCInputStr", out object _npcInputStr))
{
//send off packet to request input
player.Enqueue(new S.NPCRequestInput { NPCID = player.NPCObjectID, PageName = key });
return;
}
for (int i = 0; i < NPCPages.Count; i++)
{
NPCPage page = NPCPages[i];
if (!String.Equals(page.Key, key, StringComparison.CurrentCultureIgnoreCase)) continue;
player.NPCSpeech = new List<string>();
player.NPCSuccess.Clear();
foreach (NPCSegment segment in page.SegmentList)
{
if (page.BreakFromSegments)
{
page.BreakFromSegments = false;
break;
}
ProcessSegment(player, page, segment, objectID);
}
Response(player, page);
}
player.NPCData.Remove("NPCInputStr");
}
By adding
if (LuaFileName!=null) CallLua(blablabla)
into the Call function, I made Lua script coexists with the original script, and Lua script will be executed before original one.
Now let's see an demo:
in 00Default.lua I write:
function LOGIN()
ADDGOLD(player,5000)
end
function DIE()
ADDGOLD(player,5000)
end
in PlayerDie.txt (included by 00Default.txt) I write:
Code:
[@_DIE]
#ACT
GLOBALMESSAGE "<$USERNAME>死了" System
GIVEGOLD 500