private int loadxmlfile(IntPtr L) { var fileName = Melua.luaL_checkstring(L, 1); Melua.lua_pop(L, 1); fileName = ModPack.NormalizePath(fileName); if (!fileName.EndsWith(".xml") && !fileName.EndsWith(".xml.compiled")) { return(Melua.melua_error(L, "Expected XML file extension.")); } if (!FileExistsInPackages(fileName)) { return(Melua.melua_error(L, "File '{0}' not found in packages.", fileName)); } try { _loadedXmlFile = fileName; _loadedXmlModder = _modPack.GetXmlModder(fileName); return(0); } catch (XmlException ex) { _loadedXmlFile = null; _loadedXmlModder = null; return(Melua.melua_error(L, "Failed to parse XML: '{0}'", ex.Message)); } }
/// <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> /// Adds the specified amount of items to the character's inventory. /// </summary> /// <remarks> /// Parameters: /// - int itemId /// - int amount /// </remarks> /// <param name="L"></param> /// <returns></returns> private int additem(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.")); } try { character.Inventory.Add(itemId, amount, InventoryAddType.PickUp); } catch (Exception ex) { Log.Exception(ex); return(Melua.melua_error(L, "Failed to add item to inventory.")); } return(0); }
/// <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)); }
private int addelement(IntPtr L) { if (!IsXmlFileLoaded()) { return(Melua.melua_error(L, "No XML file loaded.")); } var selector = Melua.luaL_checkstring(L, 1); var xml = Melua.luaL_checkstring(L, 2); Melua.lua_pop(L, 2); if (!IsXPathValid(selector)) { return(Melua.melua_error(L, "Invalid XPath.")); } XElement element; try { element = XElement.Parse(xml); } catch (XmlException ex) { return(Melua.melua_error(L, "Failed to parse XML element: {0}", ex.Message)); } var modder = _loadedXmlModder; _modPack.AddMod(new XmlElementAdder(modder, selector, element)); return(0); }
/// <summary> /// Sends dialog input message, showing a message and a text field, /// for the user to put in a string. /// </summary> /// <remarks> /// Parameters: /// - string message /// /// Result: /// The string put in by the user. /// Returns empty string on error. /// </remarks> /// <param name="L"></param> /// <returns></returns> private int input(IntPtr L) { // Check arguments and return empty string on error var argc = Melua.lua_gettop(L); if (argc == 0) { Log.Warning("input: No arguments."); Melua.lua_pushstring(L, ""); return(1); } var conn = this.GetConnectionFromState(L); // Get message var msg = Melua.luaL_checkstring(L, 1); Melua.lua_pop(L, 1); this.HandleCustomCode(conn, ref msg); this.AttachNpcName(conn, ref msg); Send.ZC_DIALOG_STRINGINPUT(conn, msg); return(Melua.lua_yield(L, 1)); }
/// <summary> /// Spawns monster. /// </summary> /// <remarks> /// Parameters: /// - int monsterId /// - string mapName /// - float x /// - float y /// - float z /// </remarks> /// <param name="L"></param> /// <returns></returns> private int spawn(IntPtr L) { var monsterId = Melua.luaL_checkinteger(L, 1); var mapName = Melua.luaL_checkstring(L, 2); var x = (float)Melua.luaL_checknumber(L, 3); var y = (float)Melua.luaL_checknumber(L, 4); var z = (float)Melua.luaL_checknumber(L, 5); Melua.lua_pop(L, 5); var map = ChannelServer.Instance.World.GetMap(mapName); if (map == null) { return(Melua.melua_error(L, "Map '{0}' not found.", mapName)); } var monster = new Monster(monsterId, NpcType.Monster); monster.Position = new Position(x, y, z); map.AddMonster(monster); return(0); }
/// <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)); }
private int require(IntPtr L) { var path = Melua.luaL_checkstring(L, 1); Melua.lua_pop(L, 1); return(this.DoPath(L, path, true)); }
private int include(IntPtr L) { var path = Melua.luaL_checkstring(L, 1); Melua.lua_pop(L, 1); return(this.DoPath(L, path, false)); }
//-----------------------------------------------------------------// // SCRIPT FUNCTIONS // //-----------------------------------------------------------------// /// <summary> /// Prints message in console using Console.WriteLine. /// </summary> /// <remarks> /// Parameters: /// - string message /// </remarks> /// <param name="L"></param> /// <returns></returns> private int print(IntPtr L) { var msg = Melua.luaL_checkstring(L, 1); Melua.lua_pop(L, 1); Console.WriteLine(msg); return(0); }
//-----------------------------------------------------------------// // SCRIPT FUNCTIONS // //-----------------------------------------------------------------// /// <summary> /// Prints and logs debug message. /// </summary> /// <remarks> /// Parameters: /// - string message /// </remarks> /// <param name="L"></param> /// <returns></returns> private int logdebug(IntPtr L) { var msg = Melua.luaL_checkstring(L, 1); Melua.lua_pop(L, 1); Log.Debug(msg); return(0); }
private int removeelements(IntPtr L) { if (!IsXmlFileLoaded()) { return(Melua.melua_error(L, "No XML file loaded.")); } var selectors = new HashSet <string>(); if (Melua.lua_isstring(L, -1)) { var selector = Melua.luaL_checkstring(L, 1); Melua.lua_pop(L, 1); if (!IsXPathValid(selector)) { return(Melua.melua_error(L, "Invalid XPath.")); } selectors.Add(selector); } else if (Melua.lua_istable(L, -1)) { Melua.lua_pushnil(L); while (Melua.lua_next(L, -2) != 0) { var selector = Melua.luaL_checkstring(L, -1); Melua.lua_pop(L, 1); if (!IsXPathValid(selector)) { return(Melua.melua_error(L, "Invalid XPath.")); } selectors.Add(selector); } } else { return(Melua.melua_error(L, "Invalid argument type '{0}'.", Melua.luaL_typename(L, -1))); } var modder = _loadedXmlModder; foreach (var selector in selectors) { _modPack.AddMod(new XmlElementRemover(modder, selector)); } return(0); }
/// <summary> /// Changes the player's hairstyle. /// </summary> /// <remarks> /// Parameters: /// - int hairId /// </remarks> /// <param name="L"></param> /// <returns></returns> private int changehair(IntPtr L) { var conn = this.GetConnectionFromState(L); var character = conn.SelectedCharacter; var hairId = Melua.luaL_checkinteger(L, 1); Melua.lua_pop(L, 1); character.Hair = (byte)hairId; Send.ZC_UPDATED_PCAPPEARANCE(character); return(0); }
/// <summary> /// Sends dialog message to client. /// </summary> /// <remarks> /// If message is a localization key (e.g. "ETC_20150317_000015"), /// the string is wrapped in a dict code, so the client looks it up /// in its dictionary. /// /// Parameters: /// - string message /// </remarks> /// <param name="L"></param> /// <returns></returns> private int msg(IntPtr L) { var conn = this.GetConnectionFromState(L); var msg = Melua.luaL_checkstring(L, 1); Melua.lua_pop(L, 1); this.HandleCustomCode(conn, ref msg); this.AttachNpcName(conn, ref msg); Send.ZC_DIALOG_OK(conn, msg); return(Melua.lua_yield(L, 0)); }
/// <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); }
private int setattributes(IntPtr L) { if (!IsXmlFileLoaded()) { return(Melua.melua_error(L, "No XML file loaded.")); } var modder = _loadedXmlModder; var selector = Melua.luaL_checkstring(L, 1); if (!IsXPathValid(selector)) { return(Melua.melua_error(L, "Invalid XPath.")); } if (Melua.lua_isstring(L, 2) && Melua.lua_isstring(L, 3)) { var attributeName = Melua.lua_tostring(L, 2); var attributeValue = Melua.lua_tostring(L, 3); Melua.lua_pop(L, 2); _modPack.AddMod(new XmlAttributeSetter(modder, selector, attributeName, attributeValue)); } else if (Melua.lua_istable(L, 2)) { Melua.lua_pushnil(L); while (Melua.lua_next(L, -2) != 0) { var attributeName = Melua.luaL_checkstring(L, -2); var attributeValue = Melua.luaL_checkstring(L, -1); Melua.lua_pop(L, 1); _modPack.AddMod(new XmlAttributeSetter(modder, selector, attributeName, attributeValue)); } Melua.lua_pop(L, 1); } else { return(Melua.melua_error(L, "Invalid argument type.")); } Melua.lua_pop(L, 1); return(0); }
private int replace(IntPtr L) { if (!IsXmlFileLoaded()) { return(Melua.melua_error(L, "No XML file loaded.")); } var search = Melua.luaL_checkstring(L, 1); var replace = Melua.luaL_checkstring(L, 2); Melua.lua_pop(L, 2); var modder = _loadedXmlModder; _modPack.AddMod(new XmlReplacer(modder, search, replace)); return(0); }
private int replacefile(IntPtr L) { var targetPath = Melua.luaL_checkstring(L, 1); var sourcePath = Melua.luaL_checkstring(L, 2); Melua.lua_pop(L, 2); targetPath = ModPack.NormalizePath(targetPath); sourcePath = ModPack.NormalizePath(sourcePath); var outPath = Path.Combine("data", targetPath); var localPath = Path.Combine(_cwd, sourcePath); var packPath = sourcePath; var packReader = _modPack.GetPackReader(); if (File.Exists(localPath)) { if (!IsInsideCwd(localPath)) { return(Melua.melua_error(L, "Invalid path. ({0})", localPath)); } _modPack.AddMod(new FileCopier(localPath, outPath)); } else if (packReader.Exists(packPath)) { var entry = packReader.GetEntry(packPath); var data = entry.GetData(); _modPack.AddMod(new FileSetter(outPath, data)); } else { // TODO: Replace with string? return(Melua.melua_error(L, "File not found: {0}", sourcePath)); } return(0); }
/// <summary> /// Instructs client to open the shop with the given name /// and stops script until shop is closed. /// </summary> /// <remarks> /// Parameters: /// - string shopName /// </remarks> /// <param name="L"></param> /// <returns></returns> private int openshop(IntPtr L) { var conn = this.GetConnectionFromState(L); var shopName = Melua.luaL_checkstring(L, 1); if (shopName.Length > 32) { shopName = shopName.Substring(0, 32); } Melua.lua_pop(L, 1); if (!ChannelServer.Instance.Data.ShopDb.Exists(shopName)) { return(Melua.melua_error(L, "Shop '{0}' not found.", shopName)); } conn.ScriptState.CurrentShop = shopName; Send.ZC_DIALOG_TRADE(conn, shopName); return(Melua.lua_yield(L, 0)); }
/// <summary> /// Warps player to given location. /// </summary> /// <remarks> /// Parameters: /// - string mapName /// - number x /// - number y /// - number z /// </remarks> /// <param name="L"></param> /// <returns></returns> private int warp(IntPtr L) { var conn = this.GetConnectionFromState(L); var mapName = Melua.luaL_checkstring(L, 1); var x = (float)Melua.luaL_checknumber(L, 2); var y = (float)Melua.luaL_checknumber(L, 3); var z = (float)Melua.luaL_checknumber(L, 4); Melua.lua_pop(L, 4); try { conn.SelectedCharacter.Warp(mapName, x, y, z); } catch (ArgumentException ex) { Melua.lua_pushstring(L, ex.Message); Melua.lua_error(L); } return(0); }
/// <summary> /// Adds NPC to world. /// </summary> /// <remarks> /// The parameter `dialogFunctionName` can be the name of a Lua /// function name, the name of a client-side dialog, or a /// localization key. A client-side dialog controls the NPC name /// and appearance, while a localization key will simply send the /// key in one message. A Lua function allows for completely /// custom dialog. /// /// Parameters: /// - int monsterId /// - string name / dictId /// - string mapName /// - number x /// - number y /// - number z /// - int direction /// - string dialogFunctionName /// </remarks> /// <param name="L"></param> /// <returns></returns> private int addnpc(IntPtr L) { var monsterId = Melua.luaL_checkinteger(L, 1); var name = Melua.luaL_checkstring(L, 2); var mapName = Melua.luaL_checkstring(L, 3); var x = (float)Melua.luaL_checknumber(L, 4); var y = (float)Melua.luaL_checknumber(L, 5); var z = (float)Melua.luaL_checknumber(L, 6); var direction = Melua.luaL_checkinteger(L, 7); var dialog = Melua.luaL_checkstring(L, 8); Melua.lua_pop(L, 8); var map = ChannelServer.Instance.World.GetMap(mapName); if (map == null) { return(Melua.melua_error(L, "Map '{0}' not found.", mapName)); } // Wrap name in localization code if applicable if (this.IsLocalizationKey(name)) { name = this.WrapLocalizationKey(name); } var monster = new Monster(monsterId, NpcType.NPC); monster.Name = name; monster.DialogName = dialog; monster.Position = new Position(x, y, z); monster.Direction = new Direction(direction); map.AddMonster(monster); return(0); }
/// <summary> /// Adds warp to world. /// </summary> /// <remarks> /// Parameters: /// - string warpName /// - number direction /// - string fromMapName /// - number fromX /// - number fromY /// - number fromZ /// - string toMapName /// - number toX /// - number toY /// - number toZ /// </remarks> /// <param name="L"></param> /// <returns></returns> private int addwarp(IntPtr L) { var warpName = Melua.luaL_checkstring(L, 1); var direction = Melua.luaL_checknumber(L, 2); var fromMapName = Melua.luaL_checkstring(L, 3); var fromX = (float)Melua.luaL_checknumber(L, 4); var fromY = (float)Melua.luaL_checknumber(L, 5); var fromZ = (float)Melua.luaL_checknumber(L, 6); var toMapName = Melua.luaL_checkstring(L, 7); var toX = (float)Melua.luaL_checknumber(L, 8); var toY = (float)Melua.luaL_checknumber(L, 9); var toZ = (float)Melua.luaL_checknumber(L, 10); Melua.lua_pop(L, 10); // Check "from" map data var fromMapData = ChannelServer.Instance.Data.MapDb.Find(fromMapName); if (fromMapData == null) { return(Melua.melua_error(L, "Map '{0}' not found in data.", fromMapName)); } // Check map in world var map = ChannelServer.Instance.World.GetMap(fromMapData.Id); if (map == null) { return(Melua.melua_error(L, "Map '{0}' not found in world.", fromMapName)); } // Check "to" map data var toMapData = ChannelServer.Instance.Data.MapDb.Find(toMapName); if (toMapData == null) { return(Melua.melua_error(L, "Map '{0}' not found in data.", toMapName)); } // It would be pointless to check the "to map in world" here, // since the target map could easily be on an entirely different // server. *This* channel may not have that map. // Get name, preferably a localization key var name = toMapName; if (toMapData.LocalKey != "?") { name = this.WrapLocalizationKey(toMapData.LocalKey); } // Create a warping monster... var monster = new Monster(40001, NpcType.NPC); monster.Name = name; monster.WarpName = warpName; monster.Position = new Position(fromX, fromY, fromZ); monster.Direction = new Direction(direction); monster.WarpLocation = new Location(toMapData.Id, toX, toY, toZ); map.AddMonster(monster); return(0); }
/// <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); }