diff --git a/envs/models/02_gift.gt b/envs/models/02_gift.gt
new file mode 100644
index 0000000..76ded1a
--- /dev/null
+++ b/envs/models/02_gift.gt
@@ -0,0 +1,26 @@
+name=02_gift,width=6,height=4,collision=false
+__gtransparent_unit__
+4 false 0 X
+__gtransparent_unit__
+__gtransparent_unit__
+4 false 0 X
+__gtransparent_unit__
+6 false 0 X
+6 false 0 X
+4 false 0 X
+4 false 0 X
+6 false 0 X
+6 false 0 X
+4 false 0 X
+4 false 0 X
+4 false 0 X
+4 false 0 X
+4 false 0 X
+4 false 0 X
+6 false 0 X
+6 false 0 X
+4 false 0 X
+4 false 0 X
+6 false 0 X
+6 false 0 X
+; %tsgmeng::builder.texture4_0() -> auto_generated Sat Nov 02 2024
diff --git a/envs/models/03_table.gt b/envs/models/03_table.gt
new file mode 100644
index 0000000..a9c8228
--- /dev/null
+++ b/envs/models/03_table.gt
@@ -0,0 +1,62 @@
+name=03_table,width=12,height=5,collision=false
+5 false 0 X
+5 false 0 X
+5 false 0 X
+5 false 0 X
+5 false 0 X
+5 false 0 X
+5 false 0 X
+5 false 0 X
+5 false 0 X
+5 false 0 X
+5 false 0 X
+5 false 0 X
+5 false 0 X
+0 false 1 X
+__gtransparent_unit__
+__gtransparent_unit__
+__gtransparent_unit__
+__gtransparent_unit__
+__gtransparent_unit__
+__gtransparent_unit__
+__gtransparent_unit__
+__gtransparent_unit__
+0 false 1 X
+5 false 0 X
+__gtransparent_unit__
+0 false 1 X
+__gtransparent_unit__
+__gtransparent_unit__
+__gtransparent_unit__
+__gtransparent_unit__
+__gtransparent_unit__
+__gtransparent_unit__
+__gtransparent_unit__
+__gtransparent_unit__
+0 false 1 X
+__gtransparent_unit__
+__gtransparent_unit__
+0 false 1 X
+__gtransparent_unit__
+__gtransparent_unit__
+__gtransparent_unit__
+__gtransparent_unit__
+__gtransparent_unit__
+__gtransparent_unit__
+__gtransparent_unit__
+__gtransparent_unit__
+0 false 1 X
+__gtransparent_unit__
+__gtransparent_unit__
+0 false 1 X
+__gtransparent_unit__
+__gtransparent_unit__
+__gtransparent_unit__
+__gtransparent_unit__
+__gtransparent_unit__
+__gtransparent_unit__
+__gtransparent_unit__
+__gtransparent_unit__
+0 false 1 X
+__gtransparent_unit__
+; %tsgmeng::builder.texture4_0() -> auto_generated Sat Nov 02 2024
diff --git a/envs/models/smol_player.gt b/envs/models/smol_player.gt
new file mode 100644
index 0000000..a1ad2a3
--- /dev/null
+++ b/envs/models/smol_player.gt
@@ -0,0 +1,18 @@
+name=smol_player,width=4,height=4,collision=true
+4 false 0 X
+4 false 0 X
+4 false 0 X
+__gtransparent_unit__
+0 false 0 X
+0 false 0 X
+4 false 0 X
+4 false 0 X
+4 false 0 X
+4 false 0 X
+4 false 0 X
+4 false 0 X
+4 false 0 X
+__gtransparent_unit__
+4 false 0 X
+__gtransparent_unit__
+; %tsgmeng::builder.texture4_0() -> auto_generated Sat Oct 26 2024
diff --git a/lib/bin/cli/commands.cc b/lib/bin/cli/commands.cc
index 88aeee0..69c6038 100644
--- a/lib/bin/cli/commands.cc
+++ b/lib/bin/cli/commands.cc
@@ -2,10 +2,45 @@
#include
#include
+#include "../gmeng.h"
+
#include "index.h"
using string = std::string;
+
+class inspect_texture_command_t : public Gmeng_Commandline::Subcommand {
+ public:
+ inline void run(vector args) override {
+ if (args.size() < 1) {
+ LOG("~r~ERROR!~n~ please provide a texture file name.");
+ LOG("~g~Usage:~n~ ~y~" + Gmeng::global.executable + "~n~ preview ~b~file.tx~n~");
+ return;
+ };
+ LOG("Checking for texture atlas in ~g~"+args.at(0)+"~n~...");
+ if (!filesystem::exists(args.at(0))) {
+ LOG("~r~ERROR!~n~ file ~b~" + args.at(0) + "~n~ does not exist.");
+ return;
+ }
+ Gmeng::texture txt = Gmeng::LoadTexture(args.at(0));
+ std::string render = Gmeng::Util::draw_texture_string(txt);
+ MSG("~r~Gmeng~n~ TEXTURE PREVIEW:\n");
+ MSG("~_~file: `" + args.at(0) + "`~n~\n");
+ std::cout << render << std::endl;
+ };
+
+ inspect_texture_command_t(string _name, string _description) : Subcommand(_name, _description) {
+ this->info = { _name, _description };
+ };
+};
+
+static inspect_texture_command_t inspect_command("preview", "Displays a preview of a texture file");
+
+static Gmeng_Commandline::InterfaceRegistrar
+register_inspect_command(
+ std::make_unique( ( inspect_command ) )
+);
+
class test_command_t : public Gmeng_Commandline::Subcommand {
public:
inline void run(vector) override {
@@ -66,3 +101,167 @@ static Gmeng_Commandline::InterfaceRegistrar
register_editor_command(
std::make_unique ( ( editor_command ) )
);
+
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+void modify_properties(gmeng_properties_t& properties);
+
+using namespace Gmeng;
+
+class gamestate_editor_t : public Gmeng_Commandline::Subcommand {
+ public:
+ inline void run(vector args) override {
+ if (args.size() < 1) {
+ LOG("~r~ERROR!~n~ provide a file name.");
+ MSG("~g~Usage:~n~ ~y~"+Gmeng::global.executable+" ~b~gamestate~n~ \n");
+ return;
+ };
+
+ std::string filename = args.at(0);
+
+ if (!filesystem::exists(filename)) {
+ LOG("~r~ERROR!~n~ the file `"+filename+"` does not exist.");
+ MSG("~g~Usage:~n~ ~y~"+Gmeng::global.executable+" ~b~gamestate~n~ \n");
+ return;
+ };
+
+ gmeng_properties_t properties = read_properties(filename);
+
+ initscr();
+ cbreak();
+ noecho();
+ keypad(stdscr, TRUE);
+
+ modify_properties(properties);
+ writeout_properties(filename, properties);
+
+ endwin();
+ };
+
+ gamestate_editor_t(string _name, string _description) : Subcommand(_name, _description) {
+ this->info = {
+ .name = _name,
+ .description = _description
+ };
+ };
+};
+
+static gamestate_editor_t gamestate_editor_command("gamestate", "Modifies or views a gamestate binary file");
+
+static Gmeng_Commandline::InterfaceRegistrar
+register_gamestate_command(
+ std::make_unique( gamestate_editor_command )
+);
+
+void modify_properties(gmeng_properties_t& properties) {
+ int highlight = 0;
+ int mode = 0; // 0 = Main properties, 1 = Model positions
+ const int num_main_fields = 6;
+ bool exit_program = false;
+
+ while (!exit_program) {
+ clear();
+
+ // Display section based on mode
+ if (mode == 0) {
+ mvprintw(0, 0, "Modify Main Properties (Press ENTER to edit, UP/DOWN to navigate, 'm' for model positions, 'q' to quit)");
+ mvprintw(2, 0, "1. DEF_DELTAX: %d", properties.DEF_DELTAX);
+ mvprintw(3, 0, "2. DEF_DELTAY: %d", properties.DEF_DELTAY);
+ mvprintw(4, 0, "3. SKY_WIDTH: %d", properties.SKY_WIDTH);
+ mvprintw(5, 0, "4. SKY_HEIGHT: %d", properties.SKY_HEIGHT);
+ mvprintw(6, 0, "5. SKY_COLOR: %d", properties.SKY_COLOR);
+ mvprintw(7, 0, "6. A00_CAKE_INTERACT_LOOPC: %d", properties.A00_CAKE_INTERACT_LOOPC);
+ mvchgat(2 + highlight, 0, -1, A_REVERSE, 0, NULL);
+ } else {
+ int pos_index = 0;
+ mvprintw(0, 0, "Modify Model Positions (Press ENTER to edit, 'a' to add, 'd' to delete, 'm' to go back, 'q' to quit)");
+ for (const auto& [key, point] : properties.model_positions) {
+ mvprintw(2 + pos_index, 0, "%d. %s: (%d, %d)", pos_index + 1, key.c_str(), point.x, point.y);
+ if (pos_index == highlight) {
+ mvchgat(2 + pos_index, 0, -1, A_REVERSE, 0, NULL);
+ }
+ pos_index++;
+ }
+ }
+
+ int ch = getch();
+ switch (ch) {
+ case KEY_UP:
+ highlight = (highlight - 1 + (mode == 0 ? num_main_fields : properties.model_positions.size())) %
+ (mode == 0 ? num_main_fields : properties.model_positions.size());
+ break;
+ case KEY_DOWN:
+ highlight = (highlight + 1) % (mode == 0 ? num_main_fields : properties.model_positions.size());
+ break;
+ case '\n': {
+ if (mode == 0) {
+ int new_value;
+ echo();
+ mvprintw(10, 0, "Enter new value: ");
+ scanw("%d", &new_value);
+ noecho();
+
+ switch (highlight) {
+ case 0: properties.DEF_DELTAX = new_value; break;
+ case 1: properties.DEF_DELTAY = new_value; break;
+ case 2: properties.SKY_WIDTH = new_value; break;
+ case 3: properties.SKY_HEIGHT = new_value; break;
+ case 4: properties.SKY_COLOR = (color_t)new_value; break;
+ case 5: properties.A00_CAKE_INTERACT_LOOPC = new_value; break;
+ }
+ } else if (!properties.model_positions.empty()) {
+ auto it = properties.model_positions.begin();
+ std::advance(it, highlight);
+ echo();
+ int new_x, new_y;
+ mvprintw(10, 0, "Enter new x value: ");
+ scanw("%d", &new_x);
+ mvprintw(11, 0, "Enter new y value: ");
+ scanw("%d", &new_y);
+ noecho();
+
+ it->second.x = new_x;
+ it->second.y = new_y;
+ }
+ break;
+ }
+ case 'm':
+ mode = 1 - mode; // Toggle between main properties and model positions
+ highlight = 0;
+ break;
+ case 'a':
+ if (mode == 1) {
+ char new_key[64];
+ int new_x, new_y;
+ echo();
+ mvprintw(10, 0, "Enter new key: ");
+ getstr(new_key);
+ mvprintw(11, 0, "Enter x value: ");
+ scanw("%d", &new_x);
+ mvprintw(12, 0, "Enter y value: ");
+ scanw("%d", &new_y);
+ noecho();
+
+ properties.model_positions[std::string(new_key)] = {new_x, new_y};
+ }
+ break;
+ case 'd':
+ if (mode == 1 && !properties.model_positions.empty()) {
+ auto it = properties.model_positions.begin();
+ std::advance(it, highlight);
+ properties.model_positions.erase(it);
+ highlight = std::min(highlight, static_cast(properties.model_positions.size()) - 1);
+ }
+ break;
+ case 'q':
+ exit_program = true;
+ break;
+ }
+ }
+};
diff --git a/lib/bin/gmeng.h b/lib/bin/gmeng.h
index 69de067..20f6f8e 100755
--- a/lib/bin/gmeng.h
+++ b/lib/bin/gmeng.h
@@ -316,7 +316,7 @@ namespace Gmeng {
/// "-d" suffix means the version is a developer version, high unstability level
/// "-b" suffix means the version is a beta version, low unstability level but unpolished
/// "-c" suffix means the version is a coroded version, low to medium unstability level but specific methods will not perform as expected
- static std::string version = "10.1.0-d";
+ static std::string version = "10.1.0";
enum color_t {
WHITE = 0,
BLUE = 1,
@@ -739,7 +739,7 @@ static void init_logc(int ms = 250) {
return;
#endif
__gmeng_write_log__("gmeng.log", "-- cleared previous log --\n", false);
- __gmeng_write_log__("gmeng.log", "Gmeng: Go-To Console Game Engine.\nSPAWN(1) = v_success / at " + get_curtime() + "/" + get_curdate() + "\ncontroller_t of termui/_udisplay_of(GMENG, window) handed over to: controller_t(gmeng::threads::get(0))\n");
+ __gmeng_write_log__("gmeng.log", "Gmeng "+Gmeng::version+" (build " + GMENG_BUILD_NO + ").\n\nDocumentation available in https://gmeng.org.\nGmeng is an open source project. https://gmeng.org/git.\nPlease report bugs or unexpected behaviour at https://gmeng.org/report.\n\nGmeng: Go-To Console Game Engine.\n\nSPAWN(1) = v_success / at " + get_curtime() + "/" + get_curdate() + "\ncontroller_t of termui/_udisplay_of(GMENG, window) handed over to: controller_t(gmeng::threads::get(0))\n");
__gmeng_write_log__("gmeng.log", "----------------------------------\nExecutable Name: " + Gmeng::global.executable + "\nCurrent Working Directory: " + Gmeng::global.pwd + "\nCurrent User: " + Gmeng::global.user + "\n----------------------------------\n", true);
__gmeng_write_log__("gmeng.log", "Global Variables\n\t- devmode: " + boolstr(Gmeng::global.dev_mode) + "\n\t- debugger: " + boolstr(Gmeng::global.debugger) + "\n\t- silenced: " + boolstr(Gmeng::global.shush) + "\n\t- dont_hold_back: " + boolstr(Gmeng::global.dont_hold_back) + "\n----------------------------------\n", true);
@@ -843,14 +843,14 @@ static std::string ws2s(const std::wstring& wstr) {
///// __controller_satisfy__
///// OS Check for windows
-static void __explain_why_i_cannot_run_to_dumbass_using_windows() {
- __annot__(__explain_why_i_cannot_run_to_dumbass_using_windows, "explains to a user using windows why windows cannot run gmeng.");
- __functree_call__(__explain_why_i_cannot_run_to_dumbass_using_windows);
+static void print_windows_error_message() {
+ __annot__(print_windows_error_message, "explains to a user using windows why windows cannot run gmeng.");
+ __functree_call__(print_windows_error_message);
std::cout << Gmeng::colors[4] << "libgmeng-abi: __excuse__" << std::endl;
std::cout << "INTERNAL: __gmeng_platform__, __gmeng_threading__, __stdlib__, __libc++-abi__, __std_com_apple_main_pthread__" << std::endl;
std::cout << "Gmeng is not available in a core-platform other than darwin ( apple_kernel )." << std::endl;
std::cout << "current_platform: win32 ( WINDOWS_NT )" << std::endl;
- std::cout << "__gmeng_halt_execution__( CAUSE( gmeng::global.v_exceptions->__find__( \"controller.platform\" ) ) && CAUSE( \"__environment_not_suitable__\" ) )" << std::endl;
+ std::cout << "__gmeng_halt_execution__( CAUSE( gmeng::global.v_exceptions->__find__( \"controller.platform\" ) ) && CAUSE( \"__environment_not_suitable__\" ) )" << Gmeng::resetcolor << std::endl;
exit(1);
};
@@ -858,7 +858,7 @@ static void patch_argv_global(int argc, char* argv[]) {
__annot__(patch_argv_global, "patches the Gmeng::global variable with the command-line arguments.");
__functree_call__(patch_argv_global);
#if _WIN32
- __explain_why_i_cannot_run_to_dumbass_using_windows();
+ print_windows_error_message();
return;
#endif
Gmeng::global.pwd = get_cwd();
diff --git a/lib/bin/src/gmeng.cpp b/lib/bin/src/gmeng.cpp
index 24eb6c5..7a8264e 100755
--- a/lib/bin/src/gmeng.cpp
+++ b/lib/bin/src/gmeng.cpp
@@ -5,6 +5,7 @@
#include
#include
#include
+#include
#include
#include
#include "../gmeng.h"
@@ -36,7 +37,7 @@ std::vector g_trace_trajectory(int x1, int y1, int x2, int y2) {
Objects::coord point;
point.x = x2; point.y = y2;
coordinates.push_back(point);
- return coordinates;
+return coordinates;
}
namespace Gmeng {
@@ -455,12 +456,14 @@ static void _gremote_server_apl(bool state, std::string aplpass) {
/// GMENG EVENTLOOP IMPLEMENTATION
#include
+#ifndef _WIN32
#include
+#endif
#include
#include
-
+#ifndef _WIN32
namespace Gmeng::TerminalUtil {
void enable_mouse_tracking() { std::cout << "\033[?1006h\033[?1003h\n" << std::flush; };
void disable_mouse_tracking() { std::cout << "\033[?1006l\033[?1003l\n" << std::flush; };
@@ -501,7 +504,7 @@ namespace Gmeng::TerminalUtil {
};
};
};
-
+#endif
namespace Gmeng {
@@ -585,6 +588,10 @@ namespace Gmeng {
typedef struct EventLoop {
int id; Gmeng::Level* level;
+ /// Processes, used for registering event calls for the
+ /// next tick of the event loop. Called with `UPDATE` event
+ vector processes;
+
vector hooks;
vector defaults;
@@ -593,18 +600,39 @@ namespace Gmeng {
EventLoop( vector hooks_ ) : hooks(hooks_) {
this->id = g_mkid();
gm_log("" $(id) ": created main game eventloop with id " $(this->id) ".");
- TerminalUtil::set_raw_mode(true);
- gm_log("" $(id) ": set terminal state to raw mode.");
+
+#ifndef _WIN32
TerminalUtil::enable_mouse_tracking();
gm_log("" $(id) ": enabled mouse tracking");
+ TerminalUtil::set_raw_mode(true);
+ gm_log("" $(id) ": set terminal state to raw mode.");
+#endif
};
~EventLoop() {
+#ifndef _WIN32
gm_log("" $(id) ": destroyed eventloop " $(this->id) ".");
gm_log("" $(id) ": disabled mouse tracking");
TerminalUtil::set_non_blocking(false);
gm_log(""$(id)": disabled non blocking input mode");
TerminalUtil::disable_mouse_tracking();
+#endif
+ };
+
+ void next_tick(handler_function_type handler) {
+ int id = g_mkid();
+ this->processes.push_back({ id, { UPDATE }, handler });
+ };
+
+ void progress_tick() {
+ int i = 0;
+ for (auto proc : this->processes) {
+ /// delete the process
+ this->processes.erase(this->processes.begin()+i);
+ proc.handler(this->level, &Gmeng::NO_EVENT_INFO);
+ i++;
+ };
+ /// clear next tick processes.
};
void add_hook(vector events, handler_function_type handler) {
@@ -664,6 +692,7 @@ namespace Gmeng {
#define MOUSE_REST_1_CHECKER(x) x == 65 ? Gmeng::MOUSE_SCROLL_DOWN : MOUSE_REST_2_CHECKER(x)
#define SELECT_MOUSE_EVENT(x) x == 64 ? Gmeng::MOUSE_SCROLL_UP : MOUSE_REST_1_CHECKER(x)
+#ifndef _WIN32
/// runs an event loop instance
/// (this means handling the level as the main event loop / the instance of the game)
int do_event_loop(Gmeng::EventLoop* ev) {
@@ -679,6 +708,7 @@ int do_event_loop(Gmeng::EventLoop* ev) {
Gmeng::_ucreate_thread([&]() {
while (!ev->cancelled) {
ev->call_event(Gmeng::UPDATE, Gmeng::NO_EVENT_INFO);
+ ev->progress_tick();
};
});
@@ -753,3 +783,123 @@ int do_event_loop(Gmeng::EventLoop* ev) {
Gmeng::_uclear_threads();
return 0;
};
+#endif
+
+typedef struct {
+ int DEF_DELTAX;
+ int DEF_DELTAY;
+
+ int SKY_WIDTH;
+ int SKY_HEIGHT;
+
+ Gmeng::color_t SKY_COLOR;
+
+ std::unordered_map model_positions;
+
+ int A00_CAKE_INTERACT_LOOPC;
+} gmeng_properties_t;
+
+static gmeng_properties_t default_properties = {
+ 50, 25,
+
+ 100, 100,
+
+ Gmeng::BLUE,
+
+ {
+ { "player", { 0,0 } },
+
+ { "table1", { 13, 21 } },
+ { "table2", { 24, 21 } },
+
+ { "cake", { 16, 17 } },
+
+ { "gift1", { 0, 22 } },
+ { "gift2", { 44, 22 } },
+
+ { "CAKE_INTERACT_TIMES", { 1, 20 } },
+ },
+
+ 100
+};
+
+#if _WIN32
+/// WINDOWS IMPLEMENTATIONS
+/// NOT COMING ANYTIME SOON.
+
+int do_event_loop(Gmeng::EventLoop* ev) {
+ return -1;
+};
+
+#endif
+
+void writeout_properties(const std::string& filename, const gmeng_properties_t& properties) {
+ std::ofstream outFile(filename, std::ios::binary);
+ if (!outFile) {
+ throw std::ios_base::failure("Failed to open file for writing");
+ }
+
+ // Write the int fields
+ outFile.write(reinterpret_cast(&properties.DEF_DELTAX), sizeof(properties.DEF_DELTAX));
+ outFile.write(reinterpret_cast(&properties.DEF_DELTAY), sizeof(properties.DEF_DELTAY));
+ outFile.write(reinterpret_cast(&properties.SKY_WIDTH), sizeof(properties.SKY_WIDTH));
+ outFile.write(reinterpret_cast(&properties.SKY_HEIGHT), sizeof(properties.SKY_HEIGHT));
+ outFile.write(reinterpret_cast(&properties.SKY_COLOR), sizeof(properties.SKY_COLOR));
+ outFile.write(reinterpret_cast(&properties.A00_CAKE_INTERACT_LOOPC), sizeof(properties.A00_CAKE_INTERACT_LOOPC));
+
+ size_t map_size = properties.model_positions.size();
+ outFile.write(reinterpret_cast(&map_size), sizeof(map_size));
+
+ for (const auto& [key, drawpoint] : properties.model_positions) {
+ size_t key_size = key.size();
+ outFile.write(reinterpret_cast(&key_size), sizeof(key_size));
+ outFile.write(key.c_str(), key_size);
+
+ // Write the drawpoint struct (x and y)
+ outFile.write(reinterpret_cast(&drawpoint.x), sizeof(drawpoint.x));
+ outFile.write(reinterpret_cast(&drawpoint.y), sizeof(drawpoint.y));
+ }
+
+ outFile.close();
+};
+
+gmeng_properties_t read_properties(const std::string& filename) {
+ std::ifstream inFile(filename, std::ios::binary);
+ if (!inFile) {
+ throw std::ios_base::failure("Failed to open file for reading");
+ }
+
+ gmeng_properties_t properties;
+
+ // Read the int fields
+ inFile.read(reinterpret_cast(&properties.DEF_DELTAX), sizeof(properties.DEF_DELTAX));
+ inFile.read(reinterpret_cast(&properties.DEF_DELTAY), sizeof(properties.DEF_DELTAY));
+ inFile.read(reinterpret_cast(&properties.SKY_WIDTH), sizeof(properties.SKY_WIDTH));
+ inFile.read(reinterpret_cast(&properties.SKY_HEIGHT), sizeof(properties.SKY_HEIGHT));
+ inFile.read(reinterpret_cast(&properties.SKY_COLOR), sizeof(properties.SKY_COLOR));
+ inFile.read(reinterpret_cast(&properties.A00_CAKE_INTERACT_LOOPC), sizeof(properties.A00_CAKE_INTERACT_LOOPC));
+
+ // Read the map (model_positions) size
+ size_t map_size;
+ inFile.read(reinterpret_cast(&map_size), sizeof(map_size));
+
+ // Read each key-value pair in the map
+ for (size_t i = 0; i < map_size; ++i) {
+ // Read the string length, then the string (key)
+ size_t key_size;
+ inFile.read(reinterpret_cast(&key_size), sizeof(key_size));
+ std::string key(key_size, '\0');
+ inFile.read(&key[0], key_size);
+
+ // Read the drawpoint struct (x and y)
+ Gmeng::Renderer::drawpoint drawpoint;
+ inFile.read(reinterpret_cast(&drawpoint.x), sizeof(drawpoint.x));
+ inFile.read(reinterpret_cast(&drawpoint.y), sizeof(drawpoint.y));
+
+ // Insert into the map
+ properties.model_positions[key] = drawpoint;
+ }
+
+ inFile.close();
+ return properties;
+};
diff --git a/lib/bin/src/renderer.cpp b/lib/bin/src/renderer.cpp
index 8c18bdb..373bd0f 100755
--- a/lib/bin/src/renderer.cpp
+++ b/lib/bin/src/renderer.cpp
@@ -164,6 +164,7 @@ namespace Gmeng {
}
} else { std::cerr << "Gmeng::Renderer::get_placement: e_obj: out of boundaries: __getsize(__p, __s, __ws);" << std::endl; };
};
+ ASSERT("pref.log", p_no);
return vec;
};
/// returns placement coordinates for a viewpoint within a map sizeof drawpointxy
@@ -1053,6 +1054,7 @@ namespace Gmeng {
if (global.debugger) {
gm_slog(YELLOW, "DEBUGGER", "^^ above is reinterpereted_data from _vconcatenate_lvl_chunks");
};
+ ASSERT("pref.log", p_no);
return reinterpereted_data;
};
diff --git a/lib/bin/utils/util.cpp b/lib/bin/utils/util.cpp
index c2d1296..c7cd9f0 100644
--- a/lib/bin/utils/util.cpp
+++ b/lib/bin/utils/util.cpp
@@ -6,6 +6,24 @@
/// General Utilities for Gmeng
namespace Gmeng::Util {
+ /// @since 10.1.0
+ /// TODO: does not support cubic rendering, add option
+ std::string draw_texture_string(Gmeng::texture txt) {
+ Gmeng::Camera<1,1> cam;
+ std::string final;
+ for (int i = 0; i < txt.width*txt.height; i++) {
+ if (i != 0 && i % txt.width == 0) final += '\n';
+ if (i >= txt.units.size()) final += cam.draw_unit({
+ .color = 0,
+ .collidable = true,
+ .special = true,
+ .special_clr = 4,
+ .special_c_unit = "?"
+ });
+ else final += cam.draw_unit(txt.units.at(i));
+ };
+ return final;
+ };
/// fills a texture with a single color
void texture_fill(texture& tx, color_t color) {
for (int i = 0; i < tx.width*tx.height && i < tx.units.size(); i++) {
@@ -18,9 +36,14 @@ namespace Gmeng::Util {
for (int i = 0; i < tx.width*tx.height && i < tx.units.size(); i++) {
auto d = tx.units.at(i);
if (d.color == color1) d.color = color2;
+ tx.units[i] = d;
};
};
+ int calculate_proximity(Renderer::drawpoint pos1, Renderer::drawpoint pos2) {
+ return std::abs(pos1.x - pos2.x) + std::abs(pos1.y - pos2.y);
+ };
+
void level_set_skybox(Level* lvl, texture& tx) {
lvl->base.height = tx.height;
lvl->base.width = tx.width;
diff --git a/makefile b/makefile
index 139c2c2..322c301 100755
--- a/makefile
+++ b/makefile
@@ -5,6 +5,10 @@ CXXFLAGS := -Linclude -Iinclude --std=c++2a -pthread `pkg-config --libs --cflags
VERSIONFLAGS := -DGMENG_BUILD_NO="UNKNOWN"
OUTFILE := -o gmeng
+# Compiler to Windows
+WINDOWS_CXX := i686-w64-mingw32-g++ # MinGW compiler for unix-to-windows cross compile.
+WINDOWS_CXXFLAGS := -std=c++20 -Wno-write-strings -Wno-return-type -static -static-libgcc -static-libstdc++ -lpthread -pthread
+
USE_NCURSES := true
USE_EXTERNAL := false
TARGET_NAME := all
@@ -16,9 +20,11 @@ BUILD_NUMBER := $(shell printf G$$RANDOM-$$RANDOM)
ifeq ($(OS), Windows_NT)
BUILD_NUMBER := $(shell echo | set /p version="G%random%-%random%")
+ WINDOWS_CXX := g++
endif
ifeq ($(UNAME_S), Windows_NT)
BUILD_NUMBER := $(shell echo | set /p version="G%random%-%random%")
+ WINDOWS_CXX := g++
endif
$(info selected build number: $(BUILD_NUMBER))
@@ -39,6 +45,15 @@ $(info buildoptions selected.)
endif
endif
+ifeq ($(filter compile-windows, $(MAKECMDGOALS)),compile-windows)
+ifeq ($(wildcard buildoptions.mk),)
+$(error create a file named `buildoptions.mk` and add the line `TARGET_NAME := your_game_code.cpp`.)
+else
+include buildoptions.mk
+$(info buildoptions selected. (WINDOWS))
+endif
+endif
+
# If no arguments were passed, check for buildoptions.mk
ifndef skip_warning
ifeq ($(wildcard buildoptions.mk),)
@@ -175,5 +190,16 @@ compile:
@echo TARGET WILL BE NAMED\: ./game.out
$(CXX) $(VERSIONFLAGS) $(CXXFLAGS) $(TARGET_NAME) -o game.out
+compile-windows:
+ @echo CROSS COMPILING TO WINDOWS
+ @echo COMPILER\: $(WINDOWS_CXX)
+ @echo FLAGS\: $(WINDOWS_CXXFLAGS)
+ @echo
+ @echo GMENG-ACCEPTED COMPILING FLAGS
+ @echo COMPILING YOUR BUILDOPTIONS.MK
+ @echo TO CONFIGURE, EDIT THE FILE buildoptions.mk TO CHANGE YOUR TARGET
+ @echo EXECUTABLE WILL BE NAMED\: ./game.exe
+ $(WINDOWS_CXX) $(VERSIONFLAGS) $(WINDOWS_CXXFLAGS) $(TARGET_NAME) -o game.exe
+
# Phony targets
-.PHONY: all test test2 debug no-ncurses warnings configure compile
+.PHONY: all test test2 debug no-ncurses warnings configure compile compile-windows
diff --git a/readme.md b/readme.md
index d9f31db..b2a35fc 100755
--- a/readme.md
+++ b/readme.md
@@ -10,7 +10,7 @@
## Changelog
-[`See what's new`](CHANGELOG.md) **(25-Oct-2024) info**: Released version 10.0.0: Game Event Loop | [`gmeng.org/changelog`](https://gmeng.org/changelog)
+[`See what's new`](CHANGELOG.md) **(2-Nov-2024) info**: 10.1.0: game state binaries & general improvements | [`gmeng.org/changelog`](https://gmeng.org/changelog)
## Documentation
Gmeng's documentation can be found in [`gmeng.org`](https://gmeng.org). Please refer to the website for enquiries about functionality and usage.
@@ -62,15 +62,15 @@ builds:
make test (builds interface tests / test.cpp)
make test2 (builds unit tests / tests/test.cpp)
make compile (builds your target file / specified in buildoptions.mk or `make configure`)
+ make compile-windows (builds your target file / cross compiled to windows)
options:
- make [debug] [no-ncurses] [use-external] [warnings] [all/test/test2]
+ make [debug] [no-ncurses] [use-external] [warnings] [all/test/test2/compile/compile-windows]
make configure
```
- The `debug` option adds the `-g -O0 -fsanitize=address` flags to the compiler.
- The `no-ncurses` option disables the auto-imports to `utils/interface.cpp` and `types/interface.h` from the `gmeng.h` header.
- The `use-external` option enables the auto-imports to `SDL2/SDL.h` headers for SDL-based windows.
- The `warnings` option enables `-Wall` so all warnings are displayed by the compiler.
-
- The `configure` option runs the configuration utility to set up the buildconfig for a program.
## Debugging
diff --git a/tests/event_loop.cpp b/tests/event_loop.cpp
new file mode 100644
index 0000000..b2a18bf
--- /dev/null
+++ b/tests/event_loop.cpp
@@ -0,0 +1,232 @@
+#include
+#include "../lib/bin/gmeng.h"
+
+using namespace Gmeng;
+using namespace Gmeng::Util;
+
+static Level level;
+static gmeng_properties_t cfg;
+static EventLoop ev( {} );
+
+void reset() {
+ _uread_into_vgm("envs/models");
+ if (!filesystem::exists("gamestate.cfg")) writeout_properties("gamestate.cfg", default_properties);
+ cfg = read_properties("gamestate.cfg");
+
+ texture empty_texture = Renderer::generate_empty_texture(cfg.SKY_WIDTH, cfg.SKY_HEIGHT);
+ texture_fill(empty_texture, cfg.SKY_COLOR);
+ level_set_skybox(&level, empty_texture);
+
+ texture cake_texture = default_texture_search("smol_player");
+ texture_replace_color(cake_texture, RED, GREEN);
+
+ texture island_texture = default_texture_search("01_island");
+ texture gift_texture = default_texture_search("02_gift");
+ texture real_cake_texture = default_texture_search("01_cake_txtr");
+ texture table_texture = default_texture_search("03_table");
+
+ level.chunks.at(0) = { { {0, 0}, {99, 99} },
+ {
+ model_from_txtr( cake_texture, cfg.model_positions["player"] ), // this is actually the player
+
+ model_from_txtr(table_texture, cfg.model_positions["table1"]),
+ model_from_txtr(table_texture, cfg.model_positions["table2"]),
+
+ model_from_txtr(real_cake_texture, cfg.model_positions["cake"]),
+
+ model_from_txtr(gift_texture, cfg.model_positions["gift1"]),
+ model_from_txtr(gift_texture, cfg.model_positions["gift2"]),
+ }
+ };
+
+ level.display.viewpoint = { {0,0}, { cfg.DEF_DELTAX, cfg.DEF_DELTAY } };
+ level.display.set_resolution(cfg.DEF_DELTAX, cfg.DEF_DELTAY);
+
+ ev.level = &level;
+};
+
+int main(int argc, char** argv) {
+ _uread_into_vgm("envs/models");
+ patch_argv_global(argc, argv); /// gmeng initialization
+
+ if (!filesystem::exists("gamestate.cfg")) writeout_properties("gamestate.cfg", default_properties);
+ cfg = read_properties("gamestate.cfg");
+
+ int* DEF_DELTAY = &cfg.DEF_DELTAY;
+ int* DEF_DELTAX = &cfg.DEF_DELTAX;
+
+ texture empty_texture = Renderer::generate_empty_texture(cfg.SKY_WIDTH, cfg.SKY_HEIGHT);
+ texture_fill(empty_texture, cfg.SKY_COLOR);
+ level_set_skybox(&level, empty_texture);
+
+ texture cake_texture = default_texture_search("smol_player");
+ texture_replace_color(cake_texture, RED, GREEN);
+
+ texture island_texture = default_texture_search("01_island");
+ texture gift_texture = default_texture_search("02_gift");
+ texture real_cake_texture = default_texture_search("01_cake_txtr");
+ texture table_texture = default_texture_search("03_table");
+ level.load_chunk({ { {0, 0}, {99, 99} },
+ {
+ model_from_txtr( cake_texture, cfg.model_positions["player"] ), // this is actually the player
+
+ model_from_txtr(table_texture, cfg.model_positions["table1"]),
+ model_from_txtr(table_texture, cfg.model_positions["table2"]),
+
+ model_from_txtr(real_cake_texture, cfg.model_positions["cake"]),
+
+ model_from_txtr(gift_texture, cfg.model_positions["gift1"]),
+ model_from_txtr(gift_texture, cfg.model_positions["gift2"]),
+ }
+ });
+
+ level.display.viewpoint = { {0,0}, { *DEF_DELTAX, *DEF_DELTAY } };
+ level.display.set_resolution(*DEF_DELTAX, *DEF_DELTAY);
+ ev.level = &level;
+ std::vector renderscale;
+
+ ev.add_hook({ INIT }, [&](Level* level, EventInfo* info) {
+ std::cout << "Initialized the Game Event Hook (external hook received this event)\n";
+ renderscale = get_renderscale(*level);
+ level->display.camera.clear_screen();
+ ev.call_event(FIXED_UPDATE, *info);
+ });
+
+ ev.add_hook( { FIXED_UPDATE },
+ [&](Level* level, EventInfo* info) {
+ if (info->EVENT == MOUSE_MOVE) return;
+ std::string lvl_view = get_lvl_view(*level, renderscale);
+ emplace_lvl_camera(*level, lvl_view);
+ level->display.camera.reset_cur();
+ auto time = GET_TIME();
+ std::cout << level->display.camera.draw() << "\n" << Gmeng::resetcolor;
+ level->display.camera.draw_time = GET_TIME() - time;
+ level->display.camera.draw_info(vp_width(level->display.viewpoint)+2, 0);
+ });
+
+ ev.add_hook({ KEYPRESS }, [&](Level* level, EventInfo* info) {
+ if (info->KEYPRESS_CODE == 'q') {
+ std::cout << "quiting the game event loop.\n";
+ ev.cancelled = true; // cancel the game event loop
+ info->prevent_default = true; // disable other default hooks
+ };
+ auto cake_model = level->chunks.at(0).models.at(0);
+ auto real_cake_model = level->chunks.at(0).models.at(3);
+ bool nomove = info->prevent_default;
+ switch (info->KEYPRESS_CODE) {
+ case 'a': case 'A':
+ if (cake_model.position.x-((int)nomove) > 0) {
+ cake_model.position.x--;
+ if (cake_model.position.x-((int)nomove)-cake_model.width <= level->display.viewpoint.start.x) {
+ level->display.viewpoint.start.x--;
+ level->display.viewpoint.end.x--;
+ };
+ if (nomove) cake_model.position.x++;
+ };
+ break;
+ case 'd': case 'D':
+ if (cake_model.position.x+((int)nomove)+cake_model.width < level->base.width-1) {
+ cake_model.position.x++;
+ if (cake_model.position.x+((int)nomove)+((cake_model.width)*2) > level->display.viewpoint.end.x) {
+ level->display.viewpoint.start.x++;
+ level->display.viewpoint.end.x++;
+ };
+ if (nomove) cake_model.position.x--;
+ };
+ break;
+ case 'w': case 'W':
+ if (cake_model.position.y-((int)nomove) > 0) {
+ cake_model.position.y--;
+ if (cake_model.position.y-((int)nomove)-cake_model.height <= level->display.viewpoint.start.y) {
+ level->display.viewpoint.start.y--;
+ level->display.viewpoint.end.y--;
+ };
+ if (nomove) cake_model.position.y++;
+ };
+ break;
+ case 's': case 'S':
+ if (cake_model.position.y+((int)nomove)+cake_model.height < level->base.height-1) {
+ cake_model.position.y++;
+ if (cake_model.position.y+((int)nomove)+((cake_model.height)*2) >= level->display.viewpoint.end.y) {
+ level->display.viewpoint.start.y++;
+ level->display.viewpoint.end.y++;
+ };
+ if (nomove) cake_model.position.y--;
+ };
+ break;
+ case 'r': case 'R':
+ level->display.camera.clear_screen();
+ if (!info->prevent_default) { // scope
+ auto tim_ca = GET_TIME();
+ reset();
+ tim_ca = GET_TIME() - tim_ca;
+ renderscale = get_renderscale(*level);
+ level->display.camera.set_curXY(3,*DEF_DELTAX+2);
+ WRITE_PARSED("RESET performed in "$(tim_ca)"ms");
+ };
+ level->display.camera.draw_info(*DEF_DELTAX+2, 0);
+ break;
+ case 'e': case 'E':
+ if (calculate_proximity(cake_model.position, real_cake_model.position) <= 3) {
+ color_t prev_color = BLUE;
+ for (int e = 0; e < cfg.A00_CAKE_INTERACT_LOOPC; e++) {
+ color_t color = (color_t)(e%8);
+ auto tim = GET_TIME();
+ texture_replace_color(level->base.lvl_template, prev_color, color);
+ prev_color = color;
+ renderscale = get_renderscale(*level);
+ ev.call_event(FIXED_UPDATE, Gmeng::NO_EVENT_INFO);
+ sleep(ms( std::max(cfg.model_positions["CAKE_INTERACT_TIMES"].x, cfg.model_positions["CAKE_INTERACT_TIMES"].y - (int)(GET_TIME()-tim)) ));
+ };
+ texture_replace_color(level->base.lvl_template, prev_color, BLUE);
+ };
+ break;
+ default:
+ break;
+ };
+
+ if (level->display.viewpoint.start.x < 0) level->display.viewpoint.start.x = 0, level->display.viewpoint.end.x = *DEF_DELTAX;
+ if (level->display.viewpoint.start.y < 0) level->display.viewpoint.start.y = 0, level->display.viewpoint.end.y = *DEF_DELTAY;
+ if (level->display.viewpoint.end.x > level->base.width) level->display.viewpoint.end.x = level->base.width, level->display.viewpoint.start.x = level->base.width-*DEF_DELTAX;
+ if (level->display.viewpoint.end.y > level->base.height) level->display.viewpoint.end.y = level->base.height, level->display.viewpoint.start.y = level->base.height-*DEF_DELTAY;
+
+ level->chunks.at(0).models.at(0) = cake_model;
+ level->chunks.at(0).models.at(3) = real_cake_model;
+ renderscale = get_renderscale(*level);
+ });
+
+ ev.add_hook({ MOUSE_CLICK_LEFT_START, MOUSE_CLICK_MIDDLE_START, MOUSE_CLICK_RIGHT_START },
+ [&](Level* level, EventInfo* info) {
+ string button_name = info->EVENT == MOUSE_CLICK_LEFT_START ? "left" :
+ ( info->EVENT == MOUSE_CLICK_RIGHT_START ? "right" : "middle" );
+ //std::cout << "MOUSE CLICK: " << colors[GREEN] << button_name << " click at " << v_str(info->MOUSE_X_POS) << "," << v_str(info->MOUSE_Y_POS) << "\n" << colors[WHITE];
+ if (info->EVENT == MOUSE_CLICK_LEFT_START) {
+ if (!viewpoint_includes_dp(
+ level->chunks.at(0).vp, {
+ (int)level->chunks.at(0).models.at(0).width + info->MOUSE_X_POS,
+ (int)level->chunks.at(0).models.at(0).height + info->MOUSE_Y_POS
+ }
+ )) return;
+ level->chunks.at(0).models.at(0).position = { info->MOUSE_X_POS, info->MOUSE_Y_POS*2 };
+ renderscale = get_renderscale(*level);
+ };
+ });
+
+ ev.add_hook({ MOUSE_SCROLL_UP, MOUSE_SCROLL_DOWN },
+ [&](Level* lv, EventInfo* info) {
+ // asumes user goes down
+ EventInfo info_d = EventInfo { KEYPRESS, '{', -1, -1, true };
+ if (info->EVENT == MOUSE_SCROLL_UP) {
+ lv->display.viewpoint.start.y--;
+ lv->display.viewpoint.end.y--;
+ } else if (info->EVENT == MOUSE_SCROLL_DOWN) {
+ lv->display.viewpoint.start.y++;
+ lv->display.viewpoint.end.y++;
+ };
+ ev.call_event(KEYPRESS, info_d);
+ });
+
+ do_event_loop(&ev);
+ std::cout << "end program\n";
+ return 0;
+};
diff --git a/tests/prototype.cpp b/tests/prototype.cpp
new file mode 100644
index 0000000..c5bb614
--- /dev/null
+++ b/tests/prototype.cpp
@@ -0,0 +1,154 @@
+#include
+#include
+#include "../lib/bin/gmeng.h"
+
+using namespace Gmeng;
+using namespace Gmeng::Util;
+
+static Level level;
+
+int main(int argc, char** argv) {
+ _uread_into_vgm("envs/models");
+ patch_argv_global(argc, argv); /// gmeng initialization
+ EventLoop ev( {} );
+
+ int DEF_DELTAX = 50;
+ int DEF_DELTAY = 25;
+
+
+ texture empty_texture = Renderer::generate_empty_texture(100, 100);
+ texture_fill(empty_texture, BLUE);
+ level_set_skybox(&level, empty_texture);
+ texture cake_texture = default_texture_search("smol_player");
+ texture island_texture = default_texture_search("01_island");
+ texture gift_texture = default_texture_search("02_gift");
+ texture real_cake_texture = default_texture_search("01_cake_txtr");
+ texture table_texture = default_texture_search("03_table");
+ level.load_chunk({ { {0, 0}, {99, 99} },
+ {
+ model_from_txtr( cake_texture, { 0,0 } ),
+ model_from_txtr(island_texture, {10, 10}),
+ model_from_txtr(gift_texture, { 0, DEF_DELTAY-(int)gift_texture.height+1 })
+ }
+ });
+
+ level.display.viewpoint = { {0,0}, { DEF_DELTAX, DEF_DELTAY } };
+ level.display.set_resolution(DEF_DELTAX, DEF_DELTAY);
+ ev.level = &level;
+ std::vector renderscale;
+
+ ev.add_hook({ INIT }, [&](Level* level, EventInfo* info) {
+ std::cout << "Initialized the Game Event Hook (external hook received this event)\n";
+ renderscale = get_renderscale(*level);
+ level->display.camera.clear_screen();
+ });
+
+ ev.add_hook( { FIXED_UPDATE },
+ [&](Level* level, EventInfo* info) {
+ if (info->EVENT == MOUSE_MOVE) return;
+ std::string lvl_view = get_lvl_view(*level, renderscale);
+ emplace_lvl_camera(*level, lvl_view);
+ level->display.camera.reset_cur();
+ auto time = GET_TIME();
+ std::cout << level->display.camera.draw() << "\n" << Gmeng::resetcolor;
+ level->display.camera.draw_time = GET_TIME() - time;
+ level->display.camera.draw_info(vp_width(level->display.viewpoint)+2, 0);
+ });
+
+ ev.add_hook({ KEYPRESS }, [&](Level* level, EventInfo* info) {
+ if (info->KEYPRESS_CODE == 'q') {
+ std::cout << "quiting the game event loop.\n";
+ ev.cancelled = true; // cancel the game event loop
+ info->prevent_default = true; // disable other default hooks
+ };
+ auto cake_model = level->chunks.at(0).models.at(0);
+ bool nomove = info->prevent_default;
+ switch (info->KEYPRESS_CODE) {
+ case 'a': case 'A':
+ if (cake_model.position.x-((int)nomove) > 0) {
+ cake_model.position.x--;
+ if (cake_model.position.x-((int)nomove)-cake_model.width <= level->display.viewpoint.start.x) {
+ level->display.viewpoint.start.x--;
+ level->display.viewpoint.end.x--;
+ };
+ if (nomove) cake_model.position.x++;
+ };
+ break;
+ case 'd': case 'D':
+ if (cake_model.position.x+((int)nomove)+cake_model.width < level->base.width-1) {
+ cake_model.position.x++;
+ if (cake_model.position.x+((int)nomove)+((cake_model.width)*2) > level->display.viewpoint.end.x) {
+ level->display.viewpoint.start.x++;
+ level->display.viewpoint.end.x++;
+ };
+ if (nomove) cake_model.position.x--;
+ };
+ break;
+ case 'w': case 'W':
+ if (cake_model.position.y-((int)nomove) > 0) {
+ cake_model.position.y--;
+ if (cake_model.position.y-((int)nomove)-cake_model.height <= level->display.viewpoint.start.y) {
+ level->display.viewpoint.start.y--;
+ level->display.viewpoint.end.y--;
+ };
+ if (nomove) cake_model.position.y++;
+ };
+ break;
+ case 's': case 'S':
+ if (cake_model.position.y+((int)nomove)+cake_model.height < level->base.height-1) {
+ cake_model.position.y++;
+ if (cake_model.position.y+((int)nomove)+((cake_model.height)*2) >= level->display.viewpoint.end.y) {
+ level->display.viewpoint.start.y++;
+ level->display.viewpoint.end.y++;
+ };
+ if (nomove) cake_model.position.y--;
+ };
+ break;
+ default:
+ break;
+ };
+
+ if (level->display.viewpoint.start.x < 0) level->display.viewpoint.start.x = 0, level->display.viewpoint.end.x = DEF_DELTAX;
+ if (level->display.viewpoint.start.y < 0) level->display.viewpoint.start.y = 0, level->display.viewpoint.end.y = DEF_DELTAY;
+ if (level->display.viewpoint.end.x > level->base.width) level->display.viewpoint.end.x = level->base.width, level->display.viewpoint.start.x = level->base.width-DEF_DELTAX;
+ if (level->display.viewpoint.end.y > level->base.height) level->display.viewpoint.end.y = level->base.height, level->display.viewpoint.start.y = level->base.height-DEF_DELTAY;
+
+ level->chunks.at(0).models.at(0) = cake_model;
+ renderscale = get_renderscale(*level);
+ });
+
+ ev.add_hook({ MOUSE_CLICK_LEFT_START, MOUSE_CLICK_MIDDLE_START, MOUSE_CLICK_RIGHT_START },
+ [&](Level* level, EventInfo* info) {
+ string button_name = info->EVENT == MOUSE_CLICK_LEFT_START ? "left" :
+ ( info->EVENT == MOUSE_CLICK_RIGHT_START ? "right" : "middle" );
+ //std::cout << "MOUSE CLICK: " << colors[GREEN] << button_name << " click at " << v_str(info->MOUSE_X_POS) << "," << v_str(info->MOUSE_Y_POS) << "\n" << colors[WHITE];
+ if (info->EVENT == MOUSE_CLICK_LEFT_START) {
+ if (!viewpoint_includes_dp(
+ level->chunks.at(0).vp, {
+ (int)level->chunks.at(0).models.at(0).width + info->MOUSE_X_POS,
+ (int)level->chunks.at(0).models.at(0).height + info->MOUSE_Y_POS
+ }
+ )) return;
+ level->chunks.at(0).models.at(0).position = { info->MOUSE_X_POS, info->MOUSE_Y_POS*2 };
+ renderscale = get_renderscale(*level);
+ };
+ });
+
+ ev.add_hook({ MOUSE_SCROLL_UP, MOUSE_SCROLL_DOWN },
+ [&](Level* lv, EventInfo* info) {
+ // asumes user goes down
+ EventInfo info_d = EventInfo { KEYPRESS, '{', -1, -1, true };
+ if (info->EVENT == MOUSE_SCROLL_UP) {
+ lv->display.viewpoint.start.y--;
+ lv->display.viewpoint.end.y--;
+ } else if (info->EVENT == MOUSE_SCROLL_DOWN) {
+ lv->display.viewpoint.start.y++;
+ lv->display.viewpoint.end.y++;
+ };
+ ev.call_event(KEYPRESS, info_d);
+ });
+
+ do_event_loop(&ev);
+ std::cout << "end program\n";
+ return 0;
+};