Finish basic ui binding to ruby
This commit is contained in:
+43
-5
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
namespace app {
|
namespace app {
|
||||||
struct Element {
|
struct Element {
|
||||||
|
int reference_count = 0;
|
||||||
Vec2<float> position = {0, 0};
|
Vec2<float> position = {0, 0};
|
||||||
float rotation = 0;
|
float rotation = 0;
|
||||||
Vec2<float> scale = {1, 1};
|
Vec2<float> scale = {1, 1};
|
||||||
@@ -51,11 +52,10 @@ struct EImage : Element {
|
|||||||
struct EText : Element {
|
struct EText : Element {
|
||||||
Localization::Key key;
|
Localization::Key key;
|
||||||
int font_id;
|
int font_id;
|
||||||
int font_size = 16;
|
|
||||||
Color color = {255, 255, 255, 255};
|
Color color = {255, 255, 255, 255};
|
||||||
|
|
||||||
EText(Localization::Key key, int font_id, int font_size, Color color)
|
EText(Localization::Key key, int font_id, Color color)
|
||||||
: key(key), font_id(font_id), font_size(font_size), color(color) {}
|
: key(key), font_id(font_id), color(color) {}
|
||||||
|
|
||||||
Vec2<float> size(Window *window) override {
|
Vec2<float> size(Window *window) override {
|
||||||
return window->get_text_size(text_id);
|
return window->get_text_size(text_id);
|
||||||
@@ -96,9 +96,20 @@ struct ERect : Element {
|
|||||||
inline Pool<Element *> element_pool;
|
inline Pool<Element *> element_pool;
|
||||||
|
|
||||||
struct Scene {
|
struct Scene {
|
||||||
|
bool unused = false;
|
||||||
std::vector<int> element_ids;
|
std::vector<int> element_ids;
|
||||||
Ruby::Block update;
|
Ruby::Block update;
|
||||||
|
|
||||||
|
~Scene() {
|
||||||
|
for (const int &id : element_ids) {
|
||||||
|
element_pool[id]->reference_count--;
|
||||||
|
if (element_pool[id]->reference_count <= 0) {
|
||||||
|
delete element_pool[id];
|
||||||
|
element_pool.release(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void add_element(int element_id) {
|
void add_element(int element_id) {
|
||||||
auto it = std::lower_bound(
|
auto it = std::lower_bound(
|
||||||
element_ids.begin(),
|
element_ids.begin(),
|
||||||
@@ -108,9 +119,22 @@ struct Scene {
|
|||||||
return element_pool[a]->z < element_pool[b]->z || (element_pool[a]->z == element_pool[b]->z && a < b);
|
return element_pool[a]->z < element_pool[b]->z || (element_pool[a]->z == element_pool[b]->z && a < b);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
element_pool[element_id]->reference_count++;
|
||||||
element_ids.insert(it, element_id);
|
element_ids.insert(it, element_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void delete_element(int element_id) {
|
||||||
|
auto it = std::find(element_ids.begin(), element_ids.end(), element_id);
|
||||||
|
if (it != element_ids.end()) {
|
||||||
|
element_pool[element_id]->reference_count--;
|
||||||
|
if (element_pool[element_id]->reference_count <= 0) {
|
||||||
|
delete element_pool[element_id];
|
||||||
|
element_pool.release(element_id);
|
||||||
|
}
|
||||||
|
element_ids.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void update_scene(uint64_t delta_time) {
|
void update_scene(uint64_t delta_time) {
|
||||||
if (!mrb_nil_p(update.proc)) {
|
if (!mrb_nil_p(update.proc)) {
|
||||||
int x = mrb_gc_arena_save(Ruby::mrb);
|
int x = mrb_gc_arena_save(Ruby::mrb);
|
||||||
@@ -147,6 +171,21 @@ struct App {
|
|||||||
int current_scene_id = 0;
|
int current_scene_id = 0;
|
||||||
bool running = true;
|
bool running = true;
|
||||||
|
|
||||||
|
void switch_to_scene(int scene_id) {
|
||||||
|
int scene_pool_size = (int)scene_pool.all().size();
|
||||||
|
for (int i = 0; i < scene_pool_size; ++i) {
|
||||||
|
auto &val = scene_pool[i];
|
||||||
|
if (i == scene_id && val != nullptr && val->unused)
|
||||||
|
val->unused = false;
|
||||||
|
if (val != nullptr && val->unused) {
|
||||||
|
delete val;
|
||||||
|
scene_pool.release(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
current_scene_id = scene_id;
|
||||||
|
}
|
||||||
|
|
||||||
App(Vec2<float> size, const char *title, Animation loading_animation) {
|
App(Vec2<float> size, const char *title, Animation loading_animation) {
|
||||||
window.update(size, title);
|
window.update(size, title);
|
||||||
#warning "Should try to center the loading image or scale it to fit the screen"
|
#warning "Should try to center the loading image or scale it to fit the screen"
|
||||||
@@ -154,7 +193,7 @@ struct App {
|
|||||||
EImage *image = new EImage(loading_animation);
|
EImage *image = new EImage(loading_animation);
|
||||||
int image_id = element_pool.acquire(image);
|
int image_id = element_pool.acquire(image);
|
||||||
loading_scene->add_element(image_id);
|
loading_scene->add_element(image_id);
|
||||||
scene_pool.acquire(loading_scene);
|
current_scene_id = scene_pool.acquire(loading_scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
@@ -183,7 +222,6 @@ struct App {
|
|||||||
break;
|
break;
|
||||||
case Event::MOUSE_BUTTON_DOWN:
|
case Event::MOUSE_BUTTON_DOWN:
|
||||||
scene->click(&window, e.mouse_button.position);
|
scene->click(&window, e.mouse_button.position);
|
||||||
printf("DT: %lu ms\n", dt);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -12,10 +12,20 @@ struct Block {
|
|||||||
|
|
||||||
Block() : proc(mrb_nil_value()) {}
|
Block() : proc(mrb_nil_value()) {}
|
||||||
|
|
||||||
|
void set_proc(mrb_value new_proc) {
|
||||||
|
if (!mrb_nil_p(proc))
|
||||||
|
mrb_gc_unregister(mrb, proc);
|
||||||
|
proc = new_proc;
|
||||||
|
if (!mrb_nil_p(proc))
|
||||||
|
mrb_gc_register(mrb, proc);
|
||||||
|
}
|
||||||
|
|
||||||
Block(const Block &) = delete;
|
Block(const Block &) = delete;
|
||||||
Block &operator=(const Block &) = delete;
|
Block &operator=(const Block &) = delete;
|
||||||
|
|
||||||
mrb_value call(int argc = 0, mrb_value *argv = nullptr) const {
|
mrb_value call(int argc = 0, mrb_value *argv = nullptr) const {
|
||||||
|
if (mrb_nil_p(proc))
|
||||||
|
return mrb_nil_value();
|
||||||
return mrb_funcall(mrb, proc, "call", argc, argv);
|
return mrb_funcall(mrb, proc, "call", argc, argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,4 +33,10 @@ struct Block {
|
|||||||
mrb_gc_unregister(mrb, proc);
|
mrb_gc_unregister(mrb, proc);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline void run_string(const char *code, size_t length) {
|
||||||
|
int ai = mrb_gc_arena_save(mrb);
|
||||||
|
mrb_load_nstring(mrb, code, length);
|
||||||
|
mrb_gc_arena_restore(mrb, ai);
|
||||||
|
}
|
||||||
}; // namespace Ruby
|
}; // namespace Ruby
|
||||||
@@ -5,11 +5,11 @@ namespace Localization {
|
|||||||
using Key = uint16_t;
|
using Key = uint16_t;
|
||||||
|
|
||||||
// Maps localization keys to their string identifiers. This is used for text elements to look up the correct localized string.
|
// Maps localization keys to their string identifiers. This is used for text elements to look up the correct localized string.
|
||||||
extern std::unordered_map<std::string, Key> localization_key_map;
|
inline std::unordered_map<std::string, Key> localization_key_map;
|
||||||
|
|
||||||
using LanguageCode = uint32_t; // 32-bit integer to store the language code (e.g., "en", "fr", "es")
|
using LanguageCode = uint32_t; // 32-bit integer to store the language code (e.g., "en", "fr", "es")
|
||||||
|
|
||||||
extern LanguageCode current_language; // The currently active language code
|
inline LanguageCode current_language = 'e' << 24 | 'n' << 16;
|
||||||
|
|
||||||
using JoinedKey = uint64_t; // 64-bit integer to store the joined language code and key
|
using JoinedKey = uint64_t; // 64-bit integer to store the joined language code and key
|
||||||
|
|
||||||
@@ -17,12 +17,47 @@ inline JoinedKey join_keys(LanguageCode lang_code, Key loc_key) {
|
|||||||
return (static_cast<JoinedKey>(lang_code) << 16) | loc_key;
|
return (static_cast<JoinedKey>(lang_code) << 16) | loc_key;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern std::unordered_map<JoinedKey, std::string> localized_strings;
|
inline std::unordered_map<JoinedKey, std::string> localized_strings;
|
||||||
|
|
||||||
inline std::string get(Key key) {
|
inline std::string get(Key key) {
|
||||||
auto it = localized_strings.find(join_keys(current_language, key));
|
auto it = localized_strings.find(join_keys(current_language, key));
|
||||||
if (it != localized_strings.end())
|
if (it != localized_strings.end())
|
||||||
return it->second;
|
return it->second;
|
||||||
return "";
|
|
||||||
|
for (const auto &[joined, value] : localized_strings) {
|
||||||
|
Key k = (Key)(joined & 0xFFFF);
|
||||||
|
if (k == key)
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Localization not set!";
|
||||||
|
}
|
||||||
|
|
||||||
|
inline LanguageCode lang_from_sym(mrb_state *mrb, mrb_sym sym) {
|
||||||
|
const char *s = mrb_sym2name(mrb, sym);
|
||||||
|
size_t len = strlen(s);
|
||||||
|
|
||||||
|
if (len == 0 || len > 4)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
LanguageCode code = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < len; ++i) {
|
||||||
|
code |= (static_cast<LanguageCode>(s[i]) << (8 * (3 - i)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Key key_from_sym(mrb_state *mrb, mrb_sym sym) {
|
||||||
|
const char *key_name = mrb_sym2name(mrb, sym);
|
||||||
|
|
||||||
|
auto it = localization_key_map.find(key_name);
|
||||||
|
if (it != localization_key_map.end())
|
||||||
|
return it->second;
|
||||||
|
|
||||||
|
Key new_id = (Key)localization_key_map.size();
|
||||||
|
localization_key_map[key_name] = new_id;
|
||||||
|
return new_id;
|
||||||
}
|
}
|
||||||
} // namespace Localization
|
} // namespace Localization
|
||||||
@@ -4,9 +4,12 @@
|
|||||||
#include "mruby.h"
|
#include "mruby.h"
|
||||||
#include "mruby/array.h"
|
#include "mruby/array.h"
|
||||||
#include "mruby/compile.h"
|
#include "mruby/compile.h"
|
||||||
|
#include "mruby/data.h"
|
||||||
#include "mruby/hash.h"
|
#include "mruby/hash.h"
|
||||||
#include "mruby/irep.h"
|
#include "mruby/irep.h"
|
||||||
#include "mruby/string.h"
|
#include "mruby/string.h"
|
||||||
|
#include "mruby/value.h"
|
||||||
|
#include "mruby/variable.h"
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
#include <SDL3/SDL_events.h>
|
#include <SDL3/SDL_events.h>
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@
|
|||||||
#define MAX(x, y) ((x) > (y) ? (x) : (y))
|
#define MAX(x, y) ((x) > (y) ? (x) : (y))
|
||||||
|
|
||||||
struct Color {
|
struct Color {
|
||||||
uint8_t r, g, b, a;
|
uint8_t r, g, b = 0, a = 255;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
|||||||
+13
-7
@@ -1,25 +1,31 @@
|
|||||||
App.run 400, 400, title: "Button Test"
|
App.run 400, 400, "Button Test"
|
||||||
|
|
||||||
main_scene = Scene.new
|
main_scene = Scene.new
|
||||||
|
default_font = Font.new "assets/fonts/charybdis.ttf", 24, pixel_art: true
|
||||||
|
|
||||||
default_font = Font.new "assets/DejaVuSans.ttf", 24, italic: true
|
App.language = :en
|
||||||
|
App.localize(:en, :btn_text, "Click me!")
|
||||||
|
|
||||||
text = ElementText.new "Click me!", font: default_font, position: {x: 50, y: 50}, z: 1, click_through: true
|
text = ElementText.new :btn_text, default_font, position: {x: 50, y: 50}, z: 1, click_through: true
|
||||||
|
|
||||||
rect = ElementRect.new {x: 50, y: 50, w: 100, h: 25}, color: 0xFF0000, z: 0
|
rect = ElementRect.new({x: 50, y: 50, w: 100, h: 25}, 0xFF0000FF, z: 0)
|
||||||
|
|
||||||
rect.on_click do
|
rect.on_click do
|
||||||
rect.color = rect.color == 0xFF0000 ?
|
rect.color = rect.color == 0xFF0000FF ?
|
||||||
0x00FF00
|
0x00FF00FF
|
||||||
:
|
:
|
||||||
0xFF0000
|
0xFF0000FF
|
||||||
end
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
main_scene.update do |dt|
|
main_scene.update do |dt|
|
||||||
puts "Delta time: #{dt}ms"
|
puts "Delta time: #{dt}ms"
|
||||||
end
|
end
|
||||||
|
=end
|
||||||
|
|
||||||
main_scene << text
|
main_scene << text
|
||||||
main_scene << rect
|
main_scene << rect
|
||||||
|
|
||||||
|
# main_scene.delete(text) # Uncomment to test element deletion
|
||||||
|
|
||||||
App.start main_scene
|
App.start main_scene
|
||||||
+1
-1
@@ -24,7 +24,7 @@ start_button = ElementText.new "Start Game", font: default_font, position: {x: 1
|
|||||||
game_scene = Scene.new
|
game_scene = Scene.new
|
||||||
|
|
||||||
start_button.on_click do
|
start_button.on_click do
|
||||||
App.start game_scene
|
App.switch_to game_scene
|
||||||
end
|
end
|
||||||
|
|
||||||
menu_scene << start_button
|
menu_scene << start_button
|
||||||
|
|||||||
+418
-5
@@ -1,19 +1,432 @@
|
|||||||
#include "app/app.h"
|
#include "app/app.h"
|
||||||
|
|
||||||
using namespace app;
|
app::App *app_ = nullptr;
|
||||||
|
|
||||||
|
struct SceneWrapper {
|
||||||
|
int id;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void scene_free(mrb_state *mrb, void *ptr) {
|
||||||
|
int id = ((SceneWrapper *)ptr)->id;
|
||||||
|
|
||||||
|
app::Scene *scene = app::scene_pool[id];
|
||||||
|
if (scene != nullptr)
|
||||||
|
scene->unused = true;
|
||||||
|
|
||||||
|
delete (SceneWrapper *)ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct mrb_data_type scene_type = {
|
||||||
|
"Scene",
|
||||||
|
scene_free
|
||||||
|
};
|
||||||
|
|
||||||
|
static mrb_value scene_init(mrb_state *mrb, mrb_value self) {
|
||||||
|
SceneWrapper *s = new SceneWrapper();
|
||||||
|
|
||||||
|
app::Scene *loading_scene = new app::Scene{};
|
||||||
|
s->id = app::scene_pool.acquire(loading_scene);
|
||||||
|
|
||||||
|
DATA_PTR(self) = s;
|
||||||
|
DATA_TYPE(self) = &scene_type;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneWrapper *unwrap_scene(mrb_value self) {
|
||||||
|
return (SceneWrapper *)DATA_PTR(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FontWrapper {
|
||||||
|
int id;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void font_free(mrb_state *mrb, void *ptr) {
|
||||||
|
int id = ((FontWrapper *)ptr)->id;
|
||||||
|
|
||||||
|
window.unload_font(id);
|
||||||
|
|
||||||
|
delete (FontWrapper *)ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct mrb_data_type font_type = {
|
||||||
|
"Font",
|
||||||
|
font_free
|
||||||
|
};
|
||||||
|
|
||||||
|
static mrb_value font_init(mrb_state *mrb, mrb_value self) {
|
||||||
|
char *path;
|
||||||
|
mrb_int size;
|
||||||
|
mrb_value kwargs;
|
||||||
|
mrb_get_args(mrb, "zi|H", &path, &size, &kwargs);
|
||||||
|
|
||||||
|
bool pixel_art = false, bold = false, italic = false, underline = false;
|
||||||
|
if (!mrb_nil_p(kwargs)) {
|
||||||
|
mrb_value p_val = mrb_hash_get(mrb, kwargs, mrb_symbol_value(mrb_intern_cstr(mrb, "pixel_art")));
|
||||||
|
if (!mrb_nil_p(p_val))
|
||||||
|
pixel_art = mrb_bool(p_val);
|
||||||
|
mrb_value bold_val = mrb_hash_get(mrb, kwargs, mrb_symbol_value(mrb_intern_cstr(mrb, "bold")));
|
||||||
|
if (!mrb_nil_p(bold_val))
|
||||||
|
bold = mrb_bool(bold_val);
|
||||||
|
mrb_value italic_val = mrb_hash_get(mrb, kwargs, mrb_symbol_value(mrb_intern_cstr(mrb, "italic")));
|
||||||
|
if (!mrb_nil_p(italic_val))
|
||||||
|
italic = mrb_bool(italic_val);
|
||||||
|
mrb_value underline_val = mrb_hash_get(mrb, kwargs, mrb_symbol_value(mrb_intern_cstr(mrb, "underline")));
|
||||||
|
if (!mrb_nil_p(underline_val))
|
||||||
|
underline = mrb_bool(underline_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
FontWrapper *f = new FontWrapper();
|
||||||
|
f->id = window.load_font(path, size, bold, italic, underline, pixel_art);
|
||||||
|
|
||||||
|
DATA_PTR(self) = f;
|
||||||
|
DATA_TYPE(self) = &font_type;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ElementTextWrapper {
|
||||||
|
int id;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void element_text_free(mrb_state *mrb, void *ptr) {
|
||||||
|
int id = ((ElementTextWrapper *)ptr)->id;
|
||||||
|
|
||||||
|
app::Element *element = app::element_pool[id];
|
||||||
|
if (element != nullptr)
|
||||||
|
element->reference_count--;
|
||||||
|
|
||||||
|
if (element != nullptr && element->reference_count <= 0) {
|
||||||
|
delete element;
|
||||||
|
app::element_pool.release(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete (ElementTextWrapper *)ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct mrb_data_type element_text_type = {
|
||||||
|
"ElementText",
|
||||||
|
element_text_free
|
||||||
|
};
|
||||||
|
|
||||||
|
static mrb_value element_text_init(mrb_state *mrb, mrb_value self) {
|
||||||
|
mrb_value font_val;
|
||||||
|
mrb_sym key;
|
||||||
|
mrb_value kwargs;
|
||||||
|
mrb_get_args(mrb, "no|H", &key, &font_val, &kwargs);
|
||||||
|
|
||||||
|
auto key_id = Localization::key_from_sym(mrb, key);
|
||||||
|
|
||||||
|
if (!mrb_obj_is_kind_of(mrb, font_val, mrb_class_get(mrb, "Font"))) {
|
||||||
|
mrb_raise(mrb, mrb_class_get(Ruby::mrb, "Exception"), "Expected a Font object");
|
||||||
|
return mrb_nil_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
float x = 0, y = 0;
|
||||||
|
float z = 0;
|
||||||
|
bool click_through = false;
|
||||||
|
if (!mrb_nil_p(kwargs)) {
|
||||||
|
mrb_value pos_hash = mrb_hash_get(mrb, kwargs, mrb_symbol_value(mrb_intern_cstr(mrb, "position")));
|
||||||
|
if (!mrb_nil_p(pos_hash)) {
|
||||||
|
mrb_value x_val = mrb_hash_get(mrb, pos_hash, mrb_symbol_value(mrb_intern_cstr(mrb, "x")));
|
||||||
|
mrb_value y_val = mrb_hash_get(mrb, pos_hash, mrb_symbol_value(mrb_intern_cstr(mrb, "y")));
|
||||||
|
if (mrb_fixnum_p(x_val))
|
||||||
|
x = (float)mrb_fixnum(x_val);
|
||||||
|
if (mrb_fixnum_p(y_val))
|
||||||
|
y = (float)mrb_fixnum(y_val);
|
||||||
|
}
|
||||||
|
mrb_value z_val = mrb_hash_get(mrb, kwargs, mrb_symbol_value(mrb_intern_cstr(mrb, "z")));
|
||||||
|
if (mrb_fixnum_p(z_val))
|
||||||
|
z = (float)mrb_fixnum(z_val);
|
||||||
|
mrb_value click_through_val = mrb_hash_get(mrb, kwargs, mrb_symbol_value(mrb_intern_cstr(mrb, "click_through")));
|
||||||
|
if (!mrb_nil_p(click_through_val))
|
||||||
|
click_through = mrb_bool(click_through_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2<float> position = {x, y};
|
||||||
|
|
||||||
|
FontWrapper *font = (FontWrapper *)DATA_PTR(font_val);
|
||||||
|
|
||||||
|
app::EText *element = new app::EText(key_id, font->id, {255, 255, 255, 255});
|
||||||
|
element->reference_count++;
|
||||||
|
element->position = position;
|
||||||
|
element->z = z;
|
||||||
|
element->click_through = click_through;
|
||||||
|
|
||||||
|
ElementTextWrapper *wrapper = new ElementTextWrapper();
|
||||||
|
wrapper->id = app::element_pool.acquire(element);
|
||||||
|
|
||||||
|
DATA_PTR(self) = wrapper;
|
||||||
|
DATA_TYPE(self) = &element_text_type;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ElementRectWrapper {
|
||||||
|
int id;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void element_rect_free(mrb_state *mrb, void *ptr) {
|
||||||
|
int id = ((ElementRectWrapper *)ptr)->id;
|
||||||
|
|
||||||
|
app::Element *element = app::element_pool[id];
|
||||||
|
if (element != nullptr)
|
||||||
|
element->reference_count--;
|
||||||
|
|
||||||
|
if (element != nullptr && element->reference_count <= 0) {
|
||||||
|
delete element;
|
||||||
|
app::element_pool.release(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete (ElementRectWrapper *)ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct mrb_data_type element_rect_type = {
|
||||||
|
"ElementRect",
|
||||||
|
element_rect_free
|
||||||
|
};
|
||||||
|
|
||||||
|
static mrb_value element_rect_init(mrb_state *mrb, mrb_value self) {
|
||||||
|
mrb_value shape_val;
|
||||||
|
mrb_int color;
|
||||||
|
mrb_value kwargs;
|
||||||
|
mrb_get_args(mrb, "Hi|H", &shape_val, &color, &kwargs);
|
||||||
|
|
||||||
|
float x = 0, y = 0, w = 0, h = 0;
|
||||||
|
if (!mrb_nil_p(shape_val)) {
|
||||||
|
mrb_value x_val = mrb_hash_get(mrb, shape_val, mrb_symbol_value(mrb_intern_cstr(mrb, "x")));
|
||||||
|
mrb_value y_val = mrb_hash_get(mrb, shape_val, mrb_symbol_value(mrb_intern_cstr(mrb, "y")));
|
||||||
|
mrb_value w_val = mrb_hash_get(mrb, shape_val, mrb_symbol_value(mrb_intern_cstr(mrb, "w")));
|
||||||
|
mrb_value h_val = mrb_hash_get(mrb, shape_val, mrb_symbol_value(mrb_intern_cstr(mrb, "h")));
|
||||||
|
if (mrb_fixnum_p(x_val))
|
||||||
|
x = (float)mrb_fixnum(x_val);
|
||||||
|
if (mrb_fixnum_p(y_val))
|
||||||
|
y = (float)mrb_fixnum(y_val);
|
||||||
|
if (mrb_fixnum_p(w_val))
|
||||||
|
w = (float)mrb_fixnum(w_val);
|
||||||
|
if (mrb_fixnum_p(h_val))
|
||||||
|
h = (float)mrb_fixnum(h_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
float z = 0;
|
||||||
|
if (!mrb_nil_p(kwargs)) {
|
||||||
|
mrb_value z_val = mrb_hash_get(mrb, kwargs, mrb_symbol_value(mrb_intern_cstr(mrb, "z")));
|
||||||
|
if (mrb_fixnum_p(z_val))
|
||||||
|
z = (float)mrb_fixnum(z_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2<float> position = {x, y};
|
||||||
|
Vec2<float> shape = {w, h};
|
||||||
|
|
||||||
|
app::ERect *element = new app::ERect(
|
||||||
|
shape,
|
||||||
|
{(uint8_t)((color >> 24) & 0xFF),
|
||||||
|
(uint8_t)((color >> 16) & 0xFF),
|
||||||
|
(uint8_t)((color >> 8) & 0xFF),
|
||||||
|
(uint8_t)(color & 0xFF)}
|
||||||
|
);
|
||||||
|
element->reference_count++;
|
||||||
|
element->position = position;
|
||||||
|
element->z = z;
|
||||||
|
|
||||||
|
ElementRectWrapper *wrapper = new ElementRectWrapper();
|
||||||
|
wrapper->id = app::element_pool.acquire(element);
|
||||||
|
|
||||||
|
DATA_PTR(self) = wrapper;
|
||||||
|
DATA_TYPE(self) = &element_rect_type;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static mrb_value scene_add_element(mrb_state *mrb, mrb_value self) {
|
||||||
|
mrb_value element_val;
|
||||||
|
mrb_get_args(mrb, "o", &element_val);
|
||||||
|
|
||||||
|
if (!mrb_obj_is_kind_of(mrb, element_val, mrb_class_get(mrb, "ElementText")) && !mrb_obj_is_kind_of(mrb, element_val, mrb_class_get(mrb, "ElementRect"))) {
|
||||||
|
mrb_raise(mrb, mrb_class_get(Ruby::mrb, "Exception"), "Expected an ElementText or ElementRect object");
|
||||||
|
return mrb_nil_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneWrapper *s = unwrap_scene(self);
|
||||||
|
app::Scene *scene = app::scene_pool[s->id];
|
||||||
|
|
||||||
|
int element_id;
|
||||||
|
if (mrb_obj_is_kind_of(mrb, element_val, mrb_class_get(mrb, "ElementText"))) {
|
||||||
|
ElementTextWrapper *wrapper = (ElementTextWrapper *)DATA_PTR(element_val);
|
||||||
|
element_id = wrapper->id;
|
||||||
|
} else {
|
||||||
|
ElementRectWrapper *wrapper = (ElementRectWrapper *)DATA_PTR(element_val);
|
||||||
|
element_id = wrapper->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
scene->add_element(element_id);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
mrb_value app_run(mrb_state *mrb, mrb_value self) {
|
||||||
|
mrb_int width, height;
|
||||||
|
char *title = nullptr;
|
||||||
|
mrb_get_args(mrb, "iiz", &width, &height, &title);
|
||||||
|
|
||||||
int main() {
|
|
||||||
int loading_image = window.load_image("assets/images/menu_bg.png", 1.0f, true);
|
int loading_image = window.load_image("assets/images/menu_bg.png", 1.0f, true);
|
||||||
|
|
||||||
Animation loading_animation = {
|
Animation loading_animation = {
|
||||||
.frames = {loading_image},
|
.frames = {loading_image},
|
||||||
.frame_rate = 1.0f, // Not animated, so frame rate doesn't matter
|
.frame_rate = 1.0f,
|
||||||
.loop = false
|
.loop = false
|
||||||
};
|
};
|
||||||
|
|
||||||
App app({720, 480}, "Mysth Engine", loading_animation);
|
app_ = new app::App({(float)width, (float)height}, title, loading_animation);
|
||||||
|
|
||||||
app.loop();
|
return mrb_nil_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
mrb_value app_start(mrb_state *mrb, mrb_value self) {
|
||||||
|
mrb_value scene_val;
|
||||||
|
mrb_get_args(mrb, "o", &scene_val);
|
||||||
|
|
||||||
|
if (!mrb_obj_is_kind_of(mrb, scene_val, mrb_class_get(mrb, "Scene"))) {
|
||||||
|
mrb_raise(mrb, mrb_class_get(Ruby::mrb, "Exception"), "Expected a Scene object");
|
||||||
|
return mrb_nil_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneWrapper *s = unwrap_scene(scene_val);
|
||||||
|
app_->switch_to_scene(s->id);
|
||||||
|
app_->loop();
|
||||||
|
|
||||||
|
return mrb_nil_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
mrb_value app_switch_to(mrb_state *mrb, mrb_value self) {
|
||||||
|
mrb_value scene_val;
|
||||||
|
mrb_get_args(mrb, "o", &scene_val);
|
||||||
|
|
||||||
|
if (!mrb_obj_is_kind_of(mrb, scene_val, mrb_class_get(mrb, "Scene"))) {
|
||||||
|
mrb_raise(mrb, mrb_class_get(Ruby::mrb, "Exception"), "Expected a Scene object");
|
||||||
|
return mrb_nil_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneWrapper *s = unwrap_scene(scene_val);
|
||||||
|
app_->switch_to_scene(s->id);
|
||||||
|
|
||||||
|
return mrb_nil_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
static mrb_value app_localize(mrb_state *mrb, mrb_value self) {
|
||||||
|
mrb_sym lang_sym, key_sym;
|
||||||
|
char *text;
|
||||||
|
|
||||||
|
mrb_get_args(mrb, "nnz", &lang_sym, &key_sym, &text);
|
||||||
|
|
||||||
|
auto lang = Localization::lang_from_sym(mrb, lang_sym);
|
||||||
|
|
||||||
|
const char *key_name = mrb_sym2name(mrb, key_sym);
|
||||||
|
|
||||||
|
auto it = Localization::localization_key_map.find(key_name);
|
||||||
|
Localization::Key key_id;
|
||||||
|
|
||||||
|
if (it == Localization::localization_key_map.end()) {
|
||||||
|
key_id = (Localization::Key)Localization::localization_key_map.size();
|
||||||
|
Localization::localization_key_map[key_name] = key_id;
|
||||||
|
} else {
|
||||||
|
key_id = it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
Localization::localized_strings[Localization::join_keys(lang, key_id)] = text;
|
||||||
|
|
||||||
|
return mrb_nil_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
RClass *font_class = mrb_define_class(Ruby::mrb, "Font", Ruby::mrb->object_class);
|
||||||
|
mrb_define_method(Ruby::mrb, font_class, "initialize", font_init, MRB_ARGS_REQ(2) | MRB_ARGS_OPT(1));
|
||||||
|
|
||||||
|
RClass *element_text_class = mrb_define_class(Ruby::mrb, "ElementText", Ruby::mrb->object_class);
|
||||||
|
mrb_define_method(Ruby::mrb, element_text_class, "initialize", element_text_init, MRB_ARGS_REQ(2) | MRB_ARGS_OPT(1));
|
||||||
|
mrb_define_method(Ruby::mrb, element_text_class, "on_click", [](mrb_state *mrb, mrb_value self) {
|
||||||
|
mrb_value block;
|
||||||
|
mrb_get_args(mrb, "&", &block);
|
||||||
|
ElementTextWrapper *wrapper = (ElementTextWrapper *)DATA_PTR(self);
|
||||||
|
app::Element *element = app::element_pool[wrapper->id];
|
||||||
|
element->on_click.set_proc(block);
|
||||||
|
return self; }, MRB_ARGS_BLOCK());
|
||||||
|
|
||||||
|
RClass *element_rect_class = mrb_define_class(Ruby::mrb, "ElementRect", Ruby::mrb->object_class);
|
||||||
|
mrb_define_method(Ruby::mrb, element_rect_class, "initialize", element_rect_init, MRB_ARGS_REQ(2) | MRB_ARGS_OPT(1));
|
||||||
|
mrb_define_method(Ruby::mrb, element_rect_class, "on_click", [](mrb_state *mrb, mrb_value self) {
|
||||||
|
mrb_value block;
|
||||||
|
mrb_get_args(mrb, "&", &block);
|
||||||
|
ElementRectWrapper *wrapper = (ElementRectWrapper *)DATA_PTR(self);
|
||||||
|
app::Element *element = app::element_pool[wrapper->id];
|
||||||
|
element->on_click.set_proc(block);
|
||||||
|
return self; }, MRB_ARGS_BLOCK());
|
||||||
|
mrb_define_method(Ruby::mrb, element_rect_class, "color=", [](mrb_state *mrb, mrb_value self) {
|
||||||
|
mrb_value color_val;
|
||||||
|
mrb_get_args(mrb, "o", &color_val);
|
||||||
|
ElementRectWrapper *wrapper = (ElementRectWrapper *)DATA_PTR(self);
|
||||||
|
app::ERect *element = (app::ERect *)app::element_pool[wrapper->id];
|
||||||
|
element->color = {
|
||||||
|
(uint8_t)((mrb_fixnum(color_val) >> 24) & 0xFF),
|
||||||
|
(uint8_t)((mrb_fixnum(color_val) >> 16) & 0xFF),
|
||||||
|
(uint8_t)((mrb_fixnum(color_val) >> 8) & 0xFF),
|
||||||
|
(uint8_t)(mrb_fixnum(color_val) & 0xFF)
|
||||||
|
};
|
||||||
|
return self; }, MRB_ARGS_REQ(1));
|
||||||
|
mrb_define_method(Ruby::mrb, element_rect_class, "color", [](mrb_state *mrb, mrb_value self) {
|
||||||
|
ElementRectWrapper *wrapper = (ElementRectWrapper *)DATA_PTR(self);
|
||||||
|
app::ERect *element = (app::ERect *)app::element_pool[wrapper->id];
|
||||||
|
uint32_t color = ((uint32_t)element->color.r << 24) |
|
||||||
|
((uint32_t)element->color.g << 16) |
|
||||||
|
((uint32_t)element->color.b << 8) |
|
||||||
|
(uint32_t)element->color.a;
|
||||||
|
return mrb_fixnum_value(color); }, MRB_ARGS_NONE());
|
||||||
|
|
||||||
|
RClass *scene_class = mrb_define_class(Ruby::mrb, "Scene", Ruby::mrb->object_class);
|
||||||
|
mrb_define_method(Ruby::mrb, scene_class, "initialize", scene_init, MRB_ARGS_NONE());
|
||||||
|
mrb_define_method(Ruby::mrb, scene_class, "<<", scene_add_element, MRB_ARGS_REQ(1));
|
||||||
|
|
||||||
|
RClass *app_module = mrb_define_module(Ruby::mrb, "App");
|
||||||
|
mrb_define_class_method(Ruby::mrb, app_module, "run", app_run, MRB_ARGS_REQ(3));
|
||||||
|
mrb_define_class_method(Ruby::mrb, app_module, "start", app_start, MRB_ARGS_REQ(1));
|
||||||
|
mrb_define_class_method(Ruby::mrb, app_module, "switch_to", app_switch_to, MRB_ARGS_REQ(1));
|
||||||
|
mrb_define_class_method(Ruby::mrb, app_module, "localize", app_localize, MRB_ARGS_REQ(3));
|
||||||
|
mrb_define_class_method(Ruby::mrb, app_module, "language=", [](mrb_state *mrb, mrb_value self) {
|
||||||
|
mrb_sym lang_sym;
|
||||||
|
mrb_get_args(mrb, "n", &lang_sym);
|
||||||
|
Localization::current_language = Localization::lang_from_sym(mrb, lang_sym);
|
||||||
|
return mrb_nil_value(); }, MRB_ARGS_REQ(1));
|
||||||
|
|
||||||
|
char *startup_script = nullptr;
|
||||||
|
if (argc > 1)
|
||||||
|
startup_script = argv[1];
|
||||||
|
|
||||||
|
if (startup_script) {
|
||||||
|
if (FILE *file = fopen(startup_script, "r")) {
|
||||||
|
fseek(file, 0, SEEK_END);
|
||||||
|
size_t size = ftell(file);
|
||||||
|
fseek(file, 0, SEEK_SET);
|
||||||
|
|
||||||
|
char *script_content = (char *)malloc(size + 1);
|
||||||
|
fread(script_content, 1, size, file);
|
||||||
|
script_content[size] = '\0';
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
Ruby::run_string(script_content, size);
|
||||||
|
free(script_content);
|
||||||
|
|
||||||
|
if (Ruby::mrb->exc) {
|
||||||
|
mrb_print_error(Ruby::mrb);
|
||||||
|
Ruby::mrb->exc = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Ran: %s\n", startup_script);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Failed to open startup script: %s\n", startup_script);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -27,6 +27,7 @@ Window::Window(Vec2<float> size, const char *title) {
|
|||||||
|
|
||||||
uint32_t pixel = 0xFFFFFFFF;
|
uint32_t pixel = 0xFFFFFFFF;
|
||||||
SDL_UpdateTexture(white_tex, nullptr, &pixel, sizeof(pixel));
|
SDL_UpdateTexture(white_tex, nullptr, &pixel, sizeof(pixel));
|
||||||
|
SDL_SetTextureBlendMode(white_tex, SDL_BLENDMODE_BLEND);
|
||||||
};
|
};
|
||||||
|
|
||||||
void Window::update(Vec2<float> new_size, const char *new_title) {
|
void Window::update(Vec2<float> new_size, const char *new_title) {
|
||||||
|
|||||||
Reference in New Issue
Block a user