[CraftingRecipe] Improved shaped recipe matching. [SmallCraftingWidget] Added. Now items are craftable from inventory.

This commit is contained in:
Quentin Bazin 2018-06-27 09:07:57 +02:00
parent 24c857b57c
commit d53f080fb7
12 changed files with 284 additions and 66 deletions

View File

@ -20,7 +20,7 @@ class CraftingRecipe;
class CraftingWidget : public Widget {
public:
CraftingWidget(Widget *parent = nullptr);
CraftingWidget(u16 width, u16 height, Widget *parent = nullptr);
void onMouseEvent(const SDL_Event &event, MouseItemWidget &mouseItemWidget);
@ -28,15 +28,16 @@ class CraftingWidget : public Widget {
const ItemWidget *currentItemWidget() const { return m_craftingResultInventoryWidget.currentItemWidget() ? m_craftingResultInventoryWidget.currentItemWidget() : m_craftingInventoryWidget.currentItemWidget(); }
private:
void draw(RenderTarget &target, RenderStates states) const override;
Inventory m_craftingInventory{3, 3};
protected:
Inventory m_craftingInventory;
InventoryWidget m_craftingInventoryWidget{this};
Inventory m_craftingResultInventory{1, 1};
InventoryWidget m_craftingResultInventoryWidget{this};
private:
void draw(RenderTarget &target, RenderStates states) const override;
const CraftingRecipe *m_recipe = nullptr;
};

View File

@ -14,7 +14,7 @@
#ifndef PLAYERINVENTORYWIDGET_HPP_
#define PLAYERINVENTORYWIDGET_HPP_
#include "InventoryWidget.hpp"
#include "SmallCraftingWidget.hpp"
class PlayerInventoryWidget : public Widget {
public:
@ -29,6 +29,8 @@ class PlayerInventoryWidget : public Widget {
Image m_background;
SmallCraftingWidget m_craftingWidget{this};
Inventory &m_playerInventory;
InventoryWidget m_playerInventoryWidget{this};

View File

@ -0,0 +1,24 @@
/*
* =====================================================================================
*
* Filename: SmallCraftingWidget.hpp
*
* Description:
*
* Created: 27/06/2018 09:03:26
*
* Author: Quentin Bazin, <quent42340@gmail.com>
*
* =====================================================================================
*/
#ifndef SMALLCRAFTINGWIDGET_HPP_
#define SMALLCRAFTINGWIDGET_HPP_
#include "CraftingWidget.hpp"
class SmallCraftingWidget : public CraftingWidget {
public:
SmallCraftingWidget(Widget *parent = nullptr);
};
#endif // SMALLCRAFTINGWIDGET_HPP_

View File

@ -29,7 +29,7 @@ class WorkbenchWidget : public Widget {
Image m_background;
CraftingWidget m_craftingWidget{this};
CraftingWidget m_craftingWidget{3, 3, this};
Inventory &m_playerInventory;
InventoryWidget m_playerInventoryWidget{this};

View File

@ -15,19 +15,23 @@
#define CRAFTINGRECIPE_HPP_
#include <array>
#include <map>
#include "Inventory.hpp"
class CraftingRecipe {
public:
CraftingRecipe(const std::array<u32, 9> &recipe, const ItemStack &result, bool isShapeless = false);
CraftingRecipe(const std::vector<std::string> &pattern, const std::map<char, std::vector<u32>> &keys, const ItemStack &result, bool isShapeless = false);
bool isMatching(const Inventory &inventory) const;
const ItemStack &result() const { return m_result; }
private:
std::array<u32, 9> m_recipe;
bool checkMatch(const Inventory &inventory, int offsetX, int offsetY) const;
std::vector<std::string> m_pattern;
std::map<char, std::vector<u32>> m_keys;
ItemStack m_result;
bool m_isShapeless;

View File

@ -66,28 +66,119 @@ void Registry::registerItems() {
}
void Registry::registerRecipes() {
m_recipes.emplace_back(std::array<u32, 9>{2, 2, 0, 2, ItemType::Stick, 0, 0, ItemType::Stick, 0}, ItemStack{ItemType::StoneAxe});
m_recipes.emplace_back(std::array<u32, 9>{2, 2, 0, 0, ItemType::Stick, 0, 0, ItemType::Stick, 0}, ItemStack{ItemType::StoneHoe});
m_recipes.emplace_back(std::array<u32, 9>{2, 2, 2, 0, ItemType::Stick, 0, 0, ItemType::Stick, 0}, ItemStack{ItemType::StonePickaxe});
m_recipes.emplace_back(std::array<u32, 9>{0, 2, 0, 0, ItemType::Stick, 0, 0, ItemType::Stick, 0}, ItemStack{ItemType::StoneShovel});
m_recipes.emplace_back(std::array<u32, 9>{0, 2, 0, 0, 2, 0, 0, ItemType::Stick, 0}, ItemStack{ItemType::StoneSword});
// m_recipes.emplace_back(std::array<u32, 9>{2, 2, 0, 2, ItemType::Stick, 0, 0, ItemType::Stick, 0}, ItemStack{ItemType::StoneAxe});
// m_recipes.emplace_back(std::array<u32, 9>{2, 2, 0, 0, ItemType::Stick, 0, 0, ItemType::Stick, 0}, ItemStack{ItemType::StoneHoe});
// m_recipes.emplace_back(std::array<u32, 9>{2, 2, 2, 0, ItemType::Stick, 0, 0, ItemType::Stick, 0}, ItemStack{ItemType::StonePickaxe});
// m_recipes.emplace_back(std::array<u32, 9>{0, 2, 0, 0, ItemType::Stick, 0, 0, ItemType::Stick, 0}, ItemStack{ItemType::StoneShovel});
// m_recipes.emplace_back(std::array<u32, 9>{0, 2, 0, 0, 2, 0, 0, ItemType::Stick, 0}, ItemStack{ItemType::StoneSword});
//
// m_recipes.emplace_back(std::array<u32, 9>{ItemType::Wood, 0, 0, 0, 0, 0, 0, 0, 0}, ItemStack{ItemType::Planks, 4}, true);
// m_recipes.emplace_back(std::array<u32, 9>{ItemType::Planks, ItemType::Planks, 0, 0, 0, 0, 0, 0, 0}, ItemStack{ItemType::Stick, 4}, true);
//
// m_recipes.emplace_back(std::array<u32, 9>{
// ItemType::Cobblestone, ItemType::Cobblestone, ItemType::Cobblestone,
// ItemType::Cobblestone, 0, ItemType::Cobblestone,
// ItemType::Cobblestone, ItemType::Cobblestone, ItemType::Cobblestone,
// }, ItemStack{ItemType::Furnace});
//
// // FIXME: This recipe will only for in the top-left corner
// // Find a way to handle recipe size
// m_recipes.emplace_back(std::array<u32, 9>{
// ItemType::Planks, ItemType::Planks, 0,
// ItemType::Planks, ItemType::Planks, 0,
// 0, 0, 0
// }, ItemStack{ItemType::Workbench});
m_recipes.emplace_back(std::array<u32, 9>{ItemType::Wood, 0, 0, 0, 0, 0, 0, 0, 0}, ItemStack{ItemType::Planks, 4}, true);
m_recipes.emplace_back(std::array<u32, 9>{ItemType::Planks, ItemType::Planks, 0, 0, 0, 0, 0, 0, 0}, ItemStack{ItemType::Stick, 4}, true);
m_recipes.emplace_back(std::vector<std::string>{
"##",
"#|",
" |"
},
std::map<char, std::vector<u32>>{
{'#', {ItemType::Cobblestone}},
{'|', {ItemType::Stick}},
},
ItemStack{ItemType::StoneAxe});
m_recipes.emplace_back(std::array<u32, 9>{
ItemType::Cobblestone, ItemType::Cobblestone, ItemType::Cobblestone,
ItemType::Cobblestone, 0, ItemType::Cobblestone,
ItemType::Cobblestone, ItemType::Cobblestone, ItemType::Cobblestone,
}, ItemStack{ItemType::Furnace});
m_recipes.emplace_back(std::vector<std::string>{
"##",
" |",
" |"
},
std::map<char, std::vector<u32>>{
{'#', {ItemType::Cobblestone}},
{'|', {ItemType::Stick}},
},
ItemStack{ItemType::StoneHoe});
// FIXME: This recipe will only for in the top-left corner
// Find a way to handle recipe size
m_recipes.emplace_back(std::array<u32, 9>{
ItemType::Planks, ItemType::Planks, 0,
ItemType::Planks, ItemType::Planks, 0,
0, 0, 0
}, ItemStack{ItemType::Workbench});
m_recipes.emplace_back(std::vector<std::string>{
"###",
" | ",
" | "
},
std::map<char, std::vector<u32>>{
{'#', {ItemType::Cobblestone}},
{'|', {ItemType::Stick}},
},
ItemStack{ItemType::StonePickaxe});
m_recipes.emplace_back(std::vector<std::string>{
"#",
"|",
"|"
},
std::map<char, std::vector<u32>>{
{'#', {ItemType::Cobblestone}},
{'|', {ItemType::Stick}},
},
ItemStack{ItemType::StoneShovel});
m_recipes.emplace_back(std::vector<std::string>{
"#",
"#",
"|"
},
std::map<char, std::vector<u32>>{
{'#', {ItemType::Cobblestone}},
{'|', {ItemType::Stick}},
},
ItemStack{ItemType::StoneSword});
m_recipes.emplace_back(std::vector<std::string>{
"#",
"#",
},
std::map<char, std::vector<u32>>{
{'#', {ItemType::Planks}},
},
ItemStack{ItemType::Stick, 4});
m_recipes.emplace_back(std::vector<std::string>{
"#",
},
std::map<char, std::vector<u32>>{
{'#', {ItemType::Wood}},
},
ItemStack{ItemType::Planks, 4});
m_recipes.emplace_back(std::vector<std::string>{
"##",
"##",
},
std::map<char, std::vector<u32>>{
{'#', {ItemType::Planks}},
},
ItemStack{ItemType::Workbench});
m_recipes.emplace_back(std::vector<std::string>{
"###",
"# #",
"###",
},
std::map<char, std::vector<u32>>{
{'#', {ItemType::Cobblestone}},
},
ItemStack{ItemType::Furnace});
}
const CraftingRecipe *Registry::getRecipe(const Inventory &inventory) const {

View File

@ -48,14 +48,17 @@ void Texture::loadFromFile(const std::string &filename) {
bind(this);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
GLenum format = (surface->format->BytesPerPixel == 4) ? GL_RGBA : GL_RGB;
glTexImage2D(GL_TEXTURE_2D, 0, format, m_width, m_height, 0, format, GL_UNSIGNED_BYTE, surface->pixels);
glGenerateMipmap(GL_TEXTURE_2D);
// FIXME: GL_NEAREST_MIPMAP_LINEAR causes blue shadows on trees, probably due to blending..
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 4);
bind(nullptr);
SDL_FreeSurface(surface);

View File

@ -14,7 +14,7 @@
#include "CraftingWidget.hpp"
#include "Registry.hpp"
CraftingWidget::CraftingWidget(Widget *parent) : Widget(parent) {
CraftingWidget::CraftingWidget(u16 width, u16 height, Widget *parent) : Widget(parent), m_craftingInventory{width, height} {
m_craftingInventoryWidget.init(m_craftingInventory);
m_craftingInventoryWidget.setPosition(29, 16, 0);
@ -27,10 +27,12 @@ void CraftingWidget::onMouseEvent(const SDL_Event &event, MouseItemWidget &mouse
m_craftingResultInventoryWidget.onMouseEvent(event, mouseItemWidget, true);
if (m_recipe && !m_craftingResultInventory.getStack(0, 0).item().id()) {
for (u8 i = 0 ; i < 9 ; ++i) {
const ItemStack &stack = m_craftingInventory.getStack(i % 3, i / 3);
if (stack.item().id()) {
m_craftingInventory.setStack(i % 3, i / 3, (stack.amount() > 1) ? stack.item().id() : 0, stack.amount() - 1);
for (u8 x = 0 ; x < m_craftingInventory.width() ; ++x) {
for (u8 y = 0 ; y < m_craftingInventory.height() ; ++y) {
const ItemStack &stack = m_craftingInventory.getStack(x, y);
if (stack.item().id()) {
m_craftingInventory.setStack(x, y, (stack.amount() > 1) ? stack.item().id() : 0, stack.amount() - 1);
}
}
}

View File

@ -27,7 +27,7 @@ PlayerInventoryWidget::PlayerInventoryWidget(Inventory &playerInventory, Invento
}
void PlayerInventoryWidget::onEvent(const SDL_Event &event) {
// m_craftingWidget.onMouseEvent(event, m_mouseItemWidget);
m_craftingWidget.onMouseEvent(event, m_mouseItemWidget);
m_playerInventoryWidget.onMouseEvent(event, m_mouseItemWidget);
m_hotbarInventoryWidget.onMouseEvent(event, m_mouseItemWidget);
@ -36,12 +36,12 @@ void PlayerInventoryWidget::onEvent(const SDL_Event &event) {
}
void PlayerInventoryWidget::update() {
// m_craftingWidget.update();
m_craftingWidget.update();
const ItemWidget *currentItemWidget = nullptr;
if ((currentItemWidget = m_playerInventoryWidget.currentItemWidget())
|| (currentItemWidget = m_hotbarInventoryWidget.currentItemWidget()))
// || (currentItemWidget = m_craftingWidget.currentItemWidget()))
|| (currentItemWidget = m_hotbarInventoryWidget.currentItemWidget())
|| (currentItemWidget = m_craftingWidget.currentItemWidget()))
m_mouseItemWidget.update(currentItemWidget);
else
m_mouseItemWidget.update(nullptr);
@ -52,7 +52,7 @@ void PlayerInventoryWidget::draw(RenderTarget &target, RenderStates states) cons
target.draw(m_background, states);
// target.draw(m_craftingWidget, states);
target.draw(m_craftingWidget, states);
target.draw(m_playerInventoryWidget, states);
target.draw(m_hotbarInventoryWidget, states);

View File

@ -0,0 +1,20 @@
/*
* =====================================================================================
*
* Filename: SmallCraftingWidget.cpp
*
* Description:
*
* Created: 27/06/2018 09:04:07
*
* Author: Quentin Bazin, <quent42340@gmail.com>
*
* =====================================================================================
*/
#include "SmallCraftingWidget.hpp"
SmallCraftingWidget::SmallCraftingWidget(Widget *parent) : CraftingWidget(2, 2, parent) {
m_craftingInventoryWidget.setPosition(97, 17, 0);
m_craftingResultInventoryWidget.setPosition(153, 27, 0);
}

View File

@ -11,41 +11,112 @@
*
* =====================================================================================
*/
#include "CraftingRecipe.hpp"
#include <algorithm>
CraftingRecipe::CraftingRecipe(const std::array<u32, 9> &recipe, const ItemStack &result, bool isShapeless) {
m_recipe = recipe;
#include "CraftingRecipe.hpp"
#include "Exception.hpp"
// FIXME: Try to handle recipes a bit more like minecraft
// - Pattern section | char[3][3]
// - Key section | map<char, vector<ItemType>>
// - Result section | ItemStack
CraftingRecipe::CraftingRecipe(const std::vector<std::string> &pattern, const std::map<char, std::vector<u32>> &keys, const ItemStack &result, bool isShapeless) {
m_pattern = pattern;
m_keys = keys;
m_result = result;
m_isShapeless = isShapeless;
}
bool CraftingRecipe::isMatching(const Inventory &inventory) const {
if (inventory.height() < m_pattern.size())
return false;
if (!m_isShapeless) {
for (u16 x = 0 ; x < 3 ; ++x) {
for (u16 y = 0 ; y < 3 ; ++y) {
if (m_recipe.at(x + y * 3) != inventory.getStack(x, y).item().id())
return false;
}
for (u16 y = 0 ; y < inventory.height() - m_pattern.size() + 1 ; ++y) {
if (inventory.width() < m_pattern.at(0).size())
return false;
for (u16 x = 0 ; x < inventory.width() - m_pattern.at(0).size() + 1 ; ++x)
if (checkMatch(inventory, x, y))
return true;
}
return true;
return false;
}
else {
std::array<bool, 9> match{false, false, false, false, false, false, false, false, false};
for (u32 id : m_recipe) {
bool matched = false;
for (u16 i = 0 ; i < 9 ; ++i) {
if (inventory.getStack(i % 3, i / 3).item().id() == id && !match[i]) {
match[i] = true;
matched = true;
break;
}
}
if (!matched)
return false;
}
return true;
// std::array<bool, 9> match{false, false, false, false, false, false, false, false, false};
// for (u32 id : m_recipe) {
// bool matched = false;
// for (u16 i = 0 ; i < 9 ; ++i) {
// if (inventory.getStack(i % 3, i / 3).item().id() == id && !match[i]) {
// match[i] = true;
// matched = true;
// break;
// }
// }
//
// if (!matched)
// return false;
// }
// return true;
return false; // FIXME
}
}
bool CraftingRecipe::checkMatch(const Inventory &inventory, int offsetX, int offsetY) const {
bool itemFound = false;
u16 y = 0;
for (y = 0 ; y < m_pattern.size() ; ++y) {
u16 x = 0;
for (x = 0 ; x < m_pattern[y].size() ; ++x) {
itemFound = false;
u32 inventoryItem = inventory.getStack(offsetX + x, offsetY + y).item().id();
if (m_pattern[y][x] == ' ') {
itemFound = !inventoryItem;
}
else {
auto it = m_keys.find(m_pattern[y][x]);
if (it == m_keys.end())
throw EXCEPTION("Recipe error: Invalid key char(", (int)m_pattern[y][x], ")'");
for (u32 item : it->second) {
if (item == inventoryItem)
itemFound = true;
}
}
if (!itemFound)
return false;
}
for (int x = offsetX - 1 ; x >= 0 ; --x) {
if (inventory.getStack(x, offsetY + y).item().id())
return false;
}
while (offsetX + x < inventory.width()) {
if (inventory.getStack(offsetX + x, offsetY + y).item().id())
return false;
++x;
}
}
for (int y = offsetY - 1 ; y >= 0 ; --y) {
for (int x = 0 ; x < inventory.width() ; ++x) {
if (inventory.getStack(x, y).item().id())
return false;
}
}
while (offsetY + y < inventory.height()) {
for (int x = 0 ; x < inventory.width() ; ++x) {
if (inventory.getStack(x, offsetY + y).item().id())
return false;
}
++y;
}
return true;
}

View File

@ -26,12 +26,12 @@
#include "PlayerInventoryWidget.hpp"
GameState::GameState() {
m_player.hotbarInventory().addStack(ItemType::Workbench, 1);
m_player.hotbarInventory().addStack(ItemType::Dirt, 64);
m_player.hotbarInventory().addStack(ItemType::Grass, 64);
m_player.hotbarInventory().addStack(ItemType::Stone, 64);
m_player.hotbarInventory().addStack(ItemType::Glass, 64);
m_player.hotbarInventory().addStack(ItemType::Glowstone, 64);
m_player.hotbarInventory().addStack(ItemType::Workbench, 1);
m_player.hotbarInventory().addStack(ItemType::Furnace, 1);
m_player.inventory().addStack(ItemType::Wood, 64);