Lua trees, ores, desert, review changes, merge

master
KdotJPG 2020-03-05 23:02:08 -05:00 committed by Quentin Bazin
parent 53bb1c14c0
commit 474cdd16c4
18 changed files with 603 additions and 230 deletions

View File

@ -1,3 +1,4 @@
The following people have contributed code to this project and hold the copyright on some portions (see the commit history for details):
* Pedro Gimeno Fortea (pgimeno) \<pgimeno@users.noreply.notabug.org\>
* Nikola Schordinger (DeatHunter) \<Discord: DH#9367\>
* Kurt Spencer (K.jpg) \<https://github.com/KdotJPG\>

View File

@ -49,6 +49,41 @@ Item &Registry::registerSerializedItem(sf::Packet &packet) {
return m_items.back();
}
Tree &Registry::registerTree(const std::string &stringID, const std::string &label) {
size_t id = m_trees.size();
m_treesID.emplace(stringID, id);
m_trees.emplace_back(id, stringID, label);
return m_trees.back();
}
Tree &Registry::registerSerializedTree(sf::Packet &packet) {
m_trees.emplace_back();
m_trees.back().deserialize(packet);
size_t id = m_trees.size() - 1;
m_treesID.emplace(m_trees.back().stringID(), id);
return m_trees.back();
}
Biome &Registry::registerBiome(const std::string &stringID, const std::string &label) {
size_t id = m_biomes.size();
m_biomesID.emplace(stringID, id);
m_biomes.emplace_back(id, stringID, label);
return m_biomes.back();
}
Biome &Registry::registerSerializedBiome(sf::Packet &packet) {
m_biomes.emplace_back();
m_biomes.back().deserialize(packet);
size_t id = m_biomes.size() - 1;
m_biomesID.emplace(m_biomes.back().stringID(), id);
return m_biomes.back();
}
const Block &Registry::getBlockFromStringID(const std::string &stringID) {
if (stringID.empty()) return getBlock(0);
auto it = m_blocksID.find(stringID);
@ -65,6 +100,24 @@ const Item &Registry::getItemFromStringID(const std::string &stringID) {
return getItem(it->second);
}
const Tree &Registry::getTreeFromStringID(const std::string &stringID) {
if (stringID.empty())
throw EXCEPTION("Trying to get tree from empty string ID.");
auto it = m_treesID.find(stringID);
if (it == m_treesID.end())
throw EXCEPTION("Unknown tree:", stringID);
return getTree(it->second);
}
const Biome &Registry::getBiomeFromStringID(const std::string &stringID) {
if (stringID.empty())
throw EXCEPTION("Trying to get tree from empty string ID.");
auto it = m_biomesID.find(stringID);
if (it == m_biomesID.end())
throw EXCEPTION("Unknown tree:", stringID);
return getBiome(it->second);
}
const Recipe *Registry::getRecipe(const Inventory &inventory) const {
for (auto &recipe : m_recipes) {
if (recipe->isMatching(inventory))
@ -87,8 +140,12 @@ void Registry::serialize(sf::Packet &packet) const {
<< *it;
}
for (auto &it : m_trees) {
packet << u8(DataType::Tree) << it;
}
for (auto &it : m_biomes) {
packet << u8(DataType::Biome) << *it;
packet << u8(DataType::Biome) << it;
}
}
@ -109,7 +166,10 @@ void Registry::deserialize(sf::Packet &packet) {
registerRecipe<SmeltingRecipe>()->deserialize(packet);
}
else if (type == u8(DataType::Biome)) {
registerSerializedBiome<Biome>(packet);
registerSerializedBiome(packet);
}
else if (type == u8(DataType::Tree)) {
registerSerializedTree(packet);
}
}
}

View File

@ -31,11 +31,12 @@
#include <unordered_map>
#include <vector>
#include "Biome.hpp"
#include "Block.hpp"
#include "Item.hpp"
#include "Network.hpp"
#include "Recipe.hpp"
#include "Biome.hpp"
#include "Tree.hpp"
class Registry : public ISerializable {
public:
@ -67,41 +68,31 @@ class Registry : public ISerializable {
return m_recipes.back().get();
}
template<typename T>
auto registerBiome(const std::string &stringID, const std::string &label) -> typename std::enable_if<std::is_base_of<Biome, T>::value, T&>::type {
size_t id = m_biomes.size();
m_biomesID.emplace(stringID, id);
m_biomes.emplace_back(std::make_unique<T>(id, stringID, label));
return *static_cast<T*>(m_biomes.back().get());
}
template<typename T>
auto registerSerializedBiome(sf::Packet &packet) -> typename std::enable_if<std::is_base_of<Biome, T>::value, T&>::type {
m_biomes.emplace_back(std::make_unique<T>());
m_biomes.back()->deserialize(packet);
size_t id = m_biomes.size() - 1;
m_biomesID.emplace(m_biomes.back()->stringID(), id);
return *static_cast<T*>(m_biomes.back().get());
}
Tree &registerTree(const std::string &stringID, const std::string &label);
Tree &registerSerializedTree(sf::Packet &packet);
Biome &registerBiome(const std::string &stringID, const std::string &label);
Biome &registerSerializedBiome(sf::Packet &packet);
const Block &getBlock(std::size_t id) const { return *m_blocks.at(id).get(); }
const Item &getItem(std::size_t id) const { return m_items.at(id); }
const Block &getBlockFromStringID(const std::string &stringID);
const Item &getItemFromStringID(const std::string &stringID);
const Tree &getTreeFromStringID(const std::string &stringID);
const Biome &getBiomeFromStringID(const std::string &stringID);
const Recipe *getRecipe(const Inventory &inventory) const;
const Biome &getBiome(std::size_t id) const { return *m_biomes.at(id).get(); }
const Tree &getTree(u32 id) const { return m_trees.at(id); }
const Biome &getBiome(u32 id) const { return m_biomes.at(id); }
void serialize(sf::Packet &packet) const override;
void deserialize(sf::Packet &packet) override;
const std::vector<std::unique_ptr<Block>> &blocks() const { return m_blocks; }
const std::vector<Item> &items() const { return m_items; }
const std::vector<std::unique_ptr<Biome>> &biomes() const { return m_biomes; }
const std::vector<Tree> &trees() const { return m_trees; }
const std::vector<Biome> &biomes() const { return m_biomes; }
static Registry &getInstance() { return *s_instance; }
static void setInstance(Registry &instance) { s_instance = &instance; }
@ -112,10 +103,12 @@ class Registry : public ISerializable {
std::vector<std::unique_ptr<Block>> m_blocks;
std::vector<Item> m_items;
std::vector<std::unique_ptr<Recipe>> m_recipes;
std::vector<std::unique_ptr<Biome>> m_biomes;
std::vector<Tree> m_trees;
std::vector<Biome> m_biomes;
std::unordered_map<std::string, u32> m_blocksID;
std::unordered_map<std::string, u32> m_itemsID;
std::unordered_map<std::string, u32> m_treesID;
std::unordered_map<std::string, u32> m_biomesID;
enum class DataType {
@ -123,6 +116,7 @@ class Registry : public ISerializable {
Item,
CraftingRecipe,
SmeltingRecipe,
Tree,
Biome
};
};

View File

@ -24,20 +24,21 @@
*
* =====================================================================================
*/
#include "Biome.hpp"
#include <SFML/Network/Packet.hpp>
#include "Biome.hpp"
#include "NetworkUtils.hpp"
Biome::Biome(u32 id, const std::string &stringID, const std::string &label) {
Biome::Biome(u16 id, const std::string &stringID, const std::string &label) {
m_id = id;
std::string m_stringID;
std::string m_label;
m_stringID = stringID;
m_label = label;
}
void Biome::serialize(sf::Packet &packet) const {
packet << u32(m_id) << m_stringID << m_label << m_params << m_topBlock << m_groundBlock << m_beachBlock << m_liquidBlock;
packet << m_id << m_stringID << m_label << m_params << m_topBlockID << m_groundBlockID << m_deepBlockID << m_beachBlockID << m_liquidBlockID << m_flora << m_ores << m_trees;
}
void Biome::deserialize(sf::Packet &packet) {
packet >> m_id >> m_stringID >> m_label >> m_params >> m_topBlock >> m_groundBlock >> m_beachBlock >> m_liquidBlock;
}
packet >> m_id >> m_stringID >> m_label >> m_params >> m_topBlockID >> m_groundBlockID >> m_deepBlockID >> m_beachBlockID >> m_liquidBlockID >> m_flora >> m_ores >> m_trees;
}

View File

@ -29,46 +29,77 @@
#include <string>
#include <vector>
#include <gk/core/IntTypes.hpp>
#include "ISerializable.hpp"
#include "PlacementEntry.hpp"
class Biome : public ISerializable {
public:
Biome() = default;
Biome(u32 id, const std::string &stringID, const std::string &label);
~Biome() = default;
public:
Biome() = default;
Biome(u16 id, const std::string &stringID, const std::string &label);
void serialize(sf::Packet &packet) const override;
void deserialize(sf::Packet &packet) override;
void serialize(sf::Packet &packet) const override;
void deserialize(sf::Packet &packet) override;
const std::string &stringID() const { return m_stringID; }
const std::string &label() const { return m_label; }
void setLabel(const std::string &label) { m_label = label; }
u16 id() const { return m_id; }
const std::string &stringID() const { return m_stringID; }
const std::string &label() const { return m_label; }
void setLabel(const std::string &label) { m_label = label; }
std::vector<double> getParams() { return m_params; }
const u16 getTopBlock() const { return m_topBlock; }
const u16 getGroundBlock() const { return m_groundBlock; }
const u16 getBeachBlock() const { return m_beachBlock; }
const u16 getLiquidBlock() const { return m_liquidBlock; }
const std::vector<double> &getParams() const { return m_params; }
void setParams(std::vector<double> value) { m_params = value; }
void setTopBlock(u16 value) { m_topBlock = value; }
void setGroundBlock(u16 value) { m_groundBlock = value; }
void setBeachBlock(u16 value) { m_beachBlock = value; }
void setLiquidBlock(u16 value) { m_liquidBlock = value; }
u16 getTopBlockID() const { return m_topBlockID; }
u16 getGroundBlockID() const { return m_groundBlockID; }
u16 getDeepBlockID() const { return m_deepBlockID; }
u16 getBeachBlockID() const { return m_beachBlockID; }
u16 getLiquidBlockID() const { return m_liquidBlockID; }
const std::vector<PlacementEntry::Flora> &getFlora() const { return m_flora; }
const std::vector<PlacementEntry::Ore> &getOres() const { return m_ores; }
const std::vector<PlacementEntry::Tree> &getTrees() const { return m_trees; }
private:
u32 m_id;
std::string m_stringID;
std::string m_label;
void setParams(std::vector<double> &value) { m_params = value; }
// TODO something to distinguish the worldtype of biome
std::vector<double> m_params;
u16 m_topBlock;
u16 m_groundBlock;
u16 m_beachBlock;
u16 m_liquidBlock;
void setTopBlockID(u16 value) { m_topBlockID = value; }
void setGroundBlockID(u16 value) { m_groundBlockID = value; }
void setDeepBlockID(u16 value) { m_deepBlockID = value; }
void setBeachBlockID(u16 value) { m_beachBlockID = value; }
void setLiquidBlockID(u16 value) { m_liquidBlockID = value; }
void setFlora(std::vector<PlacementEntry::Flora> &value) { m_flora = value; }
void setOres(std::vector<PlacementEntry::Ore> &value) { m_ores = value; }
void setTrees(std::vector<PlacementEntry::Tree> &value) { m_trees = value; }
PlacementEntry::Flora &addFlora() {
m_flora.emplace_back();
return m_flora.back();
}
PlacementEntry::Ore &addOre() {
m_ores.emplace_back();
return m_ores.back();
}
PlacementEntry::Tree &addTree() {
m_trees.emplace_back();
return m_trees.back();
}
private:
u16 m_id;
std::string m_stringID;
std::string m_label;
// TODO something to distinguish the worldtype of biome
std::vector<double> m_params;
u16 m_topBlockID;
u16 m_groundBlockID;
u16 m_deepBlockID;
u16 m_beachBlockID;
u16 m_liquidBlockID;
std::vector<PlacementEntry::Flora> m_flora;
std::vector<PlacementEntry::Ore> m_ores;
std::vector<PlacementEntry::Tree> m_trees;
};
#endif // BIOME_HPP_
#endif // BIOME_HPP_

View File

@ -0,0 +1,80 @@
/*
* =====================================================================================
*
* OpenMiner
*
* Copyright (C) 2018-2020 Unarelith, Quentin Bazin <openminer@unarelith.net>
* Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md)
*
* This file is part of OpenMiner.
*
* OpenMiner is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* OpenMiner 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with OpenMiner; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* =====================================================================================
*/
#ifndef PLACEMENTENTRY_HPP_
#define PLACEMENTENTRY_HPP_
#include <gk/core/IntTypes.hpp>
#include <SFML/Network/Packet.hpp>
#include "ISerializable.hpp"
namespace PlacementEntry {
struct Flora : public ISerializable {
u16 blockID;
u16 spawnsOnBlockID;
double probability;
void serialize(sf::Packet &packet) const override {
packet << blockID << spawnsOnBlockID << probability;
}
void deserialize(sf::Packet &packet) override {
packet >> blockID >> spawnsOnBlockID >> probability;
}
};
struct Ore : public ISerializable {
u16 blockID;
double probability;
double size;
void serialize(sf::Packet &packet) const override {
packet << blockID << probability << size;
}
void deserialize(sf::Packet &packet) override {
packet >> blockID >> probability >> size;
}
};
struct Tree : public ISerializable {
u16 treeID;
double probability;
void serialize(sf::Packet &packet) const override {
packet << treeID << probability;
}
void deserialize(sf::Packet &packet) override {
packet >> treeID >> probability;
}
};
} // namespace PlacementEntry
#endif // PLACEMENTENTRY_HPP_

View File

@ -0,0 +1,44 @@
/*
* =====================================================================================
*
* OpenMiner
*
* Copyright (C) 2018-2020 Unarelith, Quentin Bazin <openminer@unarelith.net>
* Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md)
*
* This file is part of OpenMiner.
*
* OpenMiner is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* OpenMiner 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with OpenMiner; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* =====================================================================================
*/
#include <SFML/Network/Packet.hpp>
#include "Tree.hpp"
#include "NetworkUtils.hpp"
Tree::Tree(u16 id, const std::string &stringID, const std::string &label) {
m_id = id;
m_stringID = stringID;
m_label = label;
}
void Tree::serialize(sf::Packet &packet) const {
packet << m_id << m_stringID << m_label << m_logBlockID << m_leavesBlockID;
}
void Tree::deserialize(sf::Packet &packet) {
packet >> m_id >> m_stringID >> m_label >> m_logBlockID >> m_leavesBlockID;
}

View File

@ -0,0 +1,65 @@
/*
* =====================================================================================
*
* OpenMiner
*
* Copyright (C) 2018-2020 Unarelith, Quentin Bazin <openminer@unarelith.net>
* Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md)
*
* This file is part of OpenMiner.
*
* OpenMiner is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* OpenMiner 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with OpenMiner; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* =====================================================================================
*/
#ifndef TREE_HPP_
#define TREE_HPP_
#include <string>
#include <gk/core/IntTypes.hpp>
#include "ISerializable.hpp"
class Tree : public ISerializable
{
public:
Tree() = default;
Tree(u16 id, const std::string &stringID, const std::string &label);
void serialize(sf::Packet &packet) const override;
void deserialize(sf::Packet &packet) override;
u16 id() const { return m_id; }
const std::string &stringID() const { return m_stringID; }
const std::string &label() const { return m_label; }
void setLabel(const std::string &label) { m_label = label; }
u16 getLogBlockID() const { return m_logBlockID; }
u16 getLeavesBlockID() const { return m_leavesBlockID; }
void setLogBlockID(u16 value) { m_logBlockID = value; }
void setLeavesBlockID(u16 value) { m_leavesBlockID = value; }
private:
u16 m_id;
std::string m_stringID;
std::string m_label;
u16 m_logBlockID;
u16 m_leavesBlockID;
};
#endif // TREE_HPP_

View File

@ -26,63 +26,89 @@
--
mod:biome {
id = "default:grassland",
name = "Grassland",
id = "grassland",
name = "Grassland",
params = {
temperature = -0.6,
precipitation = 0.4
},
params = {
temperature = 0.5,
precipitation = 0.3
},
top_block = "default:grass",
ground_block = "default:dirt",
beach_block = "default:sand",
liquid_block = "default:water",
flora = {
{
block = "default:tallgrass",
spawns_on = "default:grass",
probability = 0.25
},
{
block = "default:flower",
spawns_on = "default:grass",
probability = 0.1
}
}
top_block = "default:grass",
ground_block = "default:dirt",
deep_block = "default:stone",
beach_block = "default:sand",
liquid_block = "default:water",
trees = {
{
type = "default:oak",
probability = 0.01041666667
}
},
-- Currently, all ores are defined per-biome. In a later update,
-- we could see ores defined mostly by worldtype, and some extras
-- added per biome. In addition, given the large world depth, we
-- could decide whether ores at a point very far down should
-- still depend on the surface biome, or only on the worldtype.
ores = {
{
block = "default:iron_ore",
probability = 0.0003,
size = 8
}
},
flora = {
{
block = "default:tallgrass",
spawns_on = "default:grass",
probability = 0.25
},
{
block = "default:dandelion",
spawns_on = "default:grass",
probability = 0.1
}
}
}
mod:biome {
id = "default:mountain",
name = "Mountain",
id = "mountain",
name = "Mountain",
params = {
temperature = 0.5,
precipitation = 0.3
},
params = {
temperature = -0.6,
precipitation = 0.4
},
top_block = "default:stone",
ground_block = "default:stone",
beach_block = "default:stone",
liquid_block = "default:water",
flora = { }
top_block = "default:stone",
ground_block = "default:stone",
deep_block = "default:stone",
beach_block = "default:stone",
liquid_block = "default:water",
trees = {
{
type = "default:oak",
probability = 0.00390625
}
}
}
mod:biome {
id = "default:barren",
name = "Barren",
id = "desert",
name = "Desert",
params = {
temperature = 0.3,
precipitation = -0.7
},
params = {
temperature = -0.3,
precipitation = -0.7
},
top_block = "default:dirt",
ground_block = "default:dirt",
beach_block = "default:dirt",
liquid_block = "default:water",
flora = { }
}
top_block = "default:sand",
ground_block = "default:sand",
deep_block = "default:stone",
beach_block = "default:sand",
liquid_block = "default:water"
}

View File

@ -29,21 +29,9 @@ mod = LuaMod.new("default")
dofile("mods/default/blocks.lua")
dofile("mods/default/items.lua")
dofile("mods/default/recipes.lua")
dofile("mods/default/trees.lua")
dofile("mods/default/biomes.lua")
openminer:world():terrain_generator():set_blocks({
dirt = "default:dirt",
grass = "default:grass",
stone = "default:stone",
log = "default:oak_wood",
leaves = "default:oak_leaves",
flower = "default:dandelion",
water = "default:water",
sand = "default:sand",
tallgrass = "default:tallgrass",
iron_ore = "default:iron_ore"
})
function init(player)
local player_inv = player:inventory()

33
mods/default/trees.lua Normal file
View File

@ -0,0 +1,33 @@
--
-- =====================================================================================
--
-- OpenMiner
--
-- Copyright (C) 2018-2020 Unarelith, Quentin Bazin <openminer@unarelith.net>
-- Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md)
--
-- This file is part of OpenMiner.
--
-- OpenMiner is free software; you can redistribute it and/or
-- modify it under the terms of the GNU Lesser General Public
-- License as published by the Free Software Foundation; either
-- version 2.1 of the License, or (at your option) any later version.
--
-- OpenMiner 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
-- Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public License
-- along with OpenMiner; if not, write to the Free Software Foundation, Inc.,
-- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
--
-- =====================================================================================
--
mod:tree {
id = "oak",
name = "Oak",
log_block = "default:oak_wood",
leaves_block = "default:oak_leaves"
}

View File

@ -26,12 +26,14 @@
*/
#include <map>
#include "Biome.hpp"
#include "CraftingRecipe.hpp"
#include "LuaMod.hpp"
#include "PlacementEntry.hpp"
#include "Registry.hpp"
#include "ServerBlock.hpp"
#include "Biome.hpp"
#include "SmeltingRecipe.hpp"
#include "Tree.hpp"
void LuaMod::registerBlock(const sol::table &table) {
TilesDef tiles;
@ -143,13 +145,23 @@ void LuaMod::registerSmeltingRecipe(const sol::table &table) {
Registry::getInstance().registerRecipe<SmeltingRecipe>(input, output);
}
void LuaMod::registerTree(const sol::table &table) {
std::string stringID = m_id + ":" + table["id"].get<std::string>();
std::string label = table["name"].get<std::string>();
Tree &tree = Registry::getInstance().registerTree(stringID, label);
tree.setLogBlockID(Registry::getInstance().getBlockFromStringID(table["log_block"]).id());
tree.setLeavesBlockID(Registry::getInstance().getBlockFromStringID(table["leaves_block"]).id());
}
void LuaMod::registerBiome(const sol::table &table) {
std::string stringID = m_id + ":" + table["id"].get<std::string>();
std::string label = table["name"].get<std::string>();
Biome &biome = Registry::getInstance().registerBiome<Biome>(stringID, label);
Biome &biome = Registry::getInstance().registerBiome(stringID, label);
// TODO eventually a WorldType could have a list of biome parameter names in order, and we could use those as the ordered keys.
// TODO eventually a WorldType could have a list of biome parameter names in order,
// and we could use those as the ordered keys.
// Currently hardcoding "temperature" and "precipitation" to get something functional.
size_t nBiomeParams = 2;
std::vector<double> params(nBiomeParams);
@ -157,10 +169,46 @@ void LuaMod::registerBiome(const sol::table &table) {
params[1] = table["params"]["precipitation"];
biome.setParams(params);
biome.setTopBlock(Registry::getInstance().getBlockFromStringID(table["top_block"]).id());
biome.setGroundBlock(Registry::getInstance().getBlockFromStringID(table["ground_block"]).id());
biome.setBeachBlock(Registry::getInstance().getBlockFromStringID(table["beach_block"]).id());
biome.setLiquidBlock(Registry::getInstance().getBlockFromStringID(table["liquid_block"]).id());
biome.setTopBlockID(Registry::getInstance().getBlockFromStringID(table["top_block"]).id());
biome.setGroundBlockID(Registry::getInstance().getBlockFromStringID(table["ground_block"]).id());
biome.setDeepBlockID(Registry::getInstance().getBlockFromStringID(table["deep_block"]).id());
biome.setBeachBlockID(Registry::getInstance().getBlockFromStringID(table["beach_block"]).id());
biome.setLiquidBlockID(Registry::getInstance().getBlockFromStringID(table["liquid_block"]).id());
sol::optional<sol::table> treeDefinitions = table["trees"];
if (treeDefinitions != sol::nullopt) {
for (const auto &it : treeDefinitions.value()) {
sol::table treeDefinition = it.second.as<sol::table>();
PlacementEntry::Tree &treeEntry = biome.addTree();
treeEntry.treeID = Registry::getInstance().getTreeFromStringID(treeDefinition["type"]).id();
treeEntry.probability = treeDefinition["probability"];
}
}
sol::optional<sol::table> floraDefinitions = table["flora"];
if (floraDefinitions != sol::nullopt) {
for (const auto &it : floraDefinitions.value()) {
sol::table floraDefinition = it.second.as<sol::table>();
PlacementEntry::Flora &floraEntry = biome.addFlora();
floraEntry.blockID = Registry::getInstance().getBlockFromStringID(floraDefinition["block"]).id();
floraEntry.spawnsOnBlockID = Registry::getInstance().getBlockFromStringID(floraDefinition["spawns_on"]).id();
floraEntry.probability = floraDefinition["probability"];
}
}
sol::optional<sol::table> oreDefinitions = table["ore"];
if (oreDefinitions != sol::nullopt) {
for (const auto &it : oreDefinitions.value()) {
sol::table oreDefinition = it.second.as<sol::table>();
PlacementEntry::Ore &oreEntry = biome.addOre();
oreEntry.blockID = Registry::getInstance().getBlockFromStringID(oreDefinition["block"]).id();
oreEntry.probability = oreDefinition["probability"];
oreEntry.size = oreDefinition["size"];
}
}
}
void LuaMod::initUsertype(sol::state &lua) {
@ -168,10 +216,11 @@ void LuaMod::initUsertype(sol::state &lua) {
sol::constructors<LuaMod(std::string)>(),
"id", &LuaMod::id,
"block", &LuaMod::registerBlock,
"biome", &LuaMod::registerBiome,
"item", &LuaMod::registerItem,
"crafting_recipe", &LuaMod::registerCraftingRecipe,
"smelting_recipe", &LuaMod::registerSmeltingRecipe
"smelting_recipe", &LuaMod::registerSmeltingRecipe,
"tree", &LuaMod::registerTree,
"biome", &LuaMod::registerBiome
);
}

View File

@ -38,6 +38,7 @@ class LuaMod {
void registerItem(const sol::table &table);
void registerCraftingRecipe(const sol::table &table);
void registerSmeltingRecipe(const sol::table &table);
void registerTree(const sol::table &table);
void registerBiome(const sol::table &table);
const std::string &id() const { return m_id; }

View File

@ -67,10 +67,6 @@ void ScriptEngine::initUsertypes() {
"terrain_generator", &ServerWorld::terrainGenerator
);
m_lua.new_usertype<TerrainGenerator>("TerrainGenerator",
"set_blocks", &TerrainGenerator::setBlocksFromLuaTable
);
m_lua.new_usertype<Chunk>("Chunk",
"get_block", &Chunk::getBlock,
"get_data", &Chunk::getData,

View File

@ -24,32 +24,25 @@
*
* =====================================================================================
*/
#include "TerrainBiomeSampler.hpp"
#include "Registry.hpp"
#include "Biome.hpp"
#include "Registry.hpp"
#include "TerrainBiomeSampler.hpp"
TerrainBiomeSampler::TerrainBiomeSampler() {
paramNoisesPtr = new FastNoise[nBiomeParams];
for (u8 i = 0; i < nBiomeParams; i++) {
paramNoisesPtr[i].SetNoiseType(FastNoise::NoiseType::SimplexFractal);
paramNoisesPtr[i].SetFrequency(1 / 800.0f);
paramNoisesPtr[i].SetFractalOctaves(5);
paramNoisesPtr[i].SetSeed(i);
m_paramNoises = std::vector<FastNoise>(biomeParamCount);
for (u8 i = 0; i < biomeParamCount; i++) {
m_paramNoises[i].SetNoiseType(FastNoise::NoiseType::SimplexFractal);
m_paramNoises[i].SetFrequency(1 / 800.0f);
m_paramNoises[i].SetFractalOctaves(5);
m_paramNoises[i].SetSeed(i);
}
}
TerrainBiomeSampler::~TerrainBiomeSampler() {
delete paramNoisesPtr;
}
u16 TerrainBiomeSampler::getBiomeIndexAt(s32 x, s32 y) const {
// Compute noise instances
double* biomeParamsPtr = new double[nBiomeParams];
for (u8 i = 0; i < nBiomeParams; i++) {
biomeParamsPtr[i] = paramNoisesPtr[i].GetNoise(x, y);
std::vector<double> biomeParams(biomeParamCount);
for (u8 i = 0; i < biomeParamCount; i++) {
biomeParams[i] = m_paramNoises[i].GetNoise(x, y);
}
// TODO with a lot of biomes, perhaps we want an R-Tree or similar, instead of a long loop.
@ -58,12 +51,12 @@ u16 TerrainBiomeSampler::getBiomeIndexAt(s32 x, s32 y) const {
// True temp/precip values can then be re-interpolated out from the Voronoi diagram using a neighborhood figure "kernel".
// TODO with multiple worldtypes added, need to only consider biomes in one worldtype.
u16 decidedBiomeIndex = 0;
double decidedBiomeDeviation = 0xFFFF;
double decidedBiomeDeviation = INFINITY;
u16 j = 0;
for (auto &biome : Registry::getInstance().biomes()) {
double deviation = 0;
for (int i = 0; i < nBiomeParams; i++) {
double dp = biomeParamsPtr[i] - biome.get()->getParams()[i];
for (int i = 0; i < biomeParamCount; i++) {
double dp = biomeParams[i] - biome.getParams()[i];
deviation += dp * dp;
}
if (deviation < decidedBiomeDeviation) {
@ -73,6 +66,5 @@ u16 TerrainBiomeSampler::getBiomeIndexAt(s32 x, s32 y) const {
j++;
}
delete biomeParamsPtr;
return decidedBiomeIndex;
}
}

View File

@ -27,21 +27,23 @@
#ifndef TERRAINBIOMESAMPLER_HPP_
#define TERRAINBIOMESAMPLER_HPP_
#include <vector>
#include <gk/core/IntTypes.hpp>
#include "FastNoise.hpp"
#include "Biome.hpp"
#include "FastNoise.hpp"
class TerrainBiomeSampler {
public:
TerrainBiomeSampler(); // TODO should eventually take a worldtype
~TerrainBiomeSampler();
public:
TerrainBiomeSampler(); // TODO should eventually take a worldtype
u16 getBiomeIndexAt(s32 x, s32 y) const;
//std::vector<WeightedIndex> getWeightedBiomeIndicesAt(double x, double y);
u16 getBiomeIndexAt(s32 x, s32 y) const;
//std::vector<WeightedIndex> getWeightedBiomeIndicesAt(double x, double y);
private:
u8 nBiomeParams = 2; // TODO should be defined in the worldtype
FastNoise* paramNoisesPtr;
private:
static const u8 biomeParamCount = 2; // TODO if kept, should be defined in the worldtype, dynamically.
std::vector<FastNoise> m_paramNoises;
};
#endif // TERRAINBIOMESAMPLER_HPP_
#endif // TERRAINBIOMESAMPLER_HPP_

View File

@ -40,19 +40,6 @@ void TerrainGenerator::generate(ServerChunk &chunk) const {
fastNoiseGeneration(chunk);
}
void TerrainGenerator::setBlocksFromLuaTable(const sol::table &table) {
m_dirtBlockID = Registry::getInstance().getBlockFromStringID(table["dirt"].get<std::string>()).id();
m_grassBlockID = Registry::getInstance().getBlockFromStringID(table["grass"].get<std::string>()).id();
m_stoneBlockID = Registry::getInstance().getBlockFromStringID(table["stone"].get<std::string>()).id();
m_logBlockID = Registry::getInstance().getBlockFromStringID(table["log"].get<std::string>()).id();
m_leavesBlockID = Registry::getInstance().getBlockFromStringID(table["leaves"].get<std::string>()).id();
m_flowerBlockID = Registry::getInstance().getBlockFromStringID(table["flower"].get<std::string>()).id();
m_waterBlockID = Registry::getInstance().getBlockFromStringID(table["water"].get<std::string>()).id();
m_sandBlockID = Registry::getInstance().getBlockFromStringID(table["sand"].get<std::string>()).id();
m_tallgrassBlockID = Registry::getInstance().getBlockFromStringID(table["tallgrass"].get<std::string>()).id();
m_ironOreBlockID = Registry::getInstance().getBlockFromStringID(table["iron_ore"].get<std::string>()).id();
}
void TerrainGenerator::fastNoiseGeneration(ServerChunk &chunk) const {
FastNoise noise;
noise.SetNoiseType(FastNoise::NoiseType::SimplexFractal);
@ -65,7 +52,7 @@ void TerrainGenerator::fastNoiseGeneration(ServerChunk &chunk) const {
for(int x = 0 ; x < CHUNK_WIDTH ; x++) {
u16 biomeIndex = biomeSampler.getBiomeIndexAt(x + chunk.x() * CHUNK_WIDTH, y + chunk.y() * CHUNK_DEPTH);
auto &biome = Registry::getInstance().getBiome(biomeIndex);
const Biome &biome = Registry::getInstance().getBiome(biomeIndex);
// Land height
double n = noise.GetNoise(-x - chunk.x() * CHUNK_WIDTH, y + chunk.y() * CHUNK_DEPTH);
@ -78,57 +65,92 @@ void TerrainGenerator::fastNoiseGeneration(ServerChunk &chunk) const {
for(int z = 0 ; z < CHUNK_HEIGHT ; z++) {
// Are we above "ground" level?
if(z + chunk.z() * CHUNK_HEIGHT > h) {
// If we are not yet up to sea level, fill with water blocks
if(z + chunk.z() * CHUNK_HEIGHT < SEALEVEL) {
chunk.setBlockRaw(x, y, z, m_waterBlockID);
if (z + chunk.z() * CHUNK_HEIGHT < SEALEVEL) {
chunk.setBlockRaw(x, y, z, biome.getLiquidBlockID());
}
// Otherwise we are in the air, so try to make a tree
else if(chunk.getBlock(x, y, z - 1) == m_grassBlockID && (rand() % 64) == 0 && n < 4) {
// Trunk
int h = (rand() & 3) + 3;
for(int i = 0 ; i < h ; i++) {
chunk.setBlockRaw(x, y, z + i, m_logBlockID);
}
// Otherwise we are in the air
else {
bool placedTree = false;
if (chunk.getBlock(x, y, z - 1) == biome.getTopBlockID()) {
for (const PlacementEntry::Tree &treePlacement : biome.getTrees()) {
if (rand() > RAND_MAX * treePlacement.probability) continue;
const Tree &tree = Registry::getInstance().getTree(treePlacement.treeID);
// Leaves
for(int iz = -3 ; iz <= 3 ; iz++) {
for(int iy = -3 ; iy <= 3 ; iy++) {
for(int ix = -3 ; ix <= 3 ; ix++) {
if(ix * ix + iy * iy + iz * iz < 8 + (rand() & 1) && !chunk.getBlock(x + ix, y + iy, z + h + iz)) {
chunk.setBlockRaw(x + ix, y + iy, z + h + iz, m_leavesBlockID);
// Trunk
int h = (rand() & 3) + 3;
for (int i = 0; i < h; i++) {
chunk.setBlockRaw(x, y, z + i, tree.getLogBlockID());
}
// FIXME: This is a temporary fix for the second part of #41
chunk.lightmap().setSunlight(x + ix, y + iy, z + h + iz, 0);
// Leaves
for (int iz = -3; iz <= 3; iz++) {
for (int iy = -3; iy <= 3; iy++) {
for (int ix = -3; ix <= 3; ix++) {
if (ix * ix + iy * iy + iz * iz < 8 + (rand() & 1) && !chunk.getBlock(x + ix, y + iy, z + h + iz)) {
chunk.setBlockRaw(x + ix, y + iy, z + h + iz, tree.getLeavesBlockID());
// FIXME: This is a temporary fix for the second part of #41
chunk.lightmap().setSunlight(x + ix, y + iy, z + h + iz, 0);
}
}
}
}
placedTree = true;
break;
}
}
// Otherwise try to place flora.
if (!placedTree) {
bool placedFlora = false;
for (const PlacementEntry::Flora &flora : biome.getFlora()) {
if (chunk.getBlock(x, y, z - 1) != flora.spawnsOnBlockID) continue;
if (rand() > RAND_MAX * flora.probability) continue;
chunk.setBlockRaw(x, y, z, flora.blockID);
placedFlora = true;
break;
}
// Otherwise set sunlight.
if (!placedFlora && z == CHUNK_HEIGHT - 1) {
chunk.lightmap().addSunlight(x, y, z, 15);
}
}
}
// Or tallgrass
else if(chunk.getBlock(x, y, z - 1) == m_grassBlockID && (rand() % 32) == 0) {
chunk.setBlockRaw(x, y, z, m_tallgrassBlockID);
}
// Or a flower
else if(chunk.getBlock(x, y, z - 1) == m_grassBlockID && (rand() & 0xff) == 0) {
chunk.setBlockRaw(x, y, z, m_flowerBlockID);
}
// If we are on the top block of the chunk, add sunlight
else if (z == CHUNK_HEIGHT - 1) {
chunk.lightmap().addSunlight(x, y, z, 15);
}
}
else {
if (z + chunk.z() * CHUNK_HEIGHT >= h - 1 && z + chunk.z() * CHUNK_HEIGHT > SEALEVEL - 1)
chunk.setBlockRaw(x, y, z, biome.getTopBlock());
chunk.setBlockRaw(x, y, z, biome.getTopBlockID());
else if (z + chunk.z() * CHUNK_HEIGHT <= SEALEVEL - 1 && h < SEALEVEL && z + chunk.z() * CHUNK_HEIGHT > h - 3)
chunk.setBlockRaw(x, y, z, biome.getBeachBlock());
chunk.setBlockRaw(x, y, z, biome.getBeachBlockID());
else if (z + chunk.z() * CHUNK_HEIGHT > h - 3)
chunk.setBlockRaw(x, y, z, biome.getGroundBlock());
chunk.setBlockRaw(x, y, z, biome.getGroundBlockID());
else
chunk.setBlockRaw(x, y, z, m_stoneBlockID);
chunk.setBlockRaw(x, y, z, biome.getDeepBlockID());
if ((rand() % 4096) == 0)
oreFloodFill(chunk, x, y, z, m_stoneBlockID, m_ironOreBlockID, 2);
// Populate ores.
// TODO: Like trees, ores should be able to seamlessly cross chunk boundaries.
// This could be achieved either by setting up a generation pipeline with stages,
// processing neighboring chunks' ores every time, or generating them with noise.
for (const PlacementEntry::Ore &ore : biome.getOres()) {
if (rand() > RAND_MAX * ore.probability) continue;
oreFloodFill(chunk, x, y, z, biome.getDeepBlockID(), ore.blockID, 2);
break;
}
// Caves
float n2 = noise2d(-(x + chunk.x() * CHUNK_WIDTH) / 256.0, (y + chunk.y() * CHUNK_DEPTH) / 256.0, 8, 0.3) * 4;
float r2 = noise3d_abs(-(x + chunk.x() * CHUNK_WIDTH) / 512.0f, (z + chunk.z() * CHUNK_HEIGHT) / 512.0f, (y + chunk.y() * CHUNK_DEPTH) / 512.0f, 4, 0.1);
float r3 = noise3d_abs(-(x + chunk.x() * CHUNK_WIDTH) / 512.0f, (z + chunk.z() * CHUNK_HEIGHT) / 128.0f, (y + chunk.y() * CHUNK_DEPTH) / 512.0f, 4, 1);
float r4 = n2 * 5 + r2 * r3 * 20;
if (r4 > 6 && r4 < 8 && h > SEALEVEL) {
chunk.setBlockRaw(x, y, z - 1, 0);
chunk.setBlockRaw(x, y, z, 0);
chunk.setBlockRaw(x, y, z + 1, 0);
}
}
if (topChunk && topChunk->isInitialized()) {

View File

@ -29,6 +29,7 @@
#include <gk/core/IntTypes.hpp>
#include <sol.hpp>
#include "TerrainBiomeSampler.hpp"
class ServerChunk;
@ -39,8 +40,6 @@ class TerrainGenerator {
void generate(ServerChunk &chunk) const;
void setBlocksFromLuaTable(const sol::table &table);
private:
void fastNoiseGeneration(ServerChunk &chunk) const;
@ -49,17 +48,6 @@ class TerrainGenerator {
static float noise3d_abs(double x, double y, double z, int octaves, float persistence);
TerrainBiomeSampler biomeSampler;
u16 m_dirtBlockID = 0;
u16 m_grassBlockID = 0;
u16 m_stoneBlockID = 0;
u16 m_logBlockID = 0;
u16 m_leavesBlockID = 0;
u16 m_flowerBlockID = 0;
u16 m_waterBlockID = 0;
u16 m_sandBlockID = 0;
u16 m_tallgrassBlockID = 0;
u16 m_ironOreBlockID = 0;
};
#endif // TERRAINGENERATOR_HPP_