--- /dev/null
+cmake_minimum_required(VERSION 3.0)
+project(dragonblocks)
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
+add_executable(dragonblocks
+ src/game.cpp
+ src/graphics.cpp
+ src/inventory.cpp
+ src/main.cpp
+ src/map.cpp
+ src/mapgen.cpp
+ src/mods.cpp
+ src/node.cpp
+ src/texture.cpp
+ src/threads.cpp
+ src/util.cpp
+)
+
+target_link_libraries(dragonblocks
+ GL
+ freeglut
+ lua5.3
+ pthread
+ png16
+ m
+)
--- /dev/null
+DRAGONBLOCKS BEDROCK EDITION
+Written in C++
+An Open Source Project by Elias Fleckenstein
+
+1. General
+ 1.1. What this is about
+ Dragonblocks originaly was a Browsergame I made that should be some kind of 2D Minetest (Minetest (www.minetest.net) is a game similar to
+ Minecraft, except Minetest is free & open source and is meant to be modded). The JavaScript Edition is hosted at www.dragonblocks.tk and
+ further developed then this C++ Version, through the C++ Version has advantages like map saving and lot larger map. I decided to call the
+ C++ Version "Bedrock Edition" as a joke for the Minecraft C++ Version is Called Bedrock or Pocket Edition.
+ 1.2. Version
+ This is the Dragonblocks Bedrock Edition 3.0.
+ 1.4. Bugs
+ Please Report Bugs to eliasfleckenstein@web.de.
+ 1.5. License
+ Copyright 2020 Elias Fleckenstein <eliasfleckenstein@web.de>
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301, USA.
+
+2. Starting the Game
+ 2.1. Platform Compability
+ This Game was developed on linux, therefore following instructions are only for linux users, basically because I have no idea how to
+ port the game to other platforms. Feel free to add building instructions for other systems over github.
+ 2.3. Download
+ You can download the game using git:
+ $ git clone https://
+ 2.2. Dependencies:
+ You need Freeglut, OpenGl, Cmake, Liblua and Libpng.
+ If you are on Debian/Ubuntu You can install them over apt:
+ $ sudo apt install liblua5.3-dev freeglut3-dev libgl1-mesa-dev cmake libpng-dev
+ 2.3. Building
+ to Build dragonblocks run:
+ $ cd dragonblocks
+ $ cmake .
+ $ make
+ 2.4. Exectute
+ 2.4.1. Run-In-Place
+ Invoke dragonblocks by typing:
+ $ cd dragonblocks
+ $ bin/dragonblocks --worldname <your_worldname>
+ To see a list of worlds do:
+ $ bin/dragonblocks --worldlist
+ You can select a world from this list or create a new one.
+ For more info run:
+ $ bin/dragonblocks --help
+ 2.4.2. Installation
+ You can install dragonblocks by doing:
+ $ sudo ./install.sh
+ It will copy the dragonblocks directory to /usr/share/dragonblocks and place a script in
+ /usr/bin/dragonblocks that starts dragonblocks.
+
+3. Developing
+ 3.1. The Lua Modding API
+ If you want to add a lua file to the game, place it in the game folder and add a dofile() in game/init.lua
+ Note: The lua api currently consists of only two functions. If you want to add something I'm open to ideas or code.
+ dragonblocks.register_node(obj)
+ Register a new node
+ obj has to contain:
+ name: string - the itemstring. It should follow the convention "modulename:nodename"
+ texture: string - the texture path. Textures should be placed in the textures folder and have to be 16 x 16 px.
+ obj can contain:
+ stable: boolean - Nodes like water or air are unstable. [default = true]
+ hidden: boolean - The Node will be hidden in the invenotry. [default = false]
+ translucent: boolean - Whether the node's texture should be transparent. [default = false]
+ dragonblocks.log(text)
+ Log something.
+ 3.2. The C++ API
+ The C++ API is probably to big to explain here, but if you do C++ you should understand it. In case you have questions, feel free to
+ ask them on github. You can also contribute code if you want.
--- /dev/null
+local cpp_last_node = 0
+function cpp_get_next_node()
+ cpp_last_node = cpp_last_node + 1
+ return core.nodes[cpp_last_node]
+end
--- /dev/null
+dragonblocks.log = function(text)
+ print("[LUA] "..text)
+end
--- /dev/null
+core = {}
+dragonblocks = {}
+dragonblocks.settings = {}
+
+dofile("builtin/register.lua")
+dofile("builtin/functions.lua")
+dofile("builtin/cpp.lua")
+
+--local popenfile = io.popen("ls game")
+--for filename in popenfile:lines() do
+-- dofile("game/"..filename.."/init.lua")
+--end
+--popenfile:close()
+
+dofile("game/init.lua")
--- /dev/null
+core.nodes = {}
+dragonblocks.register_node = function(obj)
+ if obj and obj.name and obj.texture then
+ core.nodes[#core.nodes+1] = {}
+ core.nodes[#core.nodes].name = obj.name
+ core.nodes[#core.nodes].texture = obj.texture
+ core.nodes[#core.nodes].stable = false
+ if obj.stable == nil or obj.stable == true then
+ core.nodes[#core.nodes].stable = true
+ end
+ core.nodes[#core.nodes].hidden = false
+ if obj.hidden then
+ core.nodes[#core.nodes].hidden = true
+ end
+ core.nodes[#core.nodes].translucent = false
+ if obj.translucent then
+ core.nodes[#core.nodes].translucent = true
+ end
+ end
+end
--- /dev/null
+dragonblocks.log(" ____ _ _ _ ")
+dragonblocks.log("| _ \\ _ __ __ _ __ _ ___ _ __ | |__ | | ___ ___| | _____ ")
+dragonblocks.log("| | | | '__/ _` |/ _` |/ _ \\| '_ \\| '_ \\| |/ _ \\ / __| |/ / __| ")
+dragonblocks.log("| |_| | | | (_| | (_| | (_) | | | | |_) | | (_) | (__| <\\__ \\ ")
+dragonblocks.log("|____/|_| \\__,_|\\__, |\\___/|_| |_|_.__/|_|\\___/ \\___|_|\\_\\___/ ")
+dragonblocks.log(" |___/ ")
+dofile("game/nodes.lua")
--- /dev/null
+dragonblocks.register_node({
+ name = "dragonblocks:air",
+ texture = "textures/air.png",
+ stable = false,
+ hidden = true,
+ translucent = true,
+})
+dragonblocks.register_node({
+ name = "dragonblocks:stone",
+ texture = "textures/stone.png"
+})
+dragonblocks.register_node({
+ name = "dragonblocks:dirt",
+ texture = "textures/dirt.png"
+})
+dragonblocks.register_node({
+ name = "dragonblocks:grass",
+ texture = "textures/grass.png"
+})
+dragonblocks.register_node({
+ name = "dragonblocks:wood",
+ texture = "textures/wood.png"
+})
+dragonblocks.register_node({
+ name = "dragonblocks:water",
+ texture = "textures/water.png",
+ stable = false,
+ translucent = true,
+})
+dragonblocks.register_node({
+ name = "dragonblocks:leaves",
+ texture = "textures/leaves.png",
+ translucent = true,
+})
+dragonblocks.register_node({
+ name = "dragonblocks:bedrock",
+ texture = "textures/bedrock.png"
+})
+dragonblocks.register_node({
+ name = "dragonblocks:mese",
+ texture = "textures/mese.png"
+})
+dragonblocks.register_node({
+ name = "dragonblocks:sand",
+ texture = "textures/sand.png"
+})
+dragonblocks.register_node({
+ name = "dragonblocks:cobble",
+ texture = "textures/cobble.png"
+})
--- /dev/null
+#! /bin/bash
+cp . /usr/share/dragonblocks -R
+echo "
+cd /usr/share/dragonblocks
+bin/dragonblocks \$*
+" > /usr/bin/dragonblocks
+chmod +x /usr/bin/dragonblocks
--- /dev/null
+#include <string>
+#include <iostream>
+#include <cstdio>
+#include <sys/types.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "mods.h"
+#include "game.h"
+using namespace std;
+
+void Game::log(string text, int level){
+ string prefix;
+ int color;
+ switch(level){
+ case WARNING:
+ color = ORANGE;
+ prefix = "WARNING";
+ break;
+ case ERROR:
+ color = RED;
+ prefix = "ERROR";
+ break;
+ case INFO:
+ color = LIGHTBLUE;
+ prefix = "INFO";
+ break;
+ case LOG:
+ color = BLUE;
+ prefix = "LOG";
+ break;
+ case EASTEREGG:
+ color = VIOLET;
+ prefix = "EASTEREGG";
+ default: break;
+ }
+ cout << "\e[3" << color << "m" << "[" << prefix << "] \e[0m" << text << endl;
+ if(logfile_fd)
+ fprintf(logfile_fd, "[%s] %s\n", prefix.c_str(), text.c_str());
+}
+void Game::log(string text){
+ log(text, LOG);
+}
+void Game::help(){
+ cout << "Usage: " << argv[0] << "[OPTIONS]" << endl;
+ cout << "Options:" << endl;
+ cout << "\t" << "-h" << "\t" << "--help" << "\t\t\t" << "Display this help and exit." << endl;
+ cout << "\t" << "-v" << "\t" << "--version" << "\t\t" << "Display version info." << endl;
+ cout << "\t" << "-p" << "\t" << "--worldpath [PATH]" << "\t" << "Set world path." << endl;
+ cout << "\t" << "-w" << "\t" << "--worldname [PATH]" << "\t" << "Select world by name (Worlds are placed in ~/.dragonblocks/worlds/)." << endl;
+ cout << "\t" << "-r" << "\t" << "--worldlist" << "\t\t" << "Show a list of your worlds." << endl;
+ cout << "\t" << "-s" << "\t" << "--seed [NUMBER]" << "\t\t" << "Set seed." << endl;
+ cout << "\t" << "-l" << "\t" << "--logfile [PATH]" << "\t" << "Set logfile." << endl;
+}
+void Game::version(){
+ cout << "DRAGONBLOCKS BEDROCK EDITION" << endl;
+ cout << "Written in C++" << endl;
+ cout << "An Open Source Project by Elias Fleckenstein" << endl;
+ cout << "Dragonblocks " << VERSION << endl;
+}
+void Game::worldlist(){
+ log("Your worlds:");
+ DIR *folder;
+ struct dirent *entry;
+ int files = 0;
+
+ folder = opendir(((string)getenv("HOME")+"/.dragonblocks/worlds/").c_str());
+ if(!folder){
+ Game::log("Cant Open World Directory", ERROR);
+ exit(EXIT_FAILURE);
+ }
+ while(entry = readdir(folder))
+ {
+ files++;
+ if(files > 2)
+ cout << "\t" << entry->d_name;
+ }
+ if(files <= 2)
+ cout << "\tYou have no Worlds yet.";
+ cout << endl;
+ closedir(folder);
+
+ return;
+}
--- /dev/null
+#ifndef _GAME_H_
+#define _GAME_H_
+#include <string>
+#include <cstdio>
+
+#include "map.h"
+#include "inventory.h"
+
+#define WARNING 1
+#define ERROR 2
+#define INFO -1
+#define EASTEREGG 17
+#define LOG 0
+
+#define VERSION "3.0"
+
+#define BLACK 0
+#define RED 1
+#define GREEN 2
+#define ORANGE 3
+#define BLUE 4
+#define VIOLET 5
+#define LIGHTBLUE 6
+#define GREY 7
+
+class Game{
+ public:
+ static int *argc;
+ static char **argv;
+ static int seed;
+ static std::string mapfile;
+ static std::string logfile;
+ static FILE *logfile_fd;
+ static Map *map;
+ static void log(std::string);
+ static void log(std::string, int);
+ static void help();
+ static void version();
+ static void worldlist();
+ static Inventory *inventory;
+};
+
+#endif
--- /dev/null
+#include <GL/freeglut.h>
+#include <cstring>
+#include <string>
+#include <iostream>
+#include "graphics.h"
+#include "util.h"
+#include "game.h"
+#include "mapgen.h"
+
+position Graphics::pointed;
+position Graphics::pos = {MAPWIDTH/2 - DISPLAYWIDTH/2, MAPHEIGHT/2 - DISPLAYHEIGHT/2};
+
+using namespace std;
+void Graphics::display(){
+ glClear(GL_COLOR_BUFFER_BIT);
+ //sky
+ drawRectangle(0, 0, DISPLAYWIDTH*BLOCKWIDTH, DISPLAYHEIGHT*BLOCKWIDTH, "#87CEEB");
+ //map
+ for(int x = 0; x < DISPLAYWIDTH; x++){
+ for(int y = 0; y < DISPLAYHEIGHT; y++){
+ Game::map -> getNode(x+pos.x, y+pos.y) -> texture -> draw(x*BLOCKWIDTH, y*BLOCKWIDTH, BLOCKWIDTH, BLOCKWIDTH);
+ }
+ }
+ //pointed block
+ if(pointed.x < DISPLAYWIDTH){
+ drawRectangle(pointed.x * BLOCKWIDTH, pointed.y * BLOCKWIDTH, BLOCKWIDTH, 1, COLOR_BLACK);
+ drawRectangle(pointed.x * BLOCKWIDTH + BLOCKWIDTH - 1, pointed.y * BLOCKWIDTH, 1, BLOCKWIDTH, COLOR_BLACK);
+ drawRectangle(pointed.x * BLOCKWIDTH, pointed.y * BLOCKWIDTH + BLOCKWIDTH - 1, BLOCKWIDTH, 1, COLOR_BLACK);
+ drawRectangle(pointed.x * BLOCKWIDTH, pointed.y * BLOCKWIDTH, 1, BLOCKWIDTH, COLOR_BLACK);
+ }
+ //inventory
+
+ drawRectangle(DISPLAYWIDTH*BLOCKWIDTH, 0, INVWIDTH, DISPLAYHEIGHT*BLOCKWIDTH, "#B4B4B4");
+ drawRectangle(DISPLAYWIDTH*BLOCKWIDTH, Game::inventory->selected * INVWIDTH, INVWIDTH, INVWIDTH, "#636363");
+ for(int i = 0; i < Game::inventory->count; i++)
+ Game::inventory -> getSlot(i) -> texture -> draw(BLOCKWIDTH*DISPLAYWIDTH + (INVWIDTH-INVBLOCKWIDTH)/2, i * INVWIDTH + (INVWIDTH-INVBLOCKWIDTH)/2, INVBLOCKWIDTH, INVBLOCKWIDTH);
+ //infotext
+ writeText(5, 5, (string)"Dragonblocks "+VERSION, GLUT_BITMAP_9_BY_15, COLOR_WHITE);
+ string infotext = "pos: ("+to_string(pos.x)+", "+to_string(pos.y)+"), seed: "+to_string(Game::seed);
+ if(pointed.x < DISPLAYWIDTH)
+ infotext += ", pointed: "+ Game::map->getNode(pointed.x+pos.x, pointed.y+pos.y)->name + "("+to_string(pointed.x+pos.x)+", "+to_string(pointed.y+pos.y)+")";
+ writeText(5, 20, infotext, GLUT_BITMAP_9_BY_15, COLOR_WHITE);
+ //writeText(5, 35, "world: "+Game::mapfile, GLUT_BITMAP_9_BY_15, COLOR_WHITE);
+ glFlush();
+}
+void Graphics::reshape(int width, int height){
+ glViewport(0, 0, width, height); /* Establish viewing area to cover entire window. */
+ glMatrixMode(GL_PROJECTION); /* Start modifying the projection matrix. */
+ glLoadIdentity(); /* Reset project matrix. */
+ glOrtho(0, width, 0, height, -1, 1); /* Map abstract coords directly to window coords. */
+ glScalef(1, -1, 1); /* Invert Y axis so increasing Y goes down. */
+ glTranslatef(0, -height, 0); /* Shift origin up to upper-pos.x corner. */
+}
+void Graphics::init(){
+ glutInit(Game::argc, Game::argv);
+ glutCreateWindow("Dragonblocks");
+ glutReshapeWindow(DISPLAYWIDTH*BLOCKWIDTH+INVWIDTH, DISPLAYHEIGHT*BLOCKWIDTH);
+ glutDisplayFunc(display);
+ glutReshapeFunc(reshape);
+ glutKeyboardFunc(keyboard);
+ glutSpecialFunc(special);
+ glutMouseFunc(mouse);
+ glutPassiveMotionFunc(motion);
+ glutMotionFunc(motion);
+ glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS);
+ glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
+ glutMainLoop();
+}
+void Graphics::keyboard(unsigned char key, int x, int y){
+}
+void Graphics::special(int key, int x, int y){
+ switch(key){
+ case GLUT_KEY_LEFT:
+ if(pos.x > 0)
+ pos.x--;
+ break;
+ case GLUT_KEY_UP:
+ if(pos.y > 0)
+ pos.y--;
+ break;
+ case GLUT_KEY_RIGHT:
+ if(pos.x < MAPWIDTH-DISPLAYWIDTH)
+ pos.x++;
+ break;
+ case GLUT_KEY_DOWN:
+ if(pos.y < MAPHEIGHT-DISPLAYHEIGHT)
+ pos.y++;
+ break;
+ }
+}
+void Graphics::mouse(int key, int action, int x, int y){
+ if(action == 0){
+ if(x < BLOCKWIDTH*DISPLAYWIDTH){
+ switch(key){
+ case 0:
+ if(Game::map -> getNode(pointed.x+pos.x, pointed.y+pos.y)->stable)
+ Game::map -> setNode(pointed.x+pos.x, pointed.y+pos.y, MAPGEN_AIR);
+ break;
+ case 2:
+ if(! Game::map -> getNode(pointed.x+pos.x, pointed.y+pos.y)->stable)
+ Game::map -> setNode(pointed.x+pos.x, pointed.y+pos.y, Game::inventory->getSelectedSlot());
+ break;
+ }
+ }
+ else{
+ Game::inventory->select(y/INVWIDTH);
+ }
+ }
+}
+void Graphics::motion(int x, int y){
+ pointed.x = x / BLOCKWIDTH;
+ pointed.y = y / BLOCKWIDTH;
+}
+void Graphics::writeText(int x, int y, string text, void *font, color c){
+ glColor3f(c.red, c.green, c.blue);
+ glRasterPos2i(x, y+10);
+ char *s = strdup(text.c_str());
+ --s;
+ while(*++s)
+ glutBitmapCharacter(font, *s);
+}
+void Graphics::drawRectangle(int x, int y, int width, int height, color c){
+ glColor3f(c.red, c.green, c.blue);
+ glBegin(GL_POLYGON);
+ glVertex2i(x, y);
+ glVertex2i(x+width, y);
+ glVertex2i(x+width, y+height);
+ glVertex2i(x, y+height);
+ glEnd();
+}
--- /dev/null
+#ifndef _GRAPHICS_H_
+#define _GRAPHICS_H_
+
+#include <string>
+#include "texture.h"
+#include "util.h"
+
+
+#define BLOCKWIDTH 32
+#define DISPLAYWIDTH 25
+#define DISPLAYHEIGHT 25
+#define INVBLOCKWIDTH 64
+#define INVWIDTH 80
+
+#define COLOR_WHITE {1,1,1}
+#define COLOR_BLACK {0,0,0}
+
+
+class Graphics{
+ public:
+ static void init();
+
+ static position pointed;
+ static position pos;
+
+ //handlers
+ static void display();
+ static void keyboard(unsigned char, int, int);
+ static void mouse(int, int, int, int);
+ static void special(int, int, int);
+ static void reshape(int, int);
+ static void motion(int, int);
+
+ //functions
+ static void drawRectangle(int, int, int, int, color);
+ static void writeText(int, int, std::string, void*, color);
+};
+#endif
--- /dev/null
+#include "node.h"
+#include "inventory.h"
+Inventory::Inventory(){
+ count = 0;
+ selected = 0;
+ for(int i = 0; i < Node::count; i++){
+ if(! Node::getNodeById(i) -> hidden){
+ list[count] = Node::getNodeById(i);
+ count++;
+ }
+ }
+}
+Node *Inventory::getSlot(int nr){
+ if(nr >= 0 && nr < count)
+ return list[nr];
+ return list[0];
+}
+void Inventory::select(int nr){
+ if(nr >= 0 && nr < count)
+ selected = nr;
+}
+Node *Inventory::getSelectedSlot(){
+ return list[selected];
+}
--- /dev/null
+#ifndef _INVENTORY_H_
+#define _INVENTORY_H_
+
+#include "node.h"
+
+class Inventory{
+ public:
+ int count;
+ int selected;
+ Node *list[MAXNODES];
+ Node *getSlot(int);
+ void select(int);
+ Node *getSelectedSlot();
+ Inventory();
+};
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <string>
+#include <stdlib.h>
+#include <time.h>
+#include <getopt.h>
+#include <iostream>
+#include <cstdio>
+#include <unistd.h>
+#include "graphics.h"
+#include "map.h"
+#include "game.h"
+#include "node.h"
+#include "mapgen.h"
+#include "mods.h"
+#include "threads.h"
+#include "inventory.h"
+
+using namespace std;
+
+Map *Game::map;
+string Game::mapfile;
+string Game::logfile;
+int Game::seed;
+char **Game::argv;
+int *Game::argc;
+FILE *Game::logfile_fd;
+Inventory *Game::inventory;
+
+int main(int argc, char **argv){
+ if((string)argv[0] != "bin/dragonblocks"){
+ string command = "cd ..; bin/dragonblocks -w world ";
+ while(*++argv)
+ command += *argv;
+ exit(system(command.c_str()));
+ }
+ create_dir_if_not_exists((string)getenv("HOME")+"/.dragonblocks");
+ create_dir_if_not_exists((string)getenv("HOME")+"/.dragonblocks/worlds");
+ Game::argc = &argc;
+ Game::argv = argv;
+ Game::seed = time(0);
+ Game::logfile = (string)getenv("HOME")+"/.dragonblocks/dragonblocks.log";
+ const char *short_options = "hrvs:l:w:p:";
+ const struct option long_options[] = {
+ {"help", 0, NULL, 'h'},
+ {"version", 0, NULL, 'v'},
+ {"worldname", 1, NULL, 'w'},
+ {"worldpath", 1, NULL, 'p'},
+ {"worldlist", 0, NULL, 'r'},
+ {"seed", 1, NULL, 's'},
+ {"logfile", 1, NULL, 'l'},
+ {NULL, 0, NULL, 0},
+ };
+ int next_option;
+ while((next_option = getopt_long(argc, argv, short_options, long_options, NULL)) != -1){
+ switch(next_option){
+ case 'h':
+ Game::help();
+ exit(EXIT_SUCCESS);
+ break;
+ case 'v':
+ Game::version();
+ exit(EXIT_SUCCESS);
+ break;
+ case 'p':
+ Game::mapfile = optarg;
+ break;
+ case 'w':
+ Game::mapfile = (string)getenv("HOME")+"/.dragonblocks/worlds/"+optarg;
+ break;
+ case 's':
+ Game::seed = atoi(optarg);
+ break;
+ case 'l':
+ Game::logfile = optarg;
+ break;
+ case 'r':
+ Game::worldlist();
+ exit(0);
+ break;
+ case '?':
+ Game::log("Invalid Usage", ERROR);
+ Game::help();
+ exit(EXIT_FAILURE);
+ break;
+ };
+ }
+ if(Game::logfile_fd = fopen(Game::logfile.c_str(), "a"))
+ fprintf(Game::logfile_fd, "\n--------------------------------------------------\n");
+ else
+ Game::log((string)"Failed to open log file " + Game::logfile, WARNING);
+ Game::log((string)"Welcome to Dragonblocks "+VERSION);
+ srand(Game::seed);
+ new Node("unknown_node", "textures/unknown_node.png", true, true, false);
+ Mods::init();
+ Game::inventory = new Inventory();
+ Game::map = new Map();
+ if(Game::mapfile == ""){
+ Game::log("No World Specified", ERROR);
+ exit(EXIT_FAILURE);
+ }
+ if(fopen(Game::mapfile.c_str(), "r"))
+ Game::map->load();
+ else
+ Mapgen();
+ Threads::startMapBackupThread();
+ Threads::startGraphicUpdateThread();
+ Threads::addSignalHandlers();
+ Graphics::init();
+ Game::log("Closed Window, Exiting.");
+ Game::map -> save();
+ return 0;
+}
+
--- /dev/null
+#include <string>
+#include <stdlib.h>
+#include <cstring>
+#include <cstdio>
+#include "map.h"
+#include "node.h"
+#include "game.h"
+using namespace std;
+Node *Map::getNode(int x, int y){
+ if(x < MAPWIDTH && y < MAPHEIGHT && x > -1 && y > -1)
+ return content[x][y];
+ return NULL;
+}
+void Map::setNode(int x, int y, Node *n){
+ if(x < MAPWIDTH && y < MAPHEIGHT && x > -1 && y > -1)
+ content[x][y] = n;
+}
+void Map::setNode(int x, int y, string n){
+ setNode(x, y, Node::getNodeByName(n));
+}
+void Map::load(){
+ Game::log("Loading Map from " + Game::mapfile);
+ FILE *mapfile = fopen(Game::mapfile.c_str(), "r");
+ if(mapfile){
+ for(int x = 0; x < MAPWIDTH; x++)
+ for(int y = 0; y < MAPHEIGHT; y++){
+ char buffer[512] = {0};
+ fscanf(mapfile, "%s", buffer);
+ setNode(x, y, buffer);
+ }
+ fclose(mapfile);
+ Game::log("Loaded Map");
+ }
+ else{
+ Game::log("Could not Load Map.", ERROR);
+ exit(EXIT_FAILURE);
+ }
+}
+void Map::save(){
+ Game::log("Saving Map to " + Game::mapfile);
+ FILE *mapfile = fopen(Game::mapfile.c_str(), "w");
+ if(mapfile){
+ for(int x = 0; x < MAPWIDTH; x++){
+ for(int y = 0; y < MAPHEIGHT; y++)
+ fprintf(mapfile,"%s ", getNode(x,y)->name.c_str());
+ fprintf(mapfile, "\n");
+ }
+ fclose(mapfile);
+ Game::log("Saved Map");
+ }
+ else
+ Game::log("Could not Save Map.", ERROR);
+}
--- /dev/null
+#ifndef _MAP_H_
+#define _MAP_H_
+
+#include <string>
+
+#include "node.h"
+
+#define MAPWIDTH 500
+#define MAPHEIGHT 100
+
+class Map{
+ public:
+ Node *getNode(int, int);
+ void setNode(int, int, Node *);
+ void setNode(int, int, std::string);
+ void load();
+ void save();
+ private:
+ Node *content[MAPWIDTH][MAPHEIGHT];
+};
+#endif
--- /dev/null
+#include <string>
+#include <iostream>
+#include <stdlib.h>
+#include <math.h>
+
+#include "mapgen.h"
+#include "map.h"
+#include "node.h"
+#include "util.h"
+#include "game.h"
+
+using namespace std;
+void Mapgen(){
+ Game::log("Generating Map");
+ //Air
+ for(int x = 0; x < MAPWIDTH; x++){
+ for(int y = 0; y < MAPHEIGHT; y++){
+ Game::map->setNode(x,y,MAPGEN_AIR);
+ }
+ }
+ //Seed 13 Easteregg
+ if(Game::seed == 13){
+ for(int x = 0; x < MAPWIDTH; x++){
+ for(int y = 0; y < MAPHEIGHT; y++){
+ Game::map->setNode(x,y,MAPGEN_BEDROCK);
+ }
+ }
+ Game::log("LOL Seed 13", EASTEREGG);
+ return;
+ }
+ //Bottom
+ int bottom[MAPWIDTH];
+ bottom[0] = random(MAPHEIGHT/2 - MAPHEIGHT/10, MAPHEIGHT/2);
+ for(int x = 1; x < MAPWIDTH; x++){
+ bottom[x] = bottom[x-1] + random(-1,1);
+ }
+ //Smooth
+ for(int x = 1; x < MAPWIDTH-1; x++){
+ if(bottom[x] > bottom[x+1] && bottom[x] > bottom[x-1]) bottom[x]--;
+ else if(bottom[x] < bottom[x+1] && bottom[x] < bottom[x-1])bottom[x]++;
+ }
+ //Dirt with Grass
+ for(int x = 0; x < MAPWIDTH; x++){
+ Game::map->setNode(x,bottom[x],MAPGEN_GRASS);
+ }
+ //Dirt
+ for(int x = 0; x < MAPWIDTH; x++){
+ for(int y = bottom[x]+1; y < bottom[x]+5; y++){
+ Game::map->setNode(x,y,MAPGEN_DIRT);
+ }
+ }
+ //Stone
+ for(int x = 0; x < MAPWIDTH; x++){
+ for(int y = bottom[x]+5; y < MAPHEIGHT; y++){
+ Game::map->setNode(x,y,MAPGEN_STONE);
+ }
+ }
+ //Mese
+ for(int x = 0; x < MAPWIDTH; x++){
+ for(int y = bottom[x]+10; y < MAPHEIGHT; y++){
+ if((rand() % 100) == 0) Game::map->setNode(x,y,MAPGEN_MESE);
+ }
+ }
+ //Bedrock
+ for(int x = 0; x < MAPWIDTH; x++){
+ Game::map->setNode(x,MAPHEIGHT-1,MAPGEN_BEDROCK);
+ if(random(0,1) == 0){
+ Game::map->setNode(x,MAPHEIGHT-2,MAPGEN_BEDROCK);
+ if(random(0,2) == 0){
+ Game::map->setNode(x,MAPHEIGHT-3,MAPGEN_BEDROCK);
+ }
+ }
+ }
+ //Water
+ int flatcount = 0;
+ for(int x = 1; x < MAPWIDTH; x++){
+ if(bottom[x] == bottom[x-1]) flatcount++;
+ else if(flatcount > 7){
+ int leftborder = rand() % 2;
+ int rightborder = rand() % 2;
+ for(int mx = x-flatcount-3; mx < x+2; mx++){
+ for(int y = bottom[mx];y < bottom[mx]+5;y++){
+ if(Game::map->getNode(mx,y) == MAPGEN_STONE) break;
+ if(Game::map->getNode(mx,y) == MAPGEN_DIRT || Game::map->getNode(mx,y) == MAPGEN_GRASS) Game::map->setNode(mx,y,MAPGEN_SAND);
+ }
+ }
+ for(int mx = x-flatcount+leftborder;mx < x-1-rightborder;mx++) Game::map->setNode(mx,bottom[x-1],MAPGEN_WATER);
+ for(int mx = x-flatcount+1+leftborder;mx < x-2-rightborder;mx++) Game::map->setNode(mx,bottom[x-1]+1,MAPGEN_WATER);
+ for(int mx = x-flatcount+2+leftborder; mx < x-3-rightborder;mx++) Game::map->setNode(mx,bottom[x-1]+2,MAPGEN_WATER);
+ flatcount = 0;
+ }
+ else flatcount = 0;
+ }
+ //Tree
+ int treecount = rand() % MAPWIDTH/10;
+ int treepos[treecount];
+ for(int i = 0; i < treecount; i++){
+ while(true){
+ bool cont = true;
+ treepos[i] = rand() % MAPWIDTH;
+ for(int j = 0; j < i; j++){
+ if(abs(treepos[j]-treepos[i]) < 3) cont = false;
+ }
+ if(!(Game::map->getNode(treepos[i],bottom[treepos[i]]) == MAPGEN_GRASS)) cont = false;
+ if(cont) break;
+ }
+ int x = treepos[i];
+ int y = bottom[x] - 1;
+ Game::map->setNode(x,y,MAPGEN_WOOD);
+ Game::map->setNode(x,y-1,MAPGEN_WOOD);
+ Game::map->setNode(x,y-2,MAPGEN_LEAVES);
+ Game::map->setNode(x,y-3,MAPGEN_LEAVES);
+ Game::map->setNode(x,y-4,MAPGEN_LEAVES);
+ Game::map->setNode(x-1,y-2,MAPGEN_LEAVES);
+ Game::map->setNode(x-1,y-3,MAPGEN_LEAVES);
+ Game::map->setNode(x-1,y-4,MAPGEN_LEAVES);
+ Game::map->setNode(x+1,y-2,MAPGEN_LEAVES);
+ Game::map->setNode(x+1,y-3,MAPGEN_LEAVES);
+ Game::map->setNode(x+1,y-4,MAPGEN_LEAVES);
+ }
+ }
--- /dev/null
+#ifndef _MAPGEN_H_
+#define _MAPGEN_H_
+
+#include "map.h"
+
+#define MAPGEN_AIR Node::getNodeByName("dragonblocks:air")
+#define MAPGEN_GRASS Node::getNodeByName("dragonblocks:grass")
+#define MAPGEN_DIRT Node::getNodeByName("dragonblocks:dirt")
+#define MAPGEN_STONE Node::getNodeByName("dragonblocks:stone")
+#define MAPGEN_BEDROCK Node::getNodeByName("dragonblocks:bedrock")
+#define MAPGEN_MESE Node::getNodeByName("dragonblocks:mese")
+#define MAPGEN_LEAVES Node::getNodeByName("dragonblocks:leaves")
+#define MAPGEN_WOOD Node::getNodeByName("dragonblocks:wood")
+#define MAPGEN_WATER Node::getNodeByName("dragonblocks:water")
+#define MAPGEN_SAND Node::getNodeByName("dragonblocks:sand")
+
+void Mapgen();
+
+
+
+#endif
--- /dev/null
+#include <cstring>
+#include <cstdio>
+#include <cerrno>
+#include <string>
+#include <iostream>
+
+#include "node.h"
+#include "mods.h"
+#include "game.h"
+#include "util.h"
+#include "graphics.h"
+
+using namespace std;
+
+lua_State *Mods::lua_vm;
+
+void Mods::init(){
+ Game::log("Initializing Mods");
+ lua_vm = luaL_newstate();
+ luaL_openlibs(lua_vm);
+ if(! check_lua(luaL_dofile(lua_vm, "builtin/init.lua"))){
+ Game::log("Failed to load Builtin", ERROR);
+ exit(EXIT_FAILURE);
+ return;
+ }
+
+ for(;;){
+ lua_getglobal(lua_vm, "cpp_get_next_node");
+ if(!lua_isfunction(lua_vm, -1)){
+ Game::log("Lua is sick. What's Wrong with her?", EASTEREGG);
+ Game::log("No, seriously, something is wrong with the lua builtin (it is missing the function cpp_get_next_node)", ERROR);
+ exit(EXIT_FAILURE);
+ }
+ lua_pcall(lua_vm, 0, 1, 0);
+ if(! lua_istable(lua_vm, -1))
+ break;
+
+ lua_pushstring(lua_vm, "name");
+ lua_gettable(lua_vm, -2);
+ if(!lua_isstring(lua_vm,-1))
+ continue;
+ string name = lua_tostring(lua_vm,-1);
+ lua_pop(lua_vm, 1);
+
+ lua_pushstring(lua_vm, "texture");
+ lua_gettable(lua_vm, -2);
+ if(!lua_isstring(lua_vm,-1))
+ continue;
+ string texture = lua_tostring(lua_vm,-1);
+ lua_pop(lua_vm, 1);
+
+ lua_pushstring(lua_vm, "hidden");
+ lua_gettable(lua_vm, -2);
+ if(!lua_isboolean(lua_vm,-1))
+ continue;
+ bool hidden = lua_toboolean(lua_vm,-1);
+ lua_pop(lua_vm, 1);
+
+ lua_pushstring(lua_vm, "stable");
+ lua_gettable(lua_vm, -2);
+ if(!lua_isboolean(lua_vm,-1))
+ continue;
+ bool stable = lua_toboolean(lua_vm,-1);
+ lua_pop(lua_vm, 1);
+
+ lua_pushstring(lua_vm, "translucent");
+ lua_gettable(lua_vm, -2);
+ if(!lua_isboolean(lua_vm,-1))
+ continue;
+ bool translucent = lua_toboolean(lua_vm,-1);
+ lua_pop(lua_vm, 1);
+
+ new Node(name, texture, hidden, stable, translucent);
+ Game::log("Registered Node " + name, INFO);
+ }
+}
+bool Mods::check_lua(int code){
+ if(code == LUA_OK)
+ return true;
+ else{
+ error(lua_tostring(lua_vm, -1));
+ return false;
+ }
+}
+void Mods::error(string text){
+ Game::log("\e[34mlua: \e[0m" + text, ERROR);
+}
--- /dev/null
+#ifndef _MODS_H_
+#define _MODS_H_
+
+#include <string>
+
+extern "C"{
+ #include <lua5.3/lua.h>
+ #include <lua5.3/lauxlib.h>
+ #include <lua5.3/lualib.h>
+}
+
+class Mods{
+ public:
+ static lua_State *lua_vm;
+ static bool check_lua(int);
+ static void init();
+ static void error(std::string);
+};
+#endif
--- /dev/null
+#include <string>
+#include <stdlib.h>
+#include <iostream>
+#include "node.h"
+#include "game.h"
+#include "texture.h"
+using namespace std;
+
+int Node::count = 0;
+Node *Node::list[MAXNODES];
+
+Node::Node(string n, string t, bool h, bool s, bool tr){
+ if (Node::count >= MAXNODES)
+ Game::log("Too many registered Nodes", ERROR);
+ else{
+ name = n;
+ id = Node::count;
+ hidden = h;
+ stable = s;
+ Node::list[id] = this;
+ texture = new Texture(t,tr);
+ Node::count++;
+ }
+}
+Node *Node::getNodeById(int id){
+ if(id < count && id > -1)
+ return list[id];
+ return list[0];
+}
+Node *Node::getNodeByName(string name){
+ for(int i = 0; i < count; i++){
+ if(list[i]->name == name ){
+ return list[i];
+ }
+ }
+ Game::log("Node "+name+" not found", WARNING);
+ return list[0];
+}
+bool Node::operator==(Node *node){
+ return id == node->id;
+}
+bool Node::operator==(string n){
+ return name == n;
+}
--- /dev/null
+#ifndef _NODE_H_
+#define _NODE_H_
+#include <string>
+#include "texture.h"
+#define MAXNODES 1024
+
+
+class Node{
+ public:
+ int id;
+ std::string name;
+ static Node *getNodeById(int);
+ static Node *getNodeByName(std::string);
+ static Node *list[MAXNODES];
+ static int count;
+ bool operator==(Node *);
+ bool operator==(std::string);
+ Texture *texture;
+ bool hidden;
+ bool stable;
+ bool translucent;
+ Node(std::string, std::string, bool, bool, bool);
+};
+#endif
+
--- /dev/null
+#include <GL/freeglut.h>
+#include <png.h>
+#include <string>
+#include <iostream>
+#include <cstring>
+#include <cassert>
+#include <cstdio>
+
+#include "game.h"
+#include "graphics.h"
+#include "texture.h"
+
+
+using namespace std;
+
+
+Texture::Texture(string filename, bool t){
+ translucent = t;
+ dummyimage = false;
+ FILE *file = fopen(filename.c_str(), "rb");
+ if(!file){
+ Game::log("Failed to Load Image " + filename + ": File not found. Using a dummy Image.", WARNING);
+ dummyimage = true;
+ return;
+ }
+ png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ assert(png);
+ png_infop info = png_create_info_struct(png);
+ assert(info);
+ png_init_io(png, file);
+ png_read_info(png, info);
+ width = png_get_image_width(png, info);
+ height = png_get_image_height(png, info);
+ color_type = png_get_color_type(png, info);
+ bit_depth = png_get_bit_depth(png, info);
+ if(bit_depth == 16)
+ png_set_strip_16(png);
+ if(color_type == PNG_COLOR_TYPE_PALETTE)
+ png_set_palette_to_rgb(png);
+ #ifndef _WIN32
+ if(color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
+ png_set_expand_gray_1_2_4_to_8(png);
+ #endif
+ if(png_get_valid(png, info, PNG_INFO_tRNS))
+ png_set_tRNS_to_alpha(png);
+ if(color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_PALETTE)
+ png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
+ if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ png_set_gray_to_rgb(png);
+ png_read_update_info(png, info);
+ row_pointers = (png_bytep*)malloc(sizeof(png_bytep) *height);
+ for(int y = 0; y < height; y++) {
+ row_pointers[y] = (png_byte*)malloc(png_get_rowbytes(png,info));
+ }
+ png_read_image(png, row_pointers);
+ fclose(file);
+ png_destroy_read_struct(&png, &info, NULL);
+}
+void Texture::draw(int displayX, int displayY, int displayWidth, int displayHeight){
+ if(dummyimage){
+ Graphics::drawRectangle(displayX, displayY, displayWidth, displayHeight, {0, 0, 0});
+ return;
+ }
+ int scaleX = (float)displayWidth/width;
+ int scaleY = (float)displayHeight/height;
+ for(int y = 0; y < height; y++) {
+ png_bytep row = row_pointers[y];
+ for(int x = 0; x < width; x++) {
+ png_bytep px = &(row[x * 4]);
+ if(!translucent || px[0] || px[1] || px[2])
+ Graphics::drawRectangle(x*scaleX + displayX, y*scaleY + displayY, scaleX, scaleY, {(float)px[0]/255, (float)px[1]/255, (float)px[2]/255});
+ }
+ }
+}
--- /dev/null
+#ifndef _TEXTURE_H_
+#define _TEXTURE_H_
+
+#include <png.h>
+#include <string>
+
+class Texture{
+ public:
+ int width;
+ int height;
+ void draw(int, int, int, int);
+ Texture(std::string, bool);
+ private:
+ png_byte color_type;
+ png_byte bit_depth;
+ png_bytep *row_pointers;
+ bool dummyimage;
+ bool translucent;
+};
+#endif
--- /dev/null
+#include <pthread.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <GL/glut.h>
+#include <iostream>
+#include <algorithm>
+#include "threads.h"
+#include "graphics.h"
+#include "game.h"
+
+
+using namespace std;
+
+void Threads::startMapBackupThread(){
+ Game::log("Starting Map Backup Thread");
+ pthread_t thread_id;
+ pthread_create(&thread_id, NULL, &mapBackupThread, NULL);
+}
+void *Threads::mapBackupThread(void *unused){
+ while(true){
+ sleep(MAP_BACKUP_INTERVAL);
+ Game::map -> save();
+ }
+ return NULL;
+}
+void Threads::startGraphicUpdateThread(){
+ Game::log("Starting Graphic Update Thread");
+ pthread_t thread_id;
+ pthread_create(&thread_id, NULL, &graphicUpdateThread, NULL);
+}
+void *Threads::graphicUpdateThread(void *unused){
+ while(true){
+ usleep(1);
+ if(glutGetWindow())
+ glutPostRedisplay();
+ }
+ return NULL;
+}
+void Threads::addSignalHandlers(){
+#ifndef _WIN32
+ struct sigaction sa_sigterm;
+ sa_sigterm.sa_handler = &signal_handler;
+ sigaction(SIGTERM, &sa_sigterm, NULL);
+
+ struct sigaction sa_sigint;
+ sa_sigint.sa_handler = &signal_handler;
+ sigaction(SIGINT, &sa_sigint, NULL);
+#endif
+}
+void Threads::signal_handler(int signal_number){
+#ifndef _WIN32
+ Game::log((string)"Got "+sys_siglist[signal_number]+" Signal, Exiting.");
+ Game::map -> save();
+ exit(0);
+#endif
+}
--- /dev/null
+#ifndef _THREADS_H_
+#define _THREADS_H_
+
+#define MAP_BACKUP_INTERVAL 15
+
+class Threads{
+ public:
+ static void startMapBackupThread();
+ static void addSignalHandlers();
+ static void startGraphicUpdateThread();
+ private:
+ static void *mapBackupThread(void *);
+ static void *graphicUpdateThread(void *);
+ static void signal_handler(int);
+};
+#endif
+
--- /dev/null
+#include <stdlib.h>
+#include <cstring>
+#include <cstdio>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <cstdio>
+#include <string>
+
+#include "util.h"
+#include "game.h"
+
+using namespace std;
+int random(int min, int max){
+ return min + rand() % (max - min + 1);
+}
+color::color(const char *htmlcolor){
+ unsigned int r, g, b;
+ sscanf(htmlcolor, "#%2x%2x%2x", &r, &g, &b);
+ red = (float) r / 255;
+ green = (float) g / 255;
+ blue = (float) b / 255;
+}
+color::color(float r, float g, float b){
+ red = r;
+ green = g;
+ blue = b;
+}
+void create_dir_if_not_exists(string d){
+ const char *dir = d.c_str();
+ struct stat dir_exists_st = {0};
+ if(stat(dir, &dir_exists_st) == -1){
+ Game::log((string)dir + " doesn't exist, creating", INFO);
+ #ifdef _WIN32
+ mkdir(dir);
+ #else
+ mkdir(dir, 0700);
+ #endif
+ }
+}
--- /dev/null
+#ifndef _UTIL_H_
+#define _UTIL_H_
+#include <string>
+class color{
+ public:
+ float red;
+ float green;
+ float blue;
+ color(const char *);
+ color(float, float, float);
+};
+class position{
+ public:
+ int x;
+ int y;
+};
+
+int random(int, int);
+void create_dir_if_not_exists(std::string d);
+#endif