add a reverse crafting guide
After Width: | Height: | Size: 411 B |
After Width: | Height: | Size: 212 B |
After Width: | Height: | Size: 311 B |
After Width: | Height: | Size: 290 B |
After Width: | Height: | Size: 501 B |
After Width: | Height: | Size: 185 B |
After Width: | Height: | Size: 688 B |
|
@ -34,6 +34,9 @@
|
|||
#include "mapnode.h" // For content_t
|
||||
#include "settings.h" // for g_settings
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
|
||||
namespace crafting {
|
||||
|
||||
std::vector<CraftDef> shaped_recipes;
|
||||
|
@ -784,6 +787,161 @@ int getRecipeCount(InventoryItem *item)
|
|||
return count;
|
||||
}
|
||||
|
||||
//a little predicate type for determining whether a recipe contains a certain item
|
||||
struct CraftDefContains {
|
||||
content_t item;
|
||||
explicit CraftDefContains(content_t item_in) : item(item_in) {}
|
||||
template <typename CD>
|
||||
bool operator()(const CD& def) const
|
||||
{
|
||||
return std::find(def.recipe, def.recipe + 9, item) != def.recipe + 9;
|
||||
}
|
||||
};
|
||||
|
||||
int getReverseRecipeCount(InventoryItem *item)
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
//make a predicate object to look for recipes containing the right item
|
||||
CraftDefContains contains_item (item->getContent());
|
||||
|
||||
//count up the matching recipes
|
||||
return count_if(shaped_recipes.begin(), shaped_recipes.end(), contains_item)
|
||||
+ count_if(shapeless_recipes.begin(), shapeless_recipes.end(), contains_item);
|
||||
}
|
||||
|
||||
//how to create a FoundReverseRecipe from a CraftDef
|
||||
template <typename CD>
|
||||
FoundReverseRecipe FRRFromCD(const CD& def)
|
||||
{
|
||||
using namespace std;
|
||||
FoundReverseRecipe recipe;
|
||||
recipe.result = def.result;
|
||||
recipe.result_count = def.result_count;
|
||||
copy(def.recipe, def.recipe + 9, recipe.recipe);
|
||||
return recipe;
|
||||
}
|
||||
|
||||
//a helper function for reverse recipe lookup
|
||||
//count is used to count down to the correct recipe:
|
||||
//it is decremented by the function each time that it finds a viable recipe
|
||||
//this makes it possible to use this function for searches through multiple ranges
|
||||
template <typename It>
|
||||
FoundReverseRecipe reverseRecipeHelper(It cd_begin, It cd_end, content_t item, int &count)
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
//make a predicate object to aid with the search for recipes containing the item
|
||||
CraftDefContains contains_item (item);
|
||||
|
||||
//look through each of the recipes, finding the ones which contain item
|
||||
for (It it = cd_begin; it != cd_end; ++it) if (contains_item(*it)) {
|
||||
|
||||
//continue on if the target recipe hasn't been reached yet
|
||||
if (count--) continue;
|
||||
|
||||
//if it is the right recipe, make a copy and return it
|
||||
return FRRFromCD(*it);
|
||||
}
|
||||
|
||||
//if nothing was found yet, return a default FoundReverseRecipe
|
||||
return FoundReverseRecipe();
|
||||
}
|
||||
|
||||
FoundReverseRecipe getReverseRecipe(InventoryItem *iitem, int index)
|
||||
{
|
||||
//ignore negative indeces
|
||||
if (index < 0)
|
||||
return FoundReverseRecipe();
|
||||
|
||||
//find the content id for the item
|
||||
content_t item = iitem->getContent();
|
||||
|
||||
//where to store the recipe when found
|
||||
FoundReverseRecipe recipe;
|
||||
|
||||
//the recipe counter (counting down)
|
||||
int count = index;
|
||||
|
||||
//look in the shaped recipes
|
||||
recipe = reverseRecipeHelper(shaped_recipes.begin(), shaped_recipes.end(), item, count);
|
||||
|
||||
//if that fails, look in the shapeless recipes
|
||||
if (not recipe)
|
||||
recipe = reverseRecipeHelper(shapeless_recipes.begin(), shapeless_recipes.end(), item, count);
|
||||
|
||||
//return the located recipe, if one was found
|
||||
return recipe;
|
||||
}
|
||||
|
||||
//how to update an ingredient list given a range of new craft items
|
||||
template <typename It>
|
||||
void addToIngredientList(It results_begin, It results_end, std::vector<content_t>& ingredient_list)
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
//make a set to hold the items as the list is compiled
|
||||
set<content_t> ingredients (ingredient_list.begin(), ingredient_list.end());
|
||||
|
||||
//go through the result list
|
||||
for (It it = results_begin; it != results_end; ++it) {
|
||||
|
||||
//make a temporary inventory item for the result
|
||||
InventoryItem *result = InventoryItem::create(*it, 1);
|
||||
|
||||
//go through every recipe for this item
|
||||
for (int rec_ind = getRecipeCount(result); rec_ind--;) {
|
||||
|
||||
//get the recipe
|
||||
content_t *recipe = getRecipe(result, rec_ind);
|
||||
|
||||
//eliminate duplicates
|
||||
sort(recipe, recipe + 9);
|
||||
content_t *uniq_end = unique(recipe, recipe + 9);
|
||||
|
||||
//insert into the ingredients list
|
||||
ingredients.insert(recipe, uniq_end);
|
||||
|
||||
//clean up
|
||||
delete recipe;
|
||||
}
|
||||
|
||||
//clean up
|
||||
delete result;
|
||||
}
|
||||
|
||||
//ignore CONTENT_IGNORE
|
||||
ingredients.erase(CONTENT_IGNORE);
|
||||
|
||||
//dump the new ingredients into the ingredient list
|
||||
ingredient_list.insert(ingredient_list.end(), ingredients.begin(), ingredients.end());
|
||||
}
|
||||
|
||||
std::vector<content_t>& getCraftGuideIngredientList()
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
//the ingredient list, and the number of items that were in the craftguide list at the last check
|
||||
static vector<content_t> ingredient_list;
|
||||
static unsigned last_craftguide_count = 0;
|
||||
|
||||
//get the craftguide list
|
||||
const vector<content_t>& craft_list = lists::get("craftguide");
|
||||
|
||||
//check if more items need to be added
|
||||
if (craft_list.size() > last_craftguide_count) {
|
||||
|
||||
//if so, add the new stuff
|
||||
addToIngredientList(craft_list.begin() + last_craftguide_count, craft_list.end(), ingredient_list);
|
||||
|
||||
//and update the craftguide count
|
||||
last_craftguide_count = craft_list.size();
|
||||
}
|
||||
|
||||
//return the list
|
||||
return ingredient_list;
|
||||
}
|
||||
|
||||
void giveCreative(Player *player)
|
||||
{
|
||||
std::vector<content_t> &creativeinv = lists::get("player-creative");
|
||||
|
|
|
@ -283,6 +283,25 @@ namespace crafting {
|
|||
int getResultCount(InventoryItem *item);
|
||||
int getRecipeCount(InventoryItem *item);
|
||||
|
||||
//counts the number of recipes that use item
|
||||
int getReverseRecipeCount(InventoryItem *item);
|
||||
|
||||
//a recipe type for use in showing reverse recipes
|
||||
//invalid recipes have result == CONTENT_IGNORE
|
||||
struct FoundReverseRecipe {
|
||||
content_t recipe [9];
|
||||
content_t result;
|
||||
int result_count;
|
||||
FoundReverseRecipe() : result(CONTENT_IGNORE) {}
|
||||
operator bool() const {return result != CONTENT_IGNORE;}
|
||||
};
|
||||
|
||||
//retrieves the ith recipe that uses item (the first by default)
|
||||
FoundReverseRecipe getReverseRecipe(InventoryItem *item, int i = 0);
|
||||
|
||||
//retrieves a cached ingredient list that is automatically built from the craftguide list
|
||||
std::vector<content_t>& getCraftGuideIngredientList();
|
||||
|
||||
void giveCreative(Player *player);
|
||||
void giveInitial(Player *player);
|
||||
};
|
||||
|
|
|
@ -281,12 +281,14 @@ MapNode mapnode_translate_to_internal(MapNode n_from, u8 version);
|
|||
#define CONTENT_COOK_BOOK 0x8D4
|
||||
#define CONTENT_DECRAFT_BOOK 0x8D5
|
||||
#define CONTENT_DIARY_BOOK 0x8D6
|
||||
// FREE 8D7-8D9
|
||||
#define CONTENT_RCRAFT_BOOK 0x8D7
|
||||
// FREE 8D8-8D9
|
||||
#define CONTENT_BOOK_OPEN 0x8DA
|
||||
#define CONTENT_COOK_BOOK_OPEN 0x8DB
|
||||
#define CONTENT_DECRAFT_BOOK_OPEN 0x8DC
|
||||
#define CONTENT_DIARY_BOOK_OPEN 0x8DD
|
||||
// FREE 8D3-8DF
|
||||
#define CONTENT_RCRAFT_BOOK_OPEN 0x8DE
|
||||
// FREE 8DF-8EF
|
||||
#define CONTENT_YOUNG_TREE 0x8F0
|
||||
#define CONTENT_YOUNG_JUNGLETREE 0x8F1
|
||||
#define CONTENT_YOUNG_APPLE_TREE 0x8F2
|
||||
|
|
|
@ -799,6 +799,44 @@ void content_mapnode_special(bool repeat)
|
|||
lists::add("craftguide",i);
|
||||
lists::add("creative",i);
|
||||
|
||||
i = CONTENT_RCRAFT_BOOK;
|
||||
f = &content_features(i);
|
||||
f->description = wgettext("Reverse Craft Book");
|
||||
f->setTexture(0, "book_rcraft_cover.png");
|
||||
f->setTexture(1, "book_rcraft_cover.png^[transformFX");
|
||||
f->setTexture(2, "book_rcraft_side.png^[transformFY");
|
||||
f->setTexture(3, "book_rcraft_side.png");
|
||||
f->setTexture(4, "book_rcraft_end.png");
|
||||
f->setTexture(5, "book_rcraft_end.png^[transformFX");
|
||||
f->param_type = CPT_LIGHT;
|
||||
f->param2_type = CPT_FACEDIR_SIMPLE;
|
||||
f->draw_type = CDT_NODEBOX;
|
||||
f->rotate_tile_with_nodebox = true;
|
||||
f->light_propagates = true;
|
||||
f->air_equivalent = true;
|
||||
f->onpunch_replace_node = CONTENT_RCRAFT_BOOK_OPEN;
|
||||
f->flammable = 1;
|
||||
f->dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
|
||||
f->solidness = 0;
|
||||
content_nodebox_book(f);
|
||||
f->setInventoryTextureNodeBox(i, "book_rcraft_cover.png", "book_rcraft_end.png^[transformFX", "book_rcraft_side.png^[transformFY");
|
||||
f->type = CMT_DIRT;
|
||||
f->hardness = 1.0;
|
||||
f->pressure_type = CST_CRUSHABLE;
|
||||
f->suffocation_per_second = 0;
|
||||
{
|
||||
content_t r[9] = {
|
||||
CONTENT_CRAFTITEM_STICK, CONTENT_IGNORE, CONTENT_IGNORE,
|
||||
CONTENT_IGNORE, CONTENT_CRAFTITEM_STICK, CONTENT_IGNORE,
|
||||
CONTENT_CRAFTITEM_STICK, CONTENT_CRAFTITEM_STICK, CONTENT_IGNORE
|
||||
};
|
||||
crafting::setRecipe(r,CONTENT_RCRAFT_BOOK,1);
|
||||
}
|
||||
if (f->initial_metadata == NULL)
|
||||
f->initial_metadata = new ClosedBookNodeMetadata();
|
||||
lists::add("craftguide",i);
|
||||
lists::add("creative",i);
|
||||
|
||||
i = CONTENT_BOOK_OPEN;
|
||||
f = &content_features(i);
|
||||
f->description = wgettext("Guide");
|
||||
|
@ -941,6 +979,34 @@ void content_mapnode_special(bool repeat)
|
|||
f->initial_metadata = new CraftGuideNodeMetadata();
|
||||
f->sound_access = "open-book";
|
||||
|
||||
i = CONTENT_RCRAFT_BOOK_OPEN;
|
||||
f = &content_features(i);
|
||||
f->description = wgettext("Reverse Craft Guide");
|
||||
f->setAllTextures("guide_rcraft_side.png");
|
||||
f->setTexture(0, "guide_rcraft_top.png");
|
||||
f->setTexture(1, "guide_rcraft_bottom.png");
|
||||
f->setTexture(4, "guide_rcraft_end.png");
|
||||
f->setTexture(5, "guide_rcraft_end.png");
|
||||
f->param_type = CPT_LIGHT;
|
||||
f->param2_type = CPT_FACEDIR_SIMPLE;
|
||||
f->draw_type = CDT_NODEBOX;
|
||||
f->rotate_tile_with_nodebox = true;
|
||||
f->light_propagates = true;
|
||||
f->air_equivalent = true;
|
||||
f->onpunch_replace_node = CONTENT_RCRAFT_BOOK;
|
||||
f->flammable = 1;
|
||||
f->dug_item = std::string("MaterialItem2 ")+itos(CONTENT_RCRAFT_BOOK)+" 1";
|
||||
f->solidness = 0;
|
||||
content_nodebox_guide(f);
|
||||
f->setInventoryTextureNodeBox(i, "guide_rcraft_top.png", "guide_rcraft_end.png", "guide_rcraft_side.png");
|
||||
f->type = CMT_DIRT;
|
||||
f->hardness = 1.0;
|
||||
f->pressure_type = CST_CRUSHABLE;
|
||||
f->suffocation_per_second = 0;
|
||||
if (f->initial_metadata == NULL)
|
||||
f->initial_metadata = new ReverseCraftGuideNodeMetadata();
|
||||
f->sound_access = "open-book";
|
||||
|
||||
i = CONTENT_FIRE;
|
||||
f = &content_features(i);
|
||||
f->description = wgettext("Fire");
|
||||
|
|
|
@ -1528,6 +1528,285 @@ std::string CraftGuideNodeMetadata::getDrawSpecString()
|
|||
return spec;
|
||||
}
|
||||
|
||||
/*
|
||||
ReverseCraftGuideNodeMetadata
|
||||
*/
|
||||
|
||||
// Prototype
|
||||
ReverseCraftGuideNodeMetadata proto_ReverseCraftGuideNodeMetadata;
|
||||
|
||||
ReverseCraftGuideNodeMetadata::ReverseCraftGuideNodeMetadata()
|
||||
{
|
||||
//make sure that the type gets registered for this metadata
|
||||
NodeMetadata::registerType(typeId(), create);
|
||||
|
||||
//start on the first page, with the first recipe
|
||||
m_page = 0;
|
||||
m_recipe = 0;
|
||||
|
||||
//build the inventory
|
||||
m_inventory = new Inventory;
|
||||
m_inventory->addList("list", 300);
|
||||
m_inventory->addList("item", 1);
|
||||
m_inventory->addList("recipe", 9);
|
||||
m_inventory->addList("result", 1);
|
||||
}
|
||||
ReverseCraftGuideNodeMetadata::~ReverseCraftGuideNodeMetadata()
|
||||
{
|
||||
delete m_inventory;
|
||||
}
|
||||
u16 ReverseCraftGuideNodeMetadata::typeId() const
|
||||
{
|
||||
return CONTENT_RCRAFT_BOOK_OPEN;
|
||||
}
|
||||
void ReverseCraftGuideNodeMetadata::reloadPage()
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
//get the inventory list and clear it
|
||||
InventoryList *inv_list = m_inventory->getList("list");
|
||||
inv_list->clearItems();
|
||||
|
||||
//retrieve the list of things in the ingredient list
|
||||
vector<content_t> &ingredient_list = crafting::getCraftGuideIngredientList();
|
||||
|
||||
//get the number of pages
|
||||
if (ingredient_list.size() == 0) return;
|
||||
u16 page_count = ingredient_list.size()/40;
|
||||
if (ingredient_list.size()%40) ++page_count;
|
||||
|
||||
//make sure the page is actually in range (via modulus)
|
||||
if (s16(m_page) >= page_count) m_page %= page_count;
|
||||
else if (s16(m_page) < 0) m_page = s16(m_page)%page_count + page_count;
|
||||
|
||||
//go through each item on the current page
|
||||
vector<content_t>::iterator page_begin = ingredient_list.begin() + m_page*40;
|
||||
vector<content_t>::iterator page_end = ingredient_list.begin() + min((m_page+1)*40, signed(ingredient_list.size()));
|
||||
for (vector<content_t>::iterator it = page_begin; it != page_end; ++it) {
|
||||
|
||||
//create an inventory item for it
|
||||
InventoryItem *cur_item = InventoryItem::create(*it, 1);
|
||||
|
||||
//make extra sure that it actually has recipes in order to not look stupid
|
||||
if (not crafting::getReverseRecipe(cur_item)) delete cur_item;
|
||||
|
||||
//if it does, add it
|
||||
else inv_list->addItem(cur_item);
|
||||
}
|
||||
}
|
||||
NodeMetadata* ReverseCraftGuideNodeMetadata::clone()
|
||||
{
|
||||
//create a new metadata object
|
||||
ReverseCraftGuideNodeMetadata *d = new ReverseCraftGuideNodeMetadata;
|
||||
|
||||
//copy over the inventory
|
||||
*d->m_inventory = *m_inventory;
|
||||
|
||||
//keep the same page
|
||||
d->m_page = m_page;
|
||||
|
||||
//rebuild the page on the copy
|
||||
d->reloadPage();
|
||||
|
||||
//return the completed copy
|
||||
return d;
|
||||
}
|
||||
NodeMetadata* ReverseCraftGuideNodeMetadata::create(std::istream &is)
|
||||
{
|
||||
//create a new metadata object
|
||||
ReverseCraftGuideNodeMetadata *d = new ReverseCraftGuideNodeMetadata;
|
||||
|
||||
//deserialize the inventory
|
||||
d->m_inventory->deSerialize(is);
|
||||
|
||||
//read in the page and recipe
|
||||
is>>d->m_page;
|
||||
is>>d->m_recipe;
|
||||
|
||||
//return the completed object
|
||||
return d;
|
||||
}
|
||||
void ReverseCraftGuideNodeMetadata::serializeBody(std::ostream &os)
|
||||
{
|
||||
//serialize the inventory
|
||||
m_inventory->serialize(os);
|
||||
|
||||
//also serialize the page and recipe numbers
|
||||
os << itos(m_page) << " ";
|
||||
os << itos(m_recipe) << " ";
|
||||
}
|
||||
bool ReverseCraftGuideNodeMetadata::nodeRemovalDisabled()
|
||||
{
|
||||
//the player can always remove this node
|
||||
return false;
|
||||
}
|
||||
void ReverseCraftGuideNodeMetadata::inventoryModified()
|
||||
{
|
||||
infostream<<"ReverseCraftGuide inventory modification callback"<<std::endl;
|
||||
}
|
||||
bool ReverseCraftGuideNodeMetadata::step(float dtime, v3s16 pos, ServerEnvironment *env)
|
||||
{
|
||||
//get the item in the item box
|
||||
InventoryItem *item = m_inventory->getList("item")->getItem(0);
|
||||
|
||||
//if there's no item in the item box, do nothing
|
||||
if (not item or item->getContent() == CONTENT_IGNORE)
|
||||
return false;
|
||||
|
||||
//attempt to look up the recipe
|
||||
crafting::FoundReverseRecipe recipe = crafting::getReverseRecipe(item, m_recipe);
|
||||
|
||||
//if it doesn't exist, attempt to start over on the first recipe
|
||||
if (not recipe) {
|
||||
|
||||
//if it's already on the first recipe, give up
|
||||
if (m_recipe == 0)
|
||||
return false;
|
||||
|
||||
//otherwise, switch to the first recipe
|
||||
m_recipe = 0;
|
||||
recipe = crafting::getReverseRecipe(item, m_recipe);
|
||||
|
||||
//give up if that doesn't work
|
||||
if (not recipe)
|
||||
return false;
|
||||
}
|
||||
|
||||
//clear the recipe box
|
||||
InventoryList *rec_list = m_inventory->getList("recipe");
|
||||
rec_list->clearItems();
|
||||
|
||||
//load the recipe into the recipe box
|
||||
for (int i=0; i<9; i++) {
|
||||
if (recipe.recipe[i] == CONTENT_IGNORE)
|
||||
continue;
|
||||
InventoryItem *item = InventoryItem::create(recipe.recipe[i], 1);
|
||||
rec_list->addItem(i, item);
|
||||
}
|
||||
|
||||
//load the result box too
|
||||
{
|
||||
InventoryList *res_list = m_inventory->getList("result");
|
||||
res_list->clearItems();
|
||||
InventoryItem *result = InventoryItem::create(recipe.result, recipe.result_count);
|
||||
res_list->addItem(0, result);
|
||||
}
|
||||
|
||||
//the node has now been updated
|
||||
return true;
|
||||
}
|
||||
bool ReverseCraftGuideNodeMetadata::import(NodeMetadata *meta)
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
//if the metadata is from a book being opened, stay on the same page
|
||||
if (ClosedBookNodeMetadata *book_meta = dynamic_cast<ClosedBookNodeMetadata*>(meta))
|
||||
m_page = book_meta->getPage();
|
||||
|
||||
//reload the page
|
||||
reloadPage();
|
||||
|
||||
//node updated
|
||||
return true;
|
||||
}
|
||||
bool ReverseCraftGuideNodeMetadata::receiveFields(std::string formname, std::map<std::string, std::string> fields, Player *player)
|
||||
{
|
||||
//if the player wants to change the recipe
|
||||
if (fields["rprev"] != "" or fields["rnext"] != "") {
|
||||
|
||||
//find the ingredient item
|
||||
InventoryItem *item = m_inventory->getList("item")->getItem(0);
|
||||
|
||||
//advance the recipe counter appropriately
|
||||
if (fields["rprev"] != "") {
|
||||
if (m_recipe > 0)
|
||||
m_recipe--;
|
||||
} else {
|
||||
m_recipe++;
|
||||
}
|
||||
|
||||
//get the recipe count
|
||||
int rec_count = 1;
|
||||
if (item && item->getContent() != CONTENT_IGNORE)
|
||||
rec_count = crafting::getReverseRecipeCount(item);
|
||||
|
||||
//fix the counter if needed
|
||||
if (m_recipe >= rec_count)
|
||||
m_recipe = rec_count - 1;
|
||||
|
||||
//this node now needs updating
|
||||
step(0, v3s16(0,0,0), NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
//if the player wants to change the list page
|
||||
if (fields["prev"] != "" or fields["next"] != "") {
|
||||
|
||||
//advance m_page correctly
|
||||
if (fields["prev"] != "") --m_page;
|
||||
if (fields["next"] != "") ++m_page;
|
||||
|
||||
//reload the page (if the number is out of bounds it will fix it correctly automatically)
|
||||
reloadPage();
|
||||
|
||||
//the node has been updated
|
||||
return true;
|
||||
}
|
||||
|
||||
//nothing happened
|
||||
return false;
|
||||
}
|
||||
std::string ReverseCraftGuideNodeMetadata::getDrawSpecString()
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
//get the ingredient item
|
||||
InventoryItem *item = m_inventory->getList("item")->getItem(0);
|
||||
int recipe_count = 0;
|
||||
if (item && item->getContent() != CONTENT_IGNORE) {
|
||||
recipe_count = crafting::getReverseRecipeCount(item);
|
||||
}
|
||||
|
||||
//get the number of pages
|
||||
vector<content_t> &ingredient_list = crafting::getCraftGuideIngredientList();
|
||||
if (ingredient_list.size() == 0) return "";
|
||||
u16 page_count = ingredient_list.size()/40;
|
||||
if (ingredient_list.size()%40) ++page_count;
|
||||
|
||||
//write the page count string
|
||||
char buff[256];
|
||||
snprintf(buff, 256, gettext("Page %d of %d"), (int)(m_page+1), page_count);
|
||||
|
||||
//build the formspec
|
||||
string spec("size[8,10]");
|
||||
spec += "label[0.5,0.75;";
|
||||
spec += gettext("Add item here to see recipe");
|
||||
spec += "]";
|
||||
spec += "list[current_name;item;2,1;1,1;]";
|
||||
if (recipe_count > 1) {
|
||||
char rbuff[256];
|
||||
snprintf(rbuff,256,gettext("Recipe %d of %d"), (int)(m_recipe+1), recipe_count);
|
||||
spec += "button[2.5,3.5;1,0.75;rprev;<<]";
|
||||
spec += "label[3.5,3.5;";
|
||||
spec += rbuff;
|
||||
spec += "]";
|
||||
spec += "button[5.5,3.5;1,0.75;rnext;>>]";
|
||||
}
|
||||
spec += "list[current_name;recipe;4,0;3,3;]";
|
||||
spec += "button[0.25,4.5;2.5,0.75;prev;";
|
||||
spec += gettext("<< Previous Page");
|
||||
spec += "]";
|
||||
spec += "label[3.5,4.5;";
|
||||
spec += buff;
|
||||
spec += "]";
|
||||
spec += "button[6,4.5;2.5,0.75;next;";
|
||||
spec += gettext("Next Page >>");
|
||||
spec += "]";
|
||||
spec += "list[current_name;result;7,0;1,1;]";
|
||||
spec += "list[current_name;list;0,5;8,5;]";
|
||||
return spec;
|
||||
}
|
||||
|
||||
/*
|
||||
CookBookNodeMetadata
|
||||
*/
|
||||
|
|
|
@ -372,6 +372,37 @@ private:
|
|||
u16 m_recipe;
|
||||
};
|
||||
|
||||
class ReverseCraftGuideNodeMetadata : public NodeMetadata
|
||||
{
|
||||
public:
|
||||
ReverseCraftGuideNodeMetadata();
|
||||
~ReverseCraftGuideNodeMetadata();
|
||||
|
||||
virtual u16 typeId() const;
|
||||
NodeMetadata* clone();
|
||||
static NodeMetadata* create(std::istream &is);
|
||||
virtual void serializeBody(std::ostream &os);
|
||||
virtual std::wstring infoText() {return wgettext("Reverse Craft Guide");}
|
||||
virtual Inventory* getInventory() {return m_inventory;}
|
||||
virtual bool nodeRemovalDisabled();
|
||||
virtual void inventoryModified();
|
||||
virtual bool step(float dtime, v3s16 pos, ServerEnvironment *env);
|
||||
virtual bool import(NodeMetadata *meta);
|
||||
virtual bool receiveFields(std::string formname, std::map<std::string, std::string> fields, Player *player);
|
||||
virtual std::string getDrawSpecString();
|
||||
|
||||
u16 getPage() {return m_page;}
|
||||
|
||||
private:
|
||||
Inventory *m_inventory;
|
||||
u16 m_page;
|
||||
u16 m_recipe;
|
||||
|
||||
//a helper function to reload the current page, to reduce repeated code
|
||||
//this will also automatically wrap around the page number, casting it to a signed type and using modulus
|
||||
void reloadPage();
|
||||
};
|
||||
|
||||
class CookBookNodeMetadata : public NodeMetadata
|
||||
{
|
||||
public:
|
||||
|
|