/// <summary> /// Sends dialog select message to client, showing a message and a /// list of options to select from. /// </summary> /// <remarks> /// Select can take an arbitrary amount of options. /// /// Parameters: /// - string message /// - string options... /// /// Result: /// The number of the selected option, starting from 1. /// Returns 0 on error. /// </remarks> /// <param name="L"></param> /// <returns></returns> private int select(IntPtr L) { // Check arguments and return 0 on error var argc = Melua.lua_gettop(L); if (argc == 0) { Log.Warning("select: No arguments."); Melua.lua_pushinteger(L, 0); return(1); } var conn = this.GetConnectionFromState(L); // Get arguments, first argument is the message, everything afterwards // is one option to select from. var args = new string[argc]; for (int i = 1; i <= argc; ++i) { var arg = Melua.luaL_checkstring(L, i); this.HandleCustomCode(conn, ref arg); args[i - 1] = arg; } Melua.lua_pop(L, argc); this.AttachNpcName(conn, ref args[0]); Send.ZC_DIALOG_SELECT(conn, args); return(Melua.lua_yield(L, 1)); }
/// <summary> /// Sends dialog numberrange message, showing a message and a small text field, /// for the user to put in a number. /// </summary> /// <remarks> /// NUMBERRANGE uses CZ_DIALOG_SELECT for its response, /// which means the number range is that of a byte, 0~255. /// /// Parameters: /// - string message /// - int min (optional, defaults to 0) /// - int max (optional, defaults to 255) /// /// Result: /// The number put in by the user. /// Returns 0 on error. /// </remarks> /// <param name="L"></param> /// <returns></returns> private int numinput(IntPtr L) { // Check arguments and return 0 on error var argc = Melua.lua_gettop(L); if (argc == 0) { Log.Warning("numinput: No arguments."); Melua.lua_pushinteger(L, 0); return(1); } var conn = this.GetConnectionFromState(L); int min = 0, max = 255; // Get arguments var msg = Melua.luaL_checkstring(L, 1); if (argc >= 3) { min = Melua.luaL_checkinteger(L, 2); max = Melua.luaL_checkinteger(L, 3); } Melua.lua_pop(L, argc); this.HandleCustomCode(conn, ref msg); this.AttachNpcName(conn, ref msg); Send.ZC_DIALOG_NUMBERRANGE(conn, msg, min, max); return(Melua.lua_yield(L, 1)); }
/// <summary> /// Pushes the arguments onto the Lua stack. /// </summary> /// <param name="NL"></param> /// <param name="args"></param> private static void PushArguments(IntPtr NL, params object[] args) { foreach (var arg in args) { switch (arg) { case byte v: Melua.lua_pushinteger(NL, v); break; case bool v: Melua.lua_pushboolean(NL, v); break; case short v: Melua.lua_pushinteger(NL, v); break; case int v: Melua.lua_pushinteger(NL, v); break; case float v: Melua.lua_pushnumber(NL, v); break; case double v: Melua.lua_pushnumber(NL, v); break; case string v: Melua.lua_pushstring(NL, v); break; default: { Log.Warning("ScriptManager.PushArguments: Invalid argument type '{0}', pushing 'int 0' instead.", arg.GetType().Name); Melua.lua_pushinteger(NL, 0); break; } } } }
/// <summary> /// Removes the specified amount of items with the given id from /// character's inventory. /// </summary> /// <remarks> /// Parameters: /// - int itemId /// - int amount /// /// Result: /// - int removedCount /// </remarks> /// <param name="L"></param> /// <returns></returns> private int removeitem(IntPtr L) { var conn = this.GetConnectionFromState(L); var character = conn.SelectedCharacter; var itemId = Melua.luaL_checkinteger(L, 1); var amount = Melua.luaL_checkinteger(L, 2); Melua.lua_pop(L, 2); var itemData = ChannelServer.Instance.Data.ItemDb.Find(itemId); if (itemData == null) { return(Melua.melua_error(L, "Unknown item id.")); } amount = Math.Max(0, amount); var removed = character.Inventory.Remove(itemId, amount, InventoryItemRemoveMsg.Given); Melua.lua_pushinteger(L, removed); return(1); }
/// <summary> /// Returns table, containing information about the current date/time. /// </summary> /// <remarks> /// Result: /// { /// integer year, -- Current year /// integer month, -- Current month /// integer day, -- Current day /// integer weekday, -- Day of the week (0-6), starting on Sunday /// integer yearday, -- Day of the current year /// integer hour, -- Current hours (0-23) /// integer min, -- Current minutes (0-59) /// integer sec, -- Current seconds (0-59) /// integer msec, -- Current milliseconds (0-999) /// boolean isdst, -- Is Daylight Saving Time? /// integer unixts, -- Unix timestamp /// } /// </remarks> /// <param name="L"></param> /// <returns></returns> private int gettime(IntPtr L) { var now = DateTime.Now; // TODO: Could a general table generation like this be cached? Melua.lua_newtable(L); Melua.lua_pushstring(L, "year"); Melua.lua_pushinteger(L, now.Year); Melua.lua_settable(L, -3); Melua.lua_pushstring(L, "month"); Melua.lua_pushinteger(L, now.Month); Melua.lua_settable(L, -3); Melua.lua_pushstring(L, "day"); Melua.lua_pushinteger(L, now.Day); Melua.lua_settable(L, -3); Melua.lua_pushstring(L, "weekday"); Melua.lua_pushinteger(L, (int)now.DayOfWeek); Melua.lua_settable(L, -3); Melua.lua_pushstring(L, "yearday"); Melua.lua_pushinteger(L, now.DayOfYear); Melua.lua_settable(L, -3); Melua.lua_pushstring(L, "hour"); Melua.lua_pushinteger(L, now.Hour); Melua.lua_settable(L, -3); Melua.lua_pushstring(L, "min"); Melua.lua_pushinteger(L, now.Minute); Melua.lua_settable(L, -3); Melua.lua_pushstring(L, "sec"); Melua.lua_pushinteger(L, now.Second); Melua.lua_settable(L, -3); Melua.lua_pushstring(L, "msec"); Melua.lua_pushinteger(L, now.Millisecond); Melua.lua_settable(L, -3); Melua.lua_pushstring(L, "isdst"); Melua.lua_pushboolean(L, now.IsDaylightSavingTime()); Melua.lua_settable(L, -3); Melua.lua_pushstring(L, "unixts"); Melua.lua_pushinteger(L, (int)(now.ToUniversalTime().Subtract(UnixEpoch)).TotalSeconds); Melua.lua_settable(L, -3); return(1); }
public void typename() { var L = Melua.luaL_newstate(); Melua.lua_pushinteger(L, 123); Assert.Equal("number", Melua.luaL_typename(L, -1)); Melua.lua_pushstring(L, "123"); Assert.Equal("string", Melua.luaL_typename(L, -1)); Melua.lua_newtable(L); Assert.Equal("table", Melua.luaL_typename(L, -1)); }
public void type() { var L = Melua.luaL_newstate(); Melua.lua_pushinteger(L, 123); Assert.Equal(Melua.LUA_TNUMBER, Melua.lua_type(L, -1)); Melua.lua_pushstring(L, "123"); Assert.Equal(Melua.LUA_TSTRING, Melua.lua_type(L, -1)); Melua.lua_newtable(L); Assert.Equal(Melua.LUA_TTABLE, Melua.lua_type(L, -1)); }
/// <summary> /// Returns a table with information about the player. /// </summary> /// <remarks> /// Result: /// { /// string name, -- Character's name /// string teamName, -- Character's team name /// integer gender, -- Character's gender /// integer level, -- Character's level /// integer hp, -- Character's HP /// integer maxHp, -- Character's max HP /// integer sp, -- Character's SP /// integer maxSp, -- Character's max SP /// integer stamina, -- Character's stamina /// integer hair, -- Character's hair /// } /// </remarks> /// <param name="L"></param> /// <returns></returns> private int getpc(IntPtr L) { var conn = this.GetConnectionFromState(L); var character = conn.SelectedCharacter; Melua.lua_newtable(L); Melua.lua_pushstring(L, "name"); Melua.lua_pushstring(L, character.Name); Melua.lua_settable(L, -3); Melua.lua_pushstring(L, "teamName"); Melua.lua_pushstring(L, character.TeamName); Melua.lua_settable(L, -3); Melua.lua_pushstring(L, "gender"); Melua.lua_pushinteger(L, (int)character.Gender); Melua.lua_settable(L, -3); Melua.lua_pushstring(L, "level"); Melua.lua_pushinteger(L, character.Level); Melua.lua_settable(L, -3); Melua.lua_pushstring(L, "hp"); Melua.lua_pushinteger(L, character.Hp); Melua.lua_settable(L, -3); Melua.lua_pushstring(L, "maxHp"); Melua.lua_pushinteger(L, character.MaxHp); Melua.lua_settable(L, -3); Melua.lua_pushstring(L, "sp"); Melua.lua_pushinteger(L, character.Sp); Melua.lua_settable(L, -3); Melua.lua_pushstring(L, "maxSp"); Melua.lua_pushinteger(L, character.MaxSp); Melua.lua_settable(L, -3); Melua.lua_pushstring(L, "stamina"); Melua.lua_pushinteger(L, character.Stamina); Melua.lua_settable(L, -3); Melua.lua_pushstring(L, "hair"); Melua.lua_pushinteger(L, character.Hair); Melua.lua_settable(L, -3); return(1); }
/// <summary> /// Returns amount of items with the given id in character's inventory. /// </summary> /// <remarks> /// Parameters: /// - int itemId /// /// Result: /// - int amount /// </remarks> /// <param name="L"></param> /// <returns></returns> private int countitem(IntPtr L) { var conn = this.GetConnectionFromState(L); var character = conn.SelectedCharacter; var itemId = Melua.luaL_checkinteger(L, 1); Melua.lua_pop(L, 1); var result = character.Inventory.CountItem(itemId); Melua.lua_pushinteger(L, result); return(1); }
public void tostring() { var L = Melua.luaL_newstate(); Melua.lua_pushinteger(L, 123); var str = Melua.lua_tostring(L, -1); Assert.Equal("123", str); Melua.lua_newtable(L); Assert.Equal(null, Melua.lua_tostring(L, -1)); Melua.lua_pushstring(L, "foobar"); Assert.Equal("foobar", Melua.lua_tostring(L, -1)); }
/// <summary> /// Resumes script after yielding. /// </summary> /// <param name="conn"></param> /// <param name="argument"></param> public void Resume(ChannelConnection conn, params object[] arguments) { if (conn.ScriptState.LuaThread == null) { Send.ZC_DIALOG_CLOSE(conn); Log.Warning("ScriptManager: Resume on empty ScriptState from user '{0}'.", conn.Account.Name); return; } var NL = conn.ScriptState.LuaThread.L; var argc = (arguments != null ? arguments.Length : 0); // Reset current shop in case we came from one. conn.ScriptState.CurrentShop = null; if (argc != 0) { foreach (var arg in arguments) { if (arg is byte) { Melua.lua_pushinteger(NL, (byte)arg); } else if (arg is short) { Melua.lua_pushinteger(NL, (short)arg); } else if (arg is int) { Melua.lua_pushinteger(NL, (int)arg); } else if (arg is string) { Melua.lua_pushstring(NL, (string)arg); } else { Log.Warning("ScriptManager.Resume: Invalid argument type '{0}'.", arg.GetType().Name); Melua.lua_pushinteger(NL, 0); } } // If arguments were passed, we can assume we're coming from // a selection handler, which's windows don't disappear when // sending the next message. So let's close it before // continuing. Send.ZC_DIALOG_CLOSE(conn); } var result = Melua.lua_resume(NL, argc); // Log error if result is not success or yield if (result != 0 && result != Melua.LUA_YIELD) { Log.Error("ScriptManager.Call: Error while resuming script for {0}.\n{1}", conn.Account.Name, Melua.lua_tostring(NL, -1)); result = 0; // Set to 0 to close dialog on error } // Close dialog if end of function was reached if (result == 0) { // Only close from here if the end was reached after an // argument-less resume, since close is already called // from the argument handling to get rid of the selection // dialog. if (argc == 0) { Send.ZC_DIALOG_CLOSE(conn); } conn.ScriptState.Reset(); } }
/// <summary> /// Gets or sets a scripting variable. /// </summary> /// <remarks> /// Scripting variables are separate from Lua variables and exist /// across script and playing sessions. How the variable is saved /// depends on the used prefix. /// /// Variable names may contain the following characters, apart from /// the prefixes, and must start with a character: /// abcdefghijklmnopqrstuvwxyz0123456789_ /// /// Prefixes: /// "" - Permanent variable attached to the character. /// "@" - Temporary variable attached to the character. /// "#" - Permanent variable attached to the account. /// "$" - Permanent global variable. /// "$@" - Temporary global variable. /// /// Parameters: /// - string variableName /// - (optional) T value /// /// Result: /// - T value /// </remarks> /// <param name="L"></param> /// <returns></returns> private int var(IntPtr L) { var conn = this.GetConnectionFromState(L); var character = conn.SelectedCharacter; // Get parameters var argc = Melua.lua_gettop(L); var name = Melua.luaL_checkstring(L, 1).Trim(); object value = null; if (argc == 2) { if (Melua.lua_isnumber(L, 2)) { value = Melua.lua_tonumber(L, 2); } else if (Melua.lua_isstring(L, 2)) { value = Melua.lua_tostring(L, 2); } else if (Melua.lua_isboolean(L, 2)) { value = Melua.lua_toboolean(L, 2); } else { return(Melua.melua_error(L, "Unsupported variable type.")); } } Melua.lua_pop(L, argc); // Get variable manager and trim name VariableManager vars; if (name.StartsWith("$@")) { vars = this.Variables.Temp; name = name.Substring(2); } else if (name.StartsWith("$")) { vars = this.Variables.Perm; name = name.Substring(1); } else if (name.StartsWith("#")) { vars = conn.Account.Variables.Perm; name = name.Substring(1); } else if (name.StartsWith("@")) { vars = character.Variables.Temp; name = name.Substring(1); } else { vars = character.Variables.Perm; } // Check name syntax, if we want to add more prefixes later on, // we can't have special characters in names. if (!VarNameCheck.IsMatch(name)) { return(Melua.melua_error(L, "Invalid variable name.")); } // Update or get value if (value == null) { value = vars[name]; } else { vars[name] = value; } // Push return value if (value == null) { Melua.lua_pushnil(L); } else if (value is string) { Melua.lua_pushstring(L, (string)value); } else if (value is double) { Melua.lua_pushnumber(L, (double)value); } else if (value is float) { Melua.lua_pushnumber(L, (float)value); } else if (value is int) { Melua.lua_pushinteger(L, (int)value); } else if (value is bool) { Melua.lua_pushboolean(L, (bool)value); } else { return(Melua.melua_error(L, "Unsupported variable type '{0}'.", value.GetType().Name)); } return(1); }
public void userdata() { var L = Melua.luaL_newstate(); Melua.melua_opensafelibs(L); var n1 = 0; // Ctor Melua.luaL_register(L, "Test", new[] { new MeluaLib.Melua.LuaLib("new", NL => { var test = new UserDataTest() { N1 = 1234 }; var size = Marshal.SizeOf(test); var ptr = Melua.lua_newuserdata(L, size); Melua.luaL_getmetatable(L, "Melua.Test"); Melua.lua_setmetatable(L, -2); Marshal.StructureToPtr(test, ptr, true); return(1); }) }); // Meta table for test userdata type Melua.luaL_newmetatable(L, "Melua.Test"); Melua.lua_pushstring(L, "__index"); Melua.lua_pushvalue(L, -2); Melua.lua_settable(L, -3); Melua.luaL_register(L, null, new[] { new MeluaLib.Melua.LuaLib("setN1", _ => { var ptr = Melua.luaL_checkudata(L, 1, "Melua.Test"); var val = Melua.luaL_checkinteger(L, 2); // Either marshal back and forth or use unsafe var test = (UserDataTest)Marshal.PtrToStructure(ptr, typeof(UserDataTest)); test.N1 = val; Marshal.StructureToPtr(test, ptr, true); //unsafe //{ // var test = (UserDataTest*)ptr; // test->N1 = val; //} return(0); }), new MeluaLib.Melua.LuaLib("getN1", _ => { var ptr = Melua.luaL_checkudata(L, 1, "Melua.Test"); var test = (UserDataTest)Marshal.PtrToStructure(ptr, typeof(UserDataTest)); Melua.lua_pushinteger(L, test.N1); return(1); }) }); // Test method Melua.melua_register(L, "testgetn1", _ => { n1 = Melua.lua_tointeger(L, -1); return(0); }); var result = Melua.luaL_dostring(L, @" local t = Test.new() t:setN1(5678) testgetn1(t:getN1()) "); if (result != 0) { throw new Exception(Melua.lua_tostring(L, -1)); } Assert.Equal(n1, 5678); }