diff --git a/include/app/elements.h b/include/app/elements.h index 3178291..e6db08d 100644 --- a/include/app/elements.h +++ b/include/app/elements.h @@ -8,7 +8,8 @@ enum Type { IMAGE, TEXT, - RECT }; + RECT, + SCRIPT }; enum ClickMode { PASS, BLOCK, @@ -72,8 +73,9 @@ private: }; struct ERect : Element { - Vec2 shape = {0, 0}; Color color = {255, 255, 255}; + Vec2 shape = {0, 0}; + Vec2 size() override; ERect(Vec2 shape, Color color) : Element(Type::RECT), shape(shape), color(color) {} @@ -81,6 +83,34 @@ struct ERect : Element { void render(uint64_t) override; }; +struct EScript : Element { + Ruby::Block script; + uint64_t scripted_id = UINT64_MAX; + bool pixel_art = false; + Vec2 shape; + + EScript(Vec2 shape) : Element(Type::SCRIPT), shape(shape) { + scripted_id = window.create_scripted(shape, pixel_art); + } + + ~EScript() { + if (scripted_id != UINT64_MAX) + scripted_pool.release(scripted_id); + }; + + Vec2 size() override { + return shape; + } + + bool update_scripted(Rect content_rect, const char *new_content, uint32_t length) { + return window.update_scripted(scripted_id, content_rect, new_content, length); + } + + void render(uint64_t) override { + window.draw_scripted(scripted_id, position, z, rotation, pivot, scale, alpha); + }; +}; + inline Pool element_pool; } // namespace app diff --git a/include/bindings/definitions.h b/include/bindings/definitions.h index 27cac02..ab42ac2 100644 --- a/include/bindings/definitions.h +++ b/include/bindings/definitions.h @@ -18,6 +18,7 @@ inline struct RubyBindings { Binding ElementText; Binding ElementRect; Binding ElementImage; + Binding ElementScript; Binding Scene; } bindings; @@ -43,6 +44,7 @@ void register_animation(); void register_element_text(); void register_element_rect(); void register_element_image(); +void register_element_script(); void register_scene(); void register_app(); @@ -55,6 +57,7 @@ inline void setup() { register_element_text(); register_element_rect(); register_element_image(); + register_element_script(); register_scene(); register_app(); } diff --git a/include/window/window.h b/include/window/window.h index d725e29..6e44309 100644 --- a/include/window/window.h +++ b/include/window/window.h @@ -32,6 +32,15 @@ struct Text { } }; +struct Scripted { + SDL_Texture *texture; + Vec2 size; + + ~Scripted() { + SDL_DestroyTexture(texture); + } +}; + // Image pool inline Pool image_pool; @@ -41,6 +50,9 @@ inline Pool font_pool; // Text pool inline Pool text_pool; +// Scripted pool +inline Pool scripted_pool; + struct Event { enum Type { NONE, @@ -107,12 +119,19 @@ public: uint64_t create_text(uint64_t font_id, const char *text, uint32_t length, Color color); Vec2 get_text_size(uint64_t text_id); + uint64_t create_scripted(Vec2 size, bool pixel_art); + Vec2 get_scripted_size(uint64_t scripted_id); + + bool update_scripted(uint64_t scripted_id, Rect content_rect, const char *new_content, uint32_t length); + bool write(uint64_t text_id, Vec2 position, float z, float angle, Vec2 pivot, Vec2 scale, float alpha); bool draw(uint64_t image_id, Vec2 position, float z, float angle, Vec2 pivot, Vec2 scale, float alpha); bool draw_rect(Rect rect, Color color, float z, float angle, Vec2 pivot, Vec2 scale, float alpha); + bool draw_scripted(uint64_t scripted_id, Vec2 position, float z, float angle, Vec2 pivot, Vec2 scale, float alpha); + void render(); // the rest of the calls only build the instruction list, this actually renders everything added to the screen (following z order) void clear(); // clears the instruction list @@ -147,7 +166,8 @@ private: enum Type { DRAW_IMAGE, DRAW_TEXT, - DRAW_RECT + DRAW_RECT, + DRAW_SCRIPTED } type; float z; @@ -171,10 +191,15 @@ private: Rect rect; Color color; } draw_rect; + + struct { + uint64_t scripted_id; + Vec2 position; + } draw_scripted; }; }; - std::vector render_instructions; + std::vector render_instructions; void compute_transform(); diff --git a/samples/button.rb b/samples/button.rb index 8c0d6e6..e8628fa 100644 --- a/samples/button.rb +++ b/samples/button.rb @@ -20,7 +20,9 @@ rect = ElementRect.new 100, 25, 0xFF0000, position: {x: 50, y: 50}, z: 0, rotati image_element = ElementImage.new bg_animation, position: {x: 0, y: 0}, z: -1, click_mode: :block, alpha: 1 -pp text, rect, image_element +script_element = ElementScript.new 100, 100, position: {x: 200, y: 200}, z: 2, click_mode: :block, alpha: 1 + +pp text, rect, image_element, script_element end_scene = Scene.new end_scene << rect @@ -56,9 +58,15 @@ App.update do |delta_time| c_ += delta_time c_c += 1 - # commented out to avoid spamming the console, but you can uncomment to see the delta time being printed every frame # puts "Delta time: #{delta_time}s" + + red_pixel = [0xFF, 0, 0, 255] # ARGB + green_pixel = [0xFF, 0, 255, 0] # ARGB + c_p = c_c % 2 == 0 ? red_pixel : green_pixel + raw_bytes = (c_p * (50 * 50)).pack('C*') + script_element.update(0, 0, 50, 50, raw_bytes) + el = App.scene.elements_at(App.mouse_position) pp App.text_input if App.text_input != "" @@ -82,7 +90,7 @@ end main_scene << text main_scene << rect main_scene << image_element - +main_scene << script_element App.start main_scene time = Time.now - start diff --git a/samples/main.rb b/samples/main.rb index 8b5287f..2576b98 100644 --- a/samples/main.rb +++ b/samples/main.rb @@ -96,7 +96,7 @@ end # for entities it does similarly # mouse state can be read in systems always -query = tag(:enemy) & component(:health, true) & anti(name("Trap Preview", true)) +query = Query.from(tag(:enemy) & component(:health, true) & anti(name("Trap Preview", true))) # in the match name will return just the one entity # tag and componet return list-like tables but they will be parallel and in order # empty columns will be filled with nil (not possible if only & is used) @@ -105,7 +105,7 @@ query = tag(:enemy) & component(:health, true) & anti(name("Trap Preview", true) world.system :health_regen, for: query do |match, dt| # no trigger means always # example of a simple health regeneration system that regenerates health for all entities with a health component - healths = match[0] + healths = query.get[0] # get the first component in the query, in this case health, this will be a list of all health components for entities that match the query healths.add_scalar! :health, 1 * dt healths.clamp! :health, 0, 100 end @@ -136,12 +136,14 @@ world.system :trap_placement, trigger: trigger do |dt| end end +name_query = Query.from(name("Trap Preview", true) & component(:sprite, true)) + # could be done as so or -world.system :trap_preview, for: name("Trap Preview", true) do |match, dt| # example from teh game mist trying to be made with this engine +world.system :trap_preview do |match, dt| # example from teh game mist trying to be made with this engine # do trap placement logic when a trap is held return unless world.holding_trap? # show a preview of the trap at the mouse position, you can use the mouse position relative to the world for this - e = match.first + e = name_query.get[0] # get the first entity that matches the name query, in this case the trap preview entity, this will be nil if no entity matches e.position = world.mouse_position_world e.active = true # enable the trap preview entity to show it at the mouse position end diff --git a/src/bindings/element_script.cc b/src/bindings/element_script.cc new file mode 100644 index 0000000..9860a85 --- /dev/null +++ b/src/bindings/element_script.cc @@ -0,0 +1,291 @@ +#include "app/app.h" +#include "bindings/definitions.h" + +namespace { +void ElementScript_free(mrb_state *mrb, void *ptr) { + UNUSED(mrb); + uint64_t id = ((Wrapper *)ptr)->id; + app::element_pool.release(id); + delete (Wrapper *)ptr; +} + +const struct mrb_data_type ElementScript_type = { + "ElementScript", + ElementScript_free +}; + +mrb_value ElementScript_init(mrb_state *mrb, mrb_value self) { + Wrapper *old = (Wrapper *)DATA_PTR(self); + if (old) + mrb_free(mrb, old); + mrb_data_init(self, nullptr, &ElementScript_type); + Wrapper *s = new Wrapper(); + + mrb_float shape_x, shape_y; + mrb_value kwargs; + mrb_get_args(mrb, "ff|H", &shape_x, &shape_y, &kwargs); + + Vec2 shape = {(float)shape_x, (float)shape_y}; + + float x = 0, y = 0, z = 0, rotation = 0; + float scale_x = 1, scale_y = 1, alpha = 1, pivot_x = 0.5, pivot_y = 0.5; + mrb_sym click_mode_sym = sym_block; + if (!mrb_nil_p(kwargs)) { + mrb_value pos_hash = HASH_GET(kwargs, position); + if (!mrb_nil_p(pos_hash)) { + HASH_FLOAT(pos_hash, x, x); + HASH_FLOAT(pos_hash, y, y); + } + + mrb_value scale_hash = HASH_GET(kwargs, scale); + if (!mrb_nil_p(scale_hash)) { + HASH_FLOAT(scale_hash, x, scale_x); + HASH_FLOAT(scale_hash, y, scale_y); + } + + mrb_value pivot_hash = HASH_GET(kwargs, pivot); + if (!mrb_nil_p(pivot_hash)) { + HASH_FLOAT(pivot_hash, x, pivot_x); + HASH_FLOAT(pivot_hash, y, pivot_y); + } + + HASH_FLOAT(kwargs, z, z); + HASH_FLOAT(kwargs, rotation, rotation); + HASH_FLOAT(kwargs, alpha, alpha); + mrb_value v = HASH_GET(kwargs, click_mode); + if (!mrb_nil_p(v)) { + click_mode_sym = mrb_symbol(v); + } + } + + app::EScript *element = new app::EScript(shape); + element->position = {x, y}; + element->scale = {scale_x, scale_y}; + element->rotation = rotation; + element->pivot = {pivot_x, pivot_y}; + element->alpha = alpha; + element->z = z; + + std::string click_mode = mrb_sym2name(mrb, click_mode_sym); + + if (click_mode == "pass") + element->click_mode = ClickMode::PASS; + else if (click_mode == "block") + element->click_mode = ClickMode::BLOCK; + else if (click_mode == "ignore") + element->click_mode = ClickMode::IGNORE; + + s->id = app::element_pool.acquire(element); + app::element_pool.use(s->id); + + mrb_data_init(self, s, &ElementScript_type); + return self; +} + +mrb_value element_script_on_click(mrb_state *mrb, mrb_value self) { + mrb_value block; + mrb_get_args(mrb, "&", &block); + Wrapper *wrapper = (Wrapper *)DATA_PTR(self); + uint64_t id = wrapper->id; + app::Element *element = app::element_pool[id]; + element->on_click.set_proc(block); + return self; +} + +mrb_value element_script_equal(mrb_state *mrb, mrb_value self) { + mrb_value other; + mrb_get_args(mrb, "o", &other); + + if (!mrb_obj_is_kind_of(mrb, other, definitions::bindings.ElementScript.cls)) + return mrb_false_value(); + + Wrapper *wrapper_self = (Wrapper *)DATA_PTR(self); + Wrapper *wrapper_other = (Wrapper *)DATA_PTR(other); + return mrb_bool_value(wrapper_self->id == wrapper_other->id); +} + +mrb_value element_script_inspect(mrb_state *mrb, mrb_value self) { + Wrapper *wrapper = (Wrapper *)DATA_PTR(self); + uint64_t id = wrapper->id; + app::EScript *element = (app::EScript *)app::element_pool[id]; + std::string str = "#"; + return mrb_str_new_cstr(mrb, str.c_str()); +} + +mrb_value element_script_hash(mrb_state *, mrb_value self) { + Wrapper *wrapper = (Wrapper *)DATA_PTR(self); + return mrb_fixnum_value(wrapper->id); +} + +mrb_value element_script_position_get(mrb_state *, mrb_value self) { + Wrapper *wrapper = (Wrapper *)DATA_PTR(self); + uint64_t id = wrapper->id; + + app::EScript *element = (app::EScript *)app::element_pool[id]; + return definitions::Vec2_wrap(id, &element->position); +} + +mrb_value element_script_rotation_get(mrb_state *mrb, mrb_value self) { + Wrapper *wrapper = (Wrapper *)DATA_PTR(self); + uint64_t id = wrapper->id; + app::EScript *element = (app::EScript *)app::element_pool[id]; + return mrb_float_value(mrb, element->rotation); +} + +mrb_value element_script_rotation_set(mrb_state *mrb, mrb_value self) { + mrb_float rotation; + mrb_get_args(mrb, "f", &rotation); + + Wrapper *wrapper = (Wrapper *)DATA_PTR(self); + uint64_t id = wrapper->id; + app::EScript *element = (app::EScript *)app::element_pool[id]; + element->rotation = rotation; + + return mrb_nil_value(); +} + +mrb_value element_script_pivot_get(mrb_state *, mrb_value self) { + Wrapper *wrapper = (Wrapper *)DATA_PTR(self); + uint64_t id = wrapper->id; + app::EScript *element = (app::EScript *)app::element_pool[id]; + return definitions::Vec2_wrap(id, &element->pivot); +} + +mrb_value element_script_scale_get(mrb_state *, mrb_value self) { + Wrapper *wrapper = (Wrapper *)DATA_PTR(self); + uint64_t id = wrapper->id; + app::EScript *element = (app::EScript *)app::element_pool[id]; + return definitions::Vec2_wrap(id, &element->scale); +} + +mrb_value element_script_alpha_get(mrb_state *mrb, mrb_value self) { + Wrapper *wrapper = (Wrapper *)DATA_PTR(self); + uint64_t id = wrapper->id; + app::EScript *element = (app::EScript *)app::element_pool[id]; + return mrb_float_value(mrb, element->alpha); +} + +mrb_value element_script_alpha_set(mrb_state *mrb, mrb_value self) { + mrb_float alpha; + mrb_get_args(mrb, "f", &alpha); + + Wrapper *wrapper = (Wrapper *)DATA_PTR(self); + uint64_t id = wrapper->id; + app::EScript *element = (app::EScript *)app::element_pool[id]; + element->alpha = alpha; + + return mrb_nil_value(); +} + +mrb_value element_script_z_get(mrb_state *mrb, mrb_value self) { + Wrapper *wrapper = (Wrapper *)DATA_PTR(self); + uint64_t id = wrapper->id; + app::EScript *element = (app::EScript *)app::element_pool[id]; + return mrb_float_value(mrb, element->z); +} + +mrb_value element_script_z_set(mrb_state *mrb, mrb_value self) { + mrb_float z; + mrb_get_args(mrb, "f", &z); + + Wrapper *wrapper = (Wrapper *)DATA_PTR(self); + uint64_t id = wrapper->id; + app::EScript *element = (app::EScript *)app::element_pool[id]; + element->z = z; + + return mrb_nil_value(); +} + +mrb_value element_script_click_mode_get(mrb_state *mrb, mrb_value self) { + Wrapper *wrapper = (Wrapper *)DATA_PTR(self); + uint64_t id = wrapper->id; + app::EScript *element = (app::EScript *)app::element_pool[id]; + + std::string click_mode_str; + switch (element->click_mode) { + case ClickMode::PASS: + click_mode_str = "pass"; + break; + case ClickMode::BLOCK: + click_mode_str = "block"; + break; + case ClickMode::IGNORE: + click_mode_str = "ignore"; + break; + } + + return mrb_symbol_value(mrb_intern_cstr(mrb, click_mode_str.c_str())); +} + +mrb_value element_script_click_mode_set(mrb_state *mrb, mrb_value self) { + mrb_sym click_mode_sym; + mrb_get_args(mrb, "n", &click_mode_sym); + + Wrapper *wrapper = (Wrapper *)DATA_PTR(self); + uint64_t id = wrapper->id; + app::EScript *element = (app::EScript *)app::element_pool[id]; + + std::string click_mode = mrb_sym2name(mrb, click_mode_sym); + + if (click_mode == "pass") + element->click_mode = ClickMode::PASS; + else if (click_mode == "block") + element->click_mode = ClickMode::BLOCK; + else if (click_mode == "ignore") + element->click_mode = ClickMode::IGNORE; + + return mrb_nil_value(); +} + +mrb_value element_script_update(mrb_state *mrb, mrb_value self) { + const char *content; + mrb_int content_len; + mrb_int x, y, w, h; + mrb_get_args(mrb, "iiiis", &x, &y, &w, &h, &content, &content_len); + + uint32_t expected_len = (uint32_t)(w * h * 4); + if ((uint32_t)content_len < expected_len) { + mrb_raise(mrb, mrb_class_get(mrb, "ArgumentError"), "Pixel string length is too short for the specified rectangle bounds."); + } + + Wrapper *wrapper = (Wrapper *)DATA_PTR(self); + uint64_t id = wrapper->id; + + Rect content_rect = Rect::from({(float)x, (float)y}, {(float)w, (float)h}); + + app::EScript *element = (app::EScript *)app::element_pool[id]; + bool success = element->update_scripted(content_rect, content, content_len); + + return mrb_bool_value(success); +} +} // namespace + +namespace definitions { +void register_element_script() { + DEF_CLASS(ElementScript, MRB_ARGS_REQ(2) | MRB_ARGS_OPT(1)); + ADD_METHOD(ElementScript, "click", element_script_on_click, MRB_ARGS_BLOCK()); + ADD_METHOD(ElementScript, "==", element_script_equal, MRB_ARGS_REQ(1)); + ADD_METHOD(ElementScript, "eql?", element_script_equal, MRB_ARGS_REQ(1)); + ADD_METHOD(ElementScript, "inspect", element_script_inspect, MRB_ARGS_NONE()); + ADD_METHOD(ElementScript, "to_s", element_script_inspect, MRB_ARGS_NONE()); + ADD_METHOD(ElementScript, "hash", element_script_hash, MRB_ARGS_NONE()); + ADD_METHOD(ElementScript, "update", element_script_update, MRB_ARGS_REQ(5)); + ADD_METHOD(ElementScript, "position", element_script_position_get, MRB_ARGS_NONE()); + ADD_METHOD(ElementScript, "scale", element_script_scale_get, MRB_ARGS_NONE()); + ADD_METHOD(ElementScript, "rotation=", element_script_rotation_set, MRB_ARGS_REQ(1)); + ADD_METHOD(ElementScript, "rotation", element_script_rotation_get, MRB_ARGS_NONE()); + ADD_METHOD(ElementScript, "pivot", element_script_pivot_get, MRB_ARGS_NONE()); + ADD_METHOD(ElementScript, "alpha=", element_script_alpha_set, MRB_ARGS_REQ(1)); + ADD_METHOD(ElementScript, "alpha", element_script_alpha_get, MRB_ARGS_NONE()); + ADD_METHOD(ElementScript, "z=", element_script_z_set, MRB_ARGS_REQ(1)); + ADD_METHOD(ElementScript, "z", element_script_z_get, MRB_ARGS_NONE()); + ADD_METHOD(ElementScript, "click_mode=", element_script_click_mode_set, MRB_ARGS_REQ(1)); + ADD_METHOD(ElementScript, "click_mode", element_script_click_mode_get, MRB_ARGS_NONE()); + + bindings.ElementScript = {ElementScript_class, &ElementScript_type}; +} +} // namespace definitions \ No newline at end of file diff --git a/src/bindings/scene.cc b/src/bindings/scene.cc index f26be86..3f344e5 100644 --- a/src/bindings/scene.cc +++ b/src/bindings/scene.cc @@ -34,6 +34,7 @@ mrb_value scene_add_element(mrb_state *mrb, mrb_value self) { !mrb_obj_is_kind_of(mrb, element_val, definitions::bindings.ElementText.cls) && !mrb_obj_is_kind_of(mrb, element_val, definitions::bindings.ElementRect.cls) && !mrb_obj_is_kind_of(mrb, element_val, definitions::bindings.ElementImage.cls) + && !mrb_obj_is_kind_of(mrb, element_val, definitions::bindings.ElementScript.cls) ) { mrb_raise( mrb, @@ -60,6 +61,7 @@ mrb_value scene_delete_element(mrb_state *mrb, mrb_value self) { !mrb_obj_is_kind_of(mrb, element_val, definitions::bindings.ElementText.cls) && !mrb_obj_is_kind_of(mrb, element_val, definitions::bindings.ElementRect.cls) && !mrb_obj_is_kind_of(mrb, element_val, definitions::bindings.ElementImage.cls) + && !mrb_obj_is_kind_of(mrb, element_val, definitions::bindings.ElementScript.cls) ) { mrb_raise( mrb, @@ -107,18 +109,37 @@ mrb_value scene_elements_at(mrb_state *mrb, mrb_value self) { for (uint64_t element_id : element_ids) { app::Element *element = app::element_pool[element_id]; app::element_pool.use(element_id); - if (element->type == Type::TEXT) { - mrb_value obj = definitions::wrap(definitions::bindings.ElementText, element_id); + mrb_value obj; + switch (element->type) { + case Type::TEXT: + obj = definitions::wrap( + definitions::bindings.ElementText, + element_id + ); + break; + case Type::RECT: + obj = definitions::wrap( + definitions::bindings.ElementRect, + element_id + ); + break; + case Type::IMAGE: + obj = definitions::wrap( + definitions::bindings.ElementImage, + element_id + ); mrb_ary_push(mrb, result, obj); - } else if (element->type == Type::RECT) { - mrb_value obj = definitions::wrap(definitions::bindings.ElementRect, element_id); - mrb_ary_push(mrb, result, obj); - } else if (element->type == Type::IMAGE) { - mrb_value obj = definitions::wrap(definitions::bindings.ElementImage, element_id); - mrb_ary_push(mrb, result, obj); - } else { + break; + case Type::SCRIPT: + obj = definitions::wrap( + definitions::bindings.ElementScript, + element_id + ); + break; + default: continue; } + mrb_ary_push(mrb, result, obj); } return result; @@ -134,18 +155,37 @@ mrb_value scene_elements(mrb_state *mrb, mrb_value self) { for (uint64_t element_id : element_ids) { app::Element *element = app::element_pool[element_id]; app::element_pool.use(element_id); - if (element->type == Type::TEXT) { - mrb_value obj = definitions::wrap(definitions::bindings.ElementText, element_id); + mrb_value obj; + switch (element->type) { + case Type::TEXT: + obj = definitions::wrap( + definitions::bindings.ElementText, + element_id + ); + break; + case Type::RECT: + obj = definitions::wrap( + definitions::bindings.ElementRect, + element_id + ); + break; + case Type::IMAGE: + obj = definitions::wrap( + definitions::bindings.ElementImage, + element_id + ); mrb_ary_push(mrb, result, obj); - } else if (element->type == Type::RECT) { - mrb_value obj = definitions::wrap(definitions::bindings.ElementRect, element_id); - mrb_ary_push(mrb, result, obj); - } else if (element->type == Type::IMAGE) { - mrb_value obj = definitions::wrap(definitions::bindings.ElementImage, element_id); - mrb_ary_push(mrb, result, obj); - } else { + break; + case Type::SCRIPT: + obj = definitions::wrap( + definitions::bindings.ElementScript, + element_id + ); + break; + default: continue; } + mrb_ary_push(mrb, result, obj); } return result; diff --git a/src/window/window.cc b/src/window/window.cc index 0897921..f3c6a94 100644 --- a/src/window/window.cc +++ b/src/window/window.cc @@ -177,6 +177,83 @@ bool Window::write(uint64_t text_id, Vec2 position, float z, float angle, return true; }; +uint64_t Window::create_scripted(Vec2 size, bool pixel_art) { + SDL_Texture *texture = SDL_CreateTexture( + renderer, + SDL_PIXELFORMAT_RGBA8888, + SDL_TEXTUREACCESS_STREAMING, + (int)size.x, + (int)size.y + ); + + if (!texture) { + fprintf(stderr, "Failed to create scripted texture: %s\n", SDL_GetError()); + return UINT64_MAX; + } + + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + + if (pixel_art) + SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_NEAREST); + + Scripted *s = new Scripted{texture, size}; + uint64_t id = scripted_pool.acquire(s); + scripted_pool.use(id); + + char *buffer = (char *)malloc(size.x * size.y * 4 * sizeof(char)); + + for (unsigned int i = 0; i < (unsigned int)(size.x * size.y * 4); i += 4) { + buffer[i] = 0xFF; + buffer[i + 1] = 0xFF; + buffer[i + 2] = 0xFF; + buffer[i + 3] = 0xFF; + } + + SDL_UpdateTexture( + s->texture, + nullptr, + buffer, + (int)(size.x * 4) + ); + + free(buffer); + + return id; +} + +Vec2 Window::get_scripted_size(uint64_t scripted_id) { + if (!scripted_pool.is_valid(scripted_id)) { + fprintf(stderr, "Invalid scripted ID: %llu\n", (unsigned long long)scripted_id); + return {0, 0}; + } + return scripted_pool[scripted_id]->size; +} + +bool Window::update_scripted(uint64_t scripted_id, Rect content_rect, const char *new_content, uint32_t length) { + if (!scripted_pool.is_valid(scripted_id)) { + fprintf(stderr, "Invalid scripted ID: %llu\n", (unsigned long long)scripted_id); + return false; + } + + Scripted *s = scripted_pool[scripted_id]; + + SDL_Rect sdl_rect = { + (int)content_rect.x(), + (int)content_rect.y(), + (int)content_rect.w(), + (int)content_rect.h() + }; + + SDL_UpdateTexture( + s->texture, + &sdl_rect, + new_content, + (int)(content_rect.w() * 4) + ); + + return true; +}; + bool Window::draw(uint64_t image_id, Vec2 position, float z, float angle, Vec2 pivot, Vec2 scale, float alpha) { if (!image_pool.is_valid(image_id)) { fprintf(stderr, "Invalid image ID: %llu\n", (unsigned long long)image_id); @@ -216,6 +293,28 @@ bool Window::draw_rect(Rect rect, Color color, float z, float angle, Vec2 return true; }; +bool Window::draw_scripted(uint64_t scripted_id, Vec2 position, float z, float angle, Vec2 pivot, Vec2 scale, float alpha) { + if (!scripted_pool.is_valid(scripted_id)) { + fprintf(stderr, "Invalid scripted ID: %llu\n", (unsigned long long)scripted_id); + return false; + } + + render_instructions.push_back( + {.type = RenderInstruction::DRAW_SCRIPTED, + .z = z, + .angle = angle, + .pivot = pivot, + .scale = scale, + .alpha = alpha, + .draw_scripted = { + .scripted_id = scripted_id, + .position = position, + }} + ); + + return true; +}; + void Window::draw_letterbox() { SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); @@ -335,6 +434,45 @@ void Window::render() { ); } break; + case RenderInstruction::DRAW_SCRIPTED: { + Scripted *scripted = scripted_pool[instruction.draw_scripted.scripted_id]; + SDL_Texture *texture = scripted->texture; + + SDL_FlipMode flip = SDL_FLIP_NONE; + + if (instruction.scale.x < 0) + flip = (SDL_FlipMode)(flip | SDL_FLIP_HORIZONTAL); + if (instruction.scale.y < 0) + flip = (SDL_FlipMode)(flip | SDL_FLIP_VERTICAL); + + float scale_x = std::abs(instruction.scale.x) * scale; + float scale_y = std::abs(instruction.scale.y) * scale; + + SDL_FRect dst_rect; + dst_rect.x = instruction.draw_scripted.position.x * scale + offset.x; + dst_rect.y = instruction.draw_scripted.position.y * scale + offset.y; + + dst_rect.w = scale_x * scripted->size.x; + dst_rect.h = scale_y * scripted->size.y; + + SDL_SetTextureAlphaModFloat(texture, instruction.alpha); + + SDL_FPoint pivot_point = { + instruction.pivot.x * scale_x, + instruction.pivot.y * scale_y + }; + + SDL_RenderTextureRotated( + renderer, + texture, + nullptr, + &dst_rect, + instruction.angle, + &pivot_point, + flip + ); + } break; + case RenderInstruction::DRAW_RECT: { SDL_FRect dst_rect; dst_rect.x = instruction.draw_rect.rect.x() * scale + offset.x; diff --git a/writeup/analysis.md b/writeup/analysis.md index 00247e7..edb8bfd 100644 --- a/writeup/analysis.md +++ b/writeup/analysis.md @@ -138,7 +138,7 @@ The target users for this project could be one of 2 categories: Survey: -In order to help me gague what my users would like, +In order to help me gauge what my users would like, we'll be splitting the survey into 2 sections, one for indie developers and one for educators