1106 lines
30 KiB
C++
1106 lines
30 KiB
C++
/************************************************************************
|
|
* Minetest-c55
|
|
* Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
|
|
*
|
|
* content_craft.cpp
|
|
* voxelands - 3d voxel world sandbox game
|
|
* Copyright (C) Lisa 'darkrose' Milne 2014 <lisa@ltmnet.com>
|
|
*
|
|
* This program 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 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
* See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
*
|
|
* License updated from GPLv2 or later to GPLv3 or later by Lisa Milne
|
|
* for Voxelands.
|
|
************************************************************************/
|
|
|
|
#include "content_craft.h"
|
|
#include "inventory.h"
|
|
#include "content_mapnode.h"
|
|
#include "content_craftitem.h"
|
|
#include "content_clothesitem.h"
|
|
#include "content_toolitem.h"
|
|
#include "content_list.h"
|
|
#include "player.h"
|
|
#include "server.h"
|
|
#include "mapnode.h" // For content_t
|
|
|
|
#include <algorithm>
|
|
#include <set>
|
|
|
|
#include "list.h"
|
|
|
|
namespace crafting {
|
|
|
|
std::vector<CraftDef> shaped_recipes;
|
|
std::vector<CraftDefShapeless> shapeless_recipes;
|
|
craft_alloy_t *alloys;
|
|
|
|
void initCrafting()
|
|
{
|
|
craft_alloy_t *a;
|
|
shaped_recipes.clear();
|
|
shapeless_recipes.clear();
|
|
|
|
while ((a = (craft_alloy_t*)list_pull(&alloys))) {
|
|
free(a);
|
|
}
|
|
}
|
|
|
|
static bool checkRecipe(content_t recipe[9], content_t result)
|
|
{
|
|
for (std::vector<CraftDef>::iterator i=shaped_recipes.begin(); i!=shaped_recipes.end(); i++) {
|
|
CraftDef d = *i;
|
|
if (d.result == result && d == recipe)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool checkShapelessRecipe(content_t recipe[9], content_t result)
|
|
{
|
|
for (std::vector<CraftDefShapeless>::iterator i=shapeless_recipes.begin(); i!=shapeless_recipes.end(); i++) {
|
|
CraftDefShapeless d = *i;
|
|
if (d.result == result && d == recipe)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool checkAlloy(content_t recipe[2], content_t result)
|
|
{
|
|
craft_alloy_t *a;
|
|
|
|
a = alloys;
|
|
while (a) {
|
|
if (a->recipe[0] == recipe[0] && a->recipe[1] == recipe[1])
|
|
return true;
|
|
a = a->next;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void setRecipe(content_t recipe[9], content_t result, u16 count, uint64_t privs)
|
|
{
|
|
if (checkRecipe(recipe,result))
|
|
return;
|
|
CraftDef d;
|
|
for (int i=0; i<9; i++) {
|
|
d.recipe[i] = recipe[i];
|
|
}
|
|
d.result = result;
|
|
d.result_count = count;
|
|
d.privs = privs;
|
|
shaped_recipes.push_back(d);
|
|
}
|
|
|
|
void setShapelessRecipe(content_t recipe[9], content_t result, u16 count, uint64_t privs)
|
|
{
|
|
if (checkShapelessRecipe(recipe,result))
|
|
return;
|
|
CraftDefShapeless d;
|
|
for (int i=0; i<9; i++) {
|
|
d.recipe[i] = recipe[i];
|
|
}
|
|
d.result = result;
|
|
d.result_count = count;
|
|
d.privs = privs;
|
|
shapeless_recipes.push_back(d);
|
|
}
|
|
|
|
void setAlloy(content_t recipe[2], content_t result, uint16_t count)
|
|
{
|
|
craft_alloy_t *a;
|
|
if (checkAlloy(recipe,result))
|
|
return;
|
|
|
|
a = (craft_alloy_t*)malloc(sizeof(craft_alloy_t));
|
|
if (!a)
|
|
return;
|
|
|
|
a->recipe[0] = recipe[0];
|
|
a->recipe[1] = recipe[1];
|
|
a->result = result;
|
|
a->result_count = count;
|
|
|
|
alloys = (craft_alloy_t*)list_push(&alloys,a);
|
|
}
|
|
|
|
// one input yields one result
|
|
void set1To1Recipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,1);
|
|
}
|
|
// one input yields two result
|
|
void set1To2Recipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,2);
|
|
}
|
|
// one input yields four result
|
|
void set1To4Recipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,4);
|
|
}
|
|
// one input1 over one input2 yields one result
|
|
void set1over1Recipe(u16 input1, u16 input2, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input1, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
input2, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,1);
|
|
}
|
|
// one input1 over one input2 yields two result
|
|
void set1over2Recipe(u16 input1, u16 input2, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input1, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
input2, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,2);
|
|
}
|
|
// one input1 over one input2 yields four result
|
|
void set1over4Recipe(u16 input1, u16 input2, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input1, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
input2, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,4);
|
|
}
|
|
// four input in a square yields one result
|
|
void setSoftBlockRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, input, CONTENT_IGNORE,
|
|
input, input, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,1);
|
|
}
|
|
// four input in a square yields four result
|
|
void setBlockRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, input, CONTENT_IGNORE,
|
|
input, input, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,4);
|
|
}
|
|
// nine input yields one result
|
|
void setHardBlockRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, input, input,
|
|
input, input, input,
|
|
input, input, input
|
|
};
|
|
setRecipe(r,result,1);
|
|
}
|
|
// one input yields four result
|
|
void setUncraftBlockRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,4);
|
|
}
|
|
// one input yields nine result
|
|
void setUncraftHardBlockRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,9);
|
|
}
|
|
// four input in a Z yields four result
|
|
void setBrickRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
CONTENT_IGNORE, input, input,
|
|
input, input, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,4);
|
|
r[0] = input;
|
|
r[2] = CONTENT_IGNORE;
|
|
r[3] = CONTENT_IGNORE;
|
|
r[5] = input;
|
|
setRecipe(r,result,4);
|
|
}
|
|
// four input1 surround one input2 yields four result
|
|
void setSurroundRecipe(u16 input1, u16 input2, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
CONTENT_IGNORE, input1, CONTENT_IGNORE,
|
|
input1, input2, input1,
|
|
CONTENT_IGNORE, input1, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,4);
|
|
}
|
|
// 5 input in an X yields five result
|
|
void set5Recipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, CONTENT_IGNORE, input,
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE,
|
|
input, CONTENT_IGNORE, input
|
|
};
|
|
setRecipe(r,result,5);
|
|
}
|
|
// three input in a v yields count result
|
|
void setVRecipe(u16 input, u16 result, u16 count)
|
|
{
|
|
u16 r[9] = {
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
input, CONTENT_IGNORE, input,
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,count);
|
|
}
|
|
// five input in a u yields one result
|
|
void setURecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
input, CONTENT_IGNORE, input,
|
|
input, input, input
|
|
};
|
|
setRecipe(r,result,1);
|
|
}
|
|
// seven input in a U yields one result
|
|
void setDeepURecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, CONTENT_IGNORE, input,
|
|
input, CONTENT_IGNORE, input,
|
|
input, input, input
|
|
};
|
|
setRecipe(r,result,1);
|
|
}
|
|
// three input in a horizontal row yields one result
|
|
void setRow1Recipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, input, input,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,1);
|
|
}
|
|
// three input in a horizontal row yields two result
|
|
void setRow2Recipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, input, input,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,2);
|
|
}
|
|
// three input in a horizontal row yields three result
|
|
void setRow3Recipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, input, input,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,3);
|
|
}
|
|
// three input in a vertical row yields one result
|
|
void setCol1Recipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,1);
|
|
}
|
|
// three input in a vertical row yields two result
|
|
void setCol2Recipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,2);
|
|
}
|
|
// three input in a vertical row yields three result
|
|
void setCol3Recipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,3);
|
|
}
|
|
// six input as a stair yields six result
|
|
void setStairRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
input, input, CONTENT_IGNORE,
|
|
input, input, input
|
|
};
|
|
setRecipe(r,result,6);
|
|
u16 r1[9] = {
|
|
CONTENT_IGNORE, CONTENT_IGNORE, input,
|
|
CONTENT_IGNORE, input, input,
|
|
input, input, input
|
|
};
|
|
setRecipe(r1,result,6);
|
|
}
|
|
// two input in a horizontal row yields three result
|
|
void setTileRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, input, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,3);
|
|
}
|
|
// eight input in a circle yields one result
|
|
void setRoundRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, input, input,
|
|
input, CONTENT_IGNORE, input,
|
|
input, input, input
|
|
};
|
|
setRecipe(r,result,1);
|
|
}
|
|
// eight input1 surrounding one input2 yields one result
|
|
void setFilledRoundRecipe(u16 input1, u16 input2, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input1, input1, input1,
|
|
input1, input2, input1,
|
|
input1, input1, input1
|
|
};
|
|
setRecipe(r,result,1);
|
|
}
|
|
// six input in two horizontal rows yields six result
|
|
void setWallRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, input, input,
|
|
input, input, input,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,6);
|
|
}
|
|
// one each of inputs shapeless yields 1 result
|
|
void set1Any2Recipe(u16 input1, u16 input2, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input1, input2, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setShapelessRecipe(r,result,1);
|
|
}
|
|
// one each of inputs shapeless yields 2 result
|
|
void set2Any2Recipe(u16 input1, u16 input2, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input1, input2, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setShapelessRecipe(r,result,2);
|
|
}
|
|
// one each of inputs shapeless yields 3 result
|
|
void set1Any3Recipe(u16 input1, u16 input2, u16 input3, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input1, input2, input3,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setShapelessRecipe(r,result,1);
|
|
}
|
|
// one each of inputs shapeless yields 2 result
|
|
void set2Any3Recipe(u16 input1, u16 input2, u16 input3, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input1, input2, input3,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setShapelessRecipe(r,result,2);
|
|
}
|
|
// one each of inputs shapeless yields 3 result
|
|
void set3Any3Recipe(u16 input1, u16 input2, u16 input3, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input1, input2, input3,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setShapelessRecipe(r,result,3);
|
|
}
|
|
// special shortcuts
|
|
// bed recipe, input is bottom two cotton yields one result
|
|
void setBedRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
CONTENT_CRAFTITEM_PINE_PLANK, CONTENT_COTTON, CONTENT_CRAFTITEM_PINE_PLANK,
|
|
CONTENT_WOOD_PINE, input, CONTENT_WOOD_PINE,
|
|
CONTENT_CRAFTITEM_PINE_PLANK, input, CONTENT_CRAFTITEM_PINE_PLANK
|
|
};
|
|
setRecipe(r,result,1);
|
|
}
|
|
// sign recipe, input is top six wood yields one result
|
|
void setSignRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, input, input,
|
|
input, input, input,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_WOOD_PLANK, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,1);
|
|
r[7] = CONTENT_CRAFTITEM_PINE_PLANK;
|
|
setRecipe(r,result,1);
|
|
r[7] = CONTENT_CRAFTITEM_JUNGLE_PLANK;
|
|
setRecipe(r,result,1);
|
|
r[7] = CONTENT_CRAFTITEM_APPLE_PLANK;
|
|
setRecipe(r,result,1);
|
|
}
|
|
// shears recipe, input is blade yields one result
|
|
void setShearsRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, CONTENT_IGNORE, input,
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE,
|
|
CONTENT_CRAFTITEM_STICK, CONTENT_IGNORE, CONTENT_CRAFTITEM_STICK
|
|
};
|
|
setRecipe(r,result,0);
|
|
}
|
|
// shovel recipe, input is blade yields one result
|
|
void setShovelRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_PINE_PLANK, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_PINE_PLANK, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,0);
|
|
u16 r1[9] = {
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_WOOD_PLANK, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_WOOD_PLANK, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r1,result,0);
|
|
u16 r2[9] = {
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_JUNGLE_PLANK, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_JUNGLE_PLANK, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r2,result,0);
|
|
u16 r3[9] = {
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_APPLE_PLANK, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_APPLE_PLANK, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r3,result,0);
|
|
}
|
|
// spear recipe, input is blade yields one result
|
|
void setSpearRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_STICK, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_STICK, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,0);
|
|
}
|
|
// axe recipe, input is blade yields one result
|
|
void setAxeRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, input, CONTENT_IGNORE,
|
|
input, CONTENT_CRAFTITEM_PINE_PLANK, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_PINE_PLANK, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,0);
|
|
u16 r1[9] = {
|
|
CONTENT_IGNORE, input, input,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_PINE_PLANK, input,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_PINE_PLANK, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r1,result,0);
|
|
u16 r2[9] = {
|
|
input, input, CONTENT_IGNORE,
|
|
input, CONTENT_CRAFTITEM_WOOD_PLANK, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_WOOD_PLANK, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r2,result,0);
|
|
u16 r3[9] = {
|
|
CONTENT_IGNORE, input, input,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_WOOD_PLANK, input,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_WOOD_PLANK, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r3,result,0);
|
|
u16 r4[9] = {
|
|
input, input, CONTENT_IGNORE,
|
|
input, CONTENT_CRAFTITEM_JUNGLE_PLANK, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_JUNGLE_PLANK, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r4,result,0);
|
|
u16 r5[9] = {
|
|
CONTENT_IGNORE, input, input,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_JUNGLE_PLANK, input,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_JUNGLE_PLANK, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r5,result,0);
|
|
u16 r6[9] = {
|
|
input, input, CONTENT_IGNORE,
|
|
input, CONTENT_CRAFTITEM_APPLE_PLANK, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_APPLE_PLANK, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r6,result,0);
|
|
u16 r7[9] = {
|
|
CONTENT_IGNORE, input, input,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_APPLE_PLANK, input,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_APPLE_PLANK, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r7,result,0);
|
|
}
|
|
// pick recipe, input is blade yields one result
|
|
void setPickRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, input, input,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_PINE_PLANK, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_PINE_PLANK, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,0);
|
|
u16 r1[9] = {
|
|
input, input, input,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_WOOD_PLANK, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_WOOD_PLANK, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r1,result,0);
|
|
u16 r2[9] = {
|
|
input, input, input,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_JUNGLE_PLANK, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_JUNGLE_PLANK, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r2,result,0);
|
|
u16 r3[9] = {
|
|
input, input, input,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_APPLE_PLANK, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_APPLE_PLANK, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r3,result,0);
|
|
}
|
|
// sword recipe, input is blade yields one result
|
|
void setSwordRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_PINE_PLANK, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,0);
|
|
u16 r1[9] = {
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_WOOD_PLANK, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r1,result,0);
|
|
u16 r2[9] = {
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_JUNGLE_PLANK, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r2,result,0);
|
|
u16 r3[9] = {
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_CRAFTITEM_APPLE_PLANK, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r3,result,0);
|
|
}
|
|
// gate recipe 1 2 1 in two rows yields one result
|
|
void setGateRecipe(u16 input1, u16 input2, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input1, input2, input1,
|
|
input1, input2, input1,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,1);
|
|
}
|
|
// pants recipe 7 input in an upside-down U yields one result
|
|
void setPantsRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, input, input,
|
|
input, CONTENT_IGNORE, input,
|
|
input, CONTENT_IGNORE, input
|
|
};
|
|
setRecipe(r,result,1);
|
|
}
|
|
// shirt recipe 5 input in a T yields one result
|
|
void setShirtRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, input, input,
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,1);
|
|
}
|
|
// shirt recipe 5 input in a t yields one result
|
|
void setTShirtRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE,
|
|
input, input, input,
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,1);
|
|
}
|
|
// boots recipe 4 input in 2 separated columns yields one result
|
|
void setBootsRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, CONTENT_IGNORE, input,
|
|
input, CONTENT_IGNORE, input,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,1);
|
|
}
|
|
// hat recipe, 5 input in an upside down u yields one result
|
|
void setHatRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, input, input,
|
|
input, CONTENT_IGNORE, input,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,1);
|
|
}
|
|
// helmet recipe, 5 input in an upside down u around a glass pane yields one result
|
|
void setHelmetRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, input, input,
|
|
input, CONTENT_GLASS_PANE, input,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,1);
|
|
}
|
|
// like boots, but the top 2 input are replaced with string
|
|
void setShoesRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
CONTENT_CRAFTITEM_STRING, CONTENT_IGNORE, CONTENT_CRAFTITEM_STRING,
|
|
input, CONTENT_IGNORE, input,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,1);
|
|
}
|
|
// pants recipe 5 input in an upside-down V yields one result
|
|
void setShortsRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE,
|
|
input, CONTENT_IGNORE, input,
|
|
input, CONTENT_IGNORE, input
|
|
};
|
|
setRecipe(r,result,1);
|
|
}
|
|
// 4 spaced input yelds 1 result
|
|
void set4SpacedTo1Recipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
input, CONTENT_IGNORE, input,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
input, CONTENT_IGNORE, input
|
|
};
|
|
setRecipe(r,result,1);
|
|
}
|
|
void setKnifeRecipe(u16 input, u16 result)
|
|
{
|
|
u16 r[9] = {
|
|
CONTENT_IGNORE, input, CONTENT_IGNORE,
|
|
CONTENT_CRAFTITEM_STICK, CONTENT_IGNORE, CONTENT_IGNORE,
|
|
CONTENT_IGNORE, CONTENT_IGNORE, CONTENT_IGNORE
|
|
};
|
|
setRecipe(r,result,1);
|
|
}
|
|
|
|
|
|
InventoryItem *getResult(InventoryItem **items, Player *player, Server *server)
|
|
{
|
|
for (std::vector<CraftDef>::iterator i=shaped_recipes.begin(); i!=shaped_recipes.end(); i++) {
|
|
CraftDef d = *i;
|
|
if (d.privs != 0 && (server->getPlayerPrivs(player)&d.privs) != d.privs)
|
|
continue;
|
|
if (d == items)
|
|
return InventoryItem::create(d.result,d.result_count);
|
|
}
|
|
for (std::vector<CraftDefShapeless>::iterator i=shapeless_recipes.begin(); i!=shapeless_recipes.end(); i++) {
|
|
CraftDefShapeless d = *i;
|
|
if (d.privs != 0 && (server->getPlayerPrivs(player)&d.privs) != d.privs)
|
|
continue;
|
|
if (d == items)
|
|
return InventoryItem::create(d.result,d.result_count);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
InventoryItem *getAlloy(content_t c0, content_t c1)
|
|
{
|
|
craft_alloy_t *a;
|
|
|
|
a = alloys;
|
|
while (a) {
|
|
if (a->recipe[0] == c0 && a->recipe[1] == c1) {
|
|
return InventoryItem::create(a->result,a->result_count);
|
|
}
|
|
a = a->next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
content_t *getRecipe(InventoryItem *item)
|
|
{
|
|
content_t r = item->getContent();
|
|
content_t *recipe;
|
|
for (std::vector<CraftDef>::iterator i=shaped_recipes.begin(); i!=shaped_recipes.end(); i++) {
|
|
CraftDef d = *i;
|
|
if (d.result == r) {
|
|
recipe = new content_t[9];
|
|
for (int j=0; j<9; j++) {
|
|
recipe[j] = d.recipe[j];
|
|
}
|
|
return recipe;
|
|
}
|
|
}
|
|
for (std::vector<CraftDefShapeless>::iterator i=shapeless_recipes.begin(); i!=shapeless_recipes.end(); i++) {
|
|
CraftDefShapeless d = *i;
|
|
if (d.result == r) {
|
|
recipe = new content_t[9];
|
|
for (int j=0; j<9; j++) {
|
|
recipe[j] = d.recipe[j];
|
|
}
|
|
return recipe;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
content_t *getRecipe(InventoryItem *item, int index)
|
|
{
|
|
content_t r = item->getContent();
|
|
content_t *recipe;
|
|
int count = 0;
|
|
if (index < 0)
|
|
return NULL;
|
|
for (std::vector<CraftDef>::iterator i=shaped_recipes.begin(); i!=shaped_recipes.end(); i++) {
|
|
CraftDef d = *i;
|
|
if (d.result == r) {
|
|
if (index != count++)
|
|
continue;
|
|
recipe = new content_t[9];
|
|
for (int j=0; j<9; j++) {
|
|
recipe[j] = d.recipe[j];
|
|
}
|
|
return recipe;
|
|
}
|
|
}
|
|
for (std::vector<CraftDefShapeless>::iterator i=shapeless_recipes.begin(); i!=shapeless_recipes.end(); i++) {
|
|
CraftDefShapeless d = *i;
|
|
if (d.result == r) {
|
|
if (index != count++)
|
|
continue;
|
|
recipe = new content_t[9];
|
|
for (int j=0; j<9; j++) {
|
|
recipe[j] = d.recipe[j];
|
|
}
|
|
return recipe;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int getResultCount(InventoryItem *item)
|
|
{
|
|
content_t r = item->getContent();
|
|
for (std::vector<CraftDef>::iterator i=shaped_recipes.begin(); i!=shaped_recipes.end(); i++) {
|
|
CraftDef d = *i;
|
|
if (d.result == r)
|
|
return d.result_count;
|
|
}
|
|
for (std::vector<CraftDefShapeless>::iterator i=shapeless_recipes.begin(); i!=shapeless_recipes.end(); i++) {
|
|
CraftDefShapeless d = *i;
|
|
if (d.result == r)
|
|
return d.result_count;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int getRecipeCount(InventoryItem *item)
|
|
{
|
|
content_t r = item->getContent();
|
|
int count = 0;
|
|
for (std::vector<CraftDef>::iterator i=shaped_recipes.begin(); i!=shaped_recipes.end(); i++) {
|
|
CraftDef d = *i;
|
|
if (d.result == r)
|
|
count++;
|
|
}
|
|
for (std::vector<CraftDefShapeless>::iterator i=shapeless_recipes.begin(); i!=shapeless_recipes.end(); i++) {
|
|
CraftDefShapeless d = *i;
|
|
if (d.result == r)
|
|
count++;
|
|
}
|
|
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 (!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
|
|
void addToIngredientList(contentlist_t *list, uint32_t begin, 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());
|
|
|
|
listdata_t *d = list->data;
|
|
|
|
//go through the result list
|
|
while (d) {
|
|
|
|
//make a temporary inventory item for the result
|
|
InventoryItem *result = InventoryItem::create(d->content, 1, 0, d->data);
|
|
|
|
//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;
|
|
|
|
d = d->next;
|
|
}
|
|
|
|
//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()
|
|
{
|
|
contentlist_t *cl;
|
|
uint32_t list_size;
|
|
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 uint32_t last_craftguide_count = 0;
|
|
|
|
//get the craftguide list
|
|
cl = content_list_get("craftguide");
|
|
if (!cl)
|
|
return ingredient_list;
|
|
|
|
list_size = list_count(&cl->data);
|
|
|
|
//check if more items need to be added
|
|
if (list_size > last_craftguide_count) {
|
|
|
|
//if so, add the new stuff
|
|
addToIngredientList(cl, last_craftguide_count, ingredient_list);
|
|
|
|
//and update the craftguide count
|
|
last_craftguide_count = list_size;
|
|
}
|
|
|
|
//return the list
|
|
return ingredient_list;
|
|
}
|
|
|
|
void giveCreative(Player *player)
|
|
{
|
|
contentlist_t *cl;
|
|
listdata_t *ld;
|
|
InventoryList *l;
|
|
|
|
cl = content_list_get("player-creative");
|
|
if (!cl)
|
|
return;
|
|
|
|
l = player->inventory.getList("main");
|
|
|
|
// if the player doesn't have a creative chest, reset their inventory
|
|
if (!l || l->findItem(CONTENT_CREATIVE_CHEST,NULL) != NULL)
|
|
return;
|
|
|
|
// this stops the player being naked when the inventory is reset
|
|
player->setClothesGiven(false);
|
|
player->resetInventory();
|
|
|
|
ld = cl->data;
|
|
while (ld) {
|
|
player->inventory.addItem("main", InventoryItem::create(ld->content,ld->count,0,ld->data));
|
|
ld = ld->next;
|
|
}
|
|
}
|
|
|
|
void giveInitial(Player *player)
|
|
{
|
|
player->resetInventory();
|
|
assert(player->inventory.addItem("main", new ToolItem(CONTENT_TOOLITEM_IRON_PICK, 0, 0)) == NULL) ;
|
|
assert(player->inventory.addItem("main", new ToolItem(CONTENT_TOOLITEM_IRON_AXE, 0, 0)) == NULL) ;
|
|
assert(player->inventory.addItem("main", new ToolItem(CONTENT_TOOLITEM_IRON_SHOVEL, 0, 0)) == NULL) ;
|
|
assert(player->inventory.addItem("main", new ToolItem(CONTENT_TOOLITEM_IRON_SHEARS, 0, 0)) == NULL) ;
|
|
assert(player->inventory.addItem("main", new MaterialItem(CONTENT_TORCH, 99, 0)) == NULL) ;
|
|
assert(player->inventory.addItem("main", new MaterialItem(CONTENT_ROUGHSTONEBRICK, 99, 0)) == NULL) ;
|
|
assert(player->inventory.addItem("main", new MaterialItem(CONTENT_BORDERSTONE, 5, 0)) == NULL) ;
|
|
}
|
|
|
|
};
|