summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--example/clientdata/graphics/items/crafting/generic-ingot.pngbin0 -> 1615 bytes
-rw-r--r--example/clientdata/graphics/items/crafting/generic-rawlog.pngbin0 -> 1719 bytes
-rw-r--r--example/clientdata/items.xml10
-rw-r--r--example/serverdata/permissions.xml1
-rw-r--r--example/serverdata/scripts/crafting.lua99
-rw-r--r--src/game-server/commandhandler.cpp78
-rw-r--r--src/game-server/main-game.cpp2
-rw-r--r--src/scripting/luascript.cpp38
-rw-r--r--src/scripting/luascript.h3
-rw-r--r--src/scripting/script.cpp14
-rw-r--r--src/scripting/script.h8
11 files changed, 253 insertions, 0 deletions
diff --git a/example/clientdata/graphics/items/crafting/generic-ingot.png b/example/clientdata/graphics/items/crafting/generic-ingot.png
new file mode 100644
index 00000000..0dc7b1e7
--- /dev/null
+++ b/example/clientdata/graphics/items/crafting/generic-ingot.png
Binary files differ
diff --git a/example/clientdata/graphics/items/crafting/generic-rawlog.png b/example/clientdata/graphics/items/crafting/generic-rawlog.png
new file mode 100644
index 00000000..038d5a5f
--- /dev/null
+++ b/example/clientdata/graphics/items/crafting/generic-rawlog.png
Binary files differ
diff --git a/example/clientdata/items.xml b/example/clientdata/items.xml
index ff0b1c5e..6f72185b 100644
--- a/example/clientdata/items.xml
+++ b/example/clientdata/items.xml
@@ -160,4 +160,14 @@
<sprite gender="male">equipment/chest/chest-leather-male.xml</sprite>
<sprite gender="female">equipment/chest/chest-leather-female.xml</sprite>
</item>
+
+ <!-- example crafting ingredients -->
+ <item id="8" max-per-slot="99" name="Iron"
+ description="Combine with one wood to create a sword"
+ image="crafting/generic-ingot.png"
+ value="1" />
+ <item id="9" max-per-slot="99" name="Wood"
+ description="Combine with two iron to create a sword"
+ image="crafting/generic-rawlog.png"
+ value="1" />
</items>
diff --git a/example/serverdata/permissions.xml b/example/serverdata/permissions.xml
index 91abd845..c07bfae2 100644
--- a/example/serverdata/permissions.xml
+++ b/example/serverdata/permissions.xml
@@ -6,6 +6,7 @@
<allow>@where</allow>
<allow>@rights</allow>
<allow>@report</allow>
+ <allow>@craft</allow>
</class>
<class level="2">
<alias>tester</alias>
diff --git a/example/serverdata/scripts/crafting.lua b/example/serverdata/scripts/crafting.lua
new file mode 100644
index 00000000..e8939823
--- /dev/null
+++ b/example/serverdata/scripts/crafting.lua
@@ -0,0 +1,99 @@
+-------------------------------------------------------------
+-- Example crafting script file --
+-- --
+-- This file allows you to implement your own crafting --
+-- system. --
+----------------------------------------------------------------------------------
+-- Copyright 2011 Manasource Development Team --
+-- --
+-- This file is part of Manasource. --
+-- --
+-- Manasource is free software; you can redistribute it and/or modify it --
+-- under the terms of the GNU General Public License as published by the Free --
+-- Software Foundation; either version 2 of the License, or any later version. --
+----------------------------------------------------------------------------------
+
+-- This function is called by the game engine when a character tries to craft
+-- something from items in its inventory
+function on_craft(ch, recipe)
+ -- ch is the crafting character
+ --
+ -- recipe is a table with the ingredients.
+ -- it is a common 1-based array. each element of this array is a table with the
+ -- two keys "id" and "amount".
+ -- The engine has already checked that the character owns enough of those things,
+ -- so you needn't do this again.
+
+ -- uncomment one (but not both!) of the following three lines to enable the
+ -- example crafting systems
+
+ mana.chatmessage(ch, "There is no crafting in this game world.")
+ --craft_strict(ch, recipe)
+ --craft_lax(ch, recipe)
+end
+
+
+-- a primitive example crafting system which cares about item order and exact amount
+function craft_strict(ch, recipe)
+ if (recipe[1].id == 8 and recipe[1].amount == 2 and -- has two iron
+ recipe[2].id == 9 and recipe[2].amount == 1) -- and one wood
+ then
+ mana.chr_inv_change(ch,
+ 8, -2, --take away the iron
+ 9, -1, --take away the wood
+ 5, 1 ) -- give a sword
+ return
+ mana.chatmessage(ch, "You've crafted a sword")
+ end
+ mana.chatmessage(ch, "This wouldn't create anything useful")
+end
+
+-- a primitive example crafting system which doesn't care about item order
+-- and amount. It even allows to mention the same item multiple times.
+function craft_lax(ch, recipe)
+ recipe = make_condensed_and_sorted_item_list(recipe)
+
+ if (recipe[1].id == 8 and recipe[1].amount >= 2 and -- has at least two iron
+ recipe[2].id == 9 and recipe[2].amount >= 1) -- and at least one wood
+ then
+ mana.chr_inv_change(ch,
+ 8, -2, --take away the iron
+ 9, -1, --take away the wood
+ 5, 1 ) -- give a sword
+ return
+ mana.chatmessage(ch, "You've crafted a sword")
+ end
+ mana.chatmessage(ch, "This wouldn't create anything useful")
+end
+
+-- this turns multiple occurences of the same item into one by adding up
+-- their amounts and sorts the recipe by item ID.
+-- This makes stuff a lot easier when your crafting system isn't supposed to care
+-- about the order items are in.
+function make_condensed_and_sorted_item_list(recipe)
+
+ local condensed = {}
+ for index, item in pairs(recipe) do
+ if condensed[item.id] == nil then
+ condensed[item.id] = item.amount
+ else
+ condensed[item.id] = condensed[item.id] + item.amount
+ end
+ end
+
+ local sorted = {}
+ for id, amount in pairs(condensed) do
+ local item = {}
+ item.id = id
+ item.amount = amount
+ table.insert(sorted, item)
+ end
+
+ table.sort(sorted, function(item1, item2)
+ return (item1.id < item2.id)
+ end
+ )
+
+ return sorted
+
+end \ No newline at end of file
diff --git a/src/game-server/commandhandler.cpp b/src/game-server/commandhandler.cpp
index d7bd8f44..f0cbcf3b 100644
--- a/src/game-server/commandhandler.cpp
+++ b/src/game-server/commandhandler.cpp
@@ -33,6 +33,8 @@
#include "game-server/monstermanager.h"
#include "game-server/state.h"
+#include "scripting/script.h"
+
#include "common/configuration.h"
#include "common/permissionmanager.h"
#include "common/transaction.h"
@@ -73,6 +75,7 @@ static void handleKick(Character*, std::string&);
static void handleLog(Character*, std::string&);
static void handleLogsay(Character*, std::string&);
static void handleKillMonsters(Character*, std::string&);
+static void handleCraft(Character*, std::string&);
static CmdRef const cmdRef[] =
{
@@ -128,6 +131,8 @@ static CmdRef const cmdRef[] =
"Says something in public chat while logging it to the transaction log.", &handleLogsay},
{"killmonsters", "",
"Kills all monsters on the map.", &handleKillMonsters},
+ {"craft", "{ <item> <amount> }",
+ "Crafts something.", &handleCraft},
{NULL, NULL, NULL, NULL}
};
@@ -1291,6 +1296,79 @@ static void handleKillMonsters(Character *player, std::string &args)
TRANS_CMD_KILLMONSTERS, msg);
}
+static void handleCraft(Character *player, std::string &args)
+{
+ std::stringstream errMsg;
+ std::list<InventoryItem> recipe;
+ Inventory playerInventory(player);
+ std::map<int, int> totalAmountOfItem;
+
+ while (true)
+ {
+ // parsing
+ std::string strItem = getArgument(args);
+ ItemClass* item = itemManager->getItemByName(strItem);
+ std::string strAmount = getArgument(args);
+ int amount = utils::stringToInt(strAmount);
+
+ // syntax error checking
+ if (strItem.empty())
+ {
+ // the item list has ended
+ break;
+ }
+ if (!item)
+ {
+ // item wasn't found in the item database
+ errMsg << "Unknown item: \"" << strItem << "\".";
+ break;
+ }
+
+ if (strAmount.empty())
+ {
+ // the last item in the list has no amount defined
+ errMsg << "No amount given for \"" << strItem << "\".";
+ break;
+ }
+ if (amount < 1)
+ {
+ errMsg << "Illegal amount \""<< strAmount << "\" for item \"" << strItem << "\".";
+ break;
+ }
+
+ // inventory checking
+ int available = playerInventory.count(item->getDatabaseID());
+ if (available == 0)
+ {
+ errMsg << "You have no "<< strItem << " in your inventory.";
+ break;
+ }
+ if (available < amount)
+ {
+ errMsg << "You haven't got that many "<< strItem << "s in your inventory.";
+ break;
+ }
+
+ // when there is still no break, add the item;
+ InventoryItem recipeItem;
+ recipeItem.itemId = item->getDatabaseID();
+ recipeItem.amount = amount;
+ recipe.push_back(recipeItem);
+ }
+
+ if (!errMsg.str().empty())
+ {
+ // when an error occured, output the error
+ say(errMsg.str(), player);
+ return;
+ } else {
+ // pass to script engine. The engine is responsible for all
+ // further processing of the crafting operation, including
+ // outputting an error message when the recipe is invalid.
+ Script::performCraft(player, recipe);
+ }
+}
+
void CommandHandler::handleCommand(Character *player,
const std::string &command)
{
diff --git a/src/game-server/main-game.cpp b/src/game-server/main-game.cpp
index b54e3821..63ea0041 100644
--- a/src/game-server/main-game.cpp
+++ b/src/game-server/main-game.cpp
@@ -74,6 +74,7 @@ using utils::Logger;
#define DEFAULT_PERMISSION_FILE "permissions.xml"
#define DEFAULT_GLOBAL_EVENT_SCRIPT_FILE "scripts/global_events.lua"
#define DEFAULT_SPECIAL_ACTIONS_SCRIPT_FILE "scripts/special_actions.lua"
+#define DEFAULT_CRAFT_SCRIPT_FILE "scripts/crafting.lua"
static int const WORLD_TICK_SKIP = 2; /** tolerance for lagging behind in world calculation) **/
@@ -200,6 +201,7 @@ static void initializeServer()
LuaScript::loadGlobalEventScript(DEFAULT_GLOBAL_EVENT_SCRIPT_FILE);
LuaScript::loadSpecialActionsScript(DEFAULT_SPECIAL_ACTIONS_SCRIPT_FILE);
+ LuaScript::loadCraftScript(DEFAULT_CRAFT_SCRIPT_FILE);
// --- Initialize the global handlers
// FIXME: Make the global handlers global vars or part of a bigger
diff --git a/src/scripting/luascript.cpp b/src/scripting/luascript.cpp
index a9c43b72..dc6230ca 100644
--- a/src/scripting/luascript.cpp
+++ b/src/scripting/luascript.cpp
@@ -21,6 +21,9 @@
#include "luascript.h"
+
+#include "scripting/luautil.h"
+
#include "game-server/being.h"
#include "utils/logger.h"
@@ -61,6 +64,30 @@ void LuaScript::push(Thing *v)
++nbArgs;
}
+void LuaScript::push(const std::list<InventoryItem> &itemList)
+{
+ assert(nbArgs >= 0);
+ int position = 0;
+
+ lua_createtable(mState, itemList.size(), 0);
+ int itemTable = lua_gettop(mState);
+
+ for (std::list<InventoryItem>::const_iterator i = itemList.begin();
+ i != itemList.end();
+ i++)
+ {
+ // create the item structure
+ std::map<std::string, int> item;
+ item["id"] = i->itemId;
+ item["amount"] = i->amount;
+ // add the item structure to the item table under the next index
+ lua_pushinteger(mState, ++position);
+ pushSTLContainer<std::string, int>(mState, item);
+ lua_settable(mState, itemTable);
+ }
+ ++nbArgs;
+}
+
int LuaScript::execute()
{
assert(nbArgs >= 0);
@@ -182,3 +209,14 @@ bool LuaScript::loadSpecialActionsScript(const std::string &file)
}
return true;
}
+
+bool LuaScript::loadCraftScript(const std::string &file)
+{
+ Script::craftScript = new LuaScript();
+ if (!Script::craftScript->loadFile(file))
+ {
+ Script::craftScript = NULL;
+ return false;
+ }
+ return true;
+}
diff --git a/src/scripting/luascript.h b/src/scripting/luascript.h
index af13aa22..b9bde2d8 100644
--- a/src/scripting/luascript.h
+++ b/src/scripting/luascript.h
@@ -52,6 +52,8 @@ class LuaScript: public Script
void push(Thing *);
+ void push(const std::list<InventoryItem> &itemList);
+
int execute();
static void getQuestCallback(Character *, const std::string &,
@@ -69,6 +71,7 @@ class LuaScript: public Script
*/
static bool loadGlobalEventScript(const std::string &file);
static bool loadSpecialActionsScript(const std::string &file);
+ static bool loadCraftScript(const std::string &file);
private:
lua_State *mState;
diff --git a/src/scripting/script.cpp b/src/scripting/script.cpp
index b222b0f7..490abf09 100644
--- a/src/scripting/script.cpp
+++ b/src/scripting/script.cpp
@@ -34,6 +34,7 @@ typedef std::map< std::string, Script::Factory > Engines;
static Engines *engines = NULL;
Script *Script::globalEventScript = NULL;
Script *Script::specialActionsScript = NULL;
+Script *Script::craftScript = NULL;
Script::Script():
mMap(NULL),
@@ -158,3 +159,16 @@ bool Script::performSpecialAction(int specialId, Being* caster)
}
return true;
}
+
+bool Script::performCraft(Being* crafter, std::list<InventoryItem> recipe)
+{
+ Script *script = Script::craftScript;
+ if (script)
+ {
+ script->prepare("on_craft");
+ script->push(crafter);
+ script->push(recipe);
+ script->execute();
+ }
+ return true;
+}
diff --git a/src/scripting/script.h b/src/scripting/script.h
index 43eebe10..44a8b7ac 100644
--- a/src/scripting/script.h
+++ b/src/scripting/script.h
@@ -105,6 +105,12 @@ class Script
virtual void push(Thing *) = 0;
/**
+ * Pushes a list of items with amounts to the
+ * script engine.
+ */
+ virtual void push(const std::list<InventoryItem> &itemList) = 0;
+
+ /**
* Executes the function being prepared.
* @return the value returned by the script.
*/
@@ -135,11 +141,13 @@ class Script
static bool executeGlobalEventFunction(const std::string &function, Being *obj);
static void addDataToSpecial(int specialId, Special *special);
static bool performSpecialAction(int specialId, Being *caster);
+ static bool performCraft(Being* crafter, std::list<InventoryItem> recipe);
protected:
static Script *globalEventScript;
static Script *specialActionsScript;
+ static Script *craftScript;
std::string mScriptFile;
private: