Refactor GUI Library

master
Nicole Collings 2020-02-20 16:31:37 -08:00
parent 1f3acbcce6
commit 707ccb6a51
25 changed files with 406 additions and 426 deletions

View File

@ -218,7 +218,7 @@ set(ZEPHA_SRC
game/scene/world/ItemStack.h
server/LocalServerInstance.cpp
server/LocalServerInstance.h
game/hud/SerializedGuiElem.h
game/hud/SerialGui.h
util/Voronoi3D.cpp
util/Voronoi3D.h
def/gen/BiomeDef.cpp

View File

@ -10,14 +10,6 @@
enum class Mode { INVALID, CLIENT, SERVER };
uint64_t constexpr mix(char m, uint64_t s) {
return ((s<<7) + ~(s>>3)) + ~m;
}
uint64_t constexpr hashStr(const char * m) {
return (*m) ? mix(*m, hashStr(m+1)) : 0;
}
std::map<std::string, std::string> parseArgs(int argc, char* argv[]) {
//Collect arguments into `args` map
std::map<std::string, std::string> args;
@ -54,30 +46,30 @@ int StartGame(int argc, char* argv[]) {
//Parse the arguments map
for (auto arg : parseArgs(argc, argv)) {
switch (hashStr(arg.first.c_str())) {
switch (Util::hash(arg.first.c_str())) {
default: {
std::cout << Log::err << "Invalid argument " << arg.first << "." << Log::endl;
return -1;
}
case hashStr("--mode"): {
case Util::hash("--mode"): {
if (arg.second == "client") mode = Mode::CLIENT;
else if (arg.second == "server") mode = Mode::SERVER;
else std::cout << Log::err << "Invalid mode argument." << Log::endl;
break;
}
case hashStr("--port"): {
case Util::hash("--port"): {
addr.port = static_cast<unsigned short>(stoi(arg.second));
break;
}
case hashStr("--address"): {
case Util::hash("--address"): {
addr.host = arg.second;
break;
}
case hashStr("--subgame"): {
case Util::hash("--subgame"): {
subgame = arg.second;
break;
}
case hashStr("--noascii"): {
case Util::hash("--noascii"): {
ascii = false;
break;
}

View File

@ -5,7 +5,7 @@
#pragma once
#include "GameGuiBuilder.h"
#include "SerializedGuiElem.h"
#include "SerialGui.h"
#include "components/basic/GUIText.h"
#include "components/basic/GUIRect.h"
#include "components/basic/GUIContainer.h"

View File

@ -3,93 +3,31 @@
//
#include "GameGuiBuilder.h"
#include "components/compound/GUIInventoryList.h"
std::shared_ptr<GUIComponent> GameGuiBuilder::createComponent(SerializedGuiElem &data) {
auto comp = GuiBuilder::createComponent(data);
if (comp) return comp;
std::shared_ptr<GUIComponent> GameGuiBuilder::createComponent(SerialGui::Elem &data, glm::ivec2 bounds) {
auto c = GuiBuilder::createComponent(data, bounds);
if (c != nullptr) return c;
// Extra definitions
GUIComponent::callback cbLeftClick = nullptr;
GUIComponent::callback cbRightClick = nullptr;
GUIComponent::callback cbHover = nullptr;
glm::vec2 pos {};
std::function<void(bool, glm::ivec2)> cbLeftClick = nullptr, cbRightClick = nullptr, cbHover = nullptr;
if (callbacks.count(data.key)) {
cbLeftClick = callbacks[data.key].left;
cbRightClick = callbacks[data.key].right;
cbHover = callbacks[data.key].hover;
}
if (data.tokens.count("position")) {
auto tokens = splitValue(data.tokens["position"], 2);
pos = {stringToNum(tokens[0], PercentBehavior::BUFF_WIDTH),
stringToNum(tokens[1], PercentBehavior::BUFF_HEIGHT)};
}
glm::vec2 offset {};
if (data.tokens.count("position_anchor")) {
auto tokens = splitValue(data.tokens["position_anchor"], 2);
offset = {stringToNum(tokens[0], PercentBehavior::DECIMAL),
stringToNum(tokens[1], PercentBehavior::DECIMAL)};
}
glm::vec2 size {};
if (data.tokens.count("size")) {
auto tokens = splitValue(data.tokens["size"], 2);
size = {stringToNum(tokens[0], PercentBehavior::BUFF_WIDTH),
stringToNum(tokens[1], PercentBehavior::BUFF_HEIGHT)};
}
pos -= offset * size;
glm::vec4 padding {};
if (data.tokens.count("padding")) {
auto tokens = splitValue(data.tokens["padding"], 4);
padding = {stringToNum(tokens[0], PercentBehavior::BUFF_HEIGHT),
stringToNum(tokens[1], PercentBehavior::BUFF_WIDTH),
stringToNum(tokens[2], PercentBehavior::BUFF_HEIGHT),
stringToNum(tokens[3], PercentBehavior::BUFF_WIDTH)};
}
else if (data.type == "inventory") {
std::string source = (data.tokens.count("source") ? data.tokens["source"] : "");
std::string list = (data.tokens.count("list") ? data.tokens["list"] : "");
if (source != "current_player") {
std::cerr << "Invalid source specified, " << source << std::endl;
return nullptr;
switch (Util::hash(data.type.c_str())) {
default: break;
case Util::hash("inventory"): {
c = GUIInventoryList::fromSerialized(data, game, bounds, inventory, hand);
break;
}
if (!inventory[list]) {
std::cerr << "Invalid list specified, " << list << std::endl;
return nullptr;
}
auto invList = inventory[list];
glm::vec4 padding {};
if (data.tokens.count("padding")) {
auto tokens = splitValue(data.tokens["padding"], 4);
padding = {stringToNum(tokens[0], PercentBehavior::BUFF_HEIGHT),
stringToNum(tokens[1], PercentBehavior::BUFF_WIDTH),
stringToNum(tokens[2], PercentBehavior::BUFF_HEIGHT),
stringToNum(tokens[3], PercentBehavior::BUFF_WIDTH)};
}
glm::vec2 innerPadding {};
if (data.tokens.count("slot_spacing")) {
auto tokens = splitValue(data.tokens["slot_spacing"], 2);
innerPadding = {stringToNum(tokens[0], PercentBehavior::BUFF_HEIGHT),
stringToNum(tokens[1], PercentBehavior::BUFF_WIDTH)};
}
auto inv = std::make_shared<GUIInventoryList>(data.key);
inv->create(glm::vec2(SCALE_MODIFIER), padding * SCALE_MODIFIER, innerPadding * SCALE_MODIFIER, *invList, hand, defs);
inv->setPos(pos);
inv->setCallbacks(cbLeftClick, cbRightClick, cbHover);
return inv;
}
if (c != nullptr) c->setCallbacks(cbLeftClick, cbRightClick, cbHover);
return c;
}

View File

@ -13,7 +13,7 @@ public:
GameGuiBuilder(Inventory& inventory, InventoryList& hand, ClientGame& defs, std::shared_ptr<GUIContainer> root) :
inventory(inventory), hand(hand), GuiBuilder(defs, root) {};
std::shared_ptr<GUIComponent> createComponent(SerializedGuiElem& data) override;
std::shared_ptr<GUIComponent> createComponent(SerialGui::Elem& data, glm::ivec2 bounds) override;
private:
Inventory& inventory;

View File

@ -4,17 +4,22 @@
#include "GuiBuilder.h"
#include "components/basic/GUIRect.h"
#include "components/basic/GUIText.h"
#include "components/basic/GUIModel.h"
#include "components/basic/GUIContainer.h"
#include "components/compound/GUIImageButton.h"
GuiBuilder::GuiBuilder(ClientGame& defs, std::shared_ptr<GUIContainer> root) :
defs(defs), root(root) {}
game(defs), root(root) {}
void GuiBuilder::setGui(const std::string& menu, const std::map<std::string, ComponentCallbacks>& callbacks) {
this->menu = menu;
this->callbacks = callbacks;
deserialize();
deserialize(menu);
}
void GuiBuilder::deserialize() {
void GuiBuilder::deserialize(const std::string& menu) {
// Split the lines by the newline delimiter
std::vector<std::string> lines;
{
std::string::size_type pos = 0;
@ -33,14 +38,15 @@ void GuiBuilder::deserialize() {
}
components.clear();
std::vector<SerializedGuiElem*> stack {};
unsigned int missingKey = 0;
SerializedGuiElem* component = nullptr;
unsigned int keyInd = 0;
std::vector<SerialGui::Elem*> stack {};
SerialGui::Elem* component = nullptr;
// Parse through the serialized structure and create the Serialized tree
for (const std::string& line : lines) {
if (line.find(':') != std::string::npos) {
//Token
if (component == nullptr) throw "Property before component.";
// A Property
if (component == nullptr) throw "expected a component name before a property";
std::string::size_type delimiter = line.find(':');
@ -58,19 +64,8 @@ void GuiBuilder::deserialize() {
component->tokens.emplace(name, value);
}
else if (line == "end") {
//End Component Def
if (stack.size() > 0) {
component = stack[stack.size() - 1];
stack.pop_back();
}
else {
stack.pop_back();
component = nullptr;
}
}
else {
//Component
else if (line != "end") {
// Beginning of a component definition
std::string key = "";
std::string::size_type keyStart;
std::string::size_type keyEnd;
@ -79,15 +74,12 @@ void GuiBuilder::deserialize() {
key = line.substr(keyStart + 1, keyEnd - keyStart - 1);
}
else {
key = "__" + std::to_string(missingKey++);
// Create an implicit key because one was not specified
key = "__" + std::to_string(keyInd++);
}
std::string type = line.substr(0, keyStart);
SerializedGuiElem g;
g.key = key;
g.type = type;
g.tokens = {};
g.children = {};
std::string type = line.substr(0, keyStart);
SerialGui::Elem g {type, key, {}, {}};
if (component == nullptr) {
components.push_back(std::move(g));
@ -99,14 +91,20 @@ void GuiBuilder::deserialize() {
component = &component->children[component->children.size() - 1];
}
}
else {
// End of a component definition -- pop the stack up
if (stack.size() > 0) {
component = stack[stack.size() - 1];
stack.pop_back();
}
else component = nullptr;
}
}
}
void GuiBuilder::build(glm::ivec2 win) {
this->win = win;
void GuiBuilder::build(glm::ivec2 winBounds) {
clear(false);
recursivelyCreate(components, root);
recursivelyCreate(components, root, winBounds);
}
void GuiBuilder::clear(bool clrCallbacks) {
@ -114,248 +112,52 @@ void GuiBuilder::clear(bool clrCallbacks) {
root->empty();
}
void GuiBuilder::recursivelyCreate(std::vector<SerializedGuiElem> components, std::shared_ptr<GUIComponent> parent) {
void GuiBuilder::recursivelyCreate(std::vector<SerialGui::Elem> components, std::shared_ptr<GUIComponent> parent, glm::ivec2 bounds) {
for (auto& data : components) {
std::shared_ptr<GUIComponent> component = createComponent(data);
if (component != nullptr) {
parent->add(component);
recursivelyCreate(data.children, component);
}
std::shared_ptr<GUIComponent> component = createComponent(data, bounds);
if (component == nullptr) continue;
parent->add(component);
recursivelyCreate(data.children, component, bounds);
}
}
std::shared_ptr<GUIComponent> GuiBuilder::createComponent(SerializedGuiElem& data) {
glm::vec2 pos {};
std::shared_ptr<GUIComponent> GuiBuilder::createComponent(SerialGui::Elem& data, glm::ivec2 bounds) {
std::shared_ptr<GUIComponent> c = nullptr;
GUIComponent::callback cbLeftClick = nullptr;
GUIComponent::callback cbRightClick = nullptr;
GUIComponent::callback cbHover = nullptr;
std::function<void(bool, glm::ivec2)> cbLeftClick = nullptr, cbRightClick = nullptr, cbHover = nullptr;
if (callbacks.count(data.key)) {
cbLeftClick = callbacks[data.key].left;
cbRightClick = callbacks[data.key].right;
cbHover = callbacks[data.key].hover;
}
if (data.tokens.count("position")) {
auto tokens = splitValue(data.tokens["position"], 2);
pos = {stringToNum(tokens[0], PercentBehavior::BUFF_WIDTH),
stringToNum(tokens[1], PercentBehavior::BUFF_HEIGHT)};
}
glm::vec2 offset {};
if (data.tokens.count("position_anchor")) {
auto tokens = splitValue(data.tokens["position_anchor"], 2);
offset = {stringToNum(tokens[0], PercentBehavior::DECIMAL),
stringToNum(tokens[1], PercentBehavior::DECIMAL)};
}
glm::vec2 size {};
if (data.tokens.count("size")) {
auto tokens = splitValue(data.tokens["size"], 2);
size = {stringToNum(tokens[0], PercentBehavior::BUFF_WIDTH),
stringToNum(tokens[1], PercentBehavior::BUFF_HEIGHT)};
}
pos -= offset * size;
glm::vec4 padding {};
if (data.tokens.count("padding")) {
auto tokens = splitValue(data.tokens["padding"], 4);
padding = {stringToNum(tokens[0], PercentBehavior::BUFF_HEIGHT),
stringToNum(tokens[1], PercentBehavior::BUFF_WIDTH),
stringToNum(tokens[2], PercentBehavior::BUFF_HEIGHT),
stringToNum(tokens[3], PercentBehavior::BUFF_WIDTH)};
}
if (data.type == "body") {
auto rect = std::make_shared<GUIRect>(data.key);
std::string background = "";
if (data.tokens.count("background")) background = data.tokens["background"];
if (background[0] == '#') rect->create(win, {}, Util::hexToColorVec(background));
else if (background.substr(0, 6) == "asset(") rect->create(win, {}, defs.textures[background.substr(6, background.length() - 7)]);
else rect->create(win, {}, glm::vec4 {});
rect->setCallbacks(cbLeftClick, cbRightClick, cbHover);
return rect;
}
else if (data.type == "rect") {
size.x -= padding.y + padding.w;
size.y -= padding.x + padding.z;
std::string background = "";
if (data.tokens.count("background")) background = data.tokens["background"];
auto rect = std::make_shared<GUIRect>(data.key);
if (background[0] == '#') rect->create(size, padding, Util::hexToColorVec(background));
else if (background.substr(0, 6) == "asset(") rect->create(size, padding, defs.textures[background.substr(6, background.length() - 7)]);
else rect->create(size, padding, glm::vec4 {});
rect->setPos(pos);
rect->setCallbacks(cbLeftClick, cbRightClick, cbHover);
return rect;
}
else if (data.type == "button") {
size.x -= padding.y + padding.w;
size.y -= padding.x + padding.z;
std::string background = "";
if (data.tokens.count("background")) background = data.tokens["background"];
std::string background_hover = background;
if (data.tokens.count("background_hover")) background_hover = data.tokens["background_hover"];
auto button = std::make_shared<GUIImageButton>(data.key);
button->create(size, padding, defs.textures[background.substr(6, background.length() - 7)], defs.textures[background_hover.substr(6, background_hover.length() - 7)]);
std::string content = "";
if (data.tokens.count("content")) content = data.tokens["content"].substr(1, data.tokens["content"].size() - 2);
if (content != "") {
std::string::size_type off = 0;
while ((off = content.find("\\n", off)) != std::string::npos) {
content.replace(off, 2, "\n");
off += 1;
}
auto text = std::make_shared<GUIText>(data.key + "__TEXT");
text->create(glm::vec2(SCALE_MODIFIER), padding, {}, {1, 1, 1, 1},
{defs.textures, defs.textures["font"]});
text->setText(content);
text->setPos({6*SCALE_MODIFIER, size.y / 2 - 4.5*SCALE_MODIFIER});
button->add(text);
switch (Util::hash(data.type.c_str())) {
default: break;
case Util::hash("body"): {
auto body = GUIRect::fromSerialized(data, game, bounds);
body->setScale(bounds);
c = body;
break;
}
button->setPos(pos);
button->setCallbacks(cbLeftClick, cbRightClick, cbHover);
return button;
case Util::hash("rect"):
c = GUIRect::fromSerialized(data, game, bounds);
break;
case Util::hash("button"):
c = GUIImageButton::fromSerialized(data, game, bounds);
break;
case Util::hash("text"):
c = GUIText::fromSerialized(data, game, bounds);
break;
case Util::hash("model"):
c = GUIModel::fromSerialized(data, game, bounds);
break;
}
else if (data.type == "text") {
glm::vec2 scale = {1, 1};
if (data.tokens.count("scale")) {
auto tokens = splitValue(data.tokens["scale"], 2);
scale = {stringToNum(tokens[0], PercentBehavior::DECIMAL),
stringToNum(tokens[1], PercentBehavior::DECIMAL)};
}
glm::vec4 padding {};
if (data.tokens.count("padding")) {
auto tokens = splitValue(data.tokens["padding"], 4);
padding = {stringToNum(tokens[0], PercentBehavior::BUFF_HEIGHT),
stringToNum(tokens[1], PercentBehavior::BUFF_WIDTH),
stringToNum(tokens[2], PercentBehavior::BUFF_HEIGHT),
stringToNum(tokens[3], PercentBehavior::BUFF_WIDTH)};
}
glm::vec4 background_color = Util::hexToColorVec("#0000");
if (data.tokens.count("background")) background_color = Util::hexToColorVec(data.tokens["background"]);
glm::vec4 color = Util::hexToColorVec("#fff");
if (data.tokens.count("color")) color = Util::hexToColorVec(data.tokens["color"]);
std::string content = "Missing content string";
if (data.tokens.count("content")) content = data.tokens["content"].substr(1, data.tokens["content"].size() - 2);
std::string::size_type off = 0;
while ((off = content.find("\\n", off)) != std::string::npos) {
content.replace(off, 2, "\n");
off += 1;
}
auto text = std::make_shared<GUIText>(data.key);
text->create(scale * SCALE_MODIFIER, padding, background_color, color, {defs.textures, defs.textures["font"]});
text->setText(content);
text->setPos(pos);
text->setCallbacks(cbLeftClick, cbRightClick, cbHover);
return text;
}
else if (data.type == "model") {
glm::vec2 scale = {1, 1};
if (data.tokens.count("scale")) {
auto tokens = splitValue(data.tokens["scale"], 2);
scale = {stringToNum(tokens[0], PercentBehavior::DECIMAL),
stringToNum(tokens[1], PercentBehavior::DECIMAL)};
}
std::string type = (data.tokens.count("type") ? data.tokens["type"] : "model");
std::string source = (data.tokens.count("source") ? data.tokens["source"] : "");
std::string texture = (data.tokens.count("texture") ? data.tokens["texture"] : "");
glm::vec2 anim_range = {0, 0};
if (data.tokens.count("anim_range")) {
auto tokens = splitValue(data.tokens["anim_range"], 2);
anim_range = {stringToNum(tokens[0], PercentBehavior::DECIMAL),
stringToNum(tokens[1], PercentBehavior::DECIMAL)};
}
auto m = std::make_shared<Model>();
if (type == "model") {
m->fromSerialized(defs.models.models[source], {defs.textures[texture]});
}
auto model = std::make_shared<GUIModel>(data.key);
model->create(scale, m);
if (anim_range.y != 0) {
model->animate(anim_range);
}
model->setPos(pos);
model->setCallbacks(cbLeftClick, cbRightClick, cbHover);
return model;
}
// An unknown type was specified.
return nullptr;
}
float GuiBuilder::stringToNum(const std::string& input, PercentBehavior behavior = PercentBehavior::BUFF_WIDTH) {
if (input.find("px") != std::string::npos) {
return atof(input.substr(0, input.find("px")).c_str()) * SCALE_MODIFIER;
}
if (input.find('%') != std::string::npos) {
float decimal = atof(input.substr(0, input.find('%')).c_str()) / 100;
switch (behavior) {
case PercentBehavior::DECIMAL:
return decimal;
case PercentBehavior::BUFF_WIDTH:
return round(decimal * win.x / SCALE_MODIFIER) * SCALE_MODIFIER;
case PercentBehavior::BUFF_HEIGHT:
return round(decimal * win.y / SCALE_MODIFIER) * SCALE_MODIFIER;
}
}
return atof(input.c_str());
return 0;
}
std::vector<std::string> GuiBuilder::splitValue(const std::string &value, unsigned int targetVals) {
std::vector<std::string> vec {};
if (value == "") throw "No values for splitValue.";
size_t count = std::count(value.begin(), value.end(), ' ');
if (count + 1 > targetVals) throw "Too many values for splitValue.";
size_t begin = 0;
for (int i = 0; i < count; i++) {
size_t end = value.find(' ', begin);
vec.push_back(value.substr(begin, end - begin));
begin = end + 1;
}
vec.push_back(value.substr(begin));
while (vec.size() < targetVals) {
for (auto& v: vec) {
vec.push_back(v);
if (vec.size() >= targetVals) break;
}
}
return std::move(vec);
if (c != nullptr) c->setCallbacks(cbLeftClick, cbRightClick, cbHover);
return c;
}
GuiBuilder::~GuiBuilder() {

View File

@ -4,51 +4,31 @@
#pragma once
#include <string>
#include <algorithm>
#include "SerializedGuiElem.h"
#include "SerialGui.h"
#include "../../def/ClientGame.h"
#include "components/basic/GUIRect.h"
#include "components/basic/GUIText.h"
#include "components/basic/GUIModel.h"
#include "components/basic/GUIContainer.h"
#include "components/compound/GUIImageButton.h"
#include "components/GUIComponent.h"
class GUIContainer;
class GuiBuilder {
public:
struct ComponentCallbacks {
GUIComponent::callback left {}, right {}, hover {};
};
struct ComponentCallbacks { GUIComponent::callback left {}, right {}, hover {}; };
GuiBuilder(ClientGame& defs, std::shared_ptr<GUIContainer> root);
void setGui(const std::string& menu, const std::map<std::string, ComponentCallbacks>& callbacks = {});
void build(glm::ivec2 win);
void clear(bool clrCallbacks = true);
void build(glm::ivec2 winBounds);
~GuiBuilder();
protected:
const float SCALE_MODIFIER = 3;
void deserialize(const std::string& menu);
void recursivelyCreate(std::vector<SerialGui::Elem> components, std::shared_ptr<GUIComponent> parent, glm::ivec2 bounds);
virtual std::shared_ptr<GUIComponent> createComponent(SerialGui::Elem& component, glm::ivec2 bounds);
enum PercentBehavior {
BUFF_WIDTH,
BUFF_HEIGHT,
DECIMAL
};
void deserialize();
void recursivelyCreate(std::vector<SerializedGuiElem> components, std::shared_ptr<GUIComponent> parent);
virtual std::shared_ptr<GUIComponent> createComponent(SerializedGuiElem& component);
static std::vector<std::string> splitValue(const std::string& value, unsigned int targetVals = 0);
float stringToNum(const std::string& input, PercentBehavior behavior);
glm::ivec2 win;
std::string menu;
std::map<std::string, ComponentCallbacks> callbacks;
ClientGame& game;
std::shared_ptr<GUIContainer> root;
std::vector<SerializedGuiElem> components {};
ClientGame& defs;
std::vector<SerialGui::Elem> components {};
};

95
src/game/hud/SerialGui.h Normal file
View File

@ -0,0 +1,95 @@
//
// Created by aurailus on 2019-11-03.
//
#pragma once
#include <map>
#include <vector>
#include <string>
#include <algorithm>
#include <cerrno>
#include <cstdlib>
#include <glm/vec2.hpp>
#include <glm/vec4.hpp>
namespace SerialGui {
struct Elem {
std::string type;
std::string key;
std::map<std::string, std::string> tokens;
std::vector<SerialGui::Elem> children;
};
const float SCALE_MODIFIER = 3;
namespace {
static std::vector<std::string> split(const std::string& value, unsigned int targetCount = 0) {
std::vector<std::string> vec {};
if (value == "") throw std::runtime_error("expected one or more values to split");
size_t count = std::count(value.begin(), value.end(), ' ');
if (count + 1 > targetCount) throw std::runtime_error("expected less values");
// Split the values into the vector.
size_t begin = 0;
for (int i = 0; i < count; i++) {
size_t end = value.find(' ', begin);
vec.push_back(value.substr(begin, end - begin));
begin = end + 1;
}
vec.push_back(value.substr(begin));
// Duplicate values until we meet or surpass the expected values.
while (vec.size() < targetCount) for (auto& v: vec) vec.push_back(v);
if (vec.size() > targetCount) throw std::runtime_error("values are not a division of expectation");
return std::move(vec);
}
static double toDouble(const std::string& input, unsigned int multiple) {
char* e;
errno = 0;
if (input.find("px") == input.length() - 2) {
double v = round(std::strtod(input.substr(0, input.find("px")).c_str(), &e));
if (*e != '\0' || errno != 0) throw std::runtime_error("error decoding num from string");
return v * SCALE_MODIFIER;
}
if (input.find('%') == input.length() - 1) {
double v = std::strtod(input.substr(0, input.find("%")).c_str(), &e) / 100;
if (*e != '\0' || errno != 0) throw std::runtime_error("error decoding num from string");
if (!multiple) return v;
return round(v * multiple / SCALE_MODIFIER) * SCALE_MODIFIER;
}
double v = std::strtod(input.c_str(), &e);
if (*e != '\0' || errno != 0) throw std::runtime_error("error decoding num from string");
return v;
}
}
template <typename T> static T deserialize(const std::string& in, glm::ivec2 multiple = {}) {};
template <typename T> static T deserializeToken(const std::map<std::string, std::string>& tokens,
const std::string& req, glm::ivec2 multiple = {}) {
if (!tokens.count(req)) return T{};
return deserialize<T>(tokens.at(req), multiple);
}
template <> glm::vec2 deserialize<glm::vec2>(const std::string& in, glm::ivec2 multiple) {
auto tokens = split(in, 2);
return {toDouble(tokens[0], multiple.x), toDouble(tokens[1], multiple.y)};
}
template <> glm::vec4 deserialize<glm::vec4>(const std::string& in, glm::ivec2 multiple) {
auto tokens = split(in, 4);
return {toDouble(tokens[0], multiple.x), toDouble(tokens[1], multiple.y),
toDouble(tokens[2], multiple.x), toDouble(tokens[3], multiple.y)};
}
};

View File

@ -1,16 +0,0 @@
//
// Created by aurailus on 2019-11-03.
//
#pragma once
#include <vector>
#include <string>
#include <map>
struct SerializedGuiElem {
std::string type;
std::string key;
std::map<std::string, std::string> tokens;
std::vector<SerializedGuiElem> children;
};

View File

@ -3,8 +3,11 @@
//
#include "GUIInventoryItem.h"
#include "GUIText.h"
#include "GUIRect.h"
#include "GUIModel.h"
#include "../../../../def/texture/Font.h"
GUIInventoryItem::GUIInventoryItem(const std::string &key) : GUIContainer(key) {}

View File

@ -4,10 +4,11 @@
#pragma once
#include "GUIRect.h"
#include "../../../../def/texture/Font.h"
#include "GUIContainer.h"
class ItemDef;
class Font;
class GUIInventoryItem : public GUIContainer {
public:
GUIInventoryItem() = default;

View File

@ -4,8 +4,32 @@
#include "GUIModel.h"
#include "../../../../def/ClientGame.h"
GUIModel::GUIModel(const std::string &key) : GUIComponent(key) {}
std::shared_ptr<GUIModel> GUIModel::fromSerialized(SerialGui::Elem s, ClientGame &game, glm::ivec2 bounds) {
glm::vec2 pos = SerialGui::deserializeToken<glm::vec2>(s.tokens, "position", bounds);
glm::vec2 scale = SerialGui::deserializeToken<glm::vec2>(s.tokens, "scale");
glm::vec2 anim_range = SerialGui::deserializeToken<glm::vec2>(s.tokens, "anim_range");
if (scale == glm::vec2{0, 0}) scale = {1, 1};
std::string type = s.tokens.count("type") ? s.tokens["type"] : "model";
std::string source = s.tokens["source"];
std::string texture = s.tokens["texture"];
auto m = std::make_shared<Model>();
if (type == "model") m->fromSerialized(game.models.models[source], {game.textures[texture]});
auto model = std::make_shared<GUIModel>(s.key);
model->create(scale, m);
model->setPos(pos);
if (anim_range.y != 0) model->animate(anim_range);
return model;
}
void GUIModel::create(glm::vec2 scale, std::shared_ptr<Model> model) {
entity.setModel(model);
setScale({scale.x + padding.w + padding.y, scale.y + padding.x + padding.z});
@ -39,4 +63,4 @@ void GUIModel::draw(Renderer &renderer) {
renderer.clearDepthBuffer();
GUIComponent::draw(renderer);
renderer.toggleDepthTest(false);
}
}

View File

@ -5,14 +5,20 @@
#pragma once
#include <string>
#include "GUIContainer.h"
#include "../../../../def/ItemDef.h"
#include "../../SerialGui.h"
class ClientGame;
class GUIModel : public GUIComponent {
public:
GUIModel() = default;
GUIModel(const std::string& key);
static std::shared_ptr<GUIModel> fromSerialized(SerialGui::Elem s, ClientGame& game, glm::ivec2 bounds);
void create(glm::vec2 scale, std::shared_ptr<Model> model);
void update(double delta) override;

View File

@ -4,8 +4,31 @@
#include "GUIRect.h"
#include "../../SerialGui.h"
#include "../../../../util/Util.h"
#include "../../../../def/ClientGame.h"
GUIRect::GUIRect(const std::string &key) : GUIComponent(key) {}
std::shared_ptr<GUIRect> GUIRect::fromSerialized(SerialGui::Elem s, ClientGame& game, glm::ivec2 bounds) {
glm::vec2 pos = SerialGui::deserializeToken<glm::vec2>(s.tokens, "position", bounds);
glm::vec2 offset = SerialGui::deserializeToken<glm::vec2>(s.tokens, "position_anchor");
glm::vec2 size = SerialGui::deserializeToken<glm::vec2>(s.tokens, "size", bounds);
glm::vec4 padding = SerialGui::deserializeToken<glm::vec4>(s.tokens, "padding", bounds);
pos -= offset * size;
size -= glm::vec2 {padding.y + padding.w, padding.x + padding.z};
std::string background = s.tokens["background"];
auto rect = std::make_shared<GUIRect>(s.key);
if (background[0] == '#') rect->create(size, padding, Util::hexToColorVec(background));
else if (background.size() > 0) rect->create(size, padding, game.textures[background]);
else rect->create(size, padding, glm::vec4 {});
rect->setPos(pos);
return rect;
}
// Single Color Constructor
// Creates a GUIRect object whose background
// is a flat color defined by 'color'.

View File

@ -6,14 +6,20 @@
#include <memory>
#include "../GUIComponent.h"
#include "../../SerialGui.h"
#include "../../../../def/texture/AtlasRef.h"
class ClientGame;
class GUIRect : public GUIComponent {
public:
GUIRect() = default;
GUIRect(const std::string& key);
static std::shared_ptr<GUIRect> fromSerialized(SerialGui::Elem s, ClientGame& game, glm::ivec2 bounds);
void create(glm::vec2 scale, glm::vec4 padding, glm::vec4 color);
void create(glm::vec2 scale, glm::vec4 padding, glm::vec4 tl, glm::vec4 tr, glm::vec4 bl, glm::vec4 br);
void create(glm::vec2 scale, glm::vec4 padding, std::shared_ptr<AtlasRef> texture);

View File

@ -2,9 +2,10 @@
// Created by aurailus on 25/12/18.
//
#include "GUIText.h"
#include <utility>
#include "GUIText.h"
GUIText::GUIText(const std::string &key) : GUIComponent(key) {}
void GUIText::create(glm::vec2 scale, glm::vec4 padding, glm::vec4 bgcolor, glm::vec4 color, Font font) {
@ -22,6 +23,38 @@ void GUIText::create(glm::vec2 scale, glm::vec4 padding, glm::vec4 bgcolor, glm:
setText("");
}
std::shared_ptr<GUIText> GUIText::fromSerialized(SerialGui::Elem s, ClientGame &game, glm::ivec2 bounds) {
glm::vec2 pos = SerialGui::deserializeToken<glm::vec2>(s.tokens, "position", bounds);
glm::vec2 offset = SerialGui::deserializeToken<glm::vec2>(s.tokens, "position_anchor");
glm::vec2 size = SerialGui::deserializeToken<glm::vec2>(s.tokens, "size", bounds);
glm::vec4 padding = SerialGui::deserializeToken<glm::vec4>(s.tokens, "padding", bounds);
glm::vec2 scale = SerialGui::deserializeToken<glm::vec2>(s.tokens, "scale");
if (scale == glm::vec2{0, 0}) scale = {1, 1};
pos -= offset * size;
size -= glm::vec2 {padding.y + padding.w, padding.x + padding.z};
glm::vec4 background_color = Util::hexToColorVec("#0000");
if (s.tokens.count("background")) background_color = Util::hexToColorVec(s.tokens["background"]);
glm::vec4 color = Util::hexToColorVec("#fff");
if (s.tokens.count("color")) color = Util::hexToColorVec(s.tokens["color"]);
std::string content = "";
if (s.tokens.count("content") && s.tokens["content"].length() >= 2) content = s.tokens["content"].substr(1, s.tokens["content"].size() - 2);
std::string::size_type off = 0;
while ((off = content.find("\\n", off)) != std::string::npos) {
content.replace(off, 2, "\n");
off += 1;
}
auto text = std::make_shared<GUIText>(s.key);
text->create(scale * SerialGui::SCALE_MODIFIER, padding, background_color, color, {game.textures, game.textures["font"]});
text->setText(content);
text->setPos(pos);
return text;
}
void GUIText::setText(std::string text) {
this->text = std::move(text);
unsigned int indOffset = 0;

View File

@ -4,17 +4,21 @@
#pragma once
#include <utility>
#include "../GUIComponent.h"
#include "../../../../def/texture/AtlasRef.h"
#include "../../SerialGui.h"
#include "../../../../def/texture/Font.h"
#include <utility>
#include "../../../../def/texture/AtlasRef.h"
class GUIText : public GUIComponent {
public:
GUIText() = default;
explicit GUIText(const std::string& key);
static std::shared_ptr<GUIText> fromSerialized(SerialGui::Elem s, ClientGame& game, glm::ivec2 bounds);
void create(glm::vec2 scale, glm::vec4 padding, glm::vec4 bgcolor, glm::vec4 color, Font font);
unsigned int getWidth();

View File

@ -4,8 +4,47 @@
#include "GUIImageButton.h"
#include "../../../../def/ClientGame.h"
#include "../basic/GUIText.h"
GUIImageButton::GUIImageButton(const std::string &key) : GUIRect(key) {}
std::shared_ptr<GUIImageButton> GUIImageButton::fromSerialized(SerialGui::Elem s, ClientGame &game, glm::ivec2 bounds) {
glm::vec2 pos = SerialGui::deserializeToken<glm::vec2>(s.tokens, "position", bounds);
glm::vec2 offset = SerialGui::deserializeToken<glm::vec2>(s.tokens, "position_anchor");
glm::vec2 size = SerialGui::deserializeToken<glm::vec2>(s.tokens, "size", bounds);
glm::vec4 padding = SerialGui::deserializeToken<glm::vec4>(s.tokens, "padding", bounds);
pos -= offset * size;
size -= glm::vec2 {padding.y + padding.w, padding.x + padding.z};
std::string background = s.tokens["background"];
std::string background_hover = s.tokens["background_hover"];
if (background_hover.length() == 0) background_hover = background;
std::string content = "";
if (s.tokens.count("content") && s.tokens["content"].length() >= 2) content = s.tokens["content"].substr(1, s.tokens["content"].size() - 2);
std::string::size_type off = 0;
while ((off = content.find("\\n", off)) != std::string::npos) {
content.replace(off, 2, "\n");
off += 1;
}
auto button = std::make_shared<GUIImageButton>(s.key);
button->create(size, padding, game.textures[background], game.textures[background_hover]);
button->setPos(pos);
if (content != "") {
auto text = std::make_shared<GUIText>(s.key + "__TEXT");
text->create(glm::vec2(SerialGui::SCALE_MODIFIER), padding, {}, {1, 1, 1, 1}, {game.textures, game.textures["font"]});
text->setPos({6 * SerialGui::SCALE_MODIFIER, size.y / 2 - 4.5 * SerialGui::SCALE_MODIFIER});
text->setText(content);
button->add(text);
}
return button;
}
// Texture Constructor
// Creates a GUIImageButton object with two textures
// defined by the 'texture' & 'hoverTexture' reference.
@ -39,4 +78,4 @@ void GUIImageButton::rebuild(bool hover) {
entity.setModel(model);
setScale({scale.x + padding.w + padding.y, scale.y + padding.x + padding.z});
}
}

View File

@ -11,6 +11,8 @@ public:
GUIImageButton() = default;
GUIImageButton(const std::string& key);
static std::shared_ptr<GUIImageButton> fromSerialized(SerialGui::Elem s, ClientGame& game, glm::ivec2 bounds);
void create(glm::vec2 scale, glm::vec4 padding, std::shared_ptr<AtlasRef> texture, std::shared_ptr<AtlasRef> hoverTexture);
void setHoverCallback(const callback& hoverCallback) override;

View File

@ -4,8 +4,43 @@
#include "GUIInventoryList.h"
#include "../basic/GUIInventoryItem.h"
#include "../../../../def/texture/Font.h"
GUIInventoryList::GUIInventoryList(const std::string &key) : GUIContainer(key) {}
std::shared_ptr<GUIInventoryList> GUIInventoryList::fromSerialized(SerialGui::Elem s, ClientGame &game,
glm::ivec2 bounds, Inventory& inventory, InventoryList& hand) {
glm::vec2 pos = SerialGui::deserializeToken<glm::vec2>(s.tokens, "position", bounds);
glm::vec2 offset = SerialGui::deserializeToken<glm::vec2>(s.tokens, "position_anchor");
glm::vec2 size = SerialGui::deserializeToken<glm::vec2>(s.tokens, "size", bounds);
glm::vec4 padding = SerialGui::deserializeToken<glm::vec4>(s.tokens, "padding", bounds);
glm::vec2 slotspc = SerialGui::deserializeToken<glm::vec2>(s.tokens, "slot_spacing", bounds);
std::string source = s.tokens["source"];
std::string list = s.tokens["list"];
if (source != "current_player") {
std::cerr << "Invalid source specified, " << source << std::endl;
return nullptr;
}
if (!inventory[list]) {
std::cerr << "Invalid list specified, " << list << std::endl;
return nullptr;
}
auto invList = inventory[list];
auto inv = std::make_shared<GUIInventoryList>(s.key);
inv->create(glm::vec2(SerialGui::SCALE_MODIFIER), padding * SerialGui::SCALE_MODIFIER,
slotspc * SerialGui::SCALE_MODIFIER, *invList, hand, game);
inv->setPos(pos);
return inv;
}
void GUIInventoryList::create(glm::vec2 scale, glm::vec4 padding, glm::ivec2 innerPadding, InventoryList &list, InventoryList& hand, ClientGame &defs) {
this->list = &list;
this->hand = &hand;

View File

@ -5,10 +5,11 @@
#pragma once
#include "../basic/GUIContainer.h"
#include "../../../../def/ClientGame.h"
#include "../basic/GUIRect.h"
#include "../basic/GUIInventoryItem.h"
#include "../../../../def/texture/Font.h"
#include "../../SerialGui.h"
#include "../../../../def/ClientGame.h"
#include "../../../scene/world/Inventory.h"
#include "../../../scene/world/InventoryList.h"
class GUIInventoryList : public GUIContainer {
@ -17,6 +18,9 @@ public:
GUIInventoryList(const std::string& key);
~GUIInventoryList() override;
static std::shared_ptr<GUIInventoryList> fromSerialized(SerialGui::Elem s, ClientGame &game,
glm::ivec2 bounds, Inventory& inventory, InventoryList& hand);
void create(glm::vec2 scale, glm::vec4 padding, glm::ivec2 innerPadding, InventoryList& list, InventoryList& hand, ClientGame& defs);
void setHoverCallback(const callback& hoverCallback) override;

View File

@ -221,5 +221,15 @@ namespace Util {
case 348: return "menu";
}
}
namespace {
constexpr static uint64_t mix(char m, uint64_t s) {
return ((s << 7) + ~(s >> 3)) + ~m;
}
}
constexpr static uint64_t hash(const char * m) {
return (*m) ? mix(*m, hash(m + 1)) : 0;
}
};

View File

@ -20,8 +20,8 @@ BlockChunk::BlockChunk(const std::vector<unsigned int>& blocks, const std::vecto
}
bool BlockChunk::setBlock(unsigned int ind, unsigned int blk) {
// Exit if someone is doing something stupid
if (ind >= 4096) return false;
if (!RIE::write(ind, blk, blocks, 4096)) return false;
// Mesh emptiness manipulation
if (blk == DefinitionAtlas::AIR) {
if ((nonAirBlocks = fmax(nonAirBlocks - 1, 0)) == 0) {
@ -35,8 +35,7 @@ bool BlockChunk::setBlock(unsigned int ind, unsigned int blk) {
nonAirBlocks++;
}
// Actually set the block
return RIE::write(ind, blk, blocks, 4096);
return true;
}
const std::vector<unsigned int> &BlockChunk::cGetBlocks() const {

View File

@ -1,6 +1,6 @@
zepha.set_gui([[
body
background: asset(zeus_background)
background: zeus_background
rect[sidebar]
position: 20% 0
@ -11,22 +11,22 @@ zepha.set_gui([[
rect[logo]
position: 8px 8px
size: 86px 30px
background: asset(zeus_logo)
background: zeus_logo
end
button[buttonPlay]
position: 6px 50px
size: 90px 20px
background: asset(crop(0, 0, 90, 20, zeus_button))
background_hover: asset(crop(0, 20, 90, 20, zeus_button))
background: crop(0, 0, 90, 20, zeus_button)
background_hover: crop(0, 20, 90, 20, zeus_button)
content: "Local Play"
end
button[buttonServers]
position: 6px 74px
size: 90px 20px
background: asset(crop(0, 0, 90, 20, zeus_button))
background_hover: asset(crop(0, 20, 90, 20, zeus_button))
background: crop(0, 0, 90, 20, zeus_button)
background_hover: crop(0, 20, 90, 20, zeus_button)
content: "Browse Servers"
end
end

View File

@ -17,7 +17,7 @@ zepha.register_keybind("zeus:inventory:open_inventory", {
position: 0px 45px
size: 218px 100px
padding: 20px 10px 8px 10px
background: asset(zeus:inventory:inventory)
background: zeus:inventory:inventory
inventory
source: current_player
@ -31,7 +31,7 @@ zepha.register_keybind("zeus:inventory:open_inventory", {
size: 218px 72px
position: 0px -15px
padding: 20px 10px 8px 10px
background: asset(zeus:inventory:crafting)
background: zeus:inventory:crafting
inventory
source: current_player
@ -51,7 +51,7 @@ zepha.register_keybind("zeus:inventory:open_inventory", {
rect[hot_wheel]
size: 214px 67px
position: 2px 160px
background: asset(zeus:inventory:inventory_wheel)
background: zeus:inventory:inventory_wheel
inventory
source: current_player