From c5cf0f3cd39245720e7b0559b55fea55e57aacbc Mon Sep 17 00:00:00 2001 From: Insality Date: Thu, 4 Dec 2025 20:34:42 +0200 Subject: [PATCH 1/6] Optimize event bus, changed API for process events --- decore/internal/event_bus.lua | 59 ++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/decore/internal/event_bus.lua b/decore/internal/event_bus.lua index 0c50832..d957824 100644 --- a/decore/internal/event_bus.lua +++ b/decore/internal/event_bus.lua @@ -1,7 +1,12 @@ +-- Special key for events without entity +local NO_ENTITY_KEY = "system" + ---@class decore.event_bus ----@field events table The list of events group by event name. Array part of entities map is the order of entities event's triggers ----@field stash table Events to be processed in PostWrap ----@field merge_callbacks table The merge policy for events. If the merge policy returns true, the events are merged and not will be added as new event +---@field events table The list of events group by event name. Array part of entities map is the order of entities event's triggers +---@field events_by_entity table> Maps event_name -> entity -> array of events for fast lookup +---@field stash table Events to be processed in PostWrap +---@field stash_by_entity table> Maps event_name -> entity -> array of events for stash (fast lookup) +---@field merge_callbacks table):boolean> The merge policy for events. If the merge policy returns true, the events are merged and not will be added as new event local M = {} ---Creates a new event bus. @@ -9,7 +14,9 @@ local M = {} function M.create() local instance = { events = {}, + events_by_entity = {}, stash = {}, + stash_by_entity = {}, merge_callbacks = {}, } @@ -22,42 +29,54 @@ end ---@param data any The data to pass to the event and its associated callbacks. function M:trigger(event_name, data) self.stash[event_name] = self.stash[event_name] or {} + self.stash_by_entity[event_name] = self.stash_by_entity[event_name] or {} local stash = self.stash[event_name] + local stash_by_entity = self.stash_by_entity[event_name] local merge_callback = self.merge_callbacks[event_name] - local is_merged = merge_callback and merge_callback(data, stash) + local entity = data and data.entity + local is_merged = false + + if merge_callback then + is_merged = merge_callback(data, stash, stash_by_entity) + end if not is_merged then - table.insert(stash, data or true) + local event_data = data or true + table.insert(stash, event_data) + local entity_key = entity or NO_ENTITY_KEY + stash_by_entity[entity_key] = stash_by_entity[entity_key] or {} + table.insert(stash_by_entity[entity_key], event_data) end end ----Processes a specified event, executing the callback function with the provided context. +---Processes a specified event, returning the list of events and optionally calling callback with the full list. ---@param event_name hash|string The name of the event to process. ----@param callback fun(...) The callback function to execute. ----@param context any|nil The context in which to execute the callback. +---@param callback fun(events: any[])|fun(context: any, events: any[])|nil Optional callback function to execute with the full list of events. +---@param context any|nil Optional context to pass as first argument to callback. +---@return any[]|nil events The list of events, or nil if no events found. function M:process(event_name, callback, context) local events = self.events[event_name] - if not events then - return + if not events or #events == 0 then + return nil end - if context then - for i = 1, #events do - callback(context, events[i]) - end - else - for i = 1, #events do - callback(events[i]) + if callback then + if context then + callback(context, events) + else + callback(events) end end + + return events end ---You can set the merge policy for an event. This is useful when you want to merge events of the same type. ---@param event_name string The name of the event to set the merge policy for. ----@param merge_callback (fun(events: any[], new_event: any):boolean)|nil The callback function to merge the events. Return true if the events were merged, false otherwise. +---@param merge_callback (fun(new_event: any, events: any[], entity_map: table):boolean)|nil The callback function to merge the events. Return true if the events were merged, false otherwise. function M:set_merge_policy(event_name, merge_callback) self.merge_callbacks[event_name] = merge_callback end @@ -65,12 +84,16 @@ end function M:clear_events() self.events = {} + self.events_by_entity = {} end function M:stash_to_events() self.events = self.stash self.stash = {} + + self.events_by_entity = self.stash_by_entity + self.stash_by_entity = {} end From d7a353f688a264cf0c07a637be1ec118b5f083b1 Mon Sep 17 00:00:00 2001 From: Insality Date: Fri, 5 Dec 2025 09:57:17 +0200 Subject: [PATCH 2/6] Fix logger --- decore/internal/decore_data.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/decore/internal/decore_data.lua b/decore/internal/decore_data.lua index 661cb17..62b7bd8 100644 --- a/decore/internal/decore_data.lua +++ b/decore/internal/decore_data.lua @@ -116,7 +116,7 @@ function M.get_entity(prefab_id, pack_id) end end - decore_internal.logger:warn("Entity is not registered in Decore to spawn", { + logger:warn("Entity is not registered in Decore to spawn", { prefab_id = prefab_id, pack_id = pack_id, }) From f875e0de86c4370332a4806f2a0f62f4884936a1 Mon Sep 17 00:00:00 2001 From: Insality Date: Wed, 10 Dec 2025 19:53:27 +0200 Subject: [PATCH 3/6] Update --- decore/decore.lua | 3 +-- decore/internal/decore_logger.lua | 6 +++--- decore/internal/decore_utils.lua | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/decore/decore.lua b/decore/decore.lua index 22ed8ff..1e83a8e 100644 --- a/decore/decore.lua +++ b/decore/decore.lua @@ -8,7 +8,6 @@ local system_decore = require("decore.internal.system_decore") local system_event_bus = require("decore.internal.system_event_bus") local EMPTY_HASH = hash("") -local TYPE_TABLE = "table" local NEXT_ENTITY_ID = 0 ---@class world @@ -255,7 +254,7 @@ function M.apply_component(entity, component_id, component_data) end if component_data ~= nil then - if type(component_data) == TYPE_TABLE then + if type(component_data) == "table" then decore_utils.merge_tables(entity[component_id], component_data) else entity[component_id] = component_data diff --git a/decore/internal/decore_logger.lua b/decore/internal/decore_logger.lua index 0b71bbd..3844a7b 100644 --- a/decore/internal/decore_logger.lua +++ b/decore/internal/decore_logger.lua @@ -38,10 +38,10 @@ end ---@param t table ---@param depth number? ---@param result string|nil Internal parameter ----@return string, boolean result String representation of table, Is max string length reached +---@return string result String representation of table function M.table_to_string(t, depth, result) if type(t) ~= "table" then - return tostring(t) or "", false + return tostring(t) or "" end depth = depth or 0 @@ -71,7 +71,7 @@ function M.table_to_string(t, depth, result) end end - return result .. "}", false + return result .. "}" end diff --git a/decore/internal/decore_utils.lua b/decore/internal/decore_utils.lua index d9e1d68..320f5c2 100644 --- a/decore/internal/decore_utils.lua +++ b/decore/internal/decore_utils.lua @@ -27,7 +27,7 @@ end function M.deepcopy(value_to_copy) local orig_type = type(value_to_copy) local copy - if orig_type == 'table' then + if orig_type == "table" then copy = {} for orig_key, orig_value in next, value_to_copy, nil do copy[M.deepcopy(orig_key)] = M.deepcopy(orig_value) From 558fe0ac9f9e2c58333f9729e45e9d74024dddc5 Mon Sep 17 00:00:00 2001 From: Insality Date: Mon, 22 Dec 2025 18:50:11 +0200 Subject: [PATCH 4/6] Clear non actual files, update README --- README.md | 43 +- decore/editor_scripts/decore.editor_script | 64 - .../basic_camera_setup.collection | 47 - .../basic_camera_setup.script | 31 - examples/basic_camera_setup/entity/player.go | 53 - examples/basic_camera_setup/entity/player.lua | 10 - .../basic_collision.collection | 1110 ----------------- .../basic_collision/basic_collision.script | 28 - examples/basic_collision/entity/coin.go | 62 - .../basic_collision/entity/entity_coin.lua | 11 - examples/basic_collision/entity/player.go | 53 - examples/basic_collision/entity/player.lua | 11 - .../basic_collision/entity/score_counter.go | 13 - .../basic_collision/entity/score_counter.gui | 24 - .../entity/score_counter.gui_script | 12 - .../basic_collision/entity/score_counter.lua | 3 - .../basic_movement/basic_movement.collection | 19 - examples/basic_movement/basic_movement.script | 23 - examples/basic_movement/entity/player.go | 30 - examples/basic_movement/entity/player.lua | 10 - .../basic_movement_with_walls.collection | 111 -- .../basic_movement_with_walls.script | 27 - .../entity/player.go | 30 - .../entity/player.lua | 8 - .../basic_movement_with_walls/entity/wall.go | 41 - .../basic_platformer_controller.collection | 343 ----- .../basic_platformer_controller.script | 44 - .../entity/coin.go | 62 - .../entity/coin.lua | 9 - .../entity/player.go | 60 - .../entity/player.lua | 9 - .../entity/score_counter.go | 13 - .../entity/score_counter.gui | 24 - .../entity/score_counter.gui_script | 12 - .../entity/score_counter.lua | 3 - .../system/system_on_remove_event.lua | 26 - .../system/system_score_counter.lua | 34 - examples/boids/boid/boid.go | 24 - examples/boids/boid/boid.png | Bin 2375 -> 0 bytes examples/boids/boid/boid_panthera.lua | 66 - examples/boids/boid/boid_sensor.gui | 36 - examples/boids/boid/boid_sensor.gui_script | 9 - examples/boids/boid/boid_sensor.lua | 31 - examples/boids/boid/boid_sensor_panthera.lua | 104 -- examples/boids/boid/entity_boid.lua | 42 - examples/boids/boids.atlas | 4 - examples/boids/boids.collection | 69 - examples/boids/boids.gui | 759 ----------- examples/boids/boids.gui_script | 101 -- examples/boids/boids.script | 63 - examples/boids/system_boid/command_boid.lua | 49 - examples/boids/system_boid/ext.manifest | 1 - .../boids/system_boid/src/c_system_boid.cpp | 103 -- examples/boids/system_boid/system_boid.lua | 201 --- .../boids/system_game_object/ext.manifest | 1 - .../system_game_object/src/c_go_setter.cpp | 139 --- examples/manifest.appmanifest | 106 -- examples/nested_entities/entity/player.go | 62 - examples/nested_entities/entity/player.lua | 19 - .../nested_entities.collection | 27 - .../nested_entities/nested_entities.script | 28 - .../system_game_timer/system_game_timer.lua | 47 - resources/components.json | 16 - resources/entities.json | 29 - system/camera/camera_entity.lua | 5 - system/camera/command_camera.lua | 47 - system/camera/component_camera.script | 5 - system/camera/entity_camera.go | 48 - system/camera/system_camera.lua | 347 ------ .../camera_debug_control.lua | 91 -- system/collision/annotations_collision.lua | 32 - system/collision/component_collision.script | 15 - system/collision/system_collision.lua | 211 ---- system/color/system_color.lua | 106 -- system/debug/command_debug.lua | 52 - system/debug/debug.go | 9 - system/debug/entity_debug.lua | 13 - system/debug/system_debug.lua | 114 -- system/debug_draw/command_debug_draw.lua | 29 - system/debug_draw/component_debug_draw.script | 8 - system/debug_draw/debug_draw.atlas | 4 - system/debug_draw/system_debug_draw.lua | 163 --- .../system_debug_draw_transform.lua | 57 - system/follow_cursor/system_follow_cursor.lua | 43 - system/fsm/command_fsm.lua | 33 - system/fsm/system_fsm.lua | 78 -- system/fsm/test_fsm.lua | 74 -- system/game_object/command_game_object.lua | 38 - system/game_object/system_game_object.lua | 264 ---- system/health/command_health.lua | 25 - system/health/component_health.script | 9 - system/health/system_health.lua | 77 -- system/health/test_health.lua | 46 - system/input/input_system.lua | 36 - .../system_movement_controller.lua | 115 -- system/nakama/command_nakama.lua | 16 - system/nakama/nakama_core.lua | 309 ----- system/nakama/system_nakama.lua | 54 - .../system_on_key_released.lua | 77 -- .../component_on_spawn_command.script | 9 - .../entity_on_spawn_command.go | 38 - .../entity_on_spawn_command.lua | 7 - .../system_on_spawn_command.lua | 44 - system/panthera/command_panthera.lua | 74 -- system/panthera/component_panthera.script | 23 - system/panthera/system_panthera.lua | 97 -- system/physics/command_physics.lua | 27 - system/physics/component_physics.script | 10 - system/physics/system_physics.lua | 99 -- .../system_platformer_controller.lua | 104 -- .../command_platformer_physics.lua | 49 - .../component_platformer_physics.script | 52 - .../system_platformer_physics.lua | 304 ----- system/quadtree/command_quadtree.lua | 24 - system/quadtree/quadtree.lua | 367 ------ system/quadtree/system_quadtree.lua | 126 -- system/quadtree/test_quadtree.lua | 50 - .../component_remove_with_delay.script | 7 - .../system_remove_with_delay.lua | 33 - system/transform/command_transform.lua | 110 -- system/transform/system_transform.lua | 176 --- system/transform/test_transform.lua | 94 -- .../system_transform_animate_on_event.lua | 155 --- .../system_transform_border.lua | 91 -- system/velocity/command_velocity.lua | 65 - system/velocity/component_velocity.script | 10 - system/velocity/system_velocity.lua | 118 -- system/window_event/system_window_event.lua | 37 - test/test.script | 5 - wiki/FLOW.md | 102 -- wiki/FLOW_TILED.md | 23 - wiki/decore_components.md | 0 wiki/decore_entities.md | 0 wiki/decore_systems.md | 0 wiki/decore_worlds.md | 0 wiki/tech_solutions.md | 8 - 136 files changed, 38 insertions(+), 9799 deletions(-) delete mode 100644 decore/editor_scripts/decore.editor_script delete mode 100644 examples/basic_camera_setup/basic_camera_setup.collection delete mode 100644 examples/basic_camera_setup/basic_camera_setup.script delete mode 100644 examples/basic_camera_setup/entity/player.go delete mode 100644 examples/basic_camera_setup/entity/player.lua delete mode 100644 examples/basic_collision/basic_collision.collection delete mode 100644 examples/basic_collision/basic_collision.script delete mode 100644 examples/basic_collision/entity/coin.go delete mode 100644 examples/basic_collision/entity/entity_coin.lua delete mode 100644 examples/basic_collision/entity/player.go delete mode 100644 examples/basic_collision/entity/player.lua delete mode 100644 examples/basic_collision/entity/score_counter.go delete mode 100644 examples/basic_collision/entity/score_counter.gui delete mode 100644 examples/basic_collision/entity/score_counter.gui_script delete mode 100644 examples/basic_collision/entity/score_counter.lua delete mode 100644 examples/basic_movement/basic_movement.collection delete mode 100644 examples/basic_movement/basic_movement.script delete mode 100644 examples/basic_movement/entity/player.go delete mode 100644 examples/basic_movement/entity/player.lua delete mode 100644 examples/basic_movement_with_walls/basic_movement_with_walls.collection delete mode 100644 examples/basic_movement_with_walls/basic_movement_with_walls.script delete mode 100644 examples/basic_movement_with_walls/entity/player.go delete mode 100644 examples/basic_movement_with_walls/entity/player.lua delete mode 100644 examples/basic_movement_with_walls/entity/wall.go delete mode 100644 examples/basic_platformer_controller/basic_platformer_controller.collection delete mode 100644 examples/basic_platformer_controller/basic_platformer_controller.script delete mode 100644 examples/basic_platformer_controller/entity/coin.go delete mode 100644 examples/basic_platformer_controller/entity/coin.lua delete mode 100644 examples/basic_platformer_controller/entity/player.go delete mode 100644 examples/basic_platformer_controller/entity/player.lua delete mode 100644 examples/basic_platformer_controller/entity/score_counter.go delete mode 100644 examples/basic_platformer_controller/entity/score_counter.gui delete mode 100644 examples/basic_platformer_controller/entity/score_counter.gui_script delete mode 100644 examples/basic_platformer_controller/entity/score_counter.lua delete mode 100644 examples/basic_platformer_controller/system/system_on_remove_event.lua delete mode 100644 examples/basic_platformer_controller/system/system_score_counter.lua delete mode 100644 examples/boids/boid/boid.go delete mode 100644 examples/boids/boid/boid.png delete mode 100644 examples/boids/boid/boid_panthera.lua delete mode 100644 examples/boids/boid/boid_sensor.gui delete mode 100644 examples/boids/boid/boid_sensor.gui_script delete mode 100644 examples/boids/boid/boid_sensor.lua delete mode 100644 examples/boids/boid/boid_sensor_panthera.lua delete mode 100644 examples/boids/boid/entity_boid.lua delete mode 100644 examples/boids/boids.atlas delete mode 100644 examples/boids/boids.collection delete mode 100644 examples/boids/boids.gui delete mode 100644 examples/boids/boids.gui_script delete mode 100644 examples/boids/boids.script delete mode 100644 examples/boids/system_boid/command_boid.lua delete mode 100644 examples/boids/system_boid/ext.manifest delete mode 100644 examples/boids/system_boid/src/c_system_boid.cpp delete mode 100644 examples/boids/system_boid/system_boid.lua delete mode 100644 examples/boids/system_game_object/ext.manifest delete mode 100644 examples/boids/system_game_object/src/c_go_setter.cpp delete mode 100644 examples/manifest.appmanifest delete mode 100644 examples/nested_entities/entity/player.go delete mode 100644 examples/nested_entities/entity/player.lua delete mode 100644 examples/nested_entities/nested_entities.collection delete mode 100644 examples/nested_entities/nested_entities.script delete mode 100644 examples/nested_entities/system_game_timer/system_game_timer.lua delete mode 100644 resources/components.json delete mode 100644 resources/entities.json delete mode 100644 system/camera/camera_entity.lua delete mode 100644 system/camera/command_camera.lua delete mode 100644 system/camera/component_camera.script delete mode 100644 system/camera/entity_camera.go delete mode 100644 system/camera/system_camera.lua delete mode 100644 system/camera_debug_control/camera_debug_control.lua delete mode 100644 system/collision/annotations_collision.lua delete mode 100644 system/collision/component_collision.script delete mode 100644 system/collision/system_collision.lua delete mode 100644 system/color/system_color.lua delete mode 100644 system/debug/command_debug.lua delete mode 100644 system/debug/debug.go delete mode 100644 system/debug/entity_debug.lua delete mode 100644 system/debug/system_debug.lua delete mode 100644 system/debug_draw/command_debug_draw.lua delete mode 100644 system/debug_draw/component_debug_draw.script delete mode 100644 system/debug_draw/debug_draw.atlas delete mode 100644 system/debug_draw/system_debug_draw.lua delete mode 100644 system/debug_draw_transform/system_debug_draw_transform.lua delete mode 100644 system/follow_cursor/system_follow_cursor.lua delete mode 100644 system/fsm/command_fsm.lua delete mode 100644 system/fsm/system_fsm.lua delete mode 100644 system/fsm/test_fsm.lua delete mode 100644 system/game_object/command_game_object.lua delete mode 100644 system/game_object/system_game_object.lua delete mode 100644 system/health/command_health.lua delete mode 100644 system/health/component_health.script delete mode 100644 system/health/system_health.lua delete mode 100644 system/health/test_health.lua delete mode 100644 system/input/input_system.lua delete mode 100644 system/movement_controller/system_movement_controller.lua delete mode 100644 system/nakama/command_nakama.lua delete mode 100644 system/nakama/nakama_core.lua delete mode 100644 system/nakama/system_nakama.lua delete mode 100644 system/on_key_released/system_on_key_released.lua delete mode 100644 system/on_spawn_command/component_on_spawn_command.script delete mode 100644 system/on_spawn_command/entity_on_spawn_command.go delete mode 100644 system/on_spawn_command/entity_on_spawn_command.lua delete mode 100644 system/on_spawn_command/system_on_spawn_command.lua delete mode 100644 system/panthera/command_panthera.lua delete mode 100644 system/panthera/component_panthera.script delete mode 100644 system/panthera/system_panthera.lua delete mode 100644 system/physics/command_physics.lua delete mode 100644 system/physics/component_physics.script delete mode 100644 system/physics/system_physics.lua delete mode 100644 system/platformer_controller/system_platformer_controller.lua delete mode 100644 system/platformer_physics/command_platformer_physics.lua delete mode 100644 system/platformer_physics/component_platformer_physics.script delete mode 100644 system/platformer_physics/system_platformer_physics.lua delete mode 100644 system/quadtree/command_quadtree.lua delete mode 100644 system/quadtree/quadtree.lua delete mode 100644 system/quadtree/system_quadtree.lua delete mode 100644 system/quadtree/test_quadtree.lua delete mode 100644 system/remove_with_delay/component_remove_with_delay.script delete mode 100644 system/remove_with_delay/system_remove_with_delay.lua delete mode 100644 system/transform/command_transform.lua delete mode 100644 system/transform/system_transform.lua delete mode 100644 system/transform/test_transform.lua delete mode 100644 system/transform_animate_on_event/system_transform_animate_on_event.lua delete mode 100644 system/transform_border/system_transform_border.lua delete mode 100644 system/velocity/command_velocity.lua delete mode 100644 system/velocity/component_velocity.script delete mode 100644 system/velocity/system_velocity.lua delete mode 100644 system/window_event/system_window_event.lua delete mode 100644 wiki/FLOW.md delete mode 100644 wiki/FLOW_TILED.md delete mode 100644 wiki/decore_components.md delete mode 100644 wiki/decore_entities.md delete mode 100644 wiki/decore_systems.md delete mode 100644 wiki/decore_worlds.md delete mode 100644 wiki/tech_solutions.md diff --git a/README.md b/README.md index b107a1c..e32c88e 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,6 @@ [![Github-sponsors](https://img.shields.io/badge/sponsor-30363D?style=for-the-badge&logo=GitHub-Sponsors&logoColor=#EA4AAA)](https://github.com/sponsors/insality) [![Ko-Fi](https://img.shields.io/badge/Ko--fi-F16061?style=for-the-badge&logo=ko-fi&logoColor=white)](https://ko-fi.com/insality) [![BuyMeACoffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black)](https://www.buymeacoffee.com/insality) -# Disclaimer - -The library in development stage. May be not fully tested and README may be not full. If you have any questions, please, create an issue. - - # Decore **Decore** - a Defold library for managing ECS game entities and components in a data-driven way. The ECS is based on [tiny ECS](https://github.com/bakpakin/tiny-ecs) library. @@ -64,6 +59,27 @@ function final(self) end ``` +Look at system examples to get more information about how to use systems. +[System examples](https://github.com/Insality/asset-store/tree/main/system/Insality) + +## Entities prefab Example + +```lua +-- /entity/player/player_entity.lua +---@diagnostic disable: missing-fields +---@type entity +return { + transform = { size_x = 128, size_y = 128 }, + game_object = { factory_url = "/entities#player" }, + field = { solid = true }, + player = true, + panthera = { animation_path = require("entity.player.player_panthera"), default_animation = "idle", is_loop = true, play_on_start = "idle" }, + player_visual = true, + field_movable = true, + user_controlled = true, +} + +``` ## Quick API Reference @@ -115,6 +131,23 @@ This project is licensed under the MIT License - see the LICENSE file for detail If you have any issues, questions or suggestions please [create an issue](https://github.com/Insality/decore/issues). +## Changelog + +
+ +### **V1** + - Initial release + +### **V2** + - Reworked API and internal structure + - Updated documentation + +### **V3** + - Updated event bus system for better performance + - Update documentation + +
+ ## ❤️ Support project ❤️ Your donation helps me stay engaged in creating valuable projects for **Defold**. If you appreciate what I'm doing, please consider supporting me! diff --git a/decore/editor_scripts/decore.editor_script b/decore/editor_scripts/decore.editor_script deleted file mode 100644 index 14a5524..0000000 --- a/decore/editor_scripts/decore.editor_script +++ /dev/null @@ -1,64 +0,0 @@ -local M = {} - -local function ends_with(str, ending) - return ending == "" or str:sub(-#ending) == ending -end - - -function M.get_commands() - return { - { - label = "[Decore] Set as Bootstrap Collection", - locations = { "Assets" }, - query = { selection = { type = "resource", cardinality = "one" } }, - active = function(opts) - return ends_with(editor.get(opts.selection, "path"), ".collection") - end, - run = function(opts) - local file = opts.selection - print("Run script for", editor.get(file, "path")) - local collection_path = editor.get(file, "path") - - local project_path = editor.external_file_attributes(".").path - local game_project_path = project_path .. editor.get("/game.project", "path") - - local lines = {} - - do -- Open game.project file for reading - local game_project_file = io.open(game_project_path, "r") - if not game_project_file then - print("Error: Could not open game.project file") - return - end - - -- Read all lines and modify the main_collection line - for line in game_project_file:lines() do - if line:match("main_collection%s*=.*") then - line = "main_collection = " .. collection_path .. "c" - end - table.insert(lines, line) - end - game_project_file:close() - end - - do -- Write the modified content back to the file - local game_project_file = io.open(game_project_path, "w") - if not game_project_file then - print("Error: Could not open game.project file for writing") - return - end - - for _, line in ipairs(lines) do - game_project_file:write(line .. "\n") - end - game_project_file:close() - - print("Successfully updated main_collection to " .. collection_path .. "c") - end - end - } - } -end - - -return M diff --git a/examples/basic_camera_setup/basic_camera_setup.collection b/examples/basic_camera_setup/basic_camera_setup.collection deleted file mode 100644 index ae74e46..0000000 --- a/examples/basic_camera_setup/basic_camera_setup.collection +++ /dev/null @@ -1,47 +0,0 @@ -name: "basic_camera_setup" -instances { - id: "player" - prototype: "/examples/basic_camera_setup/entity/player.go" - position { - x: 480.0 - y: 320.0 - } -} -instances { - id: "debug" - prototype: "/core/system/debug/debug.go" -} -instances { - id: "entity_camera" - prototype: "/core/system/camera/entity_camera.go" - position { - x: 480.0 - y: 320.0 - z: 10.0 - } - component_properties { - id: "entity" - properties { - id: "size_x" - value: "960.0" - type: PROPERTY_TYPE_NUMBER - } - properties { - id: "size_y" - value: "640.0" - type: PROPERTY_TYPE_NUMBER - } - } -} -scale_along_z: 0 -embedded_instances { - id: "root" - children: "debug" - children: "entity_camera" - children: "player" - data: "components {\n" - " id: \"basic_camera_setup\"\n" - " component: \"/examples/basic_camera_setup/basic_camera_setup.script\"\n" - "}\n" - "" -} diff --git a/examples/basic_camera_setup/basic_camera_setup.script b/examples/basic_camera_setup/basic_camera_setup.script deleted file mode 100644 index 49ab038..0000000 --- a/examples/basic_camera_setup/basic_camera_setup.script +++ /dev/null @@ -1,31 +0,0 @@ -local decore = require("decore.decore") - -function init(self) - self.world = decore.world( - require("system.transform.system_transform").create(), - require("system.transform_border.system_transform_border").create(), - require("system.game_object.system_game_object").create(), - require("system.input.system_input").create_system(), - require("system.movement_controller.system_movement_controller").create(), - - -- Debug - require("system.on_key_released.system_on_key_released").create_system(), - require("system.debug.system_debug").create_system(), - require("system.camera.system_camera").create(), - require("system.window_event.system_window_event").create_system() - ) - - decore.register_entity("player", require("examples.basic_camera_setup.entity.player")) - decore.register_entity("debug", require("system.debug.entity_debug")) - decore.register_entity("camera", require("system.camera.entity_camera")) -end - - -function update(self, dt) - self.world:update(dt) -end - - -function on_input(self, action_id, action) - return decore.on_input(self.world, action_id, action) -end diff --git a/examples/basic_camera_setup/entity/player.go b/examples/basic_camera_setup/entity/player.go deleted file mode 100644 index 1dd0aae..0000000 --- a/examples/basic_camera_setup/entity/player.go +++ /dev/null @@ -1,53 +0,0 @@ -components { - id: "entity" - component: "/decore/entity.script" - properties { - id: "prefab_id" - value: "player" - type: PROPERTY_TYPE_HASH - } - properties { - id: "size_x" - value: "64.0" - type: PROPERTY_TYPE_NUMBER - } - properties { - id: "size_y" - value: "64.0" - type: PROPERTY_TYPE_NUMBER - } -} -embedded_components { - id: "sprite" - type: "sprite" - data: "default_animation: \"ui_circle_64\"\n" - "material: \"/builtins/materials/sprite.material\"\n" - "textures {\n" - " sampler: \"texture_sampler\"\n" - " texture: \"/assets/atlases/game.atlas\"\n" - "}\n" - "" -} -embedded_components { - id: "collisionobject" - type: "collisionobject" - data: "type: COLLISION_OBJECT_TYPE_KINEMATIC\n" - "mass: 0.0\n" - "friction: 0.1\n" - "restitution: 0.5\n" - "group: \"player\"\n" - "mask: \"level\"\n" - "embedded_collision_shape {\n" - " shapes {\n" - " shape_type: TYPE_SPHERE\n" - " position {\n" - " }\n" - " rotation {\n" - " }\n" - " index: 0\n" - " count: 1\n" - " }\n" - " data: 32.0\n" - "}\n" - "" -} diff --git a/examples/basic_camera_setup/entity/player.lua b/examples/basic_camera_setup/entity/player.lua deleted file mode 100644 index 81b433a..0000000 --- a/examples/basic_camera_setup/entity/player.lua +++ /dev/null @@ -1,10 +0,0 @@ -return { - transform = {}, - game_object = {}, - transform_border = { - border = vmath.vector4(0, 640, 960, 0), - }, - movement_controller = { - speed = 20 - } -} \ No newline at end of file diff --git a/examples/basic_collision/basic_collision.collection b/examples/basic_collision/basic_collision.collection deleted file mode 100644 index 6e2c5e2..0000000 --- a/examples/basic_collision/basic_collision.collection +++ /dev/null @@ -1,1110 +0,0 @@ -name: "basic_collision" -instances { - id: "player" - prototype: "/examples/basic_collision/entity/player.go" - position { - x: 480.0 - y: 320.0 - } -} -instances { - id: "score_counter" - prototype: "/examples/basic_collision/entity/score_counter.go" -} -instances { - id: "coin21" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 438.0 - y: -70.0 - } -} -instances { - id: "coin1" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -194.0 - y: -170.0 - } -} -instances { - id: "coin26" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -122.0 - y: 30.0 - } -} -instances { - id: "coin17" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 290.0 - y: 30.0 - } -} -instances { - id: "coin2" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -194.0 - y: -70.0 - } -} -instances { - id: "coin14" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 290.0 - y: -70.0 - } -} -instances { - id: "coin23" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -122.0 - y: -170.0 - } -} -instances { - id: "coin16" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -270.0 - y: -270.0 - } -} -instances { - id: "coin10" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -270.0 - y: -170.0 - } -} -instances { - id: "coin5" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 366.0 - y: -270.0 - } -} -instances { - id: "coin4" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -194.0 - y: 130.0 - } -} -instances { - id: "coin27" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 438.0 - y: 130.0 - } -} -instances { - id: "coin7" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 366.0 - y: -70.0 - } -} -instances { - id: "coin9" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 366.0 - y: 130.0 - } -} -instances { - id: "coin12" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 290.0 - y: -270.0 - } -} -instances { - id: "coin" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -194.0 - y: -270.0 - } -} -instances { - id: "coin8" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 366.0 - y: 30.0 - } -} -instances { - id: "coin28" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 438.0 - y: -170.0 - } -} -instances { - id: "coin11" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -270.0 - y: -70.0 - } -} -instances { - id: "coin3" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -194.0 - y: 30.0 - } -} -instances { - id: "coin18" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -270.0 - y: 30.0 - } -} -instances { - id: "coin20" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 438.0 - y: 30.0 - } -} -instances { - id: "coin25" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -122.0 - y: -70.0 - } -} -instances { - id: "coin15" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 290.0 - y: 130.0 - } -} -instances { - id: "coin29" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -122.0 - y: 130.0 - } -} -instances { - id: "coin22" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -122.0 - y: -270.0 - } -} -instances { - id: "coin19" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 290.0 - y: -170.0 - } -} -instances { - id: "coin6" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 366.0 - y: -170.0 - } -} -instances { - id: "coin13" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -270.0 - y: 130.0 - } -} -instances { - id: "coin24" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 438.0 - y: -270.0 - } -} -instances { - id: "coin30" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 332.0 - y: -21.0 - } -} -instances { - id: "coin31" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 332.0 - y: -121.0 - } -} -instances { - id: "coin32" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 256.0 - y: -221.0 - } -} -instances { - id: "coin33" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -304.0 - y: 179.0 - } -} -instances { - id: "coin34" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 332.0 - y: 179.0 - } -} -instances { - id: "coin35" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -228.0 - y: 79.0 - } -} -instances { - id: "coin36" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -304.0 - y: -21.0 - } -} -instances { - id: "coin37" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 256.0 - y: -121.0 - } -} -instances { - id: "coin38" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -228.0 - y: -121.0 - } -} -instances { - id: "coin39" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 404.0 - y: -221.0 - } -} -instances { - id: "coin40" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 404.0 - y: 179.0 - } -} -instances { - id: "coin41" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -156.0 - y: -121.0 - } -} -instances { - id: "coin42" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -156.0 - y: -21.0 - } -} -instances { - id: "coin43" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -156.0 - y: 79.0 - } -} -instances { - id: "coin44" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 256.0 - y: 79.0 - } -} -instances { - id: "coin45" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 256.0 - y: 179.0 - } -} -instances { - id: "coin46" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 404.0 - y: -121.0 - } -} -instances { - id: "coin47" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -304.0 - y: 79.0 - } -} -instances { - id: "coin48" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 404.0 - y: -21.0 - } -} -instances { - id: "coin49" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -304.0 - y: -221.0 - } -} -instances { - id: "coin50" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 256.0 - y: -21.0 - } -} -instances { - id: "coin51" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -228.0 - y: -21.0 - } -} -instances { - id: "coin52" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 404.0 - y: 79.0 - } -} -instances { - id: "coin53" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -228.0 - y: -221.0 - } -} -instances { - id: "coin54" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -228.0 - y: 179.0 - } -} -instances { - id: "coin55" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -156.0 - y: -221.0 - } -} -instances { - id: "coin56" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -156.0 - y: 179.0 - } -} -instances { - id: "coin57" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 332.0 - y: -221.0 - } -} -instances { - id: "coin58" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -304.0 - y: -121.0 - } -} -instances { - id: "coin59" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 332.0 - y: 79.0 - } -} -instances { - id: "coin60" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 218.0 - y: -221.0 - } -} -instances { - id: "coin61" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -342.0 - y: 179.0 - } -} -instances { - id: "coin62" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 328.0 - y: -70.0 - } -} -instances { - id: "coin63" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -266.0 - y: 179.0 - } -} -instances { - id: "coin64" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 328.0 - y: -170.0 - } -} -instances { - id: "coin65" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -194.0 - y: -21.0 - } -} -instances { - id: "coin66" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -194.0 - y: -221.0 - } -} -instances { - id: "coin67" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 294.0 - y: -21.0 - } -} -instances { - id: "coin68" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -266.0 - y: -121.0 - } -} -instances { - id: "coin69" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 252.0 - y: -270.0 - } -} -instances { - id: "coin70" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -308.0 - y: 130.0 - } -} -instances { - id: "coin71" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 218.0 - y: -21.0 - } -} -instances { - id: "coin72" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 328.0 - y: 130.0 - } -} -instances { - id: "coin73" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -342.0 - y: -121.0 - } -} -instances { - id: "coin74" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -232.0 - y: 30.0 - } -} -instances { - id: "coin75" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 294.0 - y: -121.0 - } -} -instances { - id: "coin76" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -308.0 - y: -70.0 - } -} -instances { - id: "coin77" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 252.0 - y: -170.0 - } -} -instances { - id: "coin78" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -232.0 - y: -170.0 - } -} -instances { - id: "coin79" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 400.0 - y: -270.0 - } -} -instances { - id: "coin80" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 400.0 - y: 130.0 - } -} -instances { - id: "coin81" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 366.0 - y: -21.0 - } -} -instances { - id: "coin82" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -194.0 - y: 79.0 - } -} -instances { - id: "coin83" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -160.0 - y: -170.0 - } -} -instances { - id: "coin84" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -266.0 - y: -221.0 - } -} -instances { - id: "coin85" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -160.0 - y: -70.0 - } -} -instances { - id: "coin86" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 294.0 - y: 179.0 - } -} -instances { - id: "coin87" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 218.0 - y: -121.0 - } -} -instances { - id: "coin88" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 294.0 - y: 79.0 - } -} -instances { - id: "coin89" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 366.0 - y: 179.0 - } -} -instances { - id: "coin90" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -342.0 - y: -21.0 - } -} -instances { - id: "coin91" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -160.0 - y: 30.0 - } -} -instances { - id: "coin92" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 252.0 - y: 30.0 - } -} -instances { - id: "coin93" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 252.0 - y: 130.0 - } -} -instances { - id: "coin94" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 218.0 - y: 79.0 - } -} -instances { - id: "coin95" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 400.0 - y: -170.0 - } -} -instances { - id: "coin96" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -308.0 - y: 30.0 - } -} -instances { - id: "coin97" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 366.0 - y: -121.0 - } -} -instances { - id: "coin98" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 218.0 - y: 179.0 - } -} -instances { - id: "coin99" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 400.0 - y: -70.0 - } -} -instances { - id: "coin100" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -308.0 - y: -270.0 - } -} -instances { - id: "coin101" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 252.0 - y: -70.0 - } -} -instances { - id: "coin102" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -194.0 - y: 179.0 - } -} -instances { - id: "coin103" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -232.0 - y: -70.0 - } -} -instances { - id: "coin104" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 400.0 - y: 30.0 - } -} -instances { - id: "coin105" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -232.0 - y: -270.0 - } -} -instances { - id: "coin106" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -232.0 - y: 130.0 - } -} -instances { - id: "coin107" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -194.0 - y: -121.0 - } -} -instances { - id: "coin108" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -266.0 - y: -21.0 - } -} -instances { - id: "coin109" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 366.0 - y: 79.0 - } -} -instances { - id: "coin110" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -160.0 - y: -270.0 - } -} -instances { - id: "coin111" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -160.0 - y: 130.0 - } -} -instances { - id: "coin112" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 366.0 - y: -221.0 - } -} -instances { - id: "coin113" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 328.0 - y: -270.0 - } -} -instances { - id: "coin114" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -342.0 - y: -221.0 - } -} -instances { - id: "coin115" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -342.0 - y: 79.0 - } -} -instances { - id: "coin116" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -308.0 - y: -170.0 - } -} -instances { - id: "coin117" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 328.0 - y: 30.0 - } -} -instances { - id: "coin118" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: 294.0 - y: -221.0 - } -} -instances { - id: "coin119" - prototype: "/examples/basic_collision/entity/coin.go" - position { - x: -266.0 - y: 79.0 - } -} -scale_along_z: 0 -embedded_instances { - id: "system" - data: "components {\n" - " id: \"basic_collision\"\n" - " component: \"/examples/basic_collision/basic_collision.script\"\n" - "}\n" - "" -} -embedded_instances { - id: "coins" - children: "coin" - children: "coin1" - children: "coin10" - children: "coin100" - children: "coin101" - children: "coin102" - children: "coin103" - children: "coin104" - children: "coin105" - children: "coin106" - children: "coin107" - children: "coin108" - children: "coin109" - children: "coin11" - children: "coin110" - children: "coin111" - children: "coin112" - children: "coin113" - children: "coin114" - children: "coin115" - children: "coin116" - children: "coin117" - children: "coin118" - children: "coin119" - children: "coin12" - children: "coin13" - children: "coin14" - children: "coin15" - children: "coin16" - children: "coin17" - children: "coin18" - children: "coin19" - children: "coin2" - children: "coin20" - children: "coin21" - children: "coin22" - children: "coin23" - children: "coin24" - children: "coin25" - children: "coin26" - children: "coin27" - children: "coin28" - children: "coin29" - children: "coin3" - children: "coin30" - children: "coin31" - children: "coin32" - children: "coin33" - children: "coin34" - children: "coin35" - children: "coin36" - children: "coin37" - children: "coin38" - children: "coin39" - children: "coin4" - children: "coin40" - children: "coin41" - children: "coin42" - children: "coin43" - children: "coin44" - children: "coin45" - children: "coin46" - children: "coin47" - children: "coin48" - children: "coin49" - children: "coin5" - children: "coin50" - children: "coin51" - children: "coin52" - children: "coin53" - children: "coin54" - children: "coin55" - children: "coin56" - children: "coin57" - children: "coin58" - children: "coin59" - children: "coin6" - children: "coin60" - children: "coin61" - children: "coin62" - children: "coin63" - children: "coin64" - children: "coin65" - children: "coin66" - children: "coin67" - children: "coin68" - children: "coin69" - children: "coin7" - children: "coin70" - children: "coin71" - children: "coin72" - children: "coin73" - children: "coin74" - children: "coin75" - children: "coin76" - children: "coin77" - children: "coin78" - children: "coin79" - children: "coin8" - children: "coin80" - children: "coin81" - children: "coin82" - children: "coin83" - children: "coin84" - children: "coin85" - children: "coin86" - children: "coin87" - children: "coin88" - children: "coin89" - children: "coin9" - children: "coin90" - children: "coin91" - children: "coin92" - children: "coin93" - children: "coin94" - children: "coin95" - children: "coin96" - children: "coin97" - children: "coin98" - children: "coin99" - data: "" - position { - x: 480.0 - y: 320.0 - } -} diff --git a/examples/basic_collision/basic_collision.script b/examples/basic_collision/basic_collision.script deleted file mode 100644 index 55673ae..0000000 --- a/examples/basic_collision/basic_collision.script +++ /dev/null @@ -1,28 +0,0 @@ -local decore = require("decore.decore") - -function init(self) - self.world = decore.world( - require("system.transform.system_transform").create(), - require("system.transform_border.system_transform_border").create(), - require("system.game_object.system_game_object").create(), - require("system.input.system_input").create_system(), - require("system.movement_controller.system_movement_controller").create(), - require("system.collision.system_collision").create_system(), - - require("examples.basic_platformer_controller.system.system_score_counter").create_system() - ) - - decore.register_entity("player", require("examples.basic_collision.entity.player")) - decore.register_entity("coin", require("examples.basic_collision.entity.entity_coin")) - decore.register_entity("score_counter", require("examples.basic_collision.entity.score_counter")) -end - - -function update(self, dt) - self.world:update(dt) -end - - -function on_input(self, action_id, action) - return decore.on_input(self.world, action_id, action) -end diff --git a/examples/basic_collision/entity/coin.go b/examples/basic_collision/entity/coin.go deleted file mode 100644 index 1629ba3..0000000 --- a/examples/basic_collision/entity/coin.go +++ /dev/null @@ -1,62 +0,0 @@ -components { - id: "entity" - component: "/decore/entity.script" - properties { - id: "prefab_id" - value: "coin" - type: PROPERTY_TYPE_HASH - } - properties { - id: "size_x" - value: "32.0" - type: PROPERTY_TYPE_NUMBER - } - properties { - id: "size_y" - value: "32.0" - type: PROPERTY_TYPE_NUMBER - } -} -embedded_components { - id: "sprite" - type: "sprite" - data: "default_animation: \"ui_circle_32\"\n" - "material: \"/panthera/materials/sprite.material\"\n" - "attributes {\n" - " name: \"color\"\n" - " double_values {\n" - " v: 1.0\n" - " v: 1.0\n" - " v: 0.502\n" - " v: 1.0\n" - " }\n" - "}\n" - "textures {\n" - " sampler: \"texture_sampler\"\n" - " texture: \"/assets/atlases/game.atlas\"\n" - "}\n" - "" -} -embedded_components { - id: "collisionobject" - type: "collisionobject" - data: "type: COLLISION_OBJECT_TYPE_TRIGGER\n" - "mass: 0.0\n" - "friction: 0.1\n" - "restitution: 0.5\n" - "group: \"level\"\n" - "mask: \"player\"\n" - "embedded_collision_shape {\n" - " shapes {\n" - " shape_type: TYPE_SPHERE\n" - " position {\n" - " }\n" - " rotation {\n" - " }\n" - " index: 0\n" - " count: 1\n" - " }\n" - " data: 16.0\n" - "}\n" - "" -} diff --git a/examples/basic_collision/entity/entity_coin.lua b/examples/basic_collision/entity/entity_coin.lua deleted file mode 100644 index 442cc57..0000000 --- a/examples/basic_collision/entity/entity_coin.lua +++ /dev/null @@ -1,11 +0,0 @@ -return { - transform = {}, - game_object = { - factory_url = "/spawner#coin", - is_factory = true - }, - collision = { - is_remove = true, - send_event = "score_plus", - }, -} \ No newline at end of file diff --git a/examples/basic_collision/entity/player.go b/examples/basic_collision/entity/player.go deleted file mode 100644 index 1dd0aae..0000000 --- a/examples/basic_collision/entity/player.go +++ /dev/null @@ -1,53 +0,0 @@ -components { - id: "entity" - component: "/decore/entity.script" - properties { - id: "prefab_id" - value: "player" - type: PROPERTY_TYPE_HASH - } - properties { - id: "size_x" - value: "64.0" - type: PROPERTY_TYPE_NUMBER - } - properties { - id: "size_y" - value: "64.0" - type: PROPERTY_TYPE_NUMBER - } -} -embedded_components { - id: "sprite" - type: "sprite" - data: "default_animation: \"ui_circle_64\"\n" - "material: \"/builtins/materials/sprite.material\"\n" - "textures {\n" - " sampler: \"texture_sampler\"\n" - " texture: \"/assets/atlases/game.atlas\"\n" - "}\n" - "" -} -embedded_components { - id: "collisionobject" - type: "collisionobject" - data: "type: COLLISION_OBJECT_TYPE_KINEMATIC\n" - "mass: 0.0\n" - "friction: 0.1\n" - "restitution: 0.5\n" - "group: \"player\"\n" - "mask: \"level\"\n" - "embedded_collision_shape {\n" - " shapes {\n" - " shape_type: TYPE_SPHERE\n" - " position {\n" - " }\n" - " rotation {\n" - " }\n" - " index: 0\n" - " count: 1\n" - " }\n" - " data: 32.0\n" - "}\n" - "" -} diff --git a/examples/basic_collision/entity/player.lua b/examples/basic_collision/entity/player.lua deleted file mode 100644 index 5e553bb..0000000 --- a/examples/basic_collision/entity/player.lua +++ /dev/null @@ -1,11 +0,0 @@ -return { - transform = {}, - game_object = {}, - transform_border = { - border = vmath.vector4(0, 640, 960, 0), - }, - movement_controller = { - speed = 20 - }, - collision = {} -} \ No newline at end of file diff --git a/examples/basic_collision/entity/score_counter.go b/examples/basic_collision/entity/score_counter.go deleted file mode 100644 index c5faf8e..0000000 --- a/examples/basic_collision/entity/score_counter.go +++ /dev/null @@ -1,13 +0,0 @@ -components { - id: "score_counter" - component: "/examples/basic_collision/entity/score_counter.gui" -} -components { - id: "entity" - component: "/decore/entity.script" - properties { - id: "prefab_id" - value: "score_counter" - type: PROPERTY_TYPE_HASH - } -} diff --git a/examples/basic_collision/entity/score_counter.gui b/examples/basic_collision/entity/score_counter.gui deleted file mode 100644 index d501c69..0000000 --- a/examples/basic_collision/entity/score_counter.gui +++ /dev/null @@ -1,24 +0,0 @@ -script: "/examples/basic_collision/entity/score_counter.gui_script" -fonts { - name: "text" - font: "/assets/fonts/text.font" -} -nodes { - position { - x: 480.0 - y: 540.0 - } - size { - x: 200.0 - y: 100.0 - } - type: TYPE_TEXT - text: "Score: 0" - font: "text" - id: "text" - inherit_alpha: true -} -material: "/builtins/materials/gui.material" -adjust_reference: ADJUST_REFERENCE_PARENT -max_nodes: 1 -max_dynamic_textures: 1 diff --git a/examples/basic_collision/entity/score_counter.gui_script b/examples/basic_collision/entity/score_counter.gui_script deleted file mode 100644 index 1ed8e2a..0000000 --- a/examples/basic_collision/entity/score_counter.gui_script +++ /dev/null @@ -1,12 +0,0 @@ -function init(self) - self.score = 0 - self.text = gui.get_node("text") -end - - -function on_message(self, message_id) - if message_id == hash("score_plus") then - self.score = self.score + 1 - gui.set_text(self.text, "Score: " .. self.score) - end -end diff --git a/examples/basic_collision/entity/score_counter.lua b/examples/basic_collision/entity/score_counter.lua deleted file mode 100644 index 8e7c3cc..0000000 --- a/examples/basic_collision/entity/score_counter.lua +++ /dev/null @@ -1,3 +0,0 @@ -return { - score_counter = true, -} \ No newline at end of file diff --git a/examples/basic_movement/basic_movement.collection b/examples/basic_movement/basic_movement.collection deleted file mode 100644 index 8819562..0000000 --- a/examples/basic_movement/basic_movement.collection +++ /dev/null @@ -1,19 +0,0 @@ -name: "basic_movement" -instances { - id: "player" - prototype: "/examples/basic_movement/entity/player.go" - position { - x: 480.0 - y: 320.0 - } -} -scale_along_z: 0 -embedded_instances { - id: "root" - children: "player" - data: "components {\n" - " id: \"basic_movement\"\n" - " component: \"/examples/basic_movement/basic_movement.script\"\n" - "}\n" - "" -} diff --git a/examples/basic_movement/basic_movement.script b/examples/basic_movement/basic_movement.script deleted file mode 100644 index 40f4635..0000000 --- a/examples/basic_movement/basic_movement.script +++ /dev/null @@ -1,23 +0,0 @@ -local decore = require("decore.decore") - -function init(self) - self.world = decore.world( - require("system.transform.system_transform").create(), - require("system.transform_border.system_transform_border").create(), - require("system.game_object.system_game_object").create(), - require("system.input.system_input").create_system(), - require("system.movement_controller.system_movement_controller").create() - ) - - decore.register_entity("player", require("examples.basic_movement.entity.player")) -end - - -function update(self, dt) - self.world:update(dt) -end - - -function on_input(self, action_id, action) - return decore.on_input(self.world, action_id, action) -end diff --git a/examples/basic_movement/entity/player.go b/examples/basic_movement/entity/player.go deleted file mode 100644 index b537cd5..0000000 --- a/examples/basic_movement/entity/player.go +++ /dev/null @@ -1,30 +0,0 @@ -components { - id: "entity" - component: "/decore/entity.script" - properties { - id: "prefab_id" - value: "player" - type: PROPERTY_TYPE_HASH - } - properties { - id: "size_x" - value: "64.0" - type: PROPERTY_TYPE_NUMBER - } - properties { - id: "size_y" - value: "64.0" - type: PROPERTY_TYPE_NUMBER - } -} -embedded_components { - id: "sprite" - type: "sprite" - data: "default_animation: \"ui_circle_64\"\n" - "material: \"/builtins/materials/sprite.material\"\n" - "textures {\n" - " sampler: \"texture_sampler\"\n" - " texture: \"/assets/atlases/game.atlas\"\n" - "}\n" - "" -} diff --git a/examples/basic_movement/entity/player.lua b/examples/basic_movement/entity/player.lua deleted file mode 100644 index 81b433a..0000000 --- a/examples/basic_movement/entity/player.lua +++ /dev/null @@ -1,10 +0,0 @@ -return { - transform = {}, - game_object = {}, - transform_border = { - border = vmath.vector4(0, 640, 960, 0), - }, - movement_controller = { - speed = 20 - } -} \ No newline at end of file diff --git a/examples/basic_movement_with_walls/basic_movement_with_walls.collection b/examples/basic_movement_with_walls/basic_movement_with_walls.collection deleted file mode 100644 index 192536b..0000000 --- a/examples/basic_movement_with_walls/basic_movement_with_walls.collection +++ /dev/null @@ -1,111 +0,0 @@ -name: "basic_movement_with_walls" -instances { - id: "player" - prototype: "/examples/basic_movement_with_walls/entity/player.go" - position { - x: 480.0 - y: 320.0 - } -} -instances { - id: "wall" - prototype: "/examples/basic_movement_with_walls/entity/wall.go" - position { - x: 193.0 - y: 153.0 - } -} -instances { - id: "wall1" - prototype: "/examples/basic_movement_with_walls/entity/wall.go" - position { - x: 499.0 - y: 153.0 - } -} -instances { - id: "wall2" - prototype: "/examples/basic_movement_with_walls/entity/wall.go" - position { - x: 826.0 - y: 142.0 - } -} -instances { - id: "wall3" - prototype: "/examples/basic_movement_with_walls/entity/wall.go" - position { - x: 116.0 - y: 271.0 - } - rotation { - z: -0.70710677 - w: 0.70710677 - } -} -instances { - id: "entity_camera" - prototype: "/system/camera/entity_camera.go" - position { - x: 480.0 - y: 320.0 - z: 10.0 - } -} -instances { - id: "wall4" - prototype: "/examples/basic_movement_with_walls/entity/wall.go" - position { - x: 499.0 - y: 523.0 - } -} -instances { - id: "wall5" - prototype: "/examples/basic_movement_with_walls/entity/wall.go" - position { - x: 797.0 - y: 509.0 - } -} -instances { - id: "wall6" - prototype: "/examples/basic_movement_with_walls/entity/wall.go" - position { - x: 652.0 - y: 367.0 - } -} -instances { - id: "wall7" - prototype: "/examples/basic_movement_with_walls/entity/wall.go" - position { - x: 573.0 - y: 448.0 - } - rotation { - z: -0.70710677 - w: 0.70710677 - } -} -instances { - id: "wall8" - prototype: "/examples/basic_movement_with_walls/entity/wall.go" - position { - x: 721.0 - y: 585.0 - } - rotation { - z: -0.70710677 - w: 0.70710677 - } -} -scale_along_z: 0 -embedded_instances { - id: "root" - data: "components {\n" - " id: \"basic_movement_with_walls\"\n" - " component: \"/examples/basic_movement_with_walls/basic_movement_with_walls.script\"\n" - "}\n" - "" -} diff --git a/examples/basic_movement_with_walls/basic_movement_with_walls.script b/examples/basic_movement_with_walls/basic_movement_with_walls.script deleted file mode 100644 index b3fca77..0000000 --- a/examples/basic_movement_with_walls/basic_movement_with_walls.script +++ /dev/null @@ -1,27 +0,0 @@ -local decore = require("decore.decore") - -function init(self) - self.world = decore.new_world( - require("system.transform.system_transform").create(), - require("system.transform_border.system_transform_border").create(), - require("system.game_object.system_game_object").create(), - require("system.input.input_system").create(), - require("system.camera.system_camera").create(), - require("system.movement_controller.system_movement_controller").create() - ) - - decore.register_entities("game", { - ["player"] = require("examples.basic_movement_with_walls.entity.player"), - ["camera"] = require("system.camera.camera_entity") - }) -end - - -function update(self, dt) - self.world:update(dt) -end - - -function on_input(self, action_id, action) - return decore.on_input(self.world, action_id, action) -end diff --git a/examples/basic_movement_with_walls/entity/player.go b/examples/basic_movement_with_walls/entity/player.go deleted file mode 100644 index b537cd5..0000000 --- a/examples/basic_movement_with_walls/entity/player.go +++ /dev/null @@ -1,30 +0,0 @@ -components { - id: "entity" - component: "/decore/entity.script" - properties { - id: "prefab_id" - value: "player" - type: PROPERTY_TYPE_HASH - } - properties { - id: "size_x" - value: "64.0" - type: PROPERTY_TYPE_NUMBER - } - properties { - id: "size_y" - value: "64.0" - type: PROPERTY_TYPE_NUMBER - } -} -embedded_components { - id: "sprite" - type: "sprite" - data: "default_animation: \"ui_circle_64\"\n" - "material: \"/builtins/materials/sprite.material\"\n" - "textures {\n" - " sampler: \"texture_sampler\"\n" - " texture: \"/assets/atlases/game.atlas\"\n" - "}\n" - "" -} diff --git a/examples/basic_movement_with_walls/entity/player.lua b/examples/basic_movement_with_walls/entity/player.lua deleted file mode 100644 index 44d0914..0000000 --- a/examples/basic_movement_with_walls/entity/player.lua +++ /dev/null @@ -1,8 +0,0 @@ ----@diagnostic disable: missing-fields ----@type entity -return { - transform = {}, - game_object = {}, - transform_border = { border = vmath.vector4(0, 640, 960, 0) }, - movement_controller = { speed = 20 } -} diff --git a/examples/basic_movement_with_walls/entity/wall.go b/examples/basic_movement_with_walls/entity/wall.go deleted file mode 100644 index ad64859..0000000 --- a/examples/basic_movement_with_walls/entity/wall.go +++ /dev/null @@ -1,41 +0,0 @@ -embedded_components { - id: "collisionobject" - type: "collisionobject" - data: "type: COLLISION_OBJECT_TYPE_STATIC\n" - "mass: 0.0\n" - "friction: 0.1\n" - "restitution: 0.5\n" - "group: \"level\"\n" - "mask: \"player\"\n" - "embedded_collision_shape {\n" - " shapes {\n" - " shape_type: TYPE_BOX\n" - " position {\n" - " }\n" - " rotation {\n" - " }\n" - " index: 0\n" - " count: 3\n" - " }\n" - " data: 100.0\n" - " data: 25.0\n" - " data: 10.0\n" - "}\n" - "" -} -embedded_components { - id: "sprite" - type: "sprite" - data: "default_animation: \"pixel\"\n" - "material: \"/builtins/materials/sprite.material\"\n" - "size {\n" - " x: 200.0\n" - " y: 50.0\n" - "}\n" - "size_mode: SIZE_MODE_MANUAL\n" - "textures {\n" - " sampler: \"texture_sampler\"\n" - " texture: \"/core/atlas/core.atlas\"\n" - "}\n" - "" -} diff --git a/examples/basic_platformer_controller/basic_platformer_controller.collection b/examples/basic_platformer_controller/basic_platformer_controller.collection deleted file mode 100644 index 111eb90..0000000 --- a/examples/basic_platformer_controller/basic_platformer_controller.collection +++ /dev/null @@ -1,343 +0,0 @@ -name: "basic_platformer_controller" -instances { - id: "player" - prototype: "/examples/basic_platformer_controller/entity/player.go" - position { - x: 480.0 - y: 320.0 - } -} -instances { - id: "score_counter" - prototype: "/examples/basic_platformer_controller/entity/score_counter.go" -} -instances { - id: "coin16" - prototype: "/examples/basic_platformer_controller/entity/coin.go" - position { - x: -270.0 - y: -270.0 - } -} -instances { - id: "coin5" - prototype: "/examples/basic_platformer_controller/entity/coin.go" - position { - x: 366.0 - y: -270.0 - } -} -instances { - id: "coin12" - prototype: "/examples/basic_platformer_controller/entity/coin.go" - position { - x: 290.0 - y: -270.0 - } -} -instances { - id: "coin" - prototype: "/examples/basic_platformer_controller/entity/coin.go" - position { - x: -194.0 - y: -270.0 - } -} -instances { - id: "coin22" - prototype: "/examples/basic_platformer_controller/entity/coin.go" - position { - x: -122.0 - y: -270.0 - } -} -instances { - id: "coin24" - prototype: "/examples/basic_platformer_controller/entity/coin.go" - position { - x: 438.0 - y: -270.0 - } -} -instances { - id: "coin69" - prototype: "/examples/basic_platformer_controller/entity/coin.go" - position { - x: 252.0 - y: -270.0 - } -} -instances { - id: "coin79" - prototype: "/examples/basic_platformer_controller/entity/coin.go" - position { - x: 400.0 - y: -270.0 - } -} -instances { - id: "coin100" - prototype: "/examples/basic_platformer_controller/entity/coin.go" - position { - x: -308.0 - y: -270.0 - } -} -instances { - id: "coin105" - prototype: "/examples/basic_platformer_controller/entity/coin.go" - position { - x: -232.0 - y: -270.0 - } -} -instances { - id: "coin110" - prototype: "/examples/basic_platformer_controller/entity/coin.go" - position { - x: -160.0 - y: -270.0 - } -} -instances { - id: "coin113" - prototype: "/examples/basic_platformer_controller/entity/coin.go" - position { - x: 328.0 - y: -270.0 - } -} -instances { - id: "entity_camera" - prototype: "/core/system/camera/entity_camera.go" - position { - x: 480.0 - y: 320.0 - z: 10.0 - } -} -scale_along_z: 0 -embedded_instances { - id: "system" - data: "components {\n" - " id: \"basic_platformer_controller\"\n" - " component: \"/examples/basic_platformer_controller/basic_platformer_controller.script\"\n" - "}\n" - "" -} -embedded_instances { - id: "coins" - children: "coin" - children: "coin100" - children: "coin105" - children: "coin110" - children: "coin113" - children: "coin12" - children: "coin16" - children: "coin22" - children: "coin24" - children: "coin5" - children: "coin69" - children: "coin79" - data: "" - position { - x: 480.0 - y: 320.0 - } -} -embedded_instances { - id: "floor" - data: "embedded_components {\n" - " id: \"collisionobject\"\n" - " type: \"collisionobject\"\n" - " data: \"type: COLLISION_OBJECT_TYPE_STATIC\\n" - "mass: 0.0\\n" - "friction: 0.1\\n" - "restitution: 0.5\\n" - "group: \\\"level\\\"\\n" - "mask: \\\"player\\\"\\n" - "embedded_collision_shape {\\n" - " shapes {\\n" - " shape_type: TYPE_BOX\\n" - " position {\\n" - " }\\n" - " rotation {\\n" - " }\\n" - " index: 0\\n" - " count: 3\\n" - " }\\n" - " data: 960.0\\n" - " data: 10.0\\n" - " data: 10.0\\n" - "}\\n" - "\"\n" - "}\n" - "embedded_components {\n" - " id: \"sprite\"\n" - " type: \"sprite\"\n" - " data: \"default_animation: \\\"pixel\\\"\\n" - "material: \\\"/builtins/materials/sprite.material\\\"\\n" - "size {\\n" - " x: 1920.0\\n" - " y: 20.0\\n" - "}\\n" - "size_mode: SIZE_MODE_MANUAL\\n" - "textures {\\n" - " sampler: \\\"texture_sampler\\\"\\n" - " texture: \\\"/core/atlas/core.atlas\\\"\\n" - "}\\n" - "\"\n" - "}\n" - "" - position { - x: 480.0 - y: 14.0 - } -} -embedded_instances { - id: "floor1" - data: "embedded_components {\n" - " id: \"collisionobject\"\n" - " type: \"collisionobject\"\n" - " data: \"type: COLLISION_OBJECT_TYPE_STATIC\\n" - "mass: 0.0\\n" - "friction: 0.1\\n" - "restitution: 0.5\\n" - "group: \\\"level\\\"\\n" - "mask: \\\"player\\\"\\n" - "embedded_collision_shape {\\n" - " shapes {\\n" - " shape_type: TYPE_BOX\\n" - " position {\\n" - " }\\n" - " rotation {\\n" - " }\\n" - " index: 0\\n" - " count: 3\\n" - " }\\n" - " data: 960.0\\n" - " data: 10.0\\n" - " data: 10.0\\n" - "}\\n" - "\"\n" - "}\n" - "embedded_components {\n" - " id: \"sprite\"\n" - " type: \"sprite\"\n" - " data: \"default_animation: \\\"pixel\\\"\\n" - "material: \\\"/builtins/materials/sprite.material\\\"\\n" - "size {\\n" - " x: 1920.0\\n" - " y: 16.0\\n" - "}\\n" - "size_mode: SIZE_MODE_MANUAL\\n" - "textures {\\n" - " sampler: \\\"texture_sampler\\\"\\n" - " texture: \\\"/core/atlas/core.atlas\\\"\\n" - "}\\n" - "\"\n" - "}\n" - "" - position { - x: -553.0 - y: 287.0 - } -} -embedded_instances { - id: "floor2" - data: "embedded_components {\n" - " id: \"collisionobject\"\n" - " type: \"collisionobject\"\n" - " data: \"type: COLLISION_OBJECT_TYPE_STATIC\\n" - "mass: 0.0\\n" - "friction: 0.1\\n" - "restitution: 0.5\\n" - "group: \\\"level\\\"\\n" - "mask: \\\"player\\\"\\n" - "embedded_collision_shape {\\n" - " shapes {\\n" - " shape_type: TYPE_BOX\\n" - " position {\\n" - " }\\n" - " rotation {\\n" - " }\\n" - " index: 0\\n" - " count: 3\\n" - " }\\n" - " data: 200.0\\n" - " data: 50.0\\n" - " data: 10.0\\n" - "}\\n" - "\"\n" - "}\n" - "embedded_components {\n" - " id: \"sprite\"\n" - " type: \"sprite\"\n" - " data: \"default_animation: \\\"pixel\\\"\\n" - "material: \\\"/builtins/materials/sprite.material\\\"\\n" - "size {\\n" - " x: 400.0\\n" - " y: 100.0\\n" - "}\\n" - "size_mode: SIZE_MODE_MANUAL\\n" - "textures {\\n" - " sampler: \\\"texture_sampler\\\"\\n" - " texture: \\\"/core/atlas/core.atlas\\\"\\n" - "}\\n" - "\"\n" - "}\n" - "" - position { - x: 809.0 - y: 169.0 - } -} -embedded_instances { - id: "floor3" - data: "embedded_components {\n" - " id: \"collisionobject\"\n" - " type: \"collisionobject\"\n" - " data: \"type: COLLISION_OBJECT_TYPE_STATIC\\n" - "mass: 0.0\\n" - "friction: 0.1\\n" - "restitution: 0.5\\n" - "group: \\\"level\\\"\\n" - "mask: \\\"player\\\"\\n" - "embedded_collision_shape {\\n" - " shapes {\\n" - " shape_type: TYPE_BOX\\n" - " position {\\n" - " }\\n" - " rotation {\\n" - " }\\n" - " index: 0\\n" - " count: 3\\n" - " }\\n" - " data: 200.0\\n" - " data: 50.0\\n" - " data: 10.0\\n" - "}\\n" - "\"\n" - "}\n" - "embedded_components {\n" - " id: \"sprite\"\n" - " type: \"sprite\"\n" - " data: \"default_animation: \\\"pixel\\\"\\n" - "material: \\\"/builtins/materials/sprite.material\\\"\\n" - "size {\\n" - " x: 400.0\\n" - " y: 100.0\\n" - "}\\n" - "size_mode: SIZE_MODE_MANUAL\\n" - "textures {\\n" - " sampler: \\\"texture_sampler\\\"\\n" - " texture: \\\"/core/atlas/core.atlas\\\"\\n" - "}\\n" - "\"\n" - "}\n" - "" - position { - x: 894.0 - y: 311.0 - } -} diff --git a/examples/basic_platformer_controller/basic_platformer_controller.script b/examples/basic_platformer_controller/basic_platformer_controller.script deleted file mode 100644 index 709055a..0000000 --- a/examples/basic_platformer_controller/basic_platformer_controller.script +++ /dev/null @@ -1,44 +0,0 @@ -local decore = require("decore.decore") - -function init(self) - self.world = decore.world( - require("system.window_event.system_window_event").create_system(), - require("system.camera.system_camera").create(), - require("system.transform_border.system_transform_border").create(), - require("system.input.system_input").create_system(), - require("system.platformer_controller.system_platformer_controller").create_system(), - require("system.platformer_physics.system_platformer_physics").create_system(), - require("system.game_object.system_game_object").create(), - require("system.collision.system_collision").create_system(), - - require("examples.basic_platformer_controller.system.system_on_remove_event").create_system(), - require("examples.basic_platformer_controller.system.system_score_counter").create_system(), - - require("system.transform.system_transform").create() - ) - - decore.register_entity("player", require("examples.basic_platformer_controller.entity.player")) - decore.register_entity("coin", require("examples.basic_platformer_controller.entity.coin")) - decore.register_entity("score_counter", require("examples.basic_platformer_controller.entity.score_counter")) - decore.register_entity("camera", require("system.camera.entity_camera")) -end - - -function update(self, dt) - self.world:update(dt) -end - - -function fixed_update(self, dt) - self.world:fixed_update(dt) -end - - -function on_message(self, message_id, message, sender) - decore.on_message(self.world, message_id, message, sender) -end - - -function on_input(self, action_id, action) - return decore.on_input(self.world, action_id, action) -end diff --git a/examples/basic_platformer_controller/entity/coin.go b/examples/basic_platformer_controller/entity/coin.go deleted file mode 100644 index 1629ba3..0000000 --- a/examples/basic_platformer_controller/entity/coin.go +++ /dev/null @@ -1,62 +0,0 @@ -components { - id: "entity" - component: "/decore/entity.script" - properties { - id: "prefab_id" - value: "coin" - type: PROPERTY_TYPE_HASH - } - properties { - id: "size_x" - value: "32.0" - type: PROPERTY_TYPE_NUMBER - } - properties { - id: "size_y" - value: "32.0" - type: PROPERTY_TYPE_NUMBER - } -} -embedded_components { - id: "sprite" - type: "sprite" - data: "default_animation: \"ui_circle_32\"\n" - "material: \"/panthera/materials/sprite.material\"\n" - "attributes {\n" - " name: \"color\"\n" - " double_values {\n" - " v: 1.0\n" - " v: 1.0\n" - " v: 0.502\n" - " v: 1.0\n" - " }\n" - "}\n" - "textures {\n" - " sampler: \"texture_sampler\"\n" - " texture: \"/assets/atlases/game.atlas\"\n" - "}\n" - "" -} -embedded_components { - id: "collisionobject" - type: "collisionobject" - data: "type: COLLISION_OBJECT_TYPE_TRIGGER\n" - "mass: 0.0\n" - "friction: 0.1\n" - "restitution: 0.5\n" - "group: \"level\"\n" - "mask: \"player\"\n" - "embedded_collision_shape {\n" - " shapes {\n" - " shape_type: TYPE_SPHERE\n" - " position {\n" - " }\n" - " rotation {\n" - " }\n" - " index: 0\n" - " count: 1\n" - " }\n" - " data: 16.0\n" - "}\n" - "" -} diff --git a/examples/basic_platformer_controller/entity/coin.lua b/examples/basic_platformer_controller/entity/coin.lua deleted file mode 100644 index e6cd61a..0000000 --- a/examples/basic_platformer_controller/entity/coin.lua +++ /dev/null @@ -1,9 +0,0 @@ ---@type entity -return { - transform = {}, - game_object = {}, - collision = { - is_remove = true, - }, - on_remove_event = "score_plus" -} diff --git a/examples/basic_platformer_controller/entity/player.go b/examples/basic_platformer_controller/entity/player.go deleted file mode 100644 index c9dcfd8..0000000 --- a/examples/basic_platformer_controller/entity/player.go +++ /dev/null @@ -1,60 +0,0 @@ -components { - id: "entity" - component: "/decore/entity.script" - properties { - id: "prefab_id" - value: "player" - type: PROPERTY_TYPE_HASH - } - properties { - id: "size_x" - value: "64.0" - type: PROPERTY_TYPE_NUMBER - } - properties { - id: "size_y" - value: "64.0" - type: PROPERTY_TYPE_NUMBER - } -} -components { - id: "component_platformer_physics" - component: "/core/system/platformer_physics/component_platformer_physics.script" -} -embedded_components { - id: "sprite" - type: "sprite" - data: "default_animation: \"ui_circle_64\"\n" - "material: \"/builtins/materials/sprite.material\"\n" - "textures {\n" - " sampler: \"texture_sampler\"\n" - " texture: \"/assets/atlases/game.atlas\"\n" - "}\n" - "" -} -embedded_components { - id: "collisionobject" - type: "collisionobject" - data: "type: COLLISION_OBJECT_TYPE_KINEMATIC\n" - "mass: 0.0\n" - "friction: 0.1\n" - "restitution: 0.5\n" - "group: \"player\"\n" - "mask: \"level\"\n" - "embedded_collision_shape {\n" - " shapes {\n" - " shape_type: TYPE_BOX\n" - " position {\n" - " }\n" - " rotation {\n" - " }\n" - " index: 0\n" - " count: 3\n" - " }\n" - " data: 32.0\n" - " data: 32.0\n" - " data: 10.0\n" - "}\n" - "bullet: true\n" - "" -} diff --git a/examples/basic_platformer_controller/entity/player.lua b/examples/basic_platformer_controller/entity/player.lua deleted file mode 100644 index de99067..0000000 --- a/examples/basic_platformer_controller/entity/player.lua +++ /dev/null @@ -1,9 +0,0 @@ -return { - transform = {}, - game_object = {}, - platformer_controller = {}, - transform_border = { - border = vmath.vector4(0, 640, 960, 0), - }, - collision = {} -} \ No newline at end of file diff --git a/examples/basic_platformer_controller/entity/score_counter.go b/examples/basic_platformer_controller/entity/score_counter.go deleted file mode 100644 index 06c760c..0000000 --- a/examples/basic_platformer_controller/entity/score_counter.go +++ /dev/null @@ -1,13 +0,0 @@ -components { - id: "score_counter" - component: "/examples/basic_platformer_controller/entity/score_counter.gui" -} -components { - id: "entity" - component: "/decore/entity.script" - properties { - id: "prefab_id" - value: "score_counter" - type: PROPERTY_TYPE_HASH - } -} diff --git a/examples/basic_platformer_controller/entity/score_counter.gui b/examples/basic_platformer_controller/entity/score_counter.gui deleted file mode 100644 index cc3a1a6..0000000 --- a/examples/basic_platformer_controller/entity/score_counter.gui +++ /dev/null @@ -1,24 +0,0 @@ -script: "/examples/basic_platformer_controller/entity/score_counter.gui_script" -fonts { - name: "text" - font: "/assets/fonts/text.font" -} -nodes { - position { - x: 480.0 - y: 540.0 - } - size { - x: 200.0 - y: 100.0 - } - type: TYPE_TEXT - text: "Score: 0" - font: "text" - id: "text" - inherit_alpha: true -} -material: "/builtins/materials/gui.material" -adjust_reference: ADJUST_REFERENCE_PARENT -max_nodes: 1 -max_dynamic_textures: 1 diff --git a/examples/basic_platformer_controller/entity/score_counter.gui_script b/examples/basic_platformer_controller/entity/score_counter.gui_script deleted file mode 100644 index 1ed8e2a..0000000 --- a/examples/basic_platformer_controller/entity/score_counter.gui_script +++ /dev/null @@ -1,12 +0,0 @@ -function init(self) - self.score = 0 - self.text = gui.get_node("text") -end - - -function on_message(self, message_id) - if message_id == hash("score_plus") then - self.score = self.score + 1 - gui.set_text(self.text, "Score: " .. self.score) - end -end diff --git a/examples/basic_platformer_controller/entity/score_counter.lua b/examples/basic_platformer_controller/entity/score_counter.lua deleted file mode 100644 index 8e7c3cc..0000000 --- a/examples/basic_platformer_controller/entity/score_counter.lua +++ /dev/null @@ -1,3 +0,0 @@ -return { - score_counter = true, -} \ No newline at end of file diff --git a/examples/basic_platformer_controller/system/system_on_remove_event.lua b/examples/basic_platformer_controller/system/system_on_remove_event.lua deleted file mode 100644 index 21999a2..0000000 --- a/examples/basic_platformer_controller/system/system_on_remove_event.lua +++ /dev/null @@ -1,26 +0,0 @@ -local decore = require("decore.decore") - ----@class entity ----@field on_remove_event string|nil - ----@class entity.on_remove_event: entity ----@field on_remove_event string -decore.register_component("on_remove_event", "") - ----@class system.on_remove_event: system ----@field entities entity.on_remove_event[] -local M = {} - - ----@return system.on_remove_event -function M.create_system() - return decore.system(M, "on_remove_event", "on_remove_event") -end - - -function M:onRemove(entity) - self.world.event_bus:trigger(entity.on_remove_event, entity) -end - - -return M diff --git a/examples/basic_platformer_controller/system/system_score_counter.lua b/examples/basic_platformer_controller/system/system_score_counter.lua deleted file mode 100644 index 7f100d5..0000000 --- a/examples/basic_platformer_controller/system/system_score_counter.lua +++ /dev/null @@ -1,34 +0,0 @@ -local decore = require("decore.decore") - ----@class entity ----@field score_counter boolean|nil - ----@class entity.score_counter: entity ----@field score_counter boolean ----@field game_object component.game_object -decore.register_component("score_counter", false) - ----@class system.score_counter: system ----@field entities entity.score_counter[] -local M = {} - - ----@return system.score_counter -function M.create_system() - return decore.system(M, "score_counter", { "score_counter", "game_object" }) -end - - -function M:postWrap() - self.world.event_bus:process("score_plus", self.process_score_plus, self) -end - - -function M:process_score_plus() - for index = 1, #self.entities do - msg.post(self.entities[index].game_object.root, "score_plus") - end -end - - -return M diff --git a/examples/boids/boid/boid.go b/examples/boids/boid/boid.go deleted file mode 100644 index 69f4f9e..0000000 --- a/examples/boids/boid/boid.go +++ /dev/null @@ -1,24 +0,0 @@ -embedded_components { - id: "sprite" - type: "sprite" - data: "default_animation: \"boid\"\n" - "material: \"/panthera/materials/sprite.material\"\n" - "size {\n" - " x: 129.0\n" - " y: 196.0\n" - "}\n" - "size_mode: SIZE_MODE_MANUAL\n" - "textures {\n" - " sampler: \"texture_sampler\"\n" - " texture: \"/examples/boids/boids.atlas\"\n" - "}\n" - "" - rotation { - z: -0.70710677 - w: 0.70710677 - } - scale { - x: 0.1 - y: 0.1 - } -} diff --git a/examples/boids/boid/boid.png b/examples/boids/boid/boid.png deleted file mode 100644 index 86db9b6180b2e39dd60811433f7db7533b96870e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2375 zcmV-N3Apx&P)5W$HIAOau)E(4Q+$w0~gGU%H*?Ns@UEbULvOm|Oz zRVtNjS@vq*_P4#WI|~dB5&a2gTv7mb>G1=D^Bcw{q{ovKgn)q=Jp`|(XIH|Aq^&W5Xrs3Cay$oB)BG03H#&U1ic zpM02@E)y^qBkJJgm7fPVwz;4JSHcL)P(fjbff!8(*Hlo5aBMO|1%+EU_Q-qvw2U1F zVI&>AUikWmK^Q3n0n7LUjvZF0U??SwQVEu-pfJSn@+ct)SjXI!(vi#t6%?MpI3rYo zwJIn?7-xhK1Z=~Vpcw`g450+;m0+t13K52}9)bWKOe^6AMoA_7S$etyAHUz+-28+= zELVaEULuTQIRxA#+^iBz>Y#!O3MQ3cwGu?s!Q@zi)ezJg2IguxMPW_{bQ=Dxb;fX3 z!BcBWuviIdmh2}ms0524s9mznDZ!KuDyyJiP6^g3L5otImGBIPuoi+=rP`Dd%;=zz z3JRu_V5t%Ww8NAVEQO$Ti8iAI6FO+Df`S<(Sg8a7?J=VSDj0{Wv~3ED#-<$#T9C8*WGbZMBB znaNcvL31ULwDqZ0g60rNyAD9D5>)CymEh^gsY&iBW2U!YSV$1 zi6SBtp)CYjj>@T;Aze)froC=0^9cq`O3+jZw4LW4p#)7K2+)NZC8!9&q=FrEp+*T> zDuLEZzEteUY6(GrPE@qC;*S_kD$snNLh*ASYN!O-ul|h?j)o8f=tjH}MC-tS3JRi? zAiWY;nFu06Fw#R1U<1)g5UYcVGnMy3tP&(wf(Tng2t{%TtleZn?DB}zfk71%L@GgQ zC5W(z$gu>eAy9Q@&H_ZXyyA2qWcHC-h@AUSVkIznpP>nnH_eb30+aU{x`(*s6Qu*= zDkz9kg0xCtSp@}g)8R@BfprxWL@lQn9Sov^f+!_OsssV!AW8|6LNJO73SyR5gboH% zK|zcXq*Q`{@erc~DIpk51qBf;ad#aIs)B-uxep~&f`D-mG54W_5R9sVLU$!})xqE@ zD0Ejs?@AEhbXP*}5R9&ZLRTep(}B>L%6rjO2|X*p2*bv6>8gaDA+UB`!B)C$6kT-S zDmY6wCG@HUu7Y3bri5N0a21>hAbqCtUUZouy?G^2GE;dky4*CQhYnl?mkDT0 za~-$}uF_lyO)G(`U>419n$cPZu7Y_)G^BM1{P@5;nqHdUECkAKTG5-PGo-hy1U_uM zjEF|G3_*aigl0-;5(1?+t>{fNCA6vpu7Y(m*^zD)f&ga?OeEsH%k8mB3ZNLqtVthakY=psEt8>A>(z<-Mq;gqoEg z!r>&M5;a5M=ML~v?enan16M&cswkmWC5UipP-QHkRtT)#w4ygvT5fh7`0OK9sWSIr zjY{C>5!R#1O*3kQz>gBvh5hqo)q$&^3G7PXt^}@v`mj%j%N+t&K^s^+vm-q1BEJhlUDra zhu=)gu}@EbLkDb1;Hm@xI>;Z`^K${nr0?(3(;Mi3O$l5f(4c}NP67CS=|xjOo=>lr z4ogeESqDl~kXQVxw1SUwe_>j#e3I-Aa$%YKkfRa=$mUymyjT)|%=G#fkX;d2=04;I zfn*hYOe^!n+MTB<2w4r1Tgc5y*rWsDDtJHb=&_KN^+S643-Z{kgsqhzKql`v1CYtb z6d?0h!e%9G4S_TjpR|56nK&NXQ#WQ_Iy*8A9g{;v*4;377Y5~7P0qOtDV-3Ap$bf|`?hdH^ zO)K(>Up)xa6Y4V@z4)dXiz)%NziGu6C=Ed7-m!$16>RZMGnVS0r3(IQ0ksC8kb^L_ zkYCV_h*~t<*?9|tq~+RE8|q9OnBF*0eR9Ym3`6aLumd;gO+Qgx4y2?7+)7%`NcF4g z9^6bTsTK!ndD&bmpz|j?yQNSad*-XRcTqm+dtc4+ox5Tky zGBCt*EMc7z=v0oj;4L5%#M449-x%{sr1k9Ku(NTUEbkI%eF=(iszK{p$Yn~Pbt=d$ zV1QE{T7O3Kd?nEGvU4DB0h>an_I>GabX@WW?iRf9!2{3-THZO#Q^NGdv)`1w1?#Wz~q_0R-o%G -#include -#include - -// List of entity references in Lua registry -static std::vector entities; - - -// Add entity to system -static int add_entity(lua_State* L) { - // Stack: { entity } - DM_LUA_STACK_CHECK(L, 0); - - luaL_checktype(L, 1, LUA_TTABLE); - - // Create a reference to the entity and store it - lua_pushvalue(L, 1); - int ref = luaL_ref(L, LUA_REGISTRYINDEX); - entities.push_back(ref); - - return 0; -}; - - -// Remove entity from system -static int remove_entity(lua_State* L) { - // Stack: { entity } - DM_LUA_STACK_CHECK(L, 0); - - luaL_checktype(L, 1, LUA_TTABLE); - - // Find and remove the entity reference - bool found = false; - for (auto it = entities.begin(); it != entities.end(); ++it) { - lua_rawgeti(L, LUA_REGISTRYINDEX, *it); - if (lua_rawequal(L, -1, 1)) { - luaL_unref(L, LUA_REGISTRYINDEX, *it); - entities.erase(it); - found = true; - lua_pop(L, 1); - break; - } - lua_pop(L, 1); - } - if (!found) { - dmLogWarning("Entity not found in system."); - } - - return 0; -}; - - -// Update entities -static int update(lua_State* L) { - // Stack: { dt } - DM_LUA_STACK_CHECK(L, 0); - - // Increase velocity by 1:1 for each entity - // velocity placed in entity.velocity.x and entity.velocity.y - for (auto it = entities.begin(); it != entities.end(); ++it) { - lua_rawgeti(L, LUA_REGISTRYINDEX, *it); // Push entity - lua_getfield(L, -1, "velocity"); // Push velocity - - // Update velocity.x - lua_getfield(L, -1, "x"); // Push velocity.x - lua_pushnumber(L, lua_tonumber(L, -1) + 1); // Push x + 1 - lua_setfield(L, -3, "x"); // Set velocity.x - lua_pop(L, 1); // Pop old x - - // Update velocity.y - lua_getfield(L, -1, "y"); // Push velocity.y - lua_pushnumber(L, lua_tonumber(L, -1) + 1); // Push y + 1 - lua_setfield(L, -3, "y"); // Set velocity.y - lua_pop(L, 1); // Pop old y - - lua_pop(L, 2); // Pop velocity and entity - } - - return 0; -} - - -// Functions exposed to Lua -static const luaL_Reg Module_methods[] = { - {"add_entity", add_entity}, - {"remove_entity", remove_entity}, - {"update", update}, - {0, 0} -}; - - -static dmExtension::Result Initialize(dmExtension::Params* params) { - lua_State* L = params->m_L; - int top = lua_gettop(L); - - luaL_register(L, "c_system_boid", Module_methods); - lua_pop(L, 1); - - assert(top == lua_gettop(L)); - return dmExtension::RESULT_OK; -} - -DM_DECLARE_EXTENSION(c_system_boid, "c_system_boid", 0, 0, Initialize, 0, 0, 0) diff --git a/examples/boids/system_boid/system_boid.lua b/examples/boids/system_boid/system_boid.lua deleted file mode 100644 index 52b51cd..0000000 --- a/examples/boids/system_boid/system_boid.lua +++ /dev/null @@ -1,201 +0,0 @@ -local decore = require("decore.decore") -local command_boid = require("examples.boids.system_boid.command_boid") - ----@class entity ----@field boid component.boid|nil - ----@class entity.boid: entity ----@field boid component.boid ----@field transform component.transform ----@field transform_border component.transform_border|nil ----@field velocity component.velocity - ----@class component.boid.neighbor ----@field dx number ----@field dy number ----@field dist_sq number ----@field entity entity.boid - ----@class component.boid ----@field widget gui.boid_sensor ----@field speed number ----@field separation_radius number ----@field alignment_radius number ----@field cohesion_radius number ----@field max_force number ----@field neighbors component.boid.neighbor[] ----@field separation_weight number ----@field alignment_weight number ----@field cohesion_weight number ----@field turn_factor number -- new ----@field visual_range number ----@field protected_range number ----@field centering_factor number ----@field avoid_factor number The force to avoid other boids ----@field matching_factor number ----@field max_bias number ----@field bias_increment number ----@field bias_val number -decore.register_component("boid", { - neighbors = {}, - turn_factor = 180, - visual_range = 40, - protected_range = 8, - centering_factor = 0.0005, - avoid_factor = 0.05, - matching_factor = 0.05, -}) - ----@class system.boid: system ----@field entities entity.boid[] ----@field debug_draw_force boolean -local M = {} - - -local COLOR_FORCE = vmath.vector4(1, 0, 0, 1) - -function M.create_system() - return decore.system(M, "boid", { "boid", "transform", "velocity" }) -end - - -function M:onAddToWorld() - self.debug_draw_force = false - self.world.command_boid = command_boid.create(self) -end - - -function M:onAdd(entity) - --c_system_boid.add_entity(entity) - --local widget = bindings.get_widget(entity.game_object.root) - --entity.boid.widget = widget -end - -function M:onRemove(entity) - --c_system_boid.remove_entity(entity) -end - - -function M:update(dt) - --c_system_boid.update(dt) - - --local divider = 60 - --local entities_per_frame = math.ceil(#self.entities / 60 * divider) - --self.current_index = self.current_index or 1 - --local entities_from = self.current_index - --local entities_to = math.min(self.current_index + entities_per_frame - 1, #self.entities) - --self.current_index = entities_to + 1 - --if self.current_index > #self.entities then - -- self.current_index = 1 - --end - - for index = 1, #self.entities do - local entity = self.entities[index] - self:process(entity, dt) - - --entity.boid.widget:set_position(entity.transform.position_x, entity.transform.position_y) - --entity.boid.widget:set_rotation(entity.transform.rotation) - end -end - - -function M:process(entity, dt) - local force_x, force_y = self:calculate_forces_new(entity) - self.world.command_velocity:add_velocity(entity, force_x * dt, force_y * dt) - - if self.debug_draw_force and self.world.command_debug_draw then - self.world.command_debug_draw:draw_line( - entity.transform.position_x, - entity.transform.position_y, - entity.transform.position_x + force_x, - entity.transform.position_y + force_y, - COLOR_FORCE - ) - end -end - - ----@param entity entity.boid -function M:calculate_forces_new(entity) - local boid = entity.boid - local protected_rande_sq = boid.protected_range * boid.protected_range - - self._entity = entity - self._protected_rande_sq = protected_rande_sq - self._average_position_x = 0 - self._average_position_y = 0 - self._average_velocity_x = 0 - self._average_velocity_y = 0 - self._neighboring_boids = 0 - self._close_dx = 0 - self._close_dy = 0 - - self.calculate_neighbor = self.calculate_neighbor or function(other_boid) - if other_boid ~= self._entity then - local t = other_boid.transform - local dx = t.position_x - self._entity.transform.position_x - local dy = t.position_y - self._entity.transform.position_y - local dist = dx * dx + dy * dy - - if dist < self._protected_rande_sq then - self._close_dx = self._close_dx + dx - self._close_dy = self._close_dy + dy - elseif dist < boid.visual_range * boid.visual_range then - self._average_position_x = self._average_position_x + t.position_x - self._average_position_y = self._average_position_y + t.position_y - self._average_velocity_x = self._average_velocity_x + other_boid.velocity.x - self._average_velocity_y = self._average_velocity_y + other_boid.velocity.y - self._neighboring_boids = self._neighboring_boids + 1 - end - end - end - - self.world.command_quadtree:get_neighbors(entity, math.max(boid.visual_range, boid.protected_range), self.calculate_neighbor) - - local average_position_x, average_position_y = self._average_position_x, self._average_position_y - local average_velocity_x, average_velocity_y = self._average_velocity_x, self._average_velocity_y - local neighboring_boids = self._neighboring_boids - local close_dx, close_dy = self._close_dx, self._close_dy - local force_x = 0 - local force_y = 0 - - if neighboring_boids > 0 then - average_position_x = average_position_x / neighboring_boids - average_position_y = average_position_y / neighboring_boids - average_velocity_x = average_velocity_x / neighboring_boids - average_velocity_y = average_velocity_y / neighboring_boids - - force_x = force_x + (average_position_x - entity.transform.position_x) * boid.centering_factor - + (average_velocity_x - entity.velocity.x) * boid.matching_factor - - force_y = force_y + (average_position_y - entity.transform.position_y) * boid.centering_factor - end - - if close_dx ~= 0 or close_dy ~= 0 then - force_x = force_x - close_dx * boid.avoid_factor - force_y = force_y - close_dy * boid.avoid_factor - end - - -- Here can be turn factor around edges - local border = entity.transform_border and entity.transform_border.border - local transform = entity.transform - if border then - if transform.position_y > border.y then - force_y = force_y - boid.turn_factor - end - if transform.position_y < border.w then - force_y = force_y + boid.turn_factor - end - if transform.position_x > border.z then - force_x = force_x - boid.turn_factor - end - if transform.position_x < border.x then - force_x = force_x + boid.turn_factor - end - end - - return force_x, force_y -end - - -return M diff --git a/examples/boids/system_game_object/ext.manifest b/examples/boids/system_game_object/ext.manifest deleted file mode 100644 index 900e5a5..0000000 --- a/examples/boids/system_game_object/ext.manifest +++ /dev/null @@ -1 +0,0 @@ -name: "GoPositionSetter" \ No newline at end of file diff --git a/examples/boids/system_game_object/src/c_go_setter.cpp b/examples/boids/system_game_object/src/c_go_setter.cpp deleted file mode 100644 index e08cec6..0000000 --- a/examples/boids/system_game_object/src/c_go_setter.cpp +++ /dev/null @@ -1,139 +0,0 @@ -#define EXTENSION_NAME GoPositionSetter -#define LIB_NAME "GoPositionSetter" -#define MODULE_NAME "go_position_setter" - -#include - -#define USERDATA_METATABLE "POSITION_SETTER_USERDATA" - -struct InstancePositionData { - dmGameObject::HInstance rootInstance; - dmVMath::Vector3 *position; - // rotation quat - dmVMath::Quat *rotation; - -}; - -class PositionSetterUserdata { - public: - dmArray instances; - explicit PositionSetterUserdata(); - ~PositionSetterUserdata(); - void addInstance(dmGameObject::HInstance rootInstance, dmVMath::Vector3 *position, dmVMath::Quat *rotation); - void update(); - void removeInstance(dmGameObject::HInstance rootInstance); -}; - -PositionSetterUserdata::PositionSetterUserdata() { -} - -PositionSetterUserdata::~PositionSetterUserdata() { -} - -void PositionSetterUserdata::addInstance(dmGameObject::HInstance rootInstance, dmVMath::Vector3 *position, dmVMath::Quat *rotation) { - InstancePositionData instanceVector; - instanceVector.rootInstance = rootInstance; - instanceVector.position = position; - instanceVector.rotation = rotation; - if (instances.Full()) { - instances.OffsetCapacity(32); - } - instances.Push(instanceVector); -} - -void PositionSetterUserdata::update() { - for (int i = 0; i < instances.Size(); ++i) { - InstancePositionData instancePositionData = instances[i]; - dmGameObject::SetPosition(instancePositionData.rootInstance, dmVMath::Point3(*instancePositionData.position)); - dmGameObject::SetRotation(instancePositionData.rootInstance, *instancePositionData.rotation); - } -} - -void PositionSetterUserdata::removeInstance(dmGameObject::HInstance rootInstance) { - for (int i = 0; i < instances.Size(); ++i) { - InstancePositionData instancePositionData = instances[i]; - if (instancePositionData.rootInstance == rootInstance) { - instances.EraseSwap(i); - return; - } - } -} - -static PositionSetterUserdata *PositionSetterUserdataCheck(lua_State *L, int index) { - return *(PositionSetterUserdata **)luaL_checkudata(L, index, USERDATA_METATABLE); -} - -static int LuaPositionSetterUserdataAddInstance(lua_State *L) { - PositionSetterUserdata *userdata = PositionSetterUserdataCheck(L, 1); - dmGameObject::HInstance rootInstance = dmScript::CheckGOInstance(L, 2); - dmVMath::Vector3 *position = dmScript::CheckVector3(L, 3); - dmVMath::Quat *rotation = dmScript::CheckQuat(L, 4); - userdata->addInstance(rootInstance, position, rotation); - return 0; -} - -static int LuaPositionSetterUserdataUpdate(lua_State *L) { - PositionSetterUserdata *userdata = PositionSetterUserdataCheck(L, 1); - userdata->update(); - return 0; -} - -static int LuaPositionSetterUserdataRemoveInstance(lua_State *L) { - PositionSetterUserdata *userdata = PositionSetterUserdataCheck(L, 1); - dmGameObject::HInstance rootInstance = dmScript::CheckGOInstance(L, 2); - userdata->removeInstance(rootInstance); - return 0; -} - -static int LuaPositionSetterUserdataGC(lua_State *L) { - PositionSetterUserdata *userdata = PositionSetterUserdataCheck(L, 1); - delete userdata; - return 0; -} - -static const luaL_Reg LuaPositionSetterUserdataMethods[] = { - {"__gc", LuaPositionSetterUserdataGC}, - {"add", LuaPositionSetterUserdataAddInstance}, - {"remove", LuaPositionSetterUserdataRemoveInstance}, - {"update", LuaPositionSetterUserdataUpdate}, - {0, 0}}; - -int NewPositionSetterUserdataLua(lua_State *L) { - PositionSetterUserdata *userdata = new PositionSetterUserdata(); - PositionSetterUserdata **ud = (PositionSetterUserdata **)lua_newuserdata(L, sizeof(PositionSetterUserdata *)); - *ud = userdata; - if (luaL_newmetatable(L, USERDATA_METATABLE)) { - lua_pushvalue(L, -1); - lua_setfield(L, -2, "__index"); - luaL_register(L, 0, LuaPositionSetterUserdataMethods); - } - lua_setmetatable(L, -2); - return 1; -} - -// Functions exposed to Lua -static const luaL_reg Module_methods[] = { - {"new", NewPositionSetterUserdataLua}, - {0, 0}}; - -static void LuaInit(lua_State *L) { - int top = lua_gettop(L); - luaL_register(L, MODULE_NAME, Module_methods); - lua_pop(L, 1); - assert(top == lua_gettop(L)); -} - -static dmExtension::Result AppInitializeMyExtension(dmExtension::AppParams *params) { return dmExtension::RESULT_OK; } -static dmExtension::Result InitializeMyExtension(dmExtension::Params *params) { - // Init Lua - LuaInit(params->m_L); - - printf("Registered %s Extension\n", MODULE_NAME); - return dmExtension::RESULT_OK; -} - -static dmExtension::Result AppFinalizeMyExtension(dmExtension::AppParams *params) { return dmExtension::RESULT_OK; } - -static dmExtension::Result FinalizeMyExtension(dmExtension::Params *params) { return dmExtension::RESULT_OK; } - -DM_DECLARE_EXTENSION(EXTENSION_NAME, LIB_NAME, AppInitializeMyExtension, AppFinalizeMyExtension, InitializeMyExtension, 0, 0, FinalizeMyExtension) \ No newline at end of file diff --git a/examples/manifest.appmanifest b/examples/manifest.appmanifest deleted file mode 100644 index 930e929..0000000 --- a/examples/manifest.appmanifest +++ /dev/null @@ -1,106 +0,0 @@ -platforms: - armv7-ios: - context: - excludeLibs: [gamesys_rig, rig, physics, LinearMath, BulletDynamics, BulletCollision, gamesys_model] - excludeSymbols: [ScriptModelExt] - symbols: [] - libs: [gamesys_rig_null, rig_null, physics_2d_defold, gamesys_model_null] - frameworks: [] - linkFlags: [] - arm64-ios: - context: - excludeLibs: [gamesys_rig, rig, physics, LinearMath, BulletDynamics, BulletCollision, gamesys_model] - excludeSymbols: [ScriptModelExt] - symbols: [] - libs: [gamesys_rig_null, rig_null, physics_2d_defold, gamesys_model_null] - frameworks: [] - linkFlags: [] - x86_64-ios: - context: - excludeLibs: [gamesys_rig, rig, physics, LinearMath, BulletDynamics, BulletCollision, gamesys_model] - excludeSymbols: [ScriptModelExt] - symbols: [] - libs: [gamesys_rig_null, rig_null, physics_2d_defold, gamesys_model_null] - frameworks: [] - linkFlags: [] - armv7-android: - context: - excludeLibs: [gamesys_rig, rig, physics, LinearMath, BulletDynamics, BulletCollision, gamesys_model] - excludeJars: [] - excludeSymbols: [ScriptModelExt] - symbols: [] - libs: [gamesys_rig_null, rig_null, physics_2d_defold, gamesys_model_null] - linkFlags: [] - jetifier: true - arm64-android: - context: - excludeLibs: [gamesys_rig, rig, physics, LinearMath, BulletDynamics, BulletCollision, gamesys_model] - excludeJars: [] - excludeSymbols: [ScriptModelExt] - symbols: [] - libs: [gamesys_rig_null, rig_null, physics_2d_defold, gamesys_model_null] - linkFlags: [] - jetifier: true - arm64-osx: - context: - excludeLibs: [gamesys_rig, rig, physics, LinearMath, BulletDynamics, BulletCollision, gamesys_model] - excludeSymbols: [ScriptModelExt] - symbols: [] - libs: [gamesys_rig_null, rig_null, physics_2d_defold, gamesys_model_null] - frameworks: [] - linkFlags: [] - x86_64-osx: - context: - excludeLibs: [gamesys_rig, rig, physics, LinearMath, BulletDynamics, BulletCollision, gamesys_model] - excludeSymbols: [ScriptModelExt] - symbols: [] - libs: [gamesys_rig_null, rig_null, physics_2d_defold, gamesys_model_null] - frameworks: [] - linkFlags: [] - x86_64-linux: - context: - excludeLibs: [gamesys_rig, rig, physics, LinearMath, BulletDynamics, BulletCollision, gamesys_model] - excludeSymbols: [ScriptModelExt] - symbols: [] - libs: [gamesys_rig_null, rig_null, physics_2d_defold, gamesys_model_null] - linkFlags: [] - arm64-linux: - context: - excludeLibs: [gamesys_rig, rig, physics, LinearMath, BulletDynamics, BulletCollision, gamesys_model] - excludeSymbols: [ScriptModelExt] - libs: [gamesys_rig_null, rig_null, physics_2d_defold, gamesys_model_null] - x86-win32: - context: - excludeLibs: [libgamesys_rig, librig, libphysics, libLinearMath, libBulletDynamics, libBulletCollision, libgamesys_model] - excludeSymbols: [ScriptModelExt] - symbols: [] - libs: [libgamesys_rig_null.lib, librig_null.lib, libphysics_2d_defold.lib, libgamesys_model_null.lib] - linkFlags: [] - x86_64-win32: - context: - excludeLibs: [libgamesys_rig, librig, libphysics, libLinearMath, libBulletDynamics, libBulletCollision, libgamesys_model] - excludeSymbols: [ScriptModelExt] - symbols: [] - libs: [libgamesys_rig_null.lib, librig_null.lib, libphysics_2d_defold.lib, libgamesys_model_null.lib] - linkFlags: [] - js-web: - context: - excludeLibs: [gamesys_rig, rig, physics, LinearMath, BulletDynamics, BulletCollision, gamesys_model] - excludeJsLibs: [] - excludeSymbols: [ScriptModelExt] - symbols: [] - libs: [gamesys_rig_null, rig_null, physics_2d_defold, gamesys_model_null] - linkFlags: [] - wasm-web: - context: - excludeLibs: [gamesys_rig, rig, physics, LinearMath, BulletDynamics, BulletCollision, gamesys_model] - excludeJsLibs: [] - excludeSymbols: [ScriptModelExt] - symbols: [] - libs: [gamesys_rig_null, rig_null, physics_2d_defold, gamesys_model_null] - linkFlags: [] - wasm_pthread-web: - context: - excludeLibs: [physics, LinearMath, BulletDynamics, BulletCollision, gamesys_model, gamesys_rig, rig] - excludeSymbols: [ScriptModelExt] - libs: [physics_2d_defold, gamesys_rig_null, gamesys_model_null, rig_null] diff --git a/examples/nested_entities/entity/player.go b/examples/nested_entities/entity/player.go deleted file mode 100644 index ea87d2c..0000000 --- a/examples/nested_entities/entity/player.go +++ /dev/null @@ -1,62 +0,0 @@ -components { - id: "entity" - component: "/decore/entity.script" - properties { - id: "prefab_id" - value: "player" - type: PROPERTY_TYPE_HASH - } - properties { - id: "size_x" - value: "64.0" - type: PROPERTY_TYPE_NUMBER - } - properties { - id: "size_y" - value: "64.0" - type: PROPERTY_TYPE_NUMBER - } -} -embedded_components { - id: "sprite" - type: "sprite" - data: "default_animation: \"ui_circle_64\"\n" - "material: \"/builtins/materials/sprite.material\"\n" - "textures {\n" - " sampler: \"texture_sampler\"\n" - " texture: \"/assets/atlases/game.atlas\"\n" - "}\n" - "" -} -embedded_components { - id: "label" - type: "label" - data: "size {\n" - " x: 80.0\n" - " y: 40.0\n" - "}\n" - "color {\n" - " x: 0.2\n" - " y: 0.2\n" - " z: 0.2\n" - "}\n" - "outline {\n" - " w: 0.0\n" - "}\n" - "shadow {\n" - " w: 0.0\n" - "}\n" - "pivot: PIVOT_W\n" - "text: \"3.52\"\n" - "font: \"/core/fonts/text.font\"\n" - "material: \"/builtins/fonts/label-df.material\"\n" - "" - position { - x: -25.0 - z: 0.1 - } - scale { - x: 0.7 - y: 0.7 - } -} diff --git a/examples/nested_entities/entity/player.lua b/examples/nested_entities/entity/player.lua deleted file mode 100644 index 4abfe29..0000000 --- a/examples/nested_entities/entity/player.lua +++ /dev/null @@ -1,19 +0,0 @@ ---@class entity -return { - transform = {}, - game_object = { - object_scheme = { - ["#label"] = true, - ["#sprite"] = true, - }, - }, - transform_border = { - border = vmath.vector4(-1920/2, 1080/2, 1920/2, -1080/2), - }, - text_game_timer = { - label_url = "#label", - }, - movement_controller = { - speed = 20 - } -} diff --git a/examples/nested_entities/nested_entities.collection b/examples/nested_entities/nested_entities.collection deleted file mode 100644 index 6d0a0db..0000000 --- a/examples/nested_entities/nested_entities.collection +++ /dev/null @@ -1,27 +0,0 @@ -name: "nested_entities" -instances { - id: "player" - prototype: "/examples/nested_entities/entity/player.go" - position { - x: 480.0 - y: 320.0 - } -} -instances { - id: "entity_camera" - prototype: "/core/system/camera/entity_camera.go" - position { - z: 15.0 - } -} -scale_along_z: 0 -embedded_instances { - id: "root" - children: "entity_camera" - children: "player" - data: "components {\n" - " id: \"nested_entities\"\n" - " component: \"/examples/nested_entities/nested_entities.script\"\n" - "}\n" - "" -} diff --git a/examples/nested_entities/nested_entities.script b/examples/nested_entities/nested_entities.script deleted file mode 100644 index 82d5455..0000000 --- a/examples/nested_entities/nested_entities.script +++ /dev/null @@ -1,28 +0,0 @@ -local decore = require("decore.decore") - -function init(self) - self.world = decore.world( - require("system.window_event.system_window_event").create_system(), - require("system.camera.system_camera").create(), - require("system.transform.system_transform").create(), - require("system.transform_border.system_transform_border").create(), - require("system.game_object.system_game_object").create(), - require("system.input.system_input").create_system(), - require("system.movement_controller.system_movement_controller").create(), - - require("examples.nested_entities.system_game_timer.system_game_timer").create_system(), - require("system.camera_debug_control.camera_debug_control").create_system() - ) - - decore.register_entity("player", require("examples.nested_entities.entity.player")) -end - - -function update(self, dt) - self.world:update(dt) -end - - -function on_input(self, action_id, action) - return decore.on_input(self.world, action_id, action) -end diff --git a/examples/nested_entities/system_game_timer/system_game_timer.lua b/examples/nested_entities/system_game_timer/system_game_timer.lua deleted file mode 100644 index 9dafc49..0000000 --- a/examples/nested_entities/system_game_timer/system_game_timer.lua +++ /dev/null @@ -1,47 +0,0 @@ -local decore = require("decore.decore") - ----@class entity ----@field text_game_timer component.text_game_timer|nil - ----@class entity.text_game_timer: entity ----@field text_game_timer component.text_game_timer ----@field game_object component.game_object - ----@class component.text_game_timer ----@field label_url string -decore.register_component("text_game_timer", { - label_url = "#label", -}) - ----@class system.text_game_timer: system ----@field entities entity.text_game_timer[] ----@field create_time number -local M = {} - -function M.create_system() - local system = decore.system(M, "text_game_timer", { "text_game_timer", "game_object" }) - system.create_time = socket.gettime() - return system -end - - ----@param entity entity.text_game_timer -function M:onAdd(entity) - entity.text_game_timer.label_url = hash(entity.text_game_timer.label_url) --[[@as string]] -end - - -function M:update(dt) - local current_time = socket.gettime() - local time_diff = current_time - self.create_time - - for index = 1, #self.entities do - local entity = self.entities[index] - local text_game_timer = entity.text_game_timer - local root_url = entity.game_object.object[text_game_timer.label_url] - label.set_text(root_url, string.format("%.2f", time_diff)) - end -end - - -return M diff --git a/resources/components.json b/resources/components.json deleted file mode 100644 index e0bfd8a..0000000 --- a/resources/components.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "pack_id": "core", - - "components": { - "id": null, - "pack_id": null, - "prefab_id": null, - "name": null, - - "movement_controller": { - "speed": 0, - "movement_x": 0, - "movement_y": 0 - } - } -} diff --git a/resources/entities.json b/resources/entities.json deleted file mode 100644 index c3ffbfa..0000000 --- a/resources/entities.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "pack_id": "core", - "entities": { - "player": { - "transform": {}, - "game_object": { - "factory_url": "/system/spawner#player" - }, - "movement_controller": { - "speed": 100 - } - }, - - "camera": { - "transform": {}, - "camera": {} - }, - - "bullet": { - "transform": { - "scale_x": 1.25, - "scale_y": 1.25 - }, - "game_object": { - "factory_url": "/spawner/spawner#bullet" - } - } - } -} diff --git a/system/camera/camera_entity.lua b/system/camera/camera_entity.lua deleted file mode 100644 index 04e64a8..0000000 --- a/system/camera/camera_entity.lua +++ /dev/null @@ -1,5 +0,0 @@ ----@return entity -return { - camera = {}, - transform = {}, -} diff --git a/system/camera/command_camera.lua b/system/camera/command_camera.lua deleted file mode 100644 index ae45527..0000000 --- a/system/camera/command_camera.lua +++ /dev/null @@ -1,47 +0,0 @@ ----@class world ----@field command_camera system.camera.command - ----@class system.camera.command ----@field camera system.camera @Current camera system -local M = {} - - ----@return system.camera.command -function M.create(camera_system) - return setmetatable({ camera = camera_system }, { __index = M }) -end - - ----@param entity entity ----@param time number? -function M:move_to_entity(entity, time) - time = time or 0.3 - self.camera:move_to(entity.transform.position_x, entity.transform.position_y, time, go.EASING_OUTSINE) -end - - ----@param power number ----@param time number -function M:shake(power, time) - self.camera.shake_power = power or 8 - self.camera.shake_time = time or 0.4 -end - - -function M:world_to_screen(x, y) - return self.camera:world_to_screen(x, y) -end - - -function M:screen_to_world(x, y) - return self.camera:screen_to_world(x, y) -end - - ----@return entity.camera -function M:get_current_camera() - return self.camera.camera -end - - -return M diff --git a/system/camera/component_camera.script b/system/camera/component_camera.script deleted file mode 100644 index 5722ba9..0000000 --- a/system/camera/component_camera.script +++ /dev/null @@ -1,5 +0,0 @@ -local component = require("decore.component") - -function init(self) - component.init("camera", {}) -end \ No newline at end of file diff --git a/system/camera/entity_camera.go b/system/camera/entity_camera.go deleted file mode 100644 index 144cd6d..0000000 --- a/system/camera/entity_camera.go +++ /dev/null @@ -1,48 +0,0 @@ -components { - id: "entity" - component: "/decore/entity.script" - properties { - id: "prefab_id" - value: "camera" - type: PROPERTY_TYPE_HASH - } - properties { - id: "size_x" - value: "1920.0" - type: PROPERTY_TYPE_NUMBER - } - properties { - id: "size_y" - value: "1080.0" - type: PROPERTY_TYPE_NUMBER - } -} -embedded_components { - id: "camera" - type: "camera" - data: "aspect_ratio: 1.0\n" - "fov: 0.7854\n" - "near_z: 0.01\n" - "far_z: 1000.0\n" - "orthographic_projection: 1\n" - "" -} -embedded_components { - id: "sprite" - type: "sprite" - data: "default_animation: \"empty\"\n" - "material: \"/builtins/materials/sprite.material\"\n" - "size {\n" - " x: 1920.0\n" - " y: 1080.0\n" - "}\n" - "size_mode: SIZE_MODE_MANUAL\n" - "textures {\n" - " sampler: \"texture_sampler\"\n" - " texture: \"/core/atlas/core.atlas\"\n" - "}\n" - "" - position { - z: -10.0 - } -} diff --git a/system/camera/system_camera.lua b/system/camera/system_camera.lua deleted file mode 100644 index 6720b94..0000000 --- a/system/camera/system_camera.lua +++ /dev/null @@ -1,347 +0,0 @@ -local decore = require("decore.decore") - -local command_camera = require("system.camera.command_camera") - -local TEMP_VECTOR = vmath.vector3() -local HASH_SIZE_X = hash("size.x") -local HASH_SIZE_Y = hash("size.y") -local HASH_SPRITE = hash("sprite") - ----@class entity ----@field camera component.camera|nil - ----@class entity.camera: entity ----@field camera component.camera ----@field transform component.transform - ----Add this component to camera entity, it will manage the camera visible size and position ----@class component.camera ----@field camera_url url ----@field follow_entity entity.transform|nil ----@field position_x number|nil ----@field position_y number|nil ----@field size_x number|nil ----@field size_y number|nil ----@field offset_x number|nil ----@field offset_y number|nil ----@field offset_size number|nil ----@field zoom number|nil -decore.register_component("camera", { - camera_url = "", -}) - ----@class system.camera.event ----@field entity entity.camera - ----@class system.camera: system ----@field entities (entity.camera)[] ----@field camera entity.camera|nil @Current camera entity ----@field camera_borders vector4|nil @Borders of camera visible area vmath.vector4(left, right, top, bottom). Camera will not move outside of these borders ----@field shake_power number|nil ----@field shake_time number|nil ----@field shake_max_time number|nil ----@field zoom number -local M = {} - -M.DEFAULT_SIZE = math.min(sys.get_config_int("display.width"), sys.get_config_int("display.height")) - ----@return system.camera -function M.create() - local system = decore.system(M, "camera", "camera") - - system.interval = 0.03 - system.camera = nil - system.camera_borders = nil - system.zoom = 1 - system.shake_power = 0 - system.shake_time = 0 - system.shake_max_time = 0 - - return system -end - - -function M:onAddToWorld() - self.world.command_camera = command_camera.create(self) -end - - -function M:postWrap() - self.world.event_bus:process("window_event", self.process_window_event, self) - self.world.event_bus:process("transform_event", self.process_transform_event, self) -end - - ----@param window_event system.window_event.event -function M:process_window_event(window_event) - if not self.camera then - return - end - - if window_event == window.WINDOW_EVENT_RESIZED then - self:update_camera_position(self.camera) - self:update_camera_zoom(self.camera) - end -end - - ----@param event system.transform.event -function M:process_transform_event(event) - if event.entity ~= self.camera then - return - end - - if event.is_position_changed then - self:update_camera_position(self.camera, event.animate_time, event.easing) - end - - if event.is_scale_changed or event.is_size_changed then - self:update_camera_zoom(self.camera, event.animate_time, event.easing) - end -end - - ----@param entity entity.camera -function M:onAdd(entity) - msg.post("@render:", "use_camera_projection") - self.camera = entity - self.is_camera_changed = true - - local camera_url = msg.url(entity.game_object.root) - camera_url.fragment = hash("camera") - entity.camera.camera_url = camera_url - - --camera.acquire_focus(entity.camera.camera_url) - - self:update_camera_position(self.camera) - self:update_camera_zoom(self.camera) -end - - ----@param entity entity.camera -function M:onRemove(entity) - if self.camera == entity then - --camera.release_focus(entity.camera.camera_url) - self.camera = nil - end -end - - -function M:update(dt) - if self.is_camera_changed then - self.world.event_bus:trigger("camera_event", self.camera) - self.is_camera_changed = false - end - - if self.shake_time > 0 then - self.shake_max_time = math.max(self.shake_max_time, self.shake_time) - - self.shake_time = self.shake_time - dt - if self.shake_time <= 0 then - self.shake_max_time = 0 - self.shake_power = 0 - self:shake(0) - else - local power = self.shake_power * (self.shake_time / self.shake_max_time) - self:shake(power) - end - end -end - - ----Move camera to the specified position ----@param position_x number ----@param position_y number ----@param animate_time number|nil ----@param easing userdata|nil -function M:move_to(position_x, position_y, animate_time, easing) - local entity = self.camera - if entity then - self.world.command_transform:set_position(entity, position_x, position_y) - if animate_time then - self.world.command_transform:set_animate_time(entity, animate_time, easing) - end - end -end - - ----Move camera to the specified size ----@param size_x number ----@param size_y number ----@param animate_time number|nil ----@param easing userdata|nil -function M:size_to(size_x, size_y, animate_time, easing) - local entity = self.camera - if entity then - self.world.command_transform:set_size(entity, size_x, size_y) - if animate_time then - self.world.command_transform:set_animate_time(entity, animate_time, easing) - end - end -end - - ----Move camera to the entity position ----@param entity entity.camera ----@param animate_time number|nil ----@param easing userdata|nil -function M:update_camera_position(entity, animate_time, easing) - TEMP_VECTOR.x = entity.transform.position_x - TEMP_VECTOR.y = entity.transform.position_y - TEMP_VECTOR.z = entity.transform.position_z - - -- Apply borders - local borders = self.camera_borders - if borders then - local size_x = entity.transform.size_x / 2 - local size_y = entity.transform.size_y / 2 - if TEMP_VECTOR.x - size_x < borders.x then - TEMP_VECTOR.x = borders.x + size_x - end - if TEMP_VECTOR.x + size_x > borders.y then - TEMP_VECTOR.x = borders.y - size_x - end - if TEMP_VECTOR.y + size_y > borders.z then - TEMP_VECTOR.y = borders.z - size_y - end - if TEMP_VECTOR.y - size_y < borders.w then - TEMP_VECTOR.y = borders.w + size_y - end - end - - if animate_time then - easing = easing or go.EASING_OUTSINE - go.animate(entity.game_object.root, "position", go.PLAYBACK_ONCE_FORWARD, TEMP_VECTOR, easing, animate_time) - else - go.set_position(TEMP_VECTOR, entity.game_object.root) - end -end - - ----Update camera zoom, so that camera fits the entity size ----@param entity entity.camera ----@param animate_time number|nil ----@param easing userdata|nil -function M:update_camera_zoom(entity, animate_time, easing) - local _, _, width, height = defos.get_view_size() - local camera_size_x = entity.transform.size_x * entity.transform.scale_x - local camera_size_y = entity.transform.size_y * entity.transform.scale_y - - local scale_x = width / camera_size_x - local scale_y = height / camera_size_y - self.zoom = math.min(scale_x, scale_y) - --self.zoom = math.max(scale_x, scale_y) - entity.camera.zoom = self.zoom - - if animate_time then - easing = easing or go.EASING_OUTSINE - go.animate(entity.camera.camera_url, "orthographic_zoom", go.PLAYBACK_ONCE_FORWARD, self.zoom, easing, animate_time) - else - go.set(entity.camera.camera_url, "orthographic_zoom", self.zoom) - end -end - - ----Convert from screen to world coordinates ----@param sx number Screen x ----@param sy number Screen y ----@param sz number Screen z ----@param window_width number Width of the window (use defos.get_view_size()) ----@param window_height number Height of the window (use defos.get_view_size()) ----@param projection matrix4 Camera/render projection (use go.get("#camera", "projection")) ----@param view vector4 Camera/render view (use go.get("#camera", "view")) ----@return number World x ----@return number World y ----@return number World z -local function screen_to_world(sx, sy, sz, window_width, window_height, projection, view) - local inv = vmath.inv(projection * view) - sx = (2 * sx / window_width) - 1 - sy = (2 * sy / window_height) - 1 - sz = (2 * sz) - 1 - local wx = sx * inv.m00 + sy * inv.m01 + sz * inv.m02 + inv.m03 - local wy = sx * inv.m10 + sy * inv.m11 + sz * inv.m12 + inv.m13 - local wz = sx * inv.m20 + sy * inv.m21 + sz * inv.m22 + inv.m23 - return wx, wy, wz -end - - ----Convert from world to screen coordinates ----@param wx number World x ----@param wy number World y ----@param wz number World z ----@param window_width number Width of the window (use defos.get_view_size()) ----@param window_height number Height of the window (use defos.get_view_size()) ----@param projection matrix4 Camera/render projection (use go.get("#camera", "projection")) ----@param view vector4 Camera/render view (use go.get("#camera", "view")) ----@return number Screen x ----@return number Screen y ----@return number Screen z -local function world_to_screen(wx, wy, wz, window_width, window_height, projection, view) - local p = vmath.matrix4() * vmath.vector4(wx, wy, wz, 1) - p = projection * view * p - p = p / p.w - local sx = ((p.x + 1) / 2) * window_width - local sy = ((p.y + 1) / 2) * window_height - local sz = ((p.z + 1) / 2) - return sx, sy, sz -end - - ----Convert from screen to world coordinates ----@param screen_x number Screen x ----@param screen_y number Screen y ----@return number, number -function M:screen_to_world(screen_x, screen_y) - if not self.camera then - return screen_x, screen_y - end - - local width, height = window.get_size() - local projection = go.get(self.camera.camera.camera_url, "projection") - local view = go.get(self.camera.camera.camera_url, "view") - - local x, y, _ = screen_to_world(screen_x, screen_y, 0, width, height, projection, view) - return x, y -end - - ----Convert from world to screen coordinates ----@param world_x number World x ----@param world_y number World y ----@return number, number -function M:world_to_screen(world_x, world_y) - if not self.camera then - return world_x, world_y - end - - local width, height = window.get_size() - local projection = go.get(self.camera.camera.camera_url, "projection") - local view = go.get(self.camera.camera.camera_url, "view") - - local x, y, _ = world_to_screen(world_x, world_y, 0, width, height, projection, view) - return x, y -end - - -function M:shake(power) - if not self.camera then - return - end - - local obj_url = self.camera.game_object.root - - local power_sqr = power * power - local dx = math.random(-power_sqr, power_sqr) - local dy = math.random(-power_sqr, power_sqr) - local x = self.camera.transform.position_x + dx - local y = self.camera.transform.position_y + dy - TEMP_VECTOR.x = x - TEMP_VECTOR.y = y - TEMP_VECTOR.z = self.camera.transform.position_z - --go.set_position(TEMP_VECTOR, obj_url) - - go.animate(obj_url, "position", go.PLAYBACK_ONCE_FORWARD,TEMP_VECTOR, go.EASING_OUTSINE, 0.03) -end - - -return M diff --git a/system/camera_debug_control/camera_debug_control.lua b/system/camera_debug_control/camera_debug_control.lua deleted file mode 100644 index 1f50971..0000000 --- a/system/camera_debug_control/camera_debug_control.lua +++ /dev/null @@ -1,91 +0,0 @@ -local decore = require("decore.decore") - ----@class system.camera_debug_control: system ----@field is_ctrl boolean -local M = {} - -local HASH_CMD = hash("key_lsuper") -local HASH_CTRL = hash("key_lctrl") - - ----@return system.camera_debug_control -function M.create_system() - local system = decore.system(M, "camera_debug_control") - system.is_ctrl = false - system.is_hold = false - - return system -end - - -function M:postWrap() - self.world.event_bus:process("input_event", self.process_input_event, self) -end - - ----@param input_event system.input.event -function M:process_input_event(input_event) - -- Process mod key - local action_id = input_event.action_id - local is_ctrl = (action_id == HASH_CTRL or action_id == HASH_CMD) - if is_ctrl and input_event.pressed then - self.is_ctrl = true - elseif is_ctrl and input_event.released then - self.is_ctrl = false - end - - -- process drag camera - if self.is_ctrl then - self:process_drag_camera(input_event) - end -end - - ----@param input_event system.input.event -function M:process_drag_camera(input_event) - local action_id = input_event.action_id - if action_id == hash("touch") then - if input_event.pressed then - self.is_hold = true - self.start_x = input_event.x - self.start_y = input_event.y - elseif input_event.released then - self.is_hold = false - end - end - - if self.is_hold and action_id == nil then - local entity = self.world.command_camera:get_current_camera() - local zoom = entity.camera.zoom - local koef = 1 / zoom - self.world.command_transform:add_position(entity, -input_event.screen_dx * koef, -input_event.screen_dy * koef) - end - - if self.is_ctrl then - -- check wheel - if input_event.action_id == hash("mouse_wheel_down") then - local entity = self.world.command_camera:get_current_camera() - --local new_size_x = entity.transform.size_x * 1.1 - --local new_size_y = entity.transform.size_y * 1.1 - --self.world.command_transform:set_size(entity, new_size_x, new_size_y) - - local new_scale_x = entity.transform.scale_x * 1.1 - local new_scale_y = entity.transform.scale_y * 1.1 - self.world.command_transform:set_scale(entity, new_scale_x, new_scale_y) - self.world.command_transform:set_animate_time(entity, 0.2, go.EASING_OUTSINE) - end - if input_event.action_id == hash("mouse_wheel_up") then - local entity = self.world.command_camera:get_current_camera() - --local new_size_x = entity.transform.size_x * 0.9 - --local new_size_y = entity.transform.size_y * 0.9 - --self.world.command_transform:set_size(entity, new_size_x, new_size_y) - local new_scale_x = entity.transform.scale_x * 0.9 - local new_scale_y = entity.transform.scale_y * 0.9 - self.world.command_transform:set_scale(entity, new_scale_x, new_scale_y) - self.world.command_transform:set_animate_time(entity, 0.2, go.EASING_OUTSINE) - end - end -end - - -return M diff --git a/system/collision/annotations_collision.lua b/system/collision/annotations_collision.lua deleted file mode 100644 index 2076392..0000000 --- a/system/collision/annotations_collision.lua +++ /dev/null @@ -1,32 +0,0 @@ ----@class physics.collision.object ----@field id hash @Id of the object ----@field group hash @Group of the object ----@field position vector3|nil @Position of the object ----@field relative_velocity vector3|nil @Relative velocity of the object ----@field mass number|nil @Mass of the object ----@field normal vector3|nil @Normal of the object - ----@class physics.collision.contact_point_event ----@field a physics.collision.object ----@field b physics.collision.object ----@field applied_impulse number @Applied impulse ----@field distance number @Distance - ----@class physics.collision.collision_event ----@field a physics.collision.object ----@field b physics.collision.object - ----@class physics.collision.ray_cast_response ----@field requst_id number @Request id ----@field group hash @Group of the object ----@field position vector3 @Position of the object ----@field normal vector3 @Normal of the object ----@field fraction number @Fraction of the object - ----@class physics.collision.ray_cast_missed ----@field requst_id number @Request id - ----@class physics.collision.trigger_event ----@field a physics.collision.object ----@field b physics.collision.object ----@field enter boolean @True if the trigger interaction is entering, false if it is exiting diff --git a/system/collision/component_collision.script b/system/collision/component_collision.script deleted file mode 100644 index 31a4d93..0000000 --- a/system/collision/component_collision.script +++ /dev/null @@ -1,15 +0,0 @@ -local component = require("decore.component") - -local HASH_EMPTY = hash("") - -go.property("is_remove", false) -go.property("trigger_event", hash("")) -go.property("is_trigger_once", true) - -function init(self) - component.init("collision", { - is_remove = self.is_remove, - trigger_event = self.trigger_event ~= HASH_EMPTY and self.trigger_event or nil, - is_trigger_once = self.is_trigger_once - }) -end \ No newline at end of file diff --git a/system/collision/system_collision.lua b/system/collision/system_collision.lua deleted file mode 100644 index ccf609f..0000000 --- a/system/collision/system_collision.lua +++ /dev/null @@ -1,211 +0,0 @@ -local decore = require("decore.decore") - ----@class entity ----@field collision component.collision|nil - ----@class entity.collision: entity ----@field collision component.collision - ----If true, will get collision events. ----@class component.collision ----@field is_remove boolean|nil ----@field trigger_event hash|string|nil ----@field is_trigger_once boolean|nil -decore.register_component("collision", {}) - ----@class system.collision.event ----@field entity entity ----@field other entity|nil ----@field trigger_event physics.collision.trigger_event|nil ----@field collision_event physics.collision.collision_event|nil ----@field contact_point_event physics.collision.contact_point_event|nil - ----@class system.collision: system ----@field entities entity.collision[] ----@field root_to_entity table ----@field collided_this_frame table -local M = {} - - ----@return system.collision -function M.create_system() - local system = decore.system(M, "collision", { "collision", "game_object" }) - - system.root_to_entity = {} - system.collided_this_frame = {} - - return system -end - - -function M:onAddToWorld() - physics.set_listener(function(_, event_id, event) - M.physics_world_listener(self, event_id, event) - end) -end - - -function M:onRemoveFromWorld() - physics.set_listener(nil) -end - - ----@param entity entity -function M:onAdd(entity) - local root = entity.game_object.root - if root then - self.root_to_entity[root] = entity - end -end - - ----@param entity entity -function M:onRemove(entity) - local root = entity.game_object.root - if root then - self.root_to_entity[root] = nil - end -end - - -function M:preWrap() - self.collided_this_frame = {} -end - - -local CONTACT_POINT_EVENT = hash("contact_point_event") -local COLLISION_EVENT = hash("collision_event") -local TRIGGER_EVENT = hash("trigger_event") -local RAY_CAST_RESPONSE = hash("ray_cast_response") -local RAY_CAST_MISSED = hash("ray_cast_missed") - ----@param self system.collision ----@param event hash @Event type ----@param data any -function M.physics_world_listener(self, event, data) - if event == CONTACT_POINT_EVENT then - self:handle_contact_point_event(data) - elseif event == COLLISION_EVENT then - self:handle_collision_event(data) - elseif event == TRIGGER_EVENT then - self:handle_trigger_event(data) - elseif event == RAY_CAST_RESPONSE then - -- Handle raycast hit data - elseif event == RAY_CAST_MISSED then - -- Handle raycast miss data - end -end - - ----@param self system.collision ----@param entity_source entity|nil ----@param entity_target entity|nil ----@param event_data physics.collision.contact_point_event|physics.collision.trigger_event|physics.collision.collision_event ----@param event_type string @"contact_point_event"|"trigger_event"|"collision_event" -local function handle_collision_event(self, entity_source, entity_target, event_data, event_type) - --pprint("======Handle Collision Event") - --pprint("Entity Source: ", entity_source) - --pprint("Entity Target: ", entity_target) - --pprint("Event Data: ", event_data) - --pprint("Event Type: ", event_type) - - if entity_source and entity_source.collision then - local collision = entity_source.collision ---@type component.collision - - if collision.is_remove then - self.world:removeEntity(entity_source) - end - - if collision.trigger_event and event_type == "trigger_event" then - self.world.event_bus:trigger(collision.trigger_event, entity_source) - if collision.is_trigger_once then - collision.trigger_event = nil - end - end - - ---@type system.collision.event - local collision_event = { - entity = entity_source, - other = entity_target, - [event_type] = event_data - } - self.world.event_bus:trigger("collision_event", collision_event) - end - - if entity_target and entity_target.collision then - local collision = entity_target.collision ---@type component.collision - - if collision.is_remove then - self.world:removeEntity(entity_target) - end - - if collision.trigger_event and event_type == "trigger_event" then - self.world.event_bus:trigger(collision.trigger_event, entity_target) - if collision.is_trigger_once then - collision.trigger_event = nil - end - end - - ---@type system.collision.event - local collision_event = { - entity = entity_target, - other = entity_source, - [event_type] = event_data - } - self.world.event_bus:trigger("collision_event", collision_event) - end -end - - ----@param event_data physics.collision.contact_point_event -function M:handle_contact_point_event(event_data) - -- Handle contact point data - local entity_source = self.root_to_entity[event_data.a.id] - local entity_target = self.root_to_entity[event_data.b.id] - handle_collision_event(self, entity_source, entity_target, event_data, "contact_point_event") -end - - ----@param event_data physics.collision.trigger_event -function M:handle_trigger_event(event_data) - -- Handle trigger interaction data - local entity_source = self.root_to_entity[event_data.a.id] - local entity_target = self.root_to_entity[event_data.b.id] - handle_collision_event(self, entity_source, entity_target, event_data, "trigger_event") -end - - ----@param event_data physics.collision.collision_event -function M:handle_collision_event(event_data) - local entity_source = self.root_to_entity[event_data.a.id] - local entity_target = self.root_to_entity[event_data.b.id] - - local is_source_collided = self.collided_this_frame[entity_source] and self.collided_this_frame[entity_source][entity_target] - if entity_source and entity_source.collision and not is_source_collided then - handle_collision_event(self, entity_source, entity_target, event_data, "collision_event") - - if entity_target then - self.collided_this_frame[entity_source] = self.collided_this_frame[entity_source] or {} - self.collided_this_frame[entity_source][entity_target] = true - - self.collided_this_frame[entity_target] = self.collided_this_frame[entity_target] or {} - self.collided_this_frame[entity_target][entity_source] = true - end - end - - local is_target_collided = self.collided_this_frame[entity_target] and self.collided_this_frame[entity_target][entity_source] - if entity_target and entity_target.collision and not is_target_collided then - handle_collision_event(self, entity_target, entity_source, event_data, "collision_event") - - if entity_source then - self.collided_this_frame[entity_target] = self.collided_this_frame[entity_target] or {} - self.collided_this_frame[entity_target][entity_source] = true - - self.collided_this_frame[entity_source] = self.collided_this_frame[entity_source] or {} - self.collided_this_frame[entity_source][entity_target] = true - end - end -end - - -return M diff --git a/system/color/system_color.lua b/system/color/system_color.lua deleted file mode 100644 index 7674e4b..0000000 --- a/system/color/system_color.lua +++ /dev/null @@ -1,106 +0,0 @@ -local color = require("druid.color") -local decore = require("decore.decore") - ----@class entity ----@field color component.color|nil - ----@class entity.color: entity ----@field color component.color ----@field game_object component.game_object - ----@class component.color ----@field color_id string|nil ----@field color vector4 # color of the entity, constructor can be string ----@field sprites string # "/root#sprite,/root#sprite2" ----@field random_color vector4[] # two colors for lerp -decore.register_component("color", { - color = vmath.vector4(1), -}) - ----@class system.color.event ----@field entity entity ----@field color vector4 - ----@class system.color: system ----@field entities entity.color[] -local M = {} - - ----@return system.color -function M.create_system() - return decore.system(M, "color", { "color", "game_object" }) -end - - ----@param entity entity.color -function M:onAdd(entity) - local random_color = entity.color.random_color - if random_color then - entity.color.color = color.lerp(math.random(), random_color[1], random_color[2]) - end - - local color_data = entity.color.color - if type(color_data) == "string" then - entity.color.color = color.hex2vector4(color_data) - end - - if entity.color.color then - self:apply_color(entity, entity.color.color, entity.color.sprites) - end -end - - ----@param entity entity.color ----@param color vector4|nil ----@param sprites string|nil "/root#sprite,/root#sprite2" -function M:apply_color(entity, color, sprites) - if not color or not sprites then - return - end - - local splitted_sprites = M.split(sprites, ",") - for index = 1, #splitted_sprites do - local target = splitted_sprites[index] - - local splitted = M.split(target, "#") - local object_id, component_id = splitted[1], splitted[2] or "sprite" - - -- If target starts with #, then it's a component id - if string.sub(target, 1, 1) == "#" then - object_id = nil - component_id = splitted[1] - end - - local object = entity.game_object.object - if object_id then - if object and object[object_id] then - local sprite_url = msg.url(nil, object[object_id], component_id) - go.set(sprite_url, "color", color) -- vertex attribute - end - else - local root = entity.game_object.root - if root then - local sprite_url = msg.url(nil, root, component_id) - go.set(sprite_url, "color", color) -- vertex attribute - end - end - end -end - - ----Split string by separator ----@param s string ----@param sep string -function M.split(s, sep) - sep = sep or "%s" - local t = {} - local i = 1 - for str in string.gmatch(s, "([^" .. sep .. "]+)") do - t[i] = str - i = i + 1 - end - return t -end - - -return M diff --git a/system/debug/command_debug.lua b/system/debug/command_debug.lua deleted file mode 100644 index a708403..0000000 --- a/system/debug/command_debug.lua +++ /dev/null @@ -1,52 +0,0 @@ ----@class world ----@field command_debug system.debug.command - ----@class system.debug.command ----@field debug system.debug -local M = {} - - ----@param debug system.debug ----@return system.debug.command -function M.create(debug) - return setmetatable({ debug = debug }, { __index = M }) -end - - -function M:toggle_profiler() - for _, e in ipairs(self.debug.entities) do - self.debug:toggle_profiler(e) - end -end - - -function M:toggle_memory_record() - for _, e in ipairs(self.debug.entities) do - self.debug:toggle_memory_record(e) - end -end - - -function M:restart() - if html5 then - html5.run('document.location.reload();') - else - msg.post("@system:", "reboot") - end -end - - -function M:reset_game() - self.debug:reset_game() -end - -function M:load_slot(slot) - self.debug:load_slot(slot) -end - -function M:save_slot(slot) - self.debug:save_slot(slot) -end - - -return M diff --git a/system/debug/debug.go b/system/debug/debug.go deleted file mode 100644 index 7781b3f..0000000 --- a/system/debug/debug.go +++ /dev/null @@ -1,9 +0,0 @@ -components { - id: "entity" - component: "/decore/entity.script" - properties { - id: "prefab_id" - value: "debug" - type: PROPERTY_TYPE_HASH - } -} diff --git a/system/debug/entity_debug.lua b/system/debug/entity_debug.lua deleted file mode 100644 index 94dad7b..0000000 --- a/system/debug/entity_debug.lua +++ /dev/null @@ -1,13 +0,0 @@ ----@return entity.debug -return { - debug = {}, - on_key_released = { - key_p = { "command_debug", "toggle_profiler", true }, - key_1 = { "command_debug", "save_slot", "save_debug.json" }, - key_2 = { "command_debug", "load_slot", "save_debug.json" }, - key_r = { "command_debug", "restart", true }, - key_n = { "command_debug", "reset_game", true }, - key_m = { "command_debug", "toggle_memory_record" }, - key_i = { "command_debug", "inspect", true }, - } -} diff --git a/system/debug/system_debug.lua b/system/debug/system_debug.lua deleted file mode 100644 index 216f205..0000000 --- a/system/debug/system_debug.lua +++ /dev/null @@ -1,114 +0,0 @@ -local log = require("log.log") -local saver = require("saver.saver") -local decore = require("decore.decore") - -local logger = log.get_logger("debug") - -local command_debug = require("system.debug.command_debug") - ----@class entity ----@field debug component.debug|nil - ----@class entity.debug: entity ----@field debug component.debug - ----@class component.debug ----@field is_profiler_active boolean ----@field profiler_mode userdata|nil ----@field timer_memory_record number|nil -decore.register_component("debug", { - is_profiler_active = false, - profiler_mode = nil, - timer_memory_record = nil, -}) - ----@class system.debug: system ----@field entities entity.debug[] -local M = {} - - ----@return system.debug -function M.create_system() - return decore.system(M, "debug", "debug") -end - - -function M:onAddToWorld() - self.world.command_debug = command_debug.create(self) -end - - ----@param entity entity.debug -function M:toggle_profiler(entity) - local d = entity.debug - - if not d.profiler_mode then - d.profiler_mode = profiler.VIEW_MODE_MINIMIZED - profiler.enable_ui(true) - profiler.set_ui_view_mode(d.profiler_mode) - elseif d.profiler_mode == profiler.VIEW_MODE_MINIMIZED then - d.profiler_mode = profiler.VIEW_MODE_FULL - profiler.enable_ui(true) - profiler.set_ui_view_mode(d.profiler_mode) - else - profiler.enable_ui(false) - d.profiler_mode = nil - end - - logger:info("Profiler is active: " .. tostring(d.is_profiler_active)) -end - - ----@param entity entity.debug -function M:toggle_memory_record(entity) - local d = entity.debug - - if d.timer_memory_record then - timer.cancel(d.timer_memory_record) - d.timer_memory_record = nil - logger:info("Memory record stopped") - collectgarbage("restart") - collectgarbage("collect") - else - collectgarbage("collect") - collectgarbage("stop") - local memory = collectgarbage("count") - d.timer_memory_record = timer.delay(1, true, function() - local new_memory = collectgarbage("count") - logger:info("Memory: " .. new_memory - memory) - memory = new_memory - end) - logger:info("Memory record started") - end -end - - -function M:reset_game() - logger:debug("Game reset") - saver.delete_game_state() - sys.reboot() -end - - -function M:load_slot(slot) - logger:debug("Game loaded from slot: " .. slot) - sys.reboot("--config=saver.save_name=" .. slot, "--config=saver.autosave_timer=0") -end - - -function M:save_slot(slot) - saver.save_game_state(slot) - logger:debug("Game saved to slot: " .. slot) -end - - -function M:restart() - if html5 then - html5.run('document.location.reload();') - else - msg.post("@system:", "reboot") - end -end - - -return M diff --git a/system/debug_draw/command_debug_draw.lua b/system/debug_draw/command_debug_draw.lua deleted file mode 100644 index fe26b06..0000000 --- a/system/debug_draw/command_debug_draw.lua +++ /dev/null @@ -1,29 +0,0 @@ ----@class world ----@field command_debug_draw system.debug_draw.command - ----@class system.debug_draw.command ----@field debug_draw system.debug_draw -local M = {} - - ----@return system.debug_draw.command -function M.create(debug_draw) - return setmetatable({ debug_draw = debug_draw }, { __index = M }) -end - - -function M:draw_rectangle(x, y, width, height, color) - self.debug_draw:draw_rectangle(x, y, width, height, color) -end - -function M:draw_line(x1, y1, x2, y2, color) - self.debug_draw:draw_line(x1, y1, x2, y2, color) -end - - -function M:draw_text(x, y, text) - self.debug_draw:draw_text(x, y, text) -end - - -return M diff --git a/system/debug_draw/component_debug_draw.script b/system/debug_draw/component_debug_draw.script deleted file mode 100644 index e944951..0000000 --- a/system/debug_draw/component_debug_draw.script +++ /dev/null @@ -1,8 +0,0 @@ -local component = require("decore.component") -go.property("sprite_url", hash("#sprite")) - -function init(self) - component.init("debug_draw", { - sprite_url = self.sprite_url - }) -end \ No newline at end of file diff --git a/system/debug_draw/debug_draw.atlas b/system/debug_draw/debug_draw.atlas deleted file mode 100644 index 62e17cf..0000000 --- a/system/debug_draw/debug_draw.atlas +++ /dev/null @@ -1,4 +0,0 @@ -images { - image: "/assets/images/empty.png" -} -inner_padding: 1018 diff --git a/system/debug_draw/system_debug_draw.lua b/system/debug_draw/system_debug_draw.lua deleted file mode 100644 index 438628a..0000000 --- a/system/debug_draw/system_debug_draw.lua +++ /dev/null @@ -1,163 +0,0 @@ -local palette = require("druid.color") -local decore = require("decore.decore") -local command_debug_draw = require("system.debug_draw.command_debug_draw") - ----@class entity ----@field debug_draw component.debug_draw|nil - ----@class entity.debug_draw: entity ----@field debug_draw component.debug_draw ----@field game_object component.game_object - ----@class component.debug_draw -decore.register_component("debug_draw", {}) - ----@class system.debug_draw: system ----@field buffer table ----@field header table ----@field entities entity.debug_draw[] ----@field is_dirty boolean -local M = { - --interval = 0.2 -} - -local HASH_DRAW_TEXT = hash("draw_text") -local MSG_DRAW_TEXT = { - text = "", - position = vmath.vector3(), -} -local DEFAULT_COLOR = palette.hex2vector4("#FF6430") -local SIZE = 1024 - ----@return system.debug_draw -function M.create_system() - local system = decore.system(M, "debug_draw", { "debug_draw" }) - - system.buffer = { - buffer = buffer.create(SIZE * SIZE, {{ - name = hash("my_buffer"), - type = buffer.VALUE_TYPE_UINT8, - count = 4 -- same as channels - }}), - width = SIZE, - height = SIZE, - channels = 4, - premultiply_alpha = false - } - - system.header = { - width = SIZE, - height = SIZE, - type = graphics.TEXTURE_TYPE_2D, - format = graphics.TEXTURE_FORMAT_RGBA, - } - - return system -end - - -function M:onAddToWorld() - self.is_dirty = false - self.world.command_debug_draw = command_debug_draw.create(self) -end - - ----@param x number center ----@param y number center ----@param width number ----@param height number ----@param color vector4 -function M:draw_rectangle(x, y, width, height, color) - local x1, y1 = self:convert_to_texture(x, y) - color = color or DEFAULT_COLOR - - local x2, y2 = self:convert_to_texture(x + width, y + height) - width = (x2 - x1) - height = (y2 - y1) - - --drawpixels.rect(self.buffer, x1, y1, width, height, color.x * 255, color.y * 255, color.z * 255, color.w * 255) - - x1 = x1 - width / 2 - y1 = y1 - height / 2 - - drawpixels.line(self.buffer, x1, y1, x1 + width, y1, color.x * 255, color.y * 255, color.z * 255, color.w * 255, false, 2) - drawpixels.line(self.buffer, x1 + width, y1, x1 + width, y1 + height, color.x * 255, color.y * 255, color.z * 255, color.w * 255, false, 2) - drawpixels.line(self.buffer, x1 + width, y1 + height, x1, y1 + height, color.x * 255, color.y * 255, color.z * 255, color.w * 255, false, 2) - drawpixels.line(self.buffer, x1, y1 + height, x1, y1, color.x * 255, color.y * 255, color.z * 255, color.w * 255, false, 2) - - self.is_dirty = true -end - - -function M:draw_text(x, y, text, color) - self.is_dirty = true - - local x1, y1 = self.world.command_camera:world_to_screen(x, y) - MSG_DRAW_TEXT.position.x = x1 - MSG_DRAW_TEXT.position.y = y1 - MSG_DRAW_TEXT.text = text - - -- Still are best way to draw text? I can't find any other way, somehow with label factories? or gui? - -- Probably GUI also can replace draw pixels to use just nodes? sounds good - msg.post("@render:", HASH_DRAW_TEXT, MSG_DRAW_TEXT) -end - - -function M:draw_line(x1, y1, x2, y2, color) - x1, y1 = self:convert_to_texture(x1, y1) - x2, y2 = self:convert_to_texture(x2, y2) - color = color or DEFAULT_COLOR - drawpixels.line(self.buffer, x1, y1, x2, y2, color.x * 255, color.y * 255, color.z * 255, color.w * 255, false, 2) - self.is_dirty = true -end - - -function M:update() - if not self.is_dirty then - if not self.is_cleared then - self.is_cleared = true - else - return - end - end - - local camera = self.world.command_camera:get_current_camera() - if not camera then - return - end - - local sprite_url = msg.url(nil, camera.game_object.root, "sprite") - local texture = go.get(sprite_url, "texture0") - - resource.set_texture(texture, self.header, self.buffer.buffer) - - -- Too slow! how update and clear faster? - drawpixels.fill(self.buffer, 0, 0, 0, 0) - - if self.is_dirty then - self.is_cleared = false - self.is_dirty = false - end -end - - -function M:convert_to_texture(x, y) - local camera = self.world.command_camera:get_current_camera() - local transform = camera.transform - local scale = transform.scale_x - local dx = transform.position_x - local dy = transform.position_y - - -- Adjust zoom and move camera - x = (x - dx) / scale + 960 - y = (y - dy) / scale + 540 - - -- World to texture - x = x * SIZE / 1920 - y = y * SIZE / 1080 - - return x, y -end - - -return M diff --git a/system/debug_draw_transform/system_debug_draw_transform.lua b/system/debug_draw_transform/system_debug_draw_transform.lua deleted file mode 100644 index ab35b44..0000000 --- a/system/debug_draw_transform/system_debug_draw_transform.lua +++ /dev/null @@ -1,57 +0,0 @@ -local decore = require("decore.decore") - ----@class entity ----@field debug_draw_transform boolean|nil - ----@class entity.debug_draw_transform: entity ----@field debug_draw_transform boolean ----@field transform component.transform - ----@class component.debug_draw_transform: boolean -decore.register_component("debug_draw_transform", false) - ----@class system.debug_draw_transform: system ----@field entities entity.debug_draw_transform[] ----@field is_draw_rectangle boolean -local M = {} - - ----@return system.debug_draw_transform -function M.create_system() - return decore.system(M, "debug_draw_transform", { "debug_draw_transform", "transform" }) -end - - -function M:onAddToWorld() - self.is_draw_rectangle = true -end - - ----@param dt number -function M:update(dt) - if not self.is_draw_rectangle then - return - end - - for index = 1, #self.entities do - local entity = self.entities[index] - if entity.debug_draw_transform then - local t = entity.transform - self.world.command_debug_draw:draw_rectangle( - t.position_x, - t.position_y, - t.size_x * t.scale_x, - t.size_y * t.scale_y - ) - - --self.world.command_debug_draw:draw_text( - -- t.position_x, - -- t.position_y + t.size_y, - -- self.entities[index].prefab_id - --) - end - end -end - - -return M diff --git a/system/follow_cursor/system_follow_cursor.lua b/system/follow_cursor/system_follow_cursor.lua deleted file mode 100644 index b6808d6..0000000 --- a/system/follow_cursor/system_follow_cursor.lua +++ /dev/null @@ -1,43 +0,0 @@ -local decore = require("decore.decore") - ----@class entity ----@field follow_cursor boolean|nil - ----@class entity.follow_cursor: entity ----@field follow_cursor boolean - -decore.register_component("follow_cursor", false) - ----@class system.follow_cursor: system ----@field entities entity.follow_cursor[] -local M = {} - - ----@return system.follow_cursor -function M.create_system() - return decore.system(M, "follow_cursor", { "follow_cursor", "transform" }) -end - - -function M:postWrap() - self.world.event_bus:process("input_event", self.process_input, self) -end - - ----@param action action -function M:process_input(action) - if #self.entities == 0 then - return - end - - local x, y = self.world.command_camera:screen_to_world(action.screen_x, action.screen_y) - for index = 1, #self.entities do - local entity = self.entities[index] - self.world.command_transform:set_position(entity, x, y) - end -end - - - - -return M diff --git a/system/fsm/command_fsm.lua b/system/fsm/command_fsm.lua deleted file mode 100644 index 6261e5b..0000000 --- a/system/fsm/command_fsm.lua +++ /dev/null @@ -1,33 +0,0 @@ ----@class world ----@field command_fsm system.fsm.command - ----@class system.fsm.command ----@field fsm system.fsm -local M = {} - - ----@return system.fsm.command -function M.create(fsm) - return setmetatable({ fsm = fsm }, { __index = M }) -end - - ----@param entity entity ----@param event string -function M:trigger(entity, event) - assert(entity.fsm, "Entity does not have a fsm component.") - ---@cast entity entity.fsm - self.fsm:trigger(entity, event) -end - - ----@param entity entity.fsm ----@return string state Current state of the entity. -function M:get_state(entity) - assert(entity.fsm, "Entity does not have a fsm component.") - ---@cast entity entity.fsm - return entity.fsm.state -end - - -return M diff --git a/system/fsm/system_fsm.lua b/system/fsm/system_fsm.lua deleted file mode 100644 index 5320f78..0000000 --- a/system/fsm/system_fsm.lua +++ /dev/null @@ -1,78 +0,0 @@ -local decore = require("decore.decore") -local command_fsm = require("system.fsm.command_fsm") - -local logger = decore.get_logger("system.fsm") - ----@class entity ----@field fsm component.fsm|nil - ----@class entity.fsm: entity ----@field fsm component.fsm - ----@class component.fsm ----@field state string|nil ----@field events table>|nil -decore.register_component("fsm") - ----@class system.fsm.event ----@field entity entity ----@field event string ----@field state_before string ----@field state_new string - ----@class system.fsm: system ----@field entities entity.fsm[] -local M = {} - - ----@return system.fsm -function M.create_system() - return decore.system(M, "fsm", "fsm") -end - - -function M:onAddToWorld() - self.world.command_fsm = command_fsm.create(self) -end - - ----@param entity entity.fsm ----@param event string -function M:trigger(entity, event) - local next_state = self:get_next_state(entity, event) - if next_state then - local state_before = entity.fsm.state - entity.fsm.state = next_state - - self.world.event_bus:trigger("fsm_event", { - entity = entity, - event = event, - state_before = state_before, - state_new = next_state, - }) - - logger:info("FSM event triggered", { - entity = entity, - event = event, - state_before = state_before, - state_new = next_state, - }) - end -end - - ----Return next state if event will be triggered. ----@param entity entity.fsm ----@param event string ----@return string|nil next_state Next state if event will be triggered. Nil if event is not allowed. -function M:get_next_state(entity, event) - local events = entity.fsm.events[event] - if events then - return events[entity.fsm.state] or events["*"] or nil - end - - return nil -end - - -return M diff --git a/system/fsm/test_fsm.lua b/system/fsm/test_fsm.lua deleted file mode 100644 index 17866a1..0000000 --- a/system/fsm/test_fsm.lua +++ /dev/null @@ -1,74 +0,0 @@ -return function() - describe("System FSM", function() - local decore ---@type decore - local system_fsm ---@type system.fsm - local world ---@type world - - before(function() - decore = require("decore.decore") - system_fsm = require("system.fsm.system_fsm") - world = decore.new_world() - world:add(system_fsm.create_system()) - end) - - local get_entity = function() - return decore.create({ fsm = { - state = "idle", - events = { - ["walk"] = { - ["idle"] = "walk", - }, - ["die"] = { - ["*"] = "dead", - }, - ["idle"] = { - ["walk"] = "idle", - } - } - }}) - end - - it("Should init system", function() - local entity = world:add(get_entity()) - world:refresh() - - assert(entity.fsm.state == "idle") - end) - - it("Should transit over states", function() - local entity = world:add(get_entity()) - world:refresh() - - world.command_fsm:trigger(entity, "walk") - assert(entity.fsm.state == "walk") - end) - - it("Should not transit over non existing states", function() - local entity = world:add(get_entity()) - world:refresh() - - world.command_fsm:trigger(entity, "non_exists") - assert(entity.fsm.state == "idle") - end) - - it("Should trigger fsm event on state change", function() - local entity = world:add(get_entity()) - world:refresh() - - world.command_fsm:trigger(entity, "walk") - assert(world.event_bus:get_stash("fsm_event")) - assert(#world.event_bus:get_stash("fsm_event") == 1) - local event = world.event_bus:get_stash("fsm_event")[1] - assert(event.entity == entity) - assert(event.event == "walk") - end) - - it("Should able to use wildcards in transitions", function() - local entity = world:add(get_entity()) - world:refresh() - - world.command_fsm:trigger(entity, "die") - assert(entity.fsm.state == "dead") - end) - end) -end diff --git a/system/game_object/command_game_object.lua b/system/game_object/command_game_object.lua deleted file mode 100644 index 1b52443..0000000 --- a/system/game_object/command_game_object.lua +++ /dev/null @@ -1,38 +0,0 @@ ----@class world ----@field command_game_object system.game_object.command - ----@class system.game_object.command ----@field game_object system.game_object -local M = {} - - ----@return system.game_object.command -function M.create(game_object) - return setmetatable({ game_object = game_object }, { __index = M }) -end - - ----@param entity entity -function M:refresh_transform(entity) - assert(entity.game_object, "Entity should have game_object component") - assert(entity.transform, "Entity should have transform component") - ---@cast entity entity.game_object - self.game_object:refresh_transform(entity) -end - - ----@param entity entity ----@param enabled boolean -function M:set_enabled(entity, enabled) - assert(entity.game_object, "Entity has no game_object component") - - for _, game_object in pairs(entity.game_object.object) do - if enabled then - msg.post(game_object, "enable") - else - msg.post(game_object, "disable") - end - end -end - -return M diff --git a/system/game_object/system_game_object.lua b/system/game_object/system_game_object.lua deleted file mode 100644 index 0a77962..0000000 --- a/system/game_object/system_game_object.lua +++ /dev/null @@ -1,264 +0,0 @@ -local decore = require("decore.decore") -local command_game_object = require("system.game_object.command_game_object") - ----@class entity ----@field game_object component.game_object|nil ----@field hidden boolean|nil - ----@class entity.game_object: entity ----@field game_object component.game_object ----@field transform component.transform ----@field hidden boolean|nil - ----@class component.game_object ----@field root string|hash ----@field object table ----@field factory_url string|nil ----@field is_slice9 boolean|nil ----@field remove_delay number|nil ----@field is_factory boolean|nil ----@field object_scheme table @For example: {["root"] = true}, used for find objects from already placed game object (not spawned by game object system) ----@field position vector3|nil ----@field rotation quaternion|nil -decore.register_component("game_object") -decore.register_component("hidden", false) - - ----@class go_position_setter ----@field add fun(self, entity, position, rotation) ----@field remove fun(self, entity) ----@field update fun(self) - ----@class system.game_object: system ----@field entities entity.game_object[] ----@field root_to_entity table -local M = {} - -M.DEBUG_PANEL_UPDATE_MEMORY_LIMIT = 2048 -M.DEBUG_PANEL_POSTWRAP_MEMORY_LIMIT = 2048 - -local TEMP_VECTOR = vmath.vector3(0, 0, 0) -local TEMP_QUAT = vmath.quat(0, 0, 0, 1) -local VECTOR3_ONE = vmath.vector3(1, 1, 1) -local ROOT_URL = hash("/root") -local HASH_POSITION = hash("position") -local HASH_SIZE = hash("size") -local HASH_SCALE = hash("scale") -local HASH_EULER_Z = hash("euler.z") -local sin = math.sin -local cos = math.cos -local rad = math.rad - ----@return system.game_object -function M.create() - local system = setmetatable(decore.ecs.system(), { __index = M }) - system.filter = decore.ecs.requireAll("game_object", "transform", decore.ecs.rejectAll("hidden")) - system.id = "game_object" - system.root_to_entity = {} - - return system -end - - -function M:onAddToWorld() - self.world.command_game_object = command_game_object.create(self) -end - - -function M:postWrap() - self.world.event_bus:process("transform_event", self.process_transform_event, self) -end - - - ----@param entity entity.game_object -function M:onAdd(entity) - local is_already_exists = entity.game_object.root or entity.game_object.object - if is_already_exists then - self.root_to_entity[entity.game_object.root] = entity - return - end - - - local object = self:create_object(entity) - local root = object[ROOT_URL] - entity.game_object.root = root - entity.game_object.object = object - - if root then - if entity.game_object.is_slice9 then - local sprite_url = msg.url(nil, root, "sprite") - TEMP_VECTOR.x = entity.transform.size_x - TEMP_VECTOR.y = entity.transform.size_y - go.set(sprite_url, HASH_SIZE, TEMP_VECTOR) - go.set(root, HASH_SCALE, VECTOR3_ONE) - else - TEMP_VECTOR.x = entity.transform.scale_x - TEMP_VECTOR.y = entity.transform.scale_y - TEMP_VECTOR.z = entity.transform.scale_z - go.set(root, HASH_SCALE, TEMP_VECTOR) - end - - go.set(root, HASH_EULER_Z, entity.transform.rotation) - - self.root_to_entity[root] = entity - end -end - - ----@param entity entity.game_object -function M:onRemove(entity) - local remove_delay = entity.game_object.remove_delay - - if not remove_delay then - self:remove_entity(entity) - else - timer.delay(remove_delay, false, function() - self:remove_entity(entity) - end) - end -end - - ----@param entity entity.game_object -function M:remove_entity(entity) - local root = entity.game_object.root - if root then - self.root_to_entity[root] = nil - - if go.exists(root) then - go.delete(root, false) - entity.game_object.root = nil - end - end - - local object = entity.game_object.object - if object then - for key, node in pairs(object) do - local related_entity = self.root_to_entity[node] - if related_entity then - self.world:removeEntity(related_entity) - else - -- TODO: it removes also a childs of the related entity - -- And I can get errors like panthera trying to play on deleted object - -- Right before it will be deleted with upper removeEntity - if go.exists(node) then - go.delete(node, false) - object[key] = nil - end - end - end - end -end - - ----@param event system.transform.event -function M:process_transform_event(event) - local entity = event.entity - local transform = entity.transform - local game_object = entity.game_object - - if not decore.is_alive(self, entity) then - return - end - - if not game_object or entity.physics then - return - end - - local root = game_object.root - if not root then - return - end - - if event.is_position_changed then - TEMP_VECTOR.x = transform.position_x - TEMP_VECTOR.y = transform.position_y - TEMP_VECTOR.z = transform.position_z - if event.animate_time then - local easing = event.easing or go.EASING_OUTSINE - go.animate(root, HASH_POSITION, go.PLAYBACK_ONCE_FORWARD, TEMP_VECTOR, easing, event.animate_time) - else - go.set_position(TEMP_VECTOR, root) - end - end - - if event.is_rotation_changed then - if event.animate_time then - local easing = event.easing or go.EASING_OUTSINE - go.animate(root, HASH_EULER_Z, go.PLAYBACK_ONCE_FORWARD, transform.rotation, easing, event.animate_time) - else - go.set(root, HASH_EULER_Z, transform.rotation) - end - end - - if event.is_scale_changed then - TEMP_VECTOR.x = transform.scale_x - TEMP_VECTOR.y = transform.scale_y - TEMP_VECTOR.z = transform.scale_z - if event.animate_time then - local easing = event.easing or go.EASING_OUTSINE - go.animate(root, HASH_SCALE, go.PLAYBACK_ONCE_FORWARD, TEMP_VECTOR, easing, event.animate_time) - else - go.set_scale(TEMP_VECTOR, root) - end - end - - if game_object.is_slice9 then - local sprite_url = msg.url(nil, root, "sprite") - TEMP_VECTOR.x = transform.size_x - TEMP_VECTOR.y = transform.size_y - if event.animate_time then - local easing = event.easing or go.EASING_OUTSINE - go.animate(sprite_url, HASH_SIZE, go.PLAYBACK_ONCE_FORWARD, TEMP_VECTOR, easing, event.animate_time) - else - go.set(sprite_url, HASH_SIZE, TEMP_VECTOR) - end - end -end - - -function M:refresh_transform(entity) - local root = entity.game_object.root - if not root then - return - end - - go.set_position(entity.transform.position, root) - go.set_scale(entity.transform.scale, root) - - TEMP_QUAT.z = sin(rad(entity.transform.rotation) * 0.5) - TEMP_QUAT.w = cos(rad(entity.transform.rotation) * 0.5) - go.set_rotation(TEMP_QUAT, root) -end - - -local PROPERTIES = { - [ROOT_URL] = { - is_factory_object = true - } -} ----@param entity entity.game_object ----@return table -function M:create_object(entity) - TEMP_VECTOR.x = entity.transform.position_x - TEMP_VECTOR.y = entity.transform.position_y - TEMP_VECTOR.z = self:get_position_z(entity.transform) - - if entity.game_object.is_factory then - local object = factory.create(entity.game_object.factory_url, TEMP_VECTOR, nil, PROPERTIES[ROOT_URL], entity.transform.scale_x) - return { [ROOT_URL] = object } - else - return collectionfactory.create(entity.game_object.factory_url, TEMP_VECTOR, nil, PROPERTIES, entity.transform.scale_x) - end -end - - ----@param t component.transform ----@return number -function M:get_position_z(t) - return -t.position_y / 10000 + t.position_x / 100000 + t.position_z / 10 -end - - -return M diff --git a/system/health/command_health.lua b/system/health/command_health.lua deleted file mode 100644 index c1bd50b..0000000 --- a/system/health/command_health.lua +++ /dev/null @@ -1,25 +0,0 @@ ----@class world ----@field command_health system.health.command - ----@class system.health.command ----@field health system.health -local M = {} - - ----@param health system.health ----@return system.health.command -function M.create(health) - return setmetatable({ health = health }, { __index = M }) -end - - ----@param entity entity ----@param damage number -function M:apply_damage(entity, damage) - assert(entity.health, "Entity does not have a health component.") - ---@cast entity entity.health - self.health:apply_damage(entity, damage) -end - - -return M diff --git a/system/health/component_health.script b/system/health/component_health.script deleted file mode 100644 index d55d3f4..0000000 --- a/system/health/component_health.script +++ /dev/null @@ -1,9 +0,0 @@ -go.property("health", 100) - -local component = require("decore.component") - -function init(self) - component.init("health", { - health = self.health - }) -end \ No newline at end of file diff --git a/system/health/system_health.lua b/system/health/system_health.lua deleted file mode 100644 index c333484..0000000 --- a/system/health/system_health.lua +++ /dev/null @@ -1,77 +0,0 @@ -local decore = require("decore.decore") -local command_health = require("system.health.command_health") - ----@class entity ----@field health component.health|nil - ----@class entity.health: entity ----@field health component.health - ----@class component.health ----@field max_health number ----@field current_health number|nil ----@field remove_on_death boolean|nil -decore.register_component("health", { - max_health = 1, -}) - ----@class system.health.event ----@field entity entity ----@field damage number - ----@class system.health: system ----@field entities entity.health[] -local M = {} - - ----@return system.health -function M.create_system() - return decore.system(M, "health", "health") -end - - -function M:onAddToWorld() - self.world.command_health = command_health.create(self) - self.world.event_bus:set_merge_policy("health_event", M.event_merge_policy) -end - - ----@param entity entity.health -function M:onAdd(entity) - local health = entity.health - health.current_health = health.max_health -end - - ----@param entity entity.health -function M:apply_damage(entity, damage) - local health = entity.health - health.current_health = math.max(0, health.current_health - damage) - self.world.event_bus:trigger("health_event", { - entity = entity, - damage = damage, - }) - - if health.current_health == 0 and health.remove_on_death then - self.world:removeEntity(entity) - end -end - - ----@param event system.health.event ----@param events system.health.event[] ----@return boolean -function M.event_merge_policy(event, events) - for index = #events, 1, -1 do - local compare_event = events[index] - if compare_event.entity == event.entity then - compare_event.damage = compare_event.damage + event.damage - return true - end - end - - return false -end - - -return M diff --git a/system/health/test_health.lua b/system/health/test_health.lua deleted file mode 100644 index a170220..0000000 --- a/system/health/test_health.lua +++ /dev/null @@ -1,46 +0,0 @@ -return function() - describe("System Health", function() - local decore --- @type decore - local world ---@type world - local system_health ---@type system.health - - before(function() - decore = require("decore.decore") - system_health = require("system.health.system_health") - - world = decore.new_world() - world:add(system_health.create_system()) - end) - - it("Should set current health", function() - local entity = decore.create({ health = { max_health = 100 } }) - world:add(entity) - world:refresh() - - assert(entity.health.current_health == 100) - end) - - it("Should catch health command", function() - local entity = decore.create({ health = { max_health = 100 } }) - world:add(entity) - world:refresh() - - world.command_health:apply_damage(entity, 10) - assert(entity.health.current_health == 90) - end) - - it("Should produce health_event", function() - local entity = decore.create({ health = { max_health = 100 } }) - world:add(entity) - world:refresh() - - world.command_health:apply_damage(entity, 10) - - assert(world.event_bus:get_stash("health_event")) - assert(#world.event_bus:get_stash("health_event") == 1) - local event = world.event_bus:get_stash("health_event")[1] - assert(event.entity == entity) - assert(event.damage == 10) - end) - end) -end diff --git a/system/input/input_system.lua b/system/input/input_system.lua deleted file mode 100644 index f02f55b..0000000 --- a/system/input/input_system.lua +++ /dev/null @@ -1,36 +0,0 @@ -local decore = require("decore.decore") - ----@class world ----@field input system.input - ----@class system.input.event: action - ----@class system.input: system -local M = {} - - ----@return system.input -function M.create() - return decore.system(M, "input") -end - - ----@protected -function M:onAddToWorld() - msg.post(".", "acquire_input_focus") - self.world.input = self -end - - ----@public ----@param action_id hash ----@param action action ----@return boolean -function M:on_input(action_id, action) - action.action_id = action_id - self.world.event_bus:trigger("input_event", action) - return false -end - - -return M diff --git a/system/movement_controller/system_movement_controller.lua b/system/movement_controller/system_movement_controller.lua deleted file mode 100644 index 2833fde..0000000 --- a/system/movement_controller/system_movement_controller.lua +++ /dev/null @@ -1,115 +0,0 @@ -local decore = require("decore.decore") - ----@class entity ----@field movement_controller component.movement_controller|nil - ----@class entity.movement_controller: entity ----@field movement_controller component.movement_controller - ----@class component.movement_controller ----@field speed number ----@field movement_x number @Runtime ----@field movement_y number @Runtime -decore.register_component("movement_controller", { - speed = 1, - movement_x = 0, - movement_y = 0, -}) - ----@class system.movement_controller: system ----@field entities entity.movement_controller[] ----@field input_keys table -local M = {} - -local ACTION_ID_TO_SIDE = { - [hash("key_w")] = { y = 1, id = "up" }, - [hash("key_s")] = { y = -1, id = "down" }, - [hash("key_a")] = { x = -1, id = "left" }, - [hash("key_d")] = { x = 1, id = "right" }, - [hash("key_up")] = { y = 1, id = "up" }, - [hash("key_down")] = { y = -1, id = "down" }, - [hash("key_left")] = { x = -1, id = "left" }, - [hash("key_right")] = { x = 1, id = "right" }, -} - ----@static ----@return system.movement_controller -function M.create() - local self = decore.processing_system(M, "movement_controller", { "movement_controller", "transform" }) - self.input_keys = {} - return self -end - - -function M:postWrap() - self.world.event_bus:process("input_event", self.process_input_event, self) -end - - ----@param input_event system.input.event -function M:process_input_event(input_event) - local action_id = input_event.action_id - local side = ACTION_ID_TO_SIDE[action_id] - if not side then - return - end - - for index = 1, #self.entities do - self:apply_input_event(self.entities[index], input_event) - end -end - - ----@param entity entity.movement_controller ----@param input_event system.input.event -function M:apply_input_event(entity, input_event) - local action_id = input_event.action_id - local action = input_event - local movement_controller = entity.movement_controller - - local side = ACTION_ID_TO_SIDE[action_id] - if action.pressed then - self.input_keys[side.id] = true - end - if action.released then - self.input_keys[side.id] = nil - end - - do -- direction_x - movement_controller.movement_x = 0 - if self.input_keys["left"] then - movement_controller.movement_x = movement_controller.movement_x - 1 - end - if self.input_keys["right"] then - movement_controller.movement_x = movement_controller.movement_x + 1 - end - end - - do -- direction_y - movement_controller.movement_y = 0 - if self.input_keys["up"] then - movement_controller.movement_y = movement_controller.movement_y + 1 - end - if self.input_keys["down"] then - movement_controller.movement_y = movement_controller.movement_y - 1 - end - end -end - - -function M:process(entity, dt) - local movement_controller = entity.movement_controller - - local speed = movement_controller.speed - local movement_x = movement_controller.movement_x - local movement_y = movement_controller.movement_y - - if movement_x ~= 0 or movement_y ~= 0 then - local force_x = movement_x * speed * dt * 60 - local force_y = movement_y * speed * dt * 60 - self.world.command_transform:add_position(entity, force_x, force_y) - end -end - - -return M diff --git a/system/nakama/command_nakama.lua b/system/nakama/command_nakama.lua deleted file mode 100644 index 242179a..0000000 --- a/system/nakama/command_nakama.lua +++ /dev/null @@ -1,16 +0,0 @@ ----@class world ----@field command_nakama command.nakama - ----@class command.nakama ----@field nakama system.nakama -local M = {} - - ----@param nakama system.nakama ----@return command.nakama -function M.create(nakama) - return setmetatable({ nakama = nakama }, { __index = M }) -end - - -return M diff --git a/system/nakama/nakama_core.lua b/system/nakama/nakama_core.lua deleted file mode 100644 index 5aa8841..0000000 --- a/system/nakama/nakama_core.lua +++ /dev/null @@ -1,309 +0,0 @@ -local defold = require("nakama.engine.defold") -local nakama = require("nakama.nakama") -local nakama_session = require("nakama.session") -local log = require("log.log") -local uuid = require("libs.uuid") - -local logger = log.get_logger("nakama") - -local M = {} - -local SESSION_FILE_PATH = sys.get_save_file(sys.get_config_string("project.title", "nakama"), "nakama_session") - ----@class nakama.client - ----@class nakama.session ----@field expires number ----@field token string - ----@class nakama.socket_result.match ----@field match nakama.match ----@field error string|nil - ----@class nakama.socket ----@field create fun(client) ----@field connect fun(callback) ----@field send fun(message, callback) ----@field on_disconnect fun(fn) ----@field channel_join fun(target, type, persistence, hidden, callback) ----@field channel_leave fun(channel_id, callback) ----@field channel_message_send fun(channel_id, content, callback) ----@field channel_message_remove fun(channel_id, message_id, callback) ----@field channel_message_update fun(channel_id, message_id, content, callback) ----@field match_data_send fun(match_id, op_code, data, presences, reliable, callback) ----@field match_create fun(name, callback): nakama.socket_result.match ----@field match_join fun(match_id, token, metadata, callback) ----@field match_leave fun(match_id, callback) ----@field matchmaker_add fun(min_count, max_count, query, string_properties, numeric_properties, count_multiple, callback) ----@field matchmaker_remove fun(ticket, callback) ----@field party_create fun(open, max_size, callback) ----@field party_join fun(party_id, callback) ----@field party_leave fun(party_id, callback) ----@field party_promote fun(party_id, presence, callback) ----@field party_accept fun(party_id, presence, callback) ----@field party_remove fun(party_id, presence, callback) ----@field party_close fun(party_id, callback) ----@field party_join_request_list fun(party_id, callback) ----@field party_matchmaker_add fun(party_id, min_count, max_count, query, string_properties, numeric_properties, count_multiple, callback) ----@field party_matchmaker_remove fun(party_id, ticket, callback) ----@field party_data_send fun(party_id, op_code, data, callback) ----@field status_follow fun(user_ids, usernames, callback) ----@field status_unfollow fun(user_ids, callback) ----@field status_update fun(status, callback) ----@field on_channel_presence_event fun(fn) ----@field on_match_presence_event fun(callback: fun(message: nakama.socket_result.on_match_presence.event)) ----@field on_match_data fun(callback: fun(message: nakama.socket_result.match_data.event)) ----@field on_match fun(fn) ----@field on_matchmaker_matched fun(fn) ----@field on_notifications fun(fn) ----@field on_party_presence_event fun(fn) ----@field on_party fun(fn) ----@field on_party_data fun(fn) ----@field on_party_join_request fun(fn) ----@field on_party_leader fun(fn) ----@field on_status_presence_event fun(fn) ----@field on_status fun(fn) ----@field on_stream_data fun(fn) ----@field on_error fun(fn) ----@field on_channel_message fun(fn) - ----@class nakama.socket_result.on_match_presence.event ----@field match_presence_event nakama.socket_result.on_match_presence - ----@class nakama.socket_result.on_match_presence ----@field match_id string ----@field joins nakama.presence[] ----@field leaves nakama.presence[] - ----@class nakama.socket_result.match_data.event ----@field match_data nakama.socket_result.match_data - ----@class nakama.socket_result.match_data ----@field match_id string ----@field op_code number ----@field data string ----@field presence nakama.presence - ----@class nakama.presence ----@field username string ----@field session_id string ----@field user_id string - ----@class nakama.match ----@field match_id string ----@field self nakama.presence ----@field presences nakama.presence[] ----@field size number - ----@class nakama.session_file ----@field token string|nil ----@field player_id string|nil -local SESSION = sys.load(SESSION_FILE_PATH) or { - token = nil, - player_id = nil -} - - ----@param str string ----@param ending string -local function string_ends(str, ending) - return ending == "" or str:sub(-#ending) == ending -end - - ----@param entity entity.nakama ----@param callback function|nil -function M.connect(entity, callback) - local token = SESSION.token - local n = entity.nakama - n.client = n.client or M.create_nakama_client(n.server_key, n.server_host, n.server_port, n.use_ssl) - n.socket = n.socket or M.create_nakama_socket(n.client) - - if n.is_connected then - logger:error("Call server connect while already connected") - return - end - - n.is_connecting = true - nakama.sync(function() - token = M.check_new_user(entity, token) - - if M.is_token_empty(token) then - logger:warn("Can't auth on server") - n.is_connecting = false - return nil - end - - token = M.check_session(entity, token) - - if M.is_token_empty(token) then - logger:warn("Can't auth on server, drop the connection flow") - n.is_connecting = false - return nil - end - - M.socket_connect(entity, callback) - end) -end - ----Return nakama client config ----@param server_key string ----@param host string ----@param port number ----@param is_ssl boolean ----@return table nakama client config -function M.create_nakama_client(server_key, host, port, is_ssl) - local config = { - host = host, - port = port, - use_ssl = is_ssl, - username = server_key, - password = "", - engine = defold, - timeout = 10, -- connection timeout in seconds - } - return nakama.create_client(config) -end - - ----Return nakama socket client ----@param client nakama.client ----@return nakama.socket -function M.create_nakama_socket(client) - return nakama.create_socket(client) -end - - ----Check and update if required the nakama session ----@param entity entity.nakama -function M.refresh_session(entity) - local n = entity.nakama - local session = n.session - if not session or M.is_session_expire_soon(session) then - logger:debug("Session token will expire soon, refresh") - nakama.sync(function() - M.auth(entity) - end) - end -end - - ----Check and update if required the nakama session ----@param entity entity.nakama ----@param token string|nil -function M.check_session(entity, token) - local session = nakama_session.create({ token = token }) - if M.is_session_expire_soon(session) then - logger:info("Session has expired, call reauth") - token = M.auth(entity) - else - logger:debug("Session token is valid") - M.set_session_token(entity, session) - end - return token -end - - ----@param entity entity.nakama ----@param callback function|nil -function M.socket_connect(entity, callback) - local n = entity.nakama - local socket_connected, socket_error = n.socket.connect() - n.is_connecting = false - - if socket_connected then - logger:info("Socket connected") - n.is_connected = true - - if callback then - callback() - end - else - logger:error("Socket error", { error = socket_error }) - M.set_session_token(entity, nil) - - if string_ends(socket_error, "401") then - logger:info("Token expired, reset") - M.connect(entity, callback) - end - end -end - - ----@param entity entity.nakama ----@param session nakama.session|nil -function M.set_session_token(entity, session) - local n = entity.nakama - - if session and session.token then - SESSION.token = session.token - sys.save(SESSION_FILE_PATH, SESSION) - - nakama.set_bearer_token(n.client, session.token) - n.session = session - else - SESSION.token = "" - sys.save(SESSION_FILE_PATH, SESSION) - - n.session = nil - end -end - - ----@param token string|nil -function M.is_token_empty(token) - return not token or token == "" -end - - ----@param session nakama.session -function M.is_session_expire_soon(session) - local time_to_expire = (session.expires - os.time()) - return time_to_expire < 300 -- 5 minutes -end - - ----@param entity entity.nakama -function M.auth(entity) - local player_id = M.get_player_id() - logger:debug("Auth started", { id = player_id }) - - local client = entity.nakama.client - local session = nakama.authenticate_device(client, player_id, nil, true, player_id) - if not session.token then - logger:info("Auth failed, no session") - return nil - end - - M.set_session_token(entity, session) - - logger:debug("Authenticated", { token = session.token, user_id = session.user_id }) - return session.token -end - - ----@param entity entity.nakama -function M.check_new_user(entity, token) - if M.is_token_empty(token) then - logger:debug("Auth for new user") - token = M.auth(entity) - end - - return token -end - - -function M.get_player_id() - -- TODO Test - SESSION.player_id = uuid() - - if not SESSION.player_id then - SESSION.player_id = uuid() - sys.save(SESSION_FILE_PATH, SESSION) - end - - return SESSION.player_id -end - - -return M diff --git a/system/nakama/system_nakama.lua b/system/nakama/system_nakama.lua deleted file mode 100644 index fe2ee91..0000000 --- a/system/nakama/system_nakama.lua +++ /dev/null @@ -1,54 +0,0 @@ -local log = require("log.log") -local nakama_core = require("system.nakama.nakama_core") -local decore = require("decore.decore") - -local logger = log.get_logger("system.nakama") - ----@class entity ----@field nakama component.nakama|nil - ----@class entity.nakama: entity ----@field nakama component.nakama - ----@class component.nakama ----@field server_host string ----@field server_port number ----@field check_connection_timer number ----@field reconnect_attempts_time number[] ----@field server_key string ----@field http_key string ----@field encryption_key string ----@field refresh_encryption_key string ----@field debug_log boolean ----@field use_ssl boolean ----@field client nakama.client|nil ----@field socket nakama.socket|nil ----@field session nakama.session|nil ----@field is_connected boolean ----@field is_connecting boolean - ----@class system.nakama: system -local M = {} - - ----@static ----@return system.nakama -function M.create_system() - return decore.system(M, "nakama", "nakama") -end - - ----@param entity entity.nakama -function M:onAdd(entity) - nakama_core.connect(entity, function() - self:on_connected() - end) -end - - -function M:on_connected() - logger:trace("on_connected") -end - - -return M diff --git a/system/on_key_released/system_on_key_released.lua b/system/on_key_released/system_on_key_released.lua deleted file mode 100644 index 04681b9..0000000 --- a/system/on_key_released/system_on_key_released.lua +++ /dev/null @@ -1,77 +0,0 @@ -local decore = require("decore.decore") - ----@class entity ----@field on_key_released component.on_key_released|nil - ----@class entity.on_key_released: entity ----@field on_key_released component.on_key_released - ----@class component.on_key_released ----@field key_to_command_json string|nil @JSON string table>. Will override key_to_command if exists ----@field key_to_command table>|nil @ table>. -decore.register_component("on_key_released") - ----@class system.on_key_released: system ----@field entities entity.on_key_released[] ----@field hash_to_string table -local M = {} - - ----@return system.on_key_released -function M.create_system() - local system = decore.system(M, "on_key_released", "on_key_released") - system.hash_to_string = {} - - return system -end - - -function M:postWrap() - self.world.event_bus:process("input_event", self.process_input_event, self) -end - - ----@param entity entity.on_key_released -function M:onAdd(entity) - local on_key_released = entity.on_key_released - - for key_id, key_command in pairs(on_key_released) do - local hash_id = hash(key_id) - if not self.hash_to_string[hash_id] then - self.hash_to_string[hash_id] = key_id - end - end -end - - ----@param input_event system.input.event -function M:process_input_event(input_event) - if not input_event.released then - return - end - - local key_id = self.hash_to_string[input_event.action_id] - if not key_id then - return - end - - local entities = self.entities - for index = 1, #entities do - local entity = entities[index] - self:on_input_released(entity, input_event.action_id, input_event) - end -end - - ----@param entity entity.on_key_released -function M:on_input_released(entity, action_id, action) - local on_key_released = entity.on_key_released - - local key_id = self.hash_to_string[action_id] - if on_key_released[key_id] then - decore.call_command(self.world, on_key_released[key_id]) - end -end - - -return M diff --git a/system/on_spawn_command/component_on_spawn_command.script b/system/on_spawn_command/component_on_spawn_command.script deleted file mode 100644 index 8e1a4d0..0000000 --- a/system/on_spawn_command/component_on_spawn_command.script +++ /dev/null @@ -1,9 +0,0 @@ -go.property("command_label", true) - -local component = require("decore.component") - -function init(self) - component.init("on_spawn_command", { - command_label = self.command_label - }) -end \ No newline at end of file diff --git a/system/on_spawn_command/entity_on_spawn_command.go b/system/on_spawn_command/entity_on_spawn_command.go deleted file mode 100644 index a93d8e4..0000000 --- a/system/on_spawn_command/entity_on_spawn_command.go +++ /dev/null @@ -1,38 +0,0 @@ -components { - id: "entity" - component: "/decore/entity.script" - properties { - id: "prefab_id" - value: "on_spawn_command" - type: PROPERTY_TYPE_HASH - } -} -components { - id: "component_on_spawn_command" - component: "/system/on_spawn_command/component_on_spawn_command.script" -} -embedded_components { - id: "command" - type: "label" - data: "size {\n" - " x: 900.0\n" - " y: 100.0\n" - "}\n" - "outline {\n" - " w: 0.0\n" - "}\n" - "shadow {\n" - " w: 0.0\n" - "}\n" - "pivot: PIVOT_W\n" - "text: \"command_game_gui, set_text, rocket\\n" - "\"\n" - " \"\"\n" - "font: \"/druid/fonts/text_regular.font\"\n" - "material: \"/core/utils/editor_only_label-df.material\"\n" - "" - scale { - x: 2.0 - y: 2.0 - } -} diff --git a/system/on_spawn_command/entity_on_spawn_command.lua b/system/on_spawn_command/entity_on_spawn_command.lua deleted file mode 100644 index 5d18d5b..0000000 --- a/system/on_spawn_command/entity_on_spawn_command.lua +++ /dev/null @@ -1,7 +0,0 @@ ----@return entity.on_spawn_command -return { - game_object = {}, - on_spawn_command = { - command_label = true - } -} \ No newline at end of file diff --git a/system/on_spawn_command/system_on_spawn_command.lua b/system/on_spawn_command/system_on_spawn_command.lua deleted file mode 100644 index 7c5c1a2..0000000 --- a/system/on_spawn_command/system_on_spawn_command.lua +++ /dev/null @@ -1,44 +0,0 @@ -local decore = require("decore.decore") - ----@class entity ----@field on_spawn_command component.on_spawn_command|nil - ----@class entity.on_spawn_command: entity ----@field on_spawn_command component.on_spawn_command -decore.register_component("on_spawn_command") - ----@class component.on_spawn_command ----@field command any[] ----@field command_label boolean - ----@class system.on_spawn_command: system ----@field entities entity.on_spawn_command[] -local M = {} - - ----@static ----@return system.on_spawn_command -function M.create_system() - return decore.system(M, "on_spawn_command", { "on_spawn_command" }) -end - - ----@param entity entity.on_spawn_command -function M:onAdd(entity) - if entity.on_spawn_command.command then - decore.call_command(self.world, entity.on_spawn_command.command) - end - - if entity.on_spawn_command.command_label and entity.game_object then - local root = entity.game_object.root - local label_url = msg.url(nil, root, "command") - local command = decore.parse_command(label.get_text(label_url)) - if command then - decore.call_command(self.world, command) - end - end -end - - - -return M diff --git a/system/panthera/command_panthera.lua b/system/panthera/command_panthera.lua deleted file mode 100644 index 718847a..0000000 --- a/system/panthera/command_panthera.lua +++ /dev/null @@ -1,74 +0,0 @@ -local decore = require("decore.decore") -local panthera = require("panthera.panthera") - ----@class world ----@field command_panthera system.panthera.command - ----@class system.panthera.command ----@field panthera system.panthera -local M = {} - - ----@return system.panthera.command -function M.create(panthera_decore) - return setmetatable({ panthera = panthera_decore }, { __index = M }) -end - - ----@param entity entity ----@param animation_state panthera.animation ----@param animation_id string -function M:play_state(entity, animation_state, animation_id) - if not decore.is_alive(self.panthera, entity) then - return - end - - panthera.play(animation_state, animation_id) -end - - ----@param entity entity ----@param animation_id string ----@param speed number|nil ----@param is_loop boolean|nil -function M:play(entity, animation_id, speed, is_loop) - local p = entity.panthera - assert(p, "Entity doesn't have panthera component") - - if not decore.is_alive(self.panthera, entity) then - return - end - - panthera.play(p.animation_state, animation_id, { - is_loop = is_loop or false, - speed = speed or 1, - callback = function(animation_path) - -- If default animation, run it after the current animation - if p.default_animation then - panthera.play(p.animation_state, p.default_animation, { - is_loop = p.is_loop or false, - speed = p.speed or 1, - }) - end - end - }) -end - - ----@param entity entity ----@param animation_id string ----@param progress number -function M:set_progress(entity, animation_id, progress) - local p = entity.panthera - assert(p, "Entity doesn't have panthera component") - - if not decore.is_alive(self.panthera, entity) then - return - end - - local time = panthera.get_duration(p.animation_state, animation_id) - panthera.set_time(p.animation_state, animation_id, time * progress) -end - - -return M diff --git a/system/panthera/component_panthera.script b/system/panthera/component_panthera.script deleted file mode 100644 index bcd7ed0..0000000 --- a/system/panthera/component_panthera.script +++ /dev/null @@ -1,23 +0,0 @@ -local component = require("decore.component") -go.property("default_animation", hash("")) -go.property("speed", 1) -go.property("is_loop", true) -go.property("play_on_start", true) - - - -function init(self) - local EMPTY_HASH = hash("") - - local animation = self.default_animation - if animation == EMPTY_HASH then - animation = nil - end - - component.init("panthera", { - default_animation = animation, - speed = self.speed, - play_on_start = self.play_on_start, - is_loop = self.is_loop, - }) -end \ No newline at end of file diff --git a/system/panthera/system_panthera.lua b/system/panthera/system_panthera.lua deleted file mode 100644 index 91c9457..0000000 --- a/system/panthera/system_panthera.lua +++ /dev/null @@ -1,97 +0,0 @@ -local decore = require("decore.decore") -local panthera = require("panthera.panthera") - -local command_panthera = require("system.panthera.command_panthera") - ----@class entity ----@field panthera component.panthera|nil - ----@class entity.panthera: entity ----@field panthera component.panthera ----@field game_object component.game_object ----@field transform component.transform - ----@class component.panthera ----@field animation_path string|table ----@field animation_state panthera.animation|nil ----@field default_animation string ----@field speed number ----@field is_loop boolean|nil ----@field play_on_start boolean|nil ----@field play_on_remove string|nil Play animation on entity remove -decore.register_component("panthera", { - default_animation = "default", -}) - ----@class system.panthera: system ----@field entities entity.panthera[] -local M = {} - - ----@return system.panthera -function M.create_system() - local system = decore.system(M, "panthera") - system.filter = decore.ecs.requireAll("panthera", "game_object", decore.ecs.rejectAll("hidden")) - - return system -end - - ----@private -function M:postWrap() - self.world.event_bus:process("window_event", self.process_window_event, self) -end - - -function M:onAddToWorld() - self.world.command_panthera = command_panthera.create(self) -end - - ----@param entity entity.panthera -function M:onAdd(entity) - local p = entity.panthera - - local animation_state - if entity.game_object.object then - animation_state = panthera.create_go(p.animation_path, nil, entity.game_object.object) - else - animation_state = panthera.create_go(p.animation_path, nil, { ["/"] = entity.game_object.root }) - end - - if animation_state then - p.animation_state = animation_state - p.animation_path = animation_state.animation_path - - -- TODO: This one should be in update? - if p.play_on_start then - panthera.play(p.animation_state, p.default_animation, { - is_loop = p.is_loop or false, - speed = p.speed or 1 - }) - end - end -end - - ----@param entity entity.panthera -function M:onRemove(entity) - local p = entity.panthera - panthera.stop(p.animation_state) - - if p.play_on_remove then - panthera.play(p.animation_state, p.play_on_remove) - end -end - - ----@private ----@param window_event system.window_event.event -function M:process_window_event(window_event) - if window_event == window.WINDOW_EVENT_FOCUS_GAINED then - panthera.reload_animation() - end -end - - -return M diff --git a/system/physics/command_physics.lua b/system/physics/command_physics.lua deleted file mode 100644 index 6e2ec25..0000000 --- a/system/physics/command_physics.lua +++ /dev/null @@ -1,27 +0,0 @@ ----@class world ----@field command_physics system.physics.command - ----@class system.physics.command ----@field physics system.physics -local M = {} - - ----@param physics system.physics ----@return system.physics.command -function M.create(physics) - return setmetatable({ physics = physics }, { __index = M }) -end - - ----@param entity entity ----@param force_x number|nil ----@param force_y number|nil -function M:add_force(entity, force_x, force_y) - assert(entity.physics, "entity must have physics component") - ---@cast entity entity.physics - - self.physics:add_force(entity, force_x, force_y) -end - - -return M diff --git a/system/physics/component_physics.script b/system/physics/component_physics.script deleted file mode 100644 index 8c20d2c..0000000 --- a/system/physics/component_physics.script +++ /dev/null @@ -1,10 +0,0 @@ -go.property("velocity", vmath.vector3(0, 0, 0)) - -local component = require("decore.component") - -function init(self) - component.init("physics", { - velocity_x = self.velocity.x, - velocity_y = self.velocity.y, - }) -end \ No newline at end of file diff --git a/system/physics/system_physics.lua b/system/physics/system_physics.lua deleted file mode 100644 index d2364f3..0000000 --- a/system/physics/system_physics.lua +++ /dev/null @@ -1,99 +0,0 @@ -local decore = require("decore.decore") -local command_physics = require("system.physics.command_physics") - ----@class entity ----@field physics component.physics|nil - ----@class entity.physics: entity ----@field physics component.physics ----@field game_object component.game_object ----@field transform component.transform - ----@class component.physics ----@field box2d_body b2Body ----@field collisionobject_url url ----@field velocity_x number ----@field velocity_y number -decore.register_component("physics", { - velocity_x = 0, - velocity_y = 0, - gravity_y = 0, -}) - ----@class system.physics: system ----@field entities entity.physics[] -local M = {} - -local TEMP_VECTOR = vmath.vector3() - ----@return system.physics -function M.create_system() - return decore.processing_system(M, "physics", { "physics", "game_object", "transform" }) -end - - -function M:onAddToWorld() - self.world.command_physics = command_physics.create(self) -end - - ----@param entity entity.physics -function M:onAdd(entity) - local collisionobject_url = msg.url(nil, entity.game_object.root, "collisionobject") - entity.physics.collisionobject_url = collisionobject_url - entity.physics.box2d_body = b2d.get_body(collisionobject_url) - - local body = entity.physics.box2d_body - TEMP_VECTOR.x = entity.physics.velocity_x - TEMP_VECTOR.y = entity.physics.velocity_y - b2d.body.set_linear_velocity(body, TEMP_VECTOR) -end - - -function M:onRemove(entity) - entity.physics.box2d_body = nil - entity.physics.collisionobject_url = nil -end - - ----@param entity entity.physics ----@param dt number -function M:process(entity, dt) - local body = entity.physics.box2d_body - local is_awake = b2d.body.is_awake(body) - if not is_awake then - return - end - - -- Is it faster? - local position = b2d.body.get_position(body) - local position_x = position.x - local position_y = position.y - - local transform = entity.transform - if position_x ~= transform.position_x or position_y ~= transform.position_y then - self.world.command_transform:set_position(entity, position_x, position_y, transform.position_z) - end - - local velocity = b2d.body.get_linear_velocity(body) - entity.physics.velocity_x = velocity.x - entity.physics.velocity_y = velocity.y -end - - ----@param entity entity.physics ----@param force_x number|nil ----@param force_y number|nil -function M:add_force(entity, force_x, force_y) - if not decore.is_alive(self, entity) then - return - end - - local body = entity.physics.box2d_body - TEMP_VECTOR.x = force_x or 0 - TEMP_VECTOR.y = force_y or 0 - b2d.body.apply_force_to_center(body, TEMP_VECTOR) -end - - -return M diff --git a/system/platformer_controller/system_platformer_controller.lua b/system/platformer_controller/system_platformer_controller.lua deleted file mode 100644 index ac7d390..0000000 --- a/system/platformer_controller/system_platformer_controller.lua +++ /dev/null @@ -1,104 +0,0 @@ -local decore = require("decore.decore") - ----@class entity ----@field platformer_controller component.platformer_controller|nil - ----@class entity.platformer_controller: entity ----@field platformer_controller component.platformer_controller ----@field physics component.physics - ----@class component.platformer_controller ----@field direction_x number ----@field direction_y number -decore.register_component("platformer_controller", { - direction_x = 0, - direction_y = 0, -}) - ----@class system.platformer_controller: system ----@field entities entity.platformer_controller[] ----@field input_keys table -local M = {} - -local KEY_A = hash("key_a") -local KEY_D = hash("key_d") -local KEY_LEFT = hash("key_left") -local KEY_RIGHT = hash("key_right") -local ACTION_ID_TO_SIDE = { - [KEY_A] = { x = -1 }, - [KEY_D] = { x = 1 }, - [KEY_LEFT] = { x = -1 }, - [KEY_RIGHT] = { x = 1 }, -} -local ACTION_JUMP = hash("key_space") - ----@static ----@return system.platformer_controller -function M.create_system() - local system = decore.processing_system(M, "platformer_controller", { "platformer_controller", "platformer_physics" }) - system.input_keys = {} - return system -end - -function M:postWrap() - self.world.event_bus:process("input_event", self.process_input_event, self) -end - - ----@param input_event system.input.event -function M:process_input_event(input_event) - local action_id = input_event.action_id - local side = ACTION_ID_TO_SIDE[action_id] - if side or action_id == ACTION_JUMP then - for index = 1, #self.entities do - self:apply_input_event(self.entities[index], input_event) - end - end -end - - ----@param entity entity.platformer_controller ----@param input_event system.input.event -function M:apply_input_event(entity, input_event) - local action_id = input_event.action_id - if not action_id then - return - end - - local action = input_event - local platformer_controller = entity.platformer_controller - - local side = ACTION_ID_TO_SIDE[action_id] - if action.pressed and side then - self.input_keys[action_id] = true - end - if action.released and side then - self.input_keys[action_id] = nil - end - - do -- direction_x - platformer_controller.direction_x = 0 - if self.input_keys[KEY_A] or self.input_keys[KEY_LEFT] then - platformer_controller.direction_x = platformer_controller.direction_x - 1 - end - if self.input_keys[KEY_D] or self.input_keys[KEY_RIGHT] then - platformer_controller.direction_x = platformer_controller.direction_x + 1 - end - end - - if action_id == ACTION_JUMP and action.pressed then - self.world.command_platformer_physics:jump(entity) - end -end - - ----@param entity entity.platformer_controller ----@param dt number -function M:process(entity, dt) - local platformer_controller = entity.platformer_controller - self.world.command_platformer_physics:move_vertical(entity, platformer_controller.direction_x) - self.world.command_platformer_physics:move_horizontal(entity, platformer_controller.direction_y) -end - - -return M diff --git a/system/platformer_physics/command_platformer_physics.lua b/system/platformer_physics/command_platformer_physics.lua deleted file mode 100644 index 0528967..0000000 --- a/system/platformer_physics/command_platformer_physics.lua +++ /dev/null @@ -1,49 +0,0 @@ ----@class world ----@field command_platformer_physics system.platformer_physics.command - ----@class system.platformer_physics.command ----@field platformer_physics system.platformer_physics -local M = {} - - ----@static ----@return system.platformer_physics.command -function M.create(platformer_physics) - return setmetatable({ platformer_physics = platformer_physics }, { __index = M }) -end - - ----@param entity entity ----@param power number Can be zero, means no input -function M:move_vertical(entity, power) - assert(entity.platformer_physics, "entity must have platformer_physics component") - ---@cast entity entity.platformer_physics - - local pf = entity.platformer_physics - pf.target_velocity_x = power * pf.speed - - if self.platformer_physics:sign(pf.target_velocity_x) > 0 and pf.contact_timers[3] > 0 then - pf.target_velocity_x = 0 - end - if self.platformer_physics:sign(pf.target_velocity_x) < 0 and pf.contact_timers[1] > 0 then - pf.target_velocity_x = 0 - end -end - - -function M:move_horizontal(entity, power) - assert(entity.platformer_physics, "entity must have platformer_physics component") - ---@cast entity entity.platformer_physics - - local pf = entity.platformer_physics - pf.target_velocity_y = power * pf.speed -end - - ----@param entity entity -function M:jump(entity) - entity.platformer_physics.desired_jump = true -end - - -return M diff --git a/system/platformer_physics/component_platformer_physics.script b/system/platformer_physics/component_platformer_physics.script deleted file mode 100644 index 205915e..0000000 --- a/system/platformer_physics/component_platformer_physics.script +++ /dev/null @@ -1,52 +0,0 @@ -go.property("gravity_x", 0) -go.property("gravity_y", -5000) -go.property("gravity_multiplier", 1) -go.property("speed", 1000) -go.property("acceleration", 2000) -go.property("deceleration", 5000) -go.property("air_acceleration", 5000) -go.property("air_deceleration", 1000) -go.property("turn_speed", 8000) -go.property("air_turn_speed", 8000) -go.property("air_control", 8000) -go.property("air_brake", 8000) - -go.property("time_to_jump_apex", 0.3) -go.property("jump_height", 280) -go.property("jump_duration", 3) -go.property("downward_movement_multiplier", 1) -go.property("is_instant_movement", false) -go.property("is_double_jump", true) -go.property("coyote_time", 0) -go.property("jump_buffer", 0) -go.property("terminal_velocity", 0) -go.property("jump_cutoff", 2.3) -- 1 is no cutoff - -local component = require("decore.component") - -function init(self) - component.init("platformer_physics", { - gravity_x = self.gravity_x, - gravity_y = self.gravity_y, - gravity_multiplier = self.gravity_multiplier, - speed = self.speed, - acceleration = self.acceleration, - deceleration = self.deceleration, - air_acceleration = self.air_acceleration, - air_deceleration = self.air_deceleration, - turn_speed = self.turn_speed, - air_turn_speed = self.air_turn_speed, - time_to_jump_apex = self.time_to_jump_apex, - jump_height = self.jump_height, - downward_movement_multiplier = self.downward_movement_multiplier, - is_instant_movement = self.is_instant_movement, - jump_duration = self.jump_duration, - is_double_jump = self.is_double_jump, - coyote_time = self.coyote_time, - jump_buffer = self.jump_buffer, - terminal_velocity = self.terminal_velocity, - air_control = self.air_control, - air_brake = self.air_brake, - jump_cutoff = self.jump_cutoff - }) -end \ No newline at end of file diff --git a/system/platformer_physics/system_platformer_physics.lua b/system/platformer_physics/system_platformer_physics.lua deleted file mode 100644 index e7153f9..0000000 --- a/system/platformer_physics/system_platformer_physics.lua +++ /dev/null @@ -1,304 +0,0 @@ -local decore = require("decore.decore") - -local command_platformer_physics = require("system.platformer_physics.command_platformer_physics") - ----@class entity ----@field platformer_physics component.platformer_physics|nil - ----@class entity.platformer_physics: entity ----@field platformer_physics component.platformer_physics ----@field transform component.transform - ----@class component.platformer_physics ----@field gravity_x number ----@field gravity_y number ----@field gravity_multiplier number ----@field gravity_scale number ----@field ground_timer number ----@field contact_timers number[] ----@field velocity_x number ----@field velocity_y number ----@field target_velocity_x number ----@field target_velocity_y number ----@field speed number ----@field acceleration number ----@field deceleration number ----@field air_acceleration number ----@field air_deceleration number ----@field turn_speed number ----@field air_turn_speed number ----@field desired_jump boolean ----@field time_to_jump_apex number ----@field jump_height number ----@field jump_speed number ----@field downward_movement_multiplier number ----@field is_instant_movement boolean ----@field jump_duration number ----@field is_double_jump boolean ----@field coyote_time number ----@field jump_buffer number ----@field terminal_velocity number ----@field air_control number ----@field air_brake number ----@field jump_cutoff boolean ----@field correction vector3 -decore.register_component("platformer_physics", { - gravity_x = 0, - gravity_y = 0, - gravity_multiplier = 1, - gravity_scale = 1, - ground_timer = 0, - contact_timers = { 0, 0, 0, 0 }, - - velocity_x = 0, - velocity_y = 0, - target_velocity_x = 0, - target_velocity_y = 0, - - speed = 10, - acceleration = 52, - deceleration = 52, - air_acceleration = 52, - air_deceleration = 52, - turn_speed = 80, - air_turn_speed = 80, - - desired_jump = false, - time_to_jump_apex = 0.3, - jump_height = 7.3, - jump_speed = 0, - upward_movement_multiplier = 1, - downward_movement_multiplier = 6.17, - - jump_buffer_counter = 0, - coyote_time_counter = 0, - - jump_cutoff = false, - jump_duration = 0, - is_double_jump = false, - coyote_time = 0, - jump_buffer = 0, - terminal_velocity = 0, - air_control = 0, - air_brake = 0, - - correction = vmath.vector3(), -}) - ----@class system.platformer_physics: system ----@field entities entity.platformer_physics[] -local M = {} - -local RAYCAST_GROUPS = { hash("level") } -local FROM, TO = vmath.vector3(), vmath.vector3() - ----@static ----@return system.platformer_physics -function M.create_system() - return decore.system(M, "platformer_physics", { "platformer_physics", "transform" }) -end - - -function M:onAddToWorld() - self.world.command_platformer_physics = command_platformer_physics.create(self) -end - - -function M:postWrap() - self.world.event_bus:process("collision_event", self.process_collision_event, self) -end - - -function M:fixed_update(dt) - for index = 1, #self.entities do - local entity = self.entities[index] - local pf = entity.platformer_physics - - if pf.desired_jump then - self:jump(entity) - return - end - - if pf.velocity_y == 0 then - pf.gravity_multiplier = 1 - end - if pf.velocity_y < -0.01 then - pf.gravity_multiplier = pf.downward_movement_multiplier - end - - pf.ground_timer = math.max(0, pf.ground_timer - dt) - for i = 1, #pf.contact_timers do - pf.contact_timers[i] = math.max(0, pf.contact_timers[i] - dt) - end - - -- apply the compensation to the player character - local corr = pf.correction - self.world.command_transform:add_position(entity, corr.x, corr.y, corr.z) - pf.correction.x = 0 - pf.correction.y = 0 - pf.correction.z = 0 - - -- update velocity - self:update_velocity(entity, dt) - end -end - - ----@param entity entity.platformer_physics -function M:update_velocity(entity, dt) - local t = entity.transform - local pf = entity.platformer_physics - - local is_on_ground = pf.contact_timers[4] > 0 - local is_on_left_wall = pf.contact_timers[1] > 0 - local is_on_ceiling = pf.contact_timers[2] > 0 - local is_on_right_wall = pf.contact_timers[3] > 0 - - -- Acceleration - local acceleration = is_on_ground and pf.acceleration or pf.air_acceleration - local deceleration = is_on_ground and pf.deceleration or pf.air_deceleration - local turn_speed = is_on_ground and pf.turn_speed or pf.air_turn_speed - - local max_speed_change = deceleration * dt - if pf.target_velocity_x ~= 0 then - if self:sign(pf.target_velocity_x) ~= self:sign(pf.velocity_x) then - max_speed_change = turn_speed * dt - else - max_speed_change = acceleration * dt - end - end - - pf.velocity_x = self:step(pf.velocity_x, pf.target_velocity_x, max_speed_change) - - local gravity_y = (-2 * pf.jump_height) / (pf.time_to_jump_apex * pf.time_to_jump_apex) - pf.gravity_scale = (gravity_y / pf.gravity_y) * pf.gravity_multiplier - - pf.velocity_y = pf.velocity_y + pf.gravity_y * dt - - if is_on_ground then - pf.velocity_y = math.max(pf.velocity_y, 0) - end - if is_on_ceiling then - pf.velocity_y = math.min(pf.velocity_y, 0) - end - if is_on_left_wall then - pf.velocity_x = math.max(pf.velocity_x, 0) - end - if is_on_right_wall then - pf.velocity_x = math.min(pf.velocity_x, 0) - end - - local velocity_x = pf.velocity_x - local velocity_y = pf.velocity_y - if velocity_x ~= 0 or velocity_y ~= 0 then - local target_x = t.position_x + velocity_x * dt - local target_y = t.position_y + velocity_y * dt - self.world.command_transform:set_position(entity, target_x, target_y) - - if entity.game_object then - self.world.command_game_object:refresh_transform(entity) - end - end -end - - ----@param entity entity.platformer_physics -function M:jump(entity) - local pf = entity.platformer_physics - - if pf.contact_timers[4] > 0 then - pf.desired_jump = false - pf.jump_speed = math.sqrt(-2 * pf.gravity_y * pf.jump_height * pf.gravity_scale) - - if pf.velocity_y > 0 then - pf.jump_speed = math.max(pf.jump_speed - pf.velocity_y, 0) - elseif pf.velocity_y < 0 then - pf.jump_speed = pf.jump_speed + math.abs(pf.velocity_y) - end - - pf.velocity_y = pf.velocity_y + pf.jump_speed - end - - pf.desired_jump = false -end - - -function M:sign(x) - if x > 0 then - return 1 - elseif x < 0 then - return -1 - else - return 0 - end -end - - ----Move value from current to target value with step amount ----@param current number Current value ----@param target number Target value ----@param step number Step amount ----@return number New value -function M:step(current, target, step) - if current < target then - return math.min(current + step, target) - else - return math.max(target, current - step) - end -end - - ----@param collision_event system.collision.event ----@param entity entity.platformer_physics -function M:process_collision_event(collision_event, entity) - local contact_point_event = collision_event.contact_point_event - - if contact_point_event then - -- project the correction vector onto the contact normal - -- (the correction vector is the 0-vector for the first contact point) - local pf = entity.platformer_physics - local normal = collision_event.contact_point_event.b.normal - if not normal then - normal = collision_event.contact_point_event.a.normal - end - - if not normal or not pf then - return - end - - local distance = collision_event.contact_point_event.distance - - local proj = vmath.dot(pf.correction, normal) - -- calculate the compensation we need to make for this contact point - local comp = (distance - proj) * normal - -- add it to the correction vector - pf.correction = pf.correction + comp - - -- check if the normal points enough up to consider the player standing on the ground - -- (0.7 is roughly equal to 45 degrees deviation from pure vertical direction) - pf.ground_timer = normal.y > 0.7 and 0.06 or 0 - - pf.contact_timers[1] = normal.x > 0.7 and 0.06 or pf.contact_timers[1] - pf.contact_timers[2] = normal.y < -0.7 and 0.06 or pf.contact_timers[2] - pf.contact_timers[3] = normal.x < -0.7 and 0.06 or pf.contact_timers[3] - pf.contact_timers[4] = normal.y > 0.7 and 0.06 or pf.contact_timers[4] - - local velocity = vmath.vector3(0) - -- project the velocity onto the normal - proj = vmath.dot(velocity, normal) - -- if the projection is negative, it means that some of the velocity points towards the contact point - if proj < 0 then - -- remove that component in that case - local c = proj * normal - pf.velocity_x = pf.velocity_x - c.x - pf.velocity_y = pf.velocity_y - c.y - end - - if pf.contact_timers[4] > 0 then - pf.velocity_y = math.max(0, pf.velocity_y) - end - end -end - - -return M diff --git a/system/quadtree/command_quadtree.lua b/system/quadtree/command_quadtree.lua deleted file mode 100644 index 63f0ad8..0000000 --- a/system/quadtree/command_quadtree.lua +++ /dev/null @@ -1,24 +0,0 @@ ----@class world ----@field command_quadtree system.quadtree.command - ----@class system.quadtree.command ----@field quadtree system.quadtree -local M = {} - - ----@return system.quadtree.command -function M.create(quadtree) - return setmetatable({ quadtree = quadtree }, { __index = M }) -end - - ----@param entity entity ----@param radius number -function M:get_neighbors(entity, radius, callback) - assert(entity.transform, "entity must have transform component") - ---@cast entity entity.transform - return self.quadtree:get_neighbors(entity, radius, callback) -end - - -return M diff --git a/system/quadtree/quadtree.lua b/system/quadtree/quadtree.lua deleted file mode 100644 index 82ec313..0000000 --- a/system/quadtree/quadtree.lua +++ /dev/null @@ -1,367 +0,0 @@ ----@class quadtree_state ----@field x number ----@field y number ----@field width number ----@field height number ----@field data any ----@field quadtree quadtree - ----@class quadtree ----@field split_threshold_count number ----@field max_levels number ----@field level number ----@field x number ----@field y number ----@field width number ----@field height number ----@field objects table -- Dictionary for O(1) access ----@field objects_count number -- Number of objects in this node ----@field len number -- Total number of objects in the quadtree ----@field sectors quadtree[] -- The 4 child nodes, [1] = bottom left, [2] = top left, [3] = top right, [4] = bottom right ----@field parent quadtree|nil -- The parent node -local M = {} - ----Create new quadtree object. Inner objects are also quadtrees ----@param split_threshold_count number ----@param max_levels number ----@param level number|nil ----@param x number|nil ----@param y number|nil ----@param width number|nil ----@param height number|nil ----@return quadtree -function M.create(split_threshold_count, max_levels, level, x, y, width, height) - local self = setmetatable({ - split_threshold_count = split_threshold_count or 4, - max_levels = max_levels or 5, - level = level or 0, - x = x or 0, - y = y or 0, - width = width or 0, - height = height or 0, - objects = nil, - objects_count = 0, - len = 0, -- Total number of objects - sectors = nil, - }, { __index = M }) - return self -end - ----Insert entity into quadtree ----@param data any ----@param x number ----@param y number ----@param width number ----@param height number ----@return quadtree_state -function M:insert(data, x, y, width, height) - local quadtree_state = { x = x, y = y, width = width, height = height, data = data } - self:insert_state(quadtree_state) - return quadtree_state -end - - ----Insert a quadtree_state into the quadtree ----@param quadtree_state quadtree_state ----@return boolean success -function M:insert_state(quadtree_state) - quadtree_state.quadtree = self - self.objects = self.objects or {} - - -- If sectors exist, insert into sector - if self.sectors then - local x, y = quadtree_state.x, quadtree_state.y - local width, height = quadtree_state.width, quadtree_state.height - local index = self:get_index(x, y, width, height) - if index ~= 0 then - return self.sectors[index]:insert_state(quadtree_state) - end - end - - local is_inserted = false - if not self.objects[quadtree_state] then - is_inserted = true - self.objects[quadtree_state] = true - self.objects_count = self.objects_count + 1 - - if not self.sectors then - if self.objects_count > self.split_threshold_count and self.level < self.max_levels then - self:subdivide() - end - end - end - - return is_inserted -end - - -function M:union() - if self.sectors and self:count_objects() - self.objects_count == 0 then - self.sectors = nil - end -end - - ----Remove entity from quadtree ----@param quadtree_state quadtree_state ----@return boolean success -function M:remove(quadtree_state) - -- If no sector or object is not in sector - if self.objects and self.objects[quadtree_state] then - self.objects[quadtree_state] = nil - self.objects_count = self.objects_count - 1 - --if self.parent then - -- self.parent:remove_empty_sectors() - --end - return true - end - - if self.sectors then - local index = self:get_index(quadtree_state.x, quadtree_state.y, quadtree_state.width, quadtree_state.height) - if index ~= 0 then - return self.sectors[index]:remove(quadtree_state) - end - end - - return false -end - ----Subdivide the node into four child nodes -function M:subdivide() - local half_width = self.width / 2 - local half_height = self.height / 2 - local x = self.x - local y = self.y - local level = self.level + 1 - - -- Bottom left - self.sectors = {} - - self.sectors[1] = M.create(self.split_threshold_count, self.max_levels, level, x, y, half_width, half_height) - self.sectors[1].parent = self - -- Top left - self.sectors[2] = M.create(self.split_threshold_count, self.max_levels, level, x, y + half_height, half_width, half_height) - self.sectors[2].parent = self - -- Top right - self.sectors[3] = M.create(self.split_threshold_count, self.max_levels, level, x + half_width, y + half_height, half_width, half_height) - self.sectors[3].parent = self - -- Bottom right - self.sectors[4] = M.create(self.split_threshold_count, self.max_levels, level, x + half_width, y, half_width, half_height) - self.sectors[4].parent = self - - -- Split objects into sectors - for obj in pairs(self.objects) do - local index = self:get_index(obj.x, obj.y, obj.width, obj.height) - if index ~= 0 then - self.objects[obj] = nil - self.objects_count = self.objects_count - 1 - obj.quadtree = nil - - self.sectors[index]:insert_state(obj) - end - end -end - ----Determine which node the object belongs to ----@param x number ----@param y number ----@param width number ----@param height number ----@return number index The 1 is bottom left, 2 is top left, 3 is top right, 4 is bottom right. 0 if object cannot completely fit within a child node -function M:get_index(x, y, width, height) - local mid_x = self.x + (self.width / 2) - local mid_y = self.y + (self.height / 2) - - local is_left = x + width <= mid_x - local is_right = x >= mid_x - local is_bottom = y + height <= mid_y - local is_top = y >= mid_y - - if is_left then - if is_bottom then - return 1 - elseif is_top then - return 2 - end - elseif is_right then - if is_top then - return 3 - elseif is_bottom then - return 4 - end - end - - return 0 -end - ----Update entity in quadtree ----@param quadtree_state quadtree_state ----@param x number ----@param y number ----@param width number ----@param height number -function M:update(quadtree_state, x, y, width, height) - local parent_quadtree = self:get_state_to_put(x, y, width, height) - local is_quadtree_state_changed = quadtree_state.quadtree ~= parent_quadtree - - if not is_quadtree_state_changed then - quadtree_state.x = x - quadtree_state.y = y - quadtree_state.width = width - quadtree_state.height = height - return - end - - local prev_quadtree = quadtree_state.quadtree - prev_quadtree:remove(quadtree_state) - - quadtree_state.x = x - quadtree_state.y = y - quadtree_state.width = width - quadtree_state.height = height - parent_quadtree:insert_state(quadtree_state) -end - - -function M:remove_empty_sectors() - if not self.sectors then - return - end - - local is_sectors_empty = true - for i = 1, 4 do - if self.sectors[i] and self.sectors[i]:count_objects() > 0 then - is_sectors_empty = false - break - end - end - - if is_sectors_empty then - self.sectors = nil - end -end - ----Get the appropriate node to place the object ----@param x number ----@param y number ----@param width number ----@param height number ----@return quadtree -function M:get_state_to_put(x, y, width, height) - if self.sectors then - local index = self:get_index(x, y, width, height) - if index ~= 0 then - return self.sectors[index]:get_state_to_put(x, y, width, height) - end - end - return self -end - ----Retrieve all entities within a given rectangular area ----@param callback fun(quadtree_state: quadtree_state) ----@param x number ----@param y number ----@param w number ----@param h number -function M:retrieve(callback, x, y, w, h) - if self.sectors then - local index = self:get_index_area(x, y, w, h) - if index ~= 0 then - self.sectors[index]:retrieve(callback, x, y, w, h) - end - end - - if self.objects then - for obj in pairs(self.objects) do - callback(obj.data) - end - end -end - ----Get index for area ----@param x number ----@param y number ----@param w number ----@param h number ----@return number -function M:get_index_area(x, y, w, h) - local index = 0 - local mid_x = self.x + (self.width / 2) - local mid_y = self.y + (self.height / 2) - - local is_bottom = y + h <= mid_y - local is_top = y >= mid_y - - local is_left = x + w <= mid_x - local is_right = x >= mid_x - - if is_left then - if is_bottom then - index = 1 - elseif is_top then - index = 2 - end - elseif is_right then - if is_top then - index = 3 - elseif is_bottom then - index = 4 - end - end - - return index -end - ----Get all entities in radius ----@param x number X position ----@param y number Y position ----@param radius number Circle radius -function M:get_in_radius(x, y, radius, callback) - local x_area = x - radius - local y_area = y - radius - local w_area = radius * 2 - local h_area = radius * 2 - self:retrieve(callback, x_area, y_area, w_area, h_area) -end - ----Get all entities in a rectangle ----@param x number ----@param y number ----@param width number ----@param height number ----@param callback fun(quadtree_state: quadtree_state) -function M:get_in_rect(x, y, width, height, callback) - return self:retrieve(callback, x, y, width, height) -end - ----Get all entities in the quadtree ----@param callback fun(quadtree_state: quadtree_state) -function M:get_all(callback) - return self:retrieve(callback, self.x, self.y, self.width, self.height) -end - ----Count the total number of objects in the quadtree ----@return number -function M:count_objects() - local objects = self.objects_count - if self.sectors then - for i = 1, #self.sectors do - objects = objects + self.sectors[i]:count_objects() - end - end - return objects -end - -function M:print_scheme(ident) - ident = ident or "" - print(ident .. "Level: " .. self.level) - print(ident .. "Objects: " .. self.objects_count, "Total: " .. self:count_objects()) - print(ident .. "Sectors: " .. self.sectors and #self.sectors) - if self.sectors then - for i = 1, #self.sectors do - self.sectors[i]:print_scheme(ident .. " ") - end - end -end - -return M diff --git a/system/quadtree/system_quadtree.lua b/system/quadtree/system_quadtree.lua deleted file mode 100644 index 08fe9ae..0000000 --- a/system/quadtree/system_quadtree.lua +++ /dev/null @@ -1,126 +0,0 @@ -local decore = require("decore.decore") -local quadtree = require("system.quadtree.quadtree") - -local command_quadtree = require("system.quadtree.command_quadtree") - -local logger = decore.get_logger("system_quadtree") - ----@class entity ----@field quadtree boolean|nil - ----@class entity.quadtree: entity ----@field quadtree component.quadtree ----@field transform component.transform - ----@class component.quadtree: boolean -decore.register_component("quadtree", false) - ----@class system.quadtree: system ----@field quadtree quadtree ----@field debug_is_draw_quadtree boolean ----@field entity_to_state table ----@field entity_to_update table -local M = {} - -local width = sys.get_config_int("display.width") -local height = sys.get_config_int("display.height") - ----@return system.quadtree -function M.create_system() - local system = decore.system(M, "quadtree", { "transform", "quadtree" }) - system.quadtree = quadtree.create(5, 6, 0, -width/2, -height/2, width, height) - system.debug_is_draw_quadtree = false - system.entity_to_state = {} - system.entity_to_update = {} - - return system -end - - -function M:onAddToWorld() - self.world.command_quadtree = command_quadtree.create(self) -end - - -function M:postWrap() - self.world.event_bus:process("transform_event", self.process_transform_event, self) - - for entity, quadtree_state in pairs(self.entity_to_update) do - local t = entity.transform - self.quadtree:update(quadtree_state, t.position_x, t.position_y, t.size_x, t.size_y) - end -end - - ----@param entity entity.transform -function M:onAdd(entity) - local t = entity.transform - local x = t.position_x - local y = t.position_y - local w = t.size_x - local h = t.size_y - - local quadtree_state = self.quadtree:insert(entity, x, y, w, h) - self.entity_to_state[entity] = quadtree_state -end - - -function M:onRemove(entity) - local quadtree_state = self.entity_to_state[entity] - self.quadtree:remove(quadtree_state) - self.entity_to_state[entity] = nil -end - - -function M:update(dt) - if self.debug_is_draw_quadtree then - self:debug_draw_quadtree(self.quadtree) - end -end - - ----@param quadtree quadtree -function M:debug_draw_quadtree(quadtree) - if not self.world.command_debug_draw then - return - end - - if quadtree.objects_count > 0 then - local x, y, w, h = quadtree.x, quadtree.y, quadtree.width, quadtree.height - self.world.command_debug_draw:draw_rectangle(x + w/2, y + h/2, w, h) - --self.world.command_debug_draw:draw_text(x, y, #quadtree.objects) - end - - if quadtree.sectors then - for index = 1, #quadtree.sectors do - self:debug_draw_quadtree(quadtree.sectors[index]) - end - end -end - - ----@param event system.transform.event -function M:process_transform_event(event) - local entity = event.entity - local quadtree_state = self.entity_to_state[entity] - if not quadtree_state then - return - end - - if event.is_position_changed or event.is_size_changed then - self.entity_to_update[entity] = quadtree_state - end -end - - ----@param entity entity.transform ----@param radius number ----@param callback fun(entity: entity) -function M:get_neighbors(entity, radius, callback) - local position_x = entity.transform.position_x - local position_y = entity.transform.position_y - self.quadtree:get_in_radius(position_x, position_y, radius, callback) -end - - -return M diff --git a/system/quadtree/test_quadtree.lua b/system/quadtree/test_quadtree.lua deleted file mode 100644 index 6f4c5c1..0000000 --- a/system/quadtree/test_quadtree.lua +++ /dev/null @@ -1,50 +0,0 @@ -return function() - describe("System Quadtree", function() - local decore --- @type decore - local world ---@type world - local system_quadtree ---@type system.quadtree - local quadtree ---@type quadtree - - before(function() - decore = require("decore.decore") - system_quadtree = require("system.quadtree.system_quadtree") - quadtree = require("system.quadtree.quadtree") - - world = decore.new_world() - world:add(system_quadtree.create_system()) - end) - - it("Quadtree can be created and requested", function() - local q = quadtree.create(3, 3) - assert(q) - - local entity = { - transform = { - position_x = 10, - position_y = 20, - size_x = 10, - size_y = 10, - } - } - local t = entity.transform - q:insert(entity, t.position_x, t.position_y, t.size_x, t.size_y) - - local counter = 0 - q:get_in_rect(0, 0, 0, 0, function() counter = counter + 1 end) - -- TODO: it's wrong - assert(counter == 1) - - counter = 0 - q:get_in_rect(0, 0, 10, 10, function() counter = counter + 1 end) - assert(counter == 1) - - counter = 0 - q:get_in_rect(0, 0, 20, 20, function() counter = counter + 1 end) - assert(counter == 1) - - counter = 0 - q:get_in_rect(0, 0, 30, 30, function() counter = counter + 1 end) - assert(counter == 1) - end) - end) -end diff --git a/system/remove_with_delay/component_remove_with_delay.script b/system/remove_with_delay/component_remove_with_delay.script deleted file mode 100644 index 95f0d10..0000000 --- a/system/remove_with_delay/component_remove_with_delay.script +++ /dev/null @@ -1,7 +0,0 @@ -go.property("delay", 1) - -local component = require("decore.component") - -function init(self) - component.init("remove_with_delay", self.delay) -end \ No newline at end of file diff --git a/system/remove_with_delay/system_remove_with_delay.lua b/system/remove_with_delay/system_remove_with_delay.lua deleted file mode 100644 index feb04d4..0000000 --- a/system/remove_with_delay/system_remove_with_delay.lua +++ /dev/null @@ -1,33 +0,0 @@ -local decore = require("decore.decore") - ----@class entity ----@field remove_with_delay number|nil - ----@class entity.remove_with_delay: entity ----@field remove_with_delay number - ----@class component.remove_with_delay -decore.register_component("remove_with_delay", 0) - ----@class system.remove_with_delay: system ----@field entities entity.remove_with_delay[] -local M = {} - - ----@return system.remove_with_delay -function M.create_system() - return decore.processing_system(M, "remove_with_delay", "remove_with_delay") -end - - ----@param entity entity.remove_with_delay ----@param dt number -function M:process(entity, dt) - entity.remove_with_delay = entity.remove_with_delay - dt - if entity.remove_with_delay <= 0 then - self.world:removeEntity(entity) - end -end - - -return M diff --git a/system/transform/command_transform.lua b/system/transform/command_transform.lua deleted file mode 100644 index 8f027cd..0000000 --- a/system/transform/command_transform.lua +++ /dev/null @@ -1,110 +0,0 @@ ----@class world ----@field command_transform system.transform.command - ----@class system.transform.command ----@field transform system.transform -local M = {} - - ----@param transform system.transform ----@return system.transform.command -function M.create(transform) - return setmetatable({ transform = transform }, { __index = M }) -end - - ----@param entity entity ----@param x number|nil ----@param y number|nil ----@param z number|nil -function M:set_position(entity, x, y, z) - assert(entity.transform, "Entity does not have a transform component.") - ---@cast entity entity.transform - self.transform:set_position(entity, x, y, z) -end - - ----@param entity entity ----@param x number|nil ----@param y number|nil ----@param z number|nil -function M:add_position(entity, x, y, z) - local t = entity.transform - assert(t, "Entity does not have a transform component.") - ---@cast entity entity.transform - - x = x and t.position_x + x - y = y and t.position_y + y - z = z and t.position_z + z - self.transform:set_position(entity, x, y, z) -end - - ----@param entity entity ----@param x number|nil ----@param y number|nil ----@param z number|nil -function M:set_scale(entity, x, y, z) - assert(entity.transform, "Entity does not have a transform component.") - ---@cast entity entity.transform - self.transform:set_scale(entity, x, y, z) -end - - ----@param entity entity ----@param x number|nil ----@param y number|nil ----@param z number|nil -function M:set_size(entity, x, y, z) - assert(entity.transform, "Entity does not have a transform component.") - ---@cast entity entity.transform - self.transform:set_size(entity, x, y, z) -end - - -function M:set_rotation(entity, rotation) - assert(entity.transform, "Entity does not have a transform component.") - ---@cast entity entity.transform - self.transform:set_rotation(entity, rotation) -end - - ----@param entity entity ----@param animate_time number|nil ----@param easing userdata|nil -function M:set_animate_time(entity, animate_time, easing) - assert(entity.transform, "Entity does not have a transform component.") - ---@cast entity entity.transform - self.transform:set_animate_time(entity, animate_time, easing) -end - - ----Return node borders relative to the current node parent ----@param entity entity ----@return number, number, number, number @left, top, right, bottom -function M:get_transform_borders(entity) - local t = entity.transform --[[@as component.transform]] - - local left = t.position_x - t.size_x * 0.5 - local top = t.position_y + t.size_y * 0.5 - local right = t.position_x + t.size_x * 0.5 - local bottom = t.position_y - t.size_y * 0.5 - - return left, top, right, bottom -end - - ----Check if two entities are overlapping ----@param entity1 entity ----@param entity2 entity ----@return boolean -function M:is_overlap(entity1, entity2) - local left1, right1, top1, bottom1 = self:get_transform_borders(entity1) - local left2, right2, top2, bottom2 = self:get_transform_borders(entity2) - - return left1 < right2 and right1 > left2 and top1 > bottom2 and bottom1 < top2 -end - - - -return M diff --git a/system/transform/system_transform.lua b/system/transform/system_transform.lua deleted file mode 100644 index a87741d..0000000 --- a/system/transform/system_transform.lua +++ /dev/null @@ -1,176 +0,0 @@ -local decore = require("decore.decore") -local command_transform = require("system.transform.command_transform") - ----@class entity ----@field transform component.transform|nil - ----@class entity.transform: entity ----@field transform component.transform - ----@class entity.command_transform: entity ----@field transform component.transform - ----@class component.transform ----@field position_x number The position x ----@field position_y number The position y ----@field position_z number The position z ----@field size_x number The size x ----@field size_y number The size y ----@field size_z number The size z ----@field scale_x number The scale x ----@field scale_y number The scale y ----@field scale_z number The scale z ----@field rotation number The rotation -decore.register_component("transform", { - position_x = 0, - position_y = 0, - position_z = 0, - size_x = 1, - size_y = 1, - size_z = 1, - scale_x = 1, - scale_y = 1, - scale_z = 1, - rotation = 0, -}) - ----@class system.transform.event ----@field entity entity.transform The entity that was changed. ----@field is_position_changed boolean|nil If true, the position was changed. ----@field is_scale_changed boolean|nil If true, the scale was changed. ----@field is_rotation_changed boolean|nil If true, the rotation was changed. ----@field is_size_changed boolean|nil If true, the size was changed. ----@field animate_time number|nil If true, the time it took to animate the transform. ----@field easing userdata|nil The easing function used for the animation. - ----@class system.transform: system ----@field entities entity.transform[] -local M = {} - - ----@return system.transform -function M.create() - return decore.system(M, "transform", "transform") -end - - -function M:onAddToWorld() - self.world.command_transform = command_transform.create(self) - self.world.event_bus:set_merge_policy("transform_event", self.event_merge_policy) -end - - ----@param entity entity.transform ----@param x number|nil ----@param y number|nil ----@param z number|nil -function M:set_position(entity, x, y, z) - local t = entity.transform - local is_changed = (x and t.position_x ~= x) or (y and t.position_y ~= y) or (z and t.position_z ~= z) - - t.position_x = x or t.position_x - t.position_y = y or t.position_y - t.position_z = z or t.position_z - - if is_changed then - self.world.event_bus:trigger("transform_event", { - entity = entity, - is_position_changed = true, - }) - end -end - - ----@param entity entity.transform ----@param x number|nil ----@param y number|nil ----@param z number|nil -function M:set_scale(entity, x, y, z) - local t = entity.transform - local is_changed = (x and t.scale_x ~= x) or (y and t.scale_y ~= y) or (z and t.scale_z ~= z) - - t.scale_x = x or t.scale_x - t.scale_y = y or t.scale_y - t.scale_z = z or t.scale_z - - if is_changed then - self.world.event_bus:trigger("transform_event", { - entity = entity, - is_scale_changed = true, - }) - end -end - - ----@param entity entity.transform ----@param x number|nil ----@param y number|nil ----@param z number|nil -function M:set_size(entity, x, y, z) - local t = entity.transform - local is_changed = (x and t.size_x ~= x) or (y and t.size_y ~= y) or (z and t.size_z ~= z) - - t.size_x = x or t.size_x - t.size_y = y or t.size_y - t.size_z = z or t.size_z - - if is_changed then - self.world.event_bus:trigger("transform_event", { - entity = entity, - is_size_changed = true, - }) - end -end - - ----@param entity entity.transform ----@param rotation number In degrees -function M:set_rotation(entity, rotation) - local t = entity.transform - local is_changed = t.rotation ~= rotation - t.rotation = rotation or t.rotation - - if is_changed then - self.world.event_bus:trigger("transform_event", { - entity = entity, - is_rotation_changed = true, - }) - end -end - - ----@param entity entity.transform ----@param animate_time number|nil ----@param easing userdata|nil -function M:set_animate_time(entity, animate_time, easing) - self.world.event_bus:trigger("transform_event", { - entity = entity, - animate_time = animate_time, - easing = easing, - }) -end - - ----@param events system.transform.event[] ----@param event system.transform.event ----@return boolean is_merged -function M.event_merge_policy(event, events) - local entity = event.entity - for i = 1, #events do - local e = events[i] - if e.entity == entity then - e.is_position_changed = event.is_position_changed or e.is_position_changed - e.is_scale_changed = event.is_scale_changed or e.is_scale_changed - e.is_rotation_changed = event.is_rotation_changed or e.is_rotation_changed - e.is_size_changed = event.is_size_changed or e.is_size_changed - e.animate_time = event.animate_time or e.animate_time - e.easing = event.easing or e.easing - return true - end - end - - return false -end - - -return M diff --git a/system/transform/test_transform.lua b/system/transform/test_transform.lua deleted file mode 100644 index 0793bb9..0000000 --- a/system/transform/test_transform.lua +++ /dev/null @@ -1,94 +0,0 @@ -return function() - describe("System transform", function() - local decore ---@type decore - local world ---@type world - local system_transform ---@type system.transform - - before(function() - decore = require("decore.decore") - system_transform = require("system.transform.system_transform") - - world = decore.new_world() - world:add(system_transform.create()) - end) - - it("Should init correctly", function() - local entity = decore.create({ transform = { - position_x = 10, - position_y = 20, - }}) - world:add(entity) - world:refresh() - - assert(entity.transform.position_x == 10) - assert(entity.transform.position_y == 20) - - -- Now it works only if we create an entity with decore - -- How or should we update it? - --assert(entity.transform.position_z == 0) - end) - - it("Should trigger transform_event on position change", function() - local entity = decore.create({ transform = { - position_x = 10, - position_y = 20, - }}) - world:add(entity) - world:refresh() - - world.command_transform:set_position(entity, 20, 30) - assert(world.event_bus:get_stash("transform_event")) - assert(#world.event_bus:get_stash("transform_event") == 1) - local event = world.event_bus:get_stash("transform_event")[1] - assert(event.entity == entity) - assert(event.is_position_changed) - end) - - it("Should trigger transform_event on scale change", function() - local entity = decore.create({ transform = { - scale_x = 1, - scale_y = 1, - }}) - world:add(entity) - world:refresh() - - world.command_transform:set_scale(entity, 2, 2) - assert(world.event_bus:get_stash("transform_event")) - assert(#world.event_bus:get_stash("transform_event") == 1) - local event = world.event_bus:get_stash("transform_event")[1] - assert(event.entity == entity) - assert(event.is_scale_changed) - end) - - it("Should trigger transform_event on size change", function() - local entity = decore.create({ transform = { - size_x = 1, - size_y = 1, - }}) - world:add(entity) - world:refresh() - - world.command_transform:set_size(entity, 2, 2) - assert(world.event_bus:get_stash("transform_event")) - assert(#world.event_bus:get_stash("transform_event") == 1) - local event = world.event_bus:get_stash("transform_event")[1] - assert(event.entity == entity) - assert(event.is_size_changed) - end) - - it("Should trigger transform_event on rotation change", function() - local entity = decore.create({ transform = { - rotation = 0, - }}) - world:add(entity) - world:refresh() - - world.command_transform:set_rotation(entity, 90) - assert(world.event_bus:get_stash("transform_event")) - assert(#world.event_bus:get_stash("transform_event") == 1) - local event = world.event_bus:get_stash("transform_event")[1] - assert(event.entity == entity) - assert(event.is_rotation_changed) - end) - end) -end diff --git a/system/transform_animate_on_event/system_transform_animate_on_event.lua b/system/transform_animate_on_event/system_transform_animate_on_event.lua deleted file mode 100644 index 2960e94..0000000 --- a/system/transform_animate_on_event/system_transform_animate_on_event.lua +++ /dev/null @@ -1,155 +0,0 @@ -local decore = require("decore.decore") - -local HASH_EMPTY = hash("") - ----@class entity ----@field transform_animate_on_event component.transform_animate_on_event|nil - ----@class entity.transform_animate_on_event: entity ----@field transform_animate_on_event component.transform_animate_on_event ----@field transform component.transform - ----@class component.transform_animate_on_event ----@field event_id hash|nil ----@field easing constant ----@field time number ----@field trigger_on_add boolean ----@field is_position_relative boolean ----@field position vector3 x, y, z ----@field is_rotation_relative boolean ----@field rotation vector3 x, y, z ----@field is_scale_relative boolean ----@field scale vector3 x, y, z ----@field is_size_relative boolean ----@field size vector3 x, y, z -decore.register_component("transform_animate_on_event", { - event_id = nil, - easing = nil, - time = 0, - - trigger_on_add = false, - - is_position_relative = false, - position = nil, - - is_rotation_relative = false, - rotation = nil, - - is_scale_relative = false, - scale = nil, - - is_size_relative = false, - size = nil, -}) - ----@class system.transform_animate_on_event: system ----@field entities entity.transform_animate_on_event[] ----@field event_to_entities table ----@field event_to_trigger_on_add entity.transform_animate_on_event[] -local M = {} - ----@return system.transform_animate_on_event -function M.create_system() - local system = decore.system(M, "transform_animate_on_event", { "transform_animate_on_event", "transform" }) - system.event_to_entities = {} - system.event_to_trigger_on_add = {} - return system -end - - -function M:postWrap() - for event_id, entities in pairs(self.event_to_entities) do - self.world.event_bus:process(event_id, function(event, entity) - for index = 1, #entities do - self:process_event(entities[index]) - end - end) - end -end - - -function M:onAdd(entity) - local event_id = entity.transform_animate_on_event.event_id - if event_id and event_id ~= HASH_EMPTY then - self.event_to_entities[event_id] = self.event_to_entities[event_id] or {} - table.insert(self.event_to_entities[event_id], entity) - end - - if entity.transform_animate_on_event.trigger_on_add then - table.insert(self.event_to_trigger_on_add, entity) - end -end - - -function M:onRemove(entity) - local event_id = entity.transform_animate_on_event.event_id - if event_id and event_id ~= HASH_EMPTY then - local entities = self.event_to_entities[event_id] - if not entities then - return - end - - for index = #entities, 1, -1 do - if entities[index] == entity then - table.remove(entities, index) - break - end - end - end -end - - -function M:update() - for index = #self.event_to_trigger_on_add, 1, -1 do - self:process_event(self.event_to_trigger_on_add[index]) - self.event_to_trigger_on_add[index] = nil - end -end - - ----@param entity entity.transform_animate_on_event -function M:process_event(entity) - local transform = entity.transform - local animate_transform = entity.transform_animate_on_event - - local position = animate_transform.position - if position then - local x, y, z = position.x, position.y, position.z - if animate_transform.is_position_relative then - x, y, z = x + transform.position_x, y + transform.position_y, z + transform.position_z - end - self.world.command_transform:set_position(entity, x, y, z) - end - - local rotation = animate_transform.rotation - if rotation then - local euler_z = rotation.z - if animate_transform.is_rotation_relative then - euler_z = euler_z + transform.rotation - end - self.world.command_transform:set_rotation(entity, euler_z) - end - - local scale = animate_transform.scale - if scale then - local x, y, z = scale.x, scale.y, scale.z - if animate_transform.is_scale_relative then - x, y, z = x + transform.scale_x, y + transform.scale_y, z + transform.scale_z - end - self.world.command_transform:set_scale(entity, x, y, z) - end - - local size = animate_transform.size - if size then - local x, y, z = size.x, size.y, size.z - if animate_transform.is_size_relative then - x, y, z = x + transform.size_x, y + transform.size_y, z + transform.size_z - end - self.world.command_transform:set_size(entity, x, y, z) - end - - self.world.command_transform:set_animate_time(entity, animate_transform.time, animate_transform.easing) -end - - -return M diff --git a/system/transform_border/system_transform_border.lua b/system/transform_border/system_transform_border.lua deleted file mode 100644 index 7b7b797..0000000 --- a/system/transform_border/system_transform_border.lua +++ /dev/null @@ -1,91 +0,0 @@ -local decore = require("decore.decore") - ----@class entity ----@field transform_border component.transform_border|nil - ----@class entity.transform_border: entity ----@field transform_border component.transform_border ----@field transform component.transform - ----@class component.transform_border ----@field border vector4 ----@field is_wrap boolean ----@field is_limit boolean ----@field random_position boolean If true, the entity will be placed randomly within the border -decore.register_component("transform_border", { - is_wrap = false, - is_limit = true, - random_position = false, -}) - ----@class system.transform_border: system ----@field entities entity.transform_border[] -local M = {} - - ----@return system.transform_border -function M.create() - return decore.system(M, "transform_border", { "transform_border", "transform" }) -end - - ----@param entity entity.transform_border -function M:onAdd(entity) - if entity.transform_border.random_position then - local border = entity.transform_border.border - local x = math.random(border.x, border.z) - local y = math.random(border.w, border.y) - self.world.command_transform:set_position(entity, x, y) - end -end - - -function M:postWrap() - self.world.event_bus:process("transform_event", self.process_transform_event, self) -end - - ----@param event system.transform.event -function M:process_transform_event(event) - local entity = event.entity - local transform_border = entity.transform_border - - if transform_border then - local border = transform_border.border - local left, top, right, bottom = self.world.command_transform:get_transform_borders(entity) - - if left < border.x or top > border.y or right > border.z or bottom < border.w then - local t = entity.transform - - if transform_border.is_wrap then - local x = t.position_x - local y = t.position_y - local size_x_half = t.size_x/2 - local size_y_half = t.size_y/2 - - -- Wrap horizontally - if x - size_x_half > border.z then - x = border.x + size_x_half - elseif x + size_x_half < border.x then - x = border.z - size_x_half - end - - -- Wrap vertically - if y - size_y_half > border.y then - y = border.w + size_y_half - elseif y + size_y_half < border.w then - y = border.y - size_y_half - end - - self.world.command_transform:set_position(entity, x, y) - elseif transform_border.is_limit then - local x = decore.clamp(t.position_x, border.x + t.size_x/2, border.z - t.size_x/2) - local y = decore.clamp(t.position_y, border.w + t.size_y/2, border.y - t.size_y/2) - self.world.command_transform:set_position(entity, x, y) - end - end - end -end - - -return M diff --git a/system/velocity/command_velocity.lua b/system/velocity/command_velocity.lua deleted file mode 100644 index 9135c2c..0000000 --- a/system/velocity/command_velocity.lua +++ /dev/null @@ -1,65 +0,0 @@ --- command_velocity.lua ----@class world ----@field command_velocity system.velocity.command - ----@class system.velocity.command ----@field velocity system.velocity -local M = {} - - ----@param velocity system.velocity ----@return system.velocity.command -function M.create(velocity) - return setmetatable({ velocity = velocity }, { __index = M }) -end - - -function M:set_velocity(entity, x, y) - assert(entity.velocity, "Entity does not have a velocity component.") - ---@cast entity entity.velocity - self.velocity:set_velocity(entity, x, y) -end - - -function M:add_velocity(entity, x, y) - assert(entity.velocity, "Entity does not have a velocity component.") - ---@cast entity entity.velocity - self.velocity:set_velocity(entity, entity.velocity.x + x, entity.velocity.y + y) -end - - ----Set entity velocity angle ----@param entity entity ----@param angle number Angle in degrees -function M:set_angle(entity, angle) - assert(entity.velocity, "Entity does not have a velocity component.") - ---@cast entity entity.velocity - self.velocity:set_angle(entity, angle) -end - - ----Set entity velocity speed ----@param entity entity ----@param speed number Speed value -function M:set_speed(entity, speed) - assert(entity.velocity, "Entity does not have a velocity component.") - ---@cast entity entity.velocity - self.velocity:set_speed(entity, speed) -end - - -function M:set_min_speed(entity, min_speed) - assert(entity.velocity, "Entity does not have a velocity component.") - ---@cast entity entity.velocity - self.velocity:set_min_speed(entity, min_speed) -end - - -function M:set_max_speed(entity, max_speed) - assert(entity.velocity, "Entity does not have a velocity component.") - ---@cast entity entity.velocity - self.velocity:set_max_speed(entity, max_speed) -end - - -return M diff --git a/system/velocity/component_velocity.script b/system/velocity/component_velocity.script deleted file mode 100644 index fb65717..0000000 --- a/system/velocity/component_velocity.script +++ /dev/null @@ -1,10 +0,0 @@ -go.property("velocity", vmath.vector3(0, 0, 0)) - -local component = require("decore.component") - -function init(self) - component.init("velocity", { - x = self.velocity.x, - y = self.velocity.y, - }) -end \ No newline at end of file diff --git a/system/velocity/system_velocity.lua b/system/velocity/system_velocity.lua deleted file mode 100644 index ae9fd06..0000000 --- a/system/velocity/system_velocity.lua +++ /dev/null @@ -1,118 +0,0 @@ -local decore = require("decore.decore") -local command_velocity = require("system.velocity.command_velocity") - ----@class entity ----@field velocity component.velocity|nil - ----@class entity.velocity: entity ----@field transform component.transform ----@field velocity component.velocity - ----@class component.velocity ----@field angle number ----@field speed number ----@field max_speed number ----@field min_speed number ----@field x number ----@field y number -decore.register_component("velocity", { - speed = 0, - angle = 0, - max_speed = 0, - min_speed = 0, - x = 0, - y = 0, -}) - ----@class system.velocity: system ----@field entities entity.velocity[] ----@field debug_draw boolean -local M = {} - - ----@return system.velocity -function M.create_system() - return decore.processing_system(M, "velocity", { "velocity", "transform" }) -end - - -function M:onAddToWorld() - self.debug_draw = false - self.world.command_velocity = command_velocity.create(self) -end - - -function M:onAdd(entity) - local velocity = entity.velocity - if velocity.speed > 0 then - self:set_angle(entity, velocity.angle) - end -end - - -function M:set_angle(entity, angle) - local velocity = entity.velocity - velocity.angle = angle - - -- Update velocity components - local rad = math.rad(angle) - velocity.x = math.cos(rad) * velocity.speed - velocity.y = math.sin(rad) * velocity.speed -end - - -function M:set_speed(entity, speed) - local velocity = entity.velocity - velocity.speed = speed - - -- Update velocity components - local rad = math.rad(velocity.angle) - velocity.x = math.cos(rad) * speed - velocity.y = math.sin(rad) * speed -end - - -function M:set_velocity(entity, x, y) - local velocity = entity.velocity - - local speed = math.sqrt(x * x + y * y) - velocity.speed = decore.clamp(speed, velocity.min_speed, velocity.max_speed) - velocity.angle = math.deg(math.atan2(y, x)) - - -- Adjust x and y - local rad = math.rad(velocity.angle) - velocity.x = math.cos(rad) * velocity.speed - velocity.y = math.sin(rad) * velocity.speed -end - - ----@param entity entity.velocity -function M:process(entity, dt) - local velocity = entity.velocity - - self.world.command_transform:add_position(entity, velocity.x * dt, velocity.y * dt) - self.world.command_transform:set_rotation(entity, velocity.angle) - - if self.debug_draw and self.world.command_debug_draw then - local t = entity.transform - self.world.command_debug_draw:draw_line( - t.position_x, - t.position_y, - t.position_x + velocity.x, - t.position_y + velocity.y - ) - end -end - - -function M:set_min_speed(entity, min_speed) - entity.velocity.min_speed = min_speed -end - - -function M:set_max_speed(entity, max_speed) - entity.velocity.max_speed = max_speed -end - - -return M diff --git a/system/window_event/system_window_event.lua b/system/window_event/system_window_event.lua deleted file mode 100644 index f01f8eb..0000000 --- a/system/window_event/system_window_event.lua +++ /dev/null @@ -1,37 +0,0 @@ -local events = require("event.events") -local decore = require("decore.decore") - ----window.WINDOW_EVENT_FOCUS_GAINED | window.WINDOW_EVENT_FOCUS_LOST | window.WINDOW_EVENT_RESIZED ----@class system.window_event.event - ----System that listens to window events and triggers events on the event bus ----@class system.window_event: system -local M = {} - - ----@return system.window_event -function M.create_system() - return decore.system(M, "window_event") -end - - -function M:onAddToWorld() - window.set_listener(function(_, window_event) - events.trigger("decore.window_event", window_event) - end) - - events.subscribe("decore.window_event", self.on_window_event, self) -end - - -function M:onRemoveFromWorld() - events.unsubscribe("decore.window_event", self.on_window_event, self) -end - - -function M:on_window_event(event) - self.world.event_bus:trigger("window_event", event) -end - - -return M diff --git a/test/test.script b/test/test.script index 640b864..4b72191 100644 --- a/test/test.script +++ b/test/test.script @@ -1,11 +1,6 @@ local deftest = require("deftest.deftest") function init(self) - deftest.add(require("system.health.test_health")) - deftest.add(require("system.fsm.test_fsm")) - deftest.add(require("system.transform.test_transform")) - deftest.add(require("system.quadtree.test_quadtree")) - local is_report = (sys.get_config_int("test.report", 0) == 1) deftest.run({ coverage = { enabled = is_report } }) end diff --git a/wiki/FLOW.md b/wiki/FLOW.md deleted file mode 100644 index b971c01..0000000 --- a/wiki/FLOW.md +++ /dev/null @@ -1,102 +0,0 @@ -# ECS - -## Entities - -## Systems - -### Add new system - -- Create a folder with system name in yout systems folder -- Copy a one of the system type ("template_system", "template_system..command or "template_system_event") to your folder -- Replace all TEMPLATE with your system name -- Register your system in game.script - -### I want to add new entity - -# Tiled - -## Add new entity -- Prepare image for tiled placement, add in /tileset/images folder -- Open tiled tileset -- Press "plus", select icon, select image -- Add required components (`game_object`), transform is not required (autofilled) -- Add entity_id (prefab_id) in tileset class field -- Save project (export should me done automatically) - -## My entity image have a offset -If your defold image matches not in center, you can adjust the offset in the tiled tilesets -- Open corresponding entity in tileset -- Open Tile Collision Editor -- Place Point object at new center of the image --- Detiled uses first point object to calculate offset - -## I want change the Z position of Tiled Layer -- Select layer -- Add custom number property "position_z" to layer -- This property will be used as Z position of the layer - -# Hints & Solutions - -## Tiled - -### I want to draw pixel at object area -- Add entity add tiled with pixel image -- Place in level and resize as required -- Add this pixel.collection image as game object in assets -- Add this pixel.collection to spawner.collection -- Use factory_url of this pixel - -## Game - -### I want to add main game input system -- Add something like "system" tiled layer -- Add object (point) with name (only visual thing) and class name (prefab_id), ex "game_input" -- Open game entities.json, add new entity with prefab_id "game_input" -- Add "input" component and "game_input" component -- Make "game_input" system - -### I want to add some logic to game -- Think about which system can make it and which data it requires -- Add component in tiled with default data -- Add components to objects or on new object -- Add new system - -### I want to animate my object with panthera -- Add panthera component to the entity with animation path - -## GUI - -### I want to add new gui -- Create gui, gui_script, collection -- Add this collection to spawner as object -- Add entity in entities.json, add component to components.json -- Add new object element with class with name of prefab_id - - -### Create GUI -To pass data and callbacks between system and GUI we need to make a "gui bindings" - -```lua -local bindings = require("gui.bindings") - --- GUI -function init(self) - -- Store at game object key - self.bindings = bindings.set({ - on_play_button = event.create(), - set_color = event.create(), - }) -end - --- System -local bindings = require("gui.bindings") - ----@param entity entity.gui -function M:onAdd(entity) - -- Get bindings for current game object - local bindings = bindings.get(entity.game_object.root) - bindings.on_play_button:subscribe(self.on_play_button, self) - - bindings.set_color:trigger("red") -end -``` diff --git a/wiki/FLOW_TILED.md b/wiki/FLOW_TILED.md deleted file mode 100644 index eb18705..0000000 --- a/wiki/FLOW_TILED.md +++ /dev/null @@ -1,23 +0,0 @@ -# Flow Tiled - -## Setup - -Open Tiled -New Project -Create `/tiled` folder (can use other folder name) -Name project with the project name to easier find it inside Tiled (it uses filename as a project name) - -### Create First Map - -Create new map, any settings -Save at /tiled/maps folder -Select Export as JSON (/tiled/exported_maps/level_name.json) -I keep other folder to separate include in game project, but seems more convinient to keep it in the same folder. - -### Create first tileset - -New Tileset (collection of images) -Press Plus -> Select image to add first entity -Seems better to create the folder with all images inside tiled. -Select Export as JSON (/resources/tilesets/tileset_name.json) - diff --git a/wiki/decore_components.md b/wiki/decore_components.md deleted file mode 100644 index e69de29..0000000 diff --git a/wiki/decore_entities.md b/wiki/decore_entities.md deleted file mode 100644 index e69de29..0000000 diff --git a/wiki/decore_systems.md b/wiki/decore_systems.md deleted file mode 100644 index e69de29..0000000 diff --git a/wiki/decore_worlds.md b/wiki/decore_worlds.md deleted file mode 100644 index e69de29..0000000 diff --git a/wiki/tech_solutions.md b/wiki/tech_solutions.md deleted file mode 100644 index c6ba207..0000000 --- a/wiki/tech_solutions.md +++ /dev/null @@ -1,8 +0,0 @@ -# Using vector or just fields in transform -Pros: -- Easier to use with game objects -- A bit faster and a bit less memory usage - -Cons: -- Not POD -- Need conversions from JSON to vector From 1373fc7492547808b8cb2f63ed4f81db533b8928 Mon Sep 17 00:00:00 2001 From: Insality Date: Mon, 22 Dec 2025 18:52:07 +0200 Subject: [PATCH 5/6] Remove not actual files --- assets/atlases/game.atlas | 19 - assets/fonts/text.font | 10 - assets/fonts/troika.otf | Bin 30232 -> 0 bytes assets/images/empty.png | Bin 2803 -> 0 bytes assets/images/pixel.png | Bin 14971 -> 0 bytes assets/images/ui_circle_16.png | Bin 257 -> 0 bytes assets/images/ui_circle_32.png | Bin 426 -> 0 bytes assets/images/ui_circle_64.png | Bin 787 -> 0 bytes assets/images/ui_circle_8.png | Bin 183 -> 0 bytes core/atlas/core.atlas | 19 - core/fonts/text.font | 10 - core/fonts/troika.otf | Bin 30232 -> 0 bytes core/images/empty.png | Bin 2803 -> 0 bytes core/images/pixel.png | Bin 14971 -> 0 bytes core/images/ui_circle_16.png | Bin 257 -> 0 bytes core/images/ui_circle_32.png | Bin 426 -> 0 bytes core/images/ui_circle_64.png | Bin 787 -> 0 bytes core/images/ui_circle_8.png | Bin 183 -> 0 bytes core/particles/confetti/fx_confetti.atlas | 4 - core/particles/confetti/fx_confetti.png | Bin 483 -> 0 bytes .../confetti/fx_quest_complete.particlefx | 636 ------------------ core/utils/editor_only_label-df.material | 7 - core/utils/editor_only_sprite.material | 25 - core/utils/play_particle.script | 10 - core/utils/set_sprite.script | 14 - game.project | 4 - 26 files changed, 758 deletions(-) delete mode 100644 assets/atlases/game.atlas delete mode 100644 assets/fonts/text.font delete mode 100644 assets/fonts/troika.otf delete mode 100755 assets/images/empty.png delete mode 100755 assets/images/pixel.png delete mode 100644 assets/images/ui_circle_16.png delete mode 100644 assets/images/ui_circle_32.png delete mode 100644 assets/images/ui_circle_64.png delete mode 100644 assets/images/ui_circle_8.png delete mode 100644 core/atlas/core.atlas delete mode 100644 core/fonts/text.font delete mode 100644 core/fonts/troika.otf delete mode 100755 core/images/empty.png delete mode 100755 core/images/pixel.png delete mode 100644 core/images/ui_circle_16.png delete mode 100644 core/images/ui_circle_32.png delete mode 100644 core/images/ui_circle_64.png delete mode 100644 core/images/ui_circle_8.png delete mode 100644 core/particles/confetti/fx_confetti.atlas delete mode 100644 core/particles/confetti/fx_confetti.png delete mode 100644 core/particles/confetti/fx_quest_complete.particlefx delete mode 100644 core/utils/editor_only_label-df.material delete mode 100644 core/utils/editor_only_sprite.material delete mode 100644 core/utils/play_particle.script delete mode 100644 core/utils/set_sprite.script diff --git a/assets/atlases/game.atlas b/assets/atlases/game.atlas deleted file mode 100644 index a9f3ed1..0000000 --- a/assets/atlases/game.atlas +++ /dev/null @@ -1,19 +0,0 @@ -images { - image: "/assets/images/ui_circle_8.png" -} -images { - image: "/assets/images/ui_circle_16.png" -} -images { - image: "/assets/images/ui_circle_32.png" -} -images { - image: "/assets/images/ui_circle_64.png" -} -images { - image: "/assets/images/empty.png" -} -images { - image: "/assets/images/pixel.png" -} -extrude_borders: 2 diff --git a/assets/fonts/text.font b/assets/fonts/text.font deleted file mode 100644 index 31dd57f..0000000 --- a/assets/fonts/text.font +++ /dev/null @@ -1,10 +0,0 @@ -font: "/assets/fonts/troika.otf" -material: "/builtins/fonts/font-df.material" -size: 40 -outline_alpha: 1.0 -shadow_alpha: 1.0 -shadow_blur: 1 -shadow_y: -3.0 -output_format: TYPE_DISTANCE_FIELD -render_mode: MODE_MULTI_LAYER -characters: " !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" diff --git a/assets/fonts/troika.otf b/assets/fonts/troika.otf deleted file mode 100644 index 60c7c52a6fc92b84ba8a653912d222067028aa1d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30232 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

!l)roue((F4KOX{Y1N8`@Qxj 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 LG_)qwpnd-X!XsBo diff --git a/assets/images/empty.png b/assets/images/empty.png deleted file mode 100755 index 36b4ff13ef659b6f359f09f9a83ee730559439be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2803 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 diff --git a/assets/images/ui_circle_16.png b/assets/images/ui_circle_16.png deleted file mode 100644 index 559c77cd6b1629a90cfe409b6cdd0e891f0f3c19..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 257 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eB{xnY)$B+ufrIWgO4;k>d^!r^9G-6Vo!kM7# zrXYQQ%V$!afSF~^3-jEoS1K>LPJFcc#-v7;Nxd%=yJR>QEkBXSUAe~kv0PN}`kBn1 zIX4SD`@83b%U<<=&$~7hu6(lN@Ht=Ra~q1U3U)AQi7aB2zmaMBfY&;A|E{n*`fhsj zj(>EP*NJ`V>EWkdm+03t=f%-V_l?G8nSaIGLU(xdaa@}SbRmPMtDnm{r-UW|Ip+fnh-~z*Q&lRjDpi|sxmUHSv+AV!aL)Beff|>py{eNq-uNum!WIYPYuShFMYp<< zbTqOw8+xOcwC3|5eD0QC3Zk5Qx|uoq=?mlNz9p^dY5Gcv*@ZfTIf6$$oeUWWX=#v` zt!AN}GEgHwFZ^+#?>pp&V_?e+>|n!C+8ICvJ1Pd?VTb1!VE@O}>U8uBKwwA#V;Df1 zg~K{}2GGKep%Q+>jxz)JgdL};6muf7_!J+CFfYF&KSz4s$8^j;dN?2-hu~?yB>RKK zqV!S_cFXb`W}=bUZJLZ*vsbwuzGHojkue)biK7d~<$v#YScv>?&!1I~NVGlw0c;JC U7Bs?Q-T(jq07*qoM6N<$f(Nj)C;$Ke diff --git a/assets/images/ui_circle_64.png b/assets/images/ui_circle_64.png deleted file mode 100644 index c74547d003d6d35f63b233192cf44fb55e95e4e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 787 zcmV+u1MK{XP)|B(;dt~AYjWN?pEK- z0%J3XUEbdRMKm(ZoX9B^yAn&R#rEuyixu|#N9+KLq9`Zd5T{eIwOGcdlZidynKWnQ z1!J)Xu~#0COzf78s^-B&jyJ(?t$EvFg*i$&p1NhQf9{dvkHciWf!Xs@Er7s6edW8g``4J{-LRWI||o%fK;fdpi)491z#666}SpWDSYQaO$Dw3G74M%E`=!I zGcJw_U^Cv9kRD>|bCm+$NFQHuIHrIy`IqF928+m+{u$|HffVqutNT%;+5@me0Io8akH%ka7@!ob-|-00rr#(5`?N1mK1AazFqMq?f~` zJ8Y+ma?%F|MDns7@Qd{Ej6)fKpQMi+4(_n{E9vPO9>)W5;WmYTkRDd$g{uI3ebfdT zrpBtZ0+j;nT$u(H?A(PC&FS&bRgjC_xB;kjhph@$6fUdgGMTP5LRch`Qdm?azf0GE z$ae~>n+#uW+WyfR04tM_QepM3tv8Yi8}^?^It6*ami=Yh=ig``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBzK^GiV@L&KYyU=G1_ch5i<~kC|4Kh*S@S?` z7h{u`6LZ`l@js#r2LuA2I?nT)u4ZkpRBT%1lJx3?Ni6fHsxmF?Fko8vzu#oON$`Sc Z(s`b}x05eO9R-@p;OXk;vd$@?2>?vgG?xGX diff --git a/core/atlas/core.atlas b/core/atlas/core.atlas deleted file mode 100644 index 3d96bb0..0000000 --- a/core/atlas/core.atlas +++ /dev/null @@ -1,19 +0,0 @@ -images { - image: "/core/images/empty.png" -} -images { - image: "/core/images/pixel.png" -} -images { - image: "/core/images/ui_circle_8.png" -} -images { - image: "/core/images/ui_circle_16.png" -} -images { - image: "/core/images/ui_circle_32.png" -} -images { - image: "/core/images/ui_circle_64.png" -} -extrude_borders: 2 diff --git a/core/fonts/text.font b/core/fonts/text.font deleted file mode 100644 index 178cf66..0000000 --- a/core/fonts/text.font +++ /dev/null @@ -1,10 +0,0 @@ -font: "/assets/fonts/troika.otf" -material: "/builtins/fonts/font-df.material" -size: 40 -outline_alpha: 1.0 -shadow_alpha: 1.0 -shadow_blur: 1 -shadow_y: -3.0 -output_format: TYPE_DISTANCE_FIELD -render_mode: MODE_MULTI_LAYER -characters: " !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\320\260\320\261\320\262\320\263\320\264\320\265\321\221\320\266\320\267\320\270\320\271\320\272\320\273\320\274\320\275\320\276\320\277\321\200\321\201\321\202\321\203\321\204\321\205\321\206\321\207\321\210\321\211\321\212\321\213\321\214\321\215\321\216\321\217\320\220\320\221\320\222\320\223\320\224\320\225\320\201\320\226\320\227\320\230\320\231\320\232\320\233\320\234\320\235\320\236\320\237\320\240\320\241\320\242\320\243\320\244\320\245\320\246\320\247\320\250\320\251\320\252\320\253\320\254\320\255\320\256\320\257\302\247\303\227" diff --git a/core/fonts/troika.otf b/core/fonts/troika.otf deleted file mode 100644 index 60c7c52a6fc92b84ba8a653912d222067028aa1d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30232 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

!l)roue((F4KOX{Y1N8`@Qxj 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 LG_)qwpnd-X!XsBo diff --git a/core/images/empty.png b/core/images/empty.png deleted file mode 100755 index 36b4ff13ef659b6f359f09f9a83ee730559439be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2803 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 diff --git a/core/images/ui_circle_16.png b/core/images/ui_circle_16.png deleted file mode 100644 index 559c77cd6b1629a90cfe409b6cdd0e891f0f3c19..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 257 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eB{xnY)$B+ufrIWgO4;k>d^!r^9G-6Vo!kM7# zrXYQQ%V$!afSF~^3-jEoS1K>LPJFcc#-v7;Nxd%=yJR>QEkBXSUAe~kv0PN}`kBn1 zIX4SD`@83b%U<<=&$~7hu6(lN@Ht=Ra~q1U3U)AQi7aB2zmaMBfY&;A|E{n*`fhsj zj(>EP*NJ`V>EWkdm+03t=f%-V_l?G8nSaIGLU(xdaa@}SbRmPMtDnm{r-UW|Ip+fnh-~z*Q&lRjDpi|sxmUHSv+AV!aL)Beff|>py{eNq-uNum!WIYPYuShFMYp<< zbTqOw8+xOcwC3|5eD0QC3Zk5Qx|uoq=?mlNz9p^dY5Gcv*@ZfTIf6$$oeUWWX=#v` zt!AN}GEgHwFZ^+#?>pp&V_?e+>|n!C+8ICvJ1Pd?VTb1!VE@O}>U8uBKwwA#V;Df1 zg~K{}2GGKep%Q+>jxz)JgdL};6muf7_!J+CFfYF&KSz4s$8^j;dN?2-hu~?yB>RKK zqV!S_cFXb`W}=bUZJLZ*vsbwuzGHojkue)biK7d~<$v#YScv>?&!1I~NVGlw0c;JC U7Bs?Q-T(jq07*qoM6N<$f(Nj)C;$Ke diff --git a/core/images/ui_circle_64.png b/core/images/ui_circle_64.png deleted file mode 100644 index c74547d003d6d35f63b233192cf44fb55e95e4e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 787 zcmV+u1MK{XP)|B(;dt~AYjWN?pEK- z0%J3XUEbdRMKm(ZoX9B^yAn&R#rEuyixu|#N9+KLq9`Zd5T{eIwOGcdlZidynKWnQ z1!J)Xu~#0COzf78s^-B&jyJ(?t$EvFg*i$&p1NhQf9{dvkHciWf!Xs@Er7s6edW8g``4J{-LRWI||o%fK;fdpi)491z#666}SpWDSYQaO$Dw3G74M%E`=!I zGcJw_U^Cv9kRD>|bCm+$NFQHuIHrIy`IqF928+m+{u$|HffVqutNT%;+5@me0Io8akH%ka7@!ob-|-00rr#(5`?N1mK1AazFqMq?f~` zJ8Y+ma?%F|MDns7@Qd{Ej6)fKpQMi+4(_n{E9vPO9>)W5;WmYTkRDd$g{uI3ebfdT zrpBtZ0+j;nT$u(H?A(PC&FS&bRgjC_xB;kjhph@$6fUdgGMTP5LRch`Qdm?azf0GE z$ae~>n+#uW+WyfR04tM_QepM3tv8Yi8}^?^It6*ami=Yh=ig``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBzK^GiV@L&KYyU=G1_ch5i<~kC|4Kh*S@S?` z7h{u`6LZ`l@js#r2LuA2I?nT)u4ZkpRBT%1lJx3?Ni6fHsxmF?Fko8vzu#oON$`Sc Z(s`b}x05eO9R-@p;OXk;vd$@?2>?vgG?xGX diff --git a/core/particles/confetti/fx_confetti.atlas b/core/particles/confetti/fx_confetti.atlas deleted file mode 100644 index 9ca951d..0000000 --- a/core/particles/confetti/fx_confetti.atlas +++ /dev/null @@ -1,4 +0,0 @@ -images { - image: "/core/particles/confetti/fx_confetti.png" -} -extrude_borders: 2 diff --git a/core/particles/confetti/fx_confetti.png b/core/particles/confetti/fx_confetti.png deleted file mode 100644 index 3712edcf70a736196906d22c3f4f0a56da5de41d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 483 zcmV<90UZ8`P)f14#v9 z1wsemC?I~^ok0>wOb#L$-wZ#%Ec>`Sj$LoPUeiRWDgqK91|r~U>!uA$AO}()6$$c| zdpp6aODI*vz!bPUA!dOQ0u~~{>V#Ma@g<>DH3ptLc*YV~poQ`d;%f<|su1|-;sFib z(IYey2{Knft;7s?a251GO3ai4vSXyo+KsHliuT3Z2PKf1V}%xM5}G4s^TAnqWvLS2 zrH3TDV(z1@hD`3Ghh@-QjKsMY^r?jYxifIUMM&^3@mKKCJLQlGee^azWOAR~|0sjP zD@zptZ$Dfw&_z<-I3J@o$q2aVD7QnI+rQY9?P~5xB$)1tU}_Ot%HBIp@DN5;A_iW~ z$pRRQ1nF<1DOFvgg(kqQt1Me6M1rtho+iMxNHB8|-Hgx#E!EYO8;Jx9Z&$a&HRc74 zaXZ}}?F;Wk!cv=V3C9bSz@$E7dX=bSgkm;dy0$ivt;bVzli^z%=xh?ZFk;R6CfnD$ Z`UIBMpe)h4rsV(t002ovPDHLkV1hz`%Ul2e diff --git a/core/particles/confetti/fx_quest_complete.particlefx b/core/particles/confetti/fx_quest_complete.particlefx deleted file mode 100644 index bf6e2cd..0000000 --- a/core/particles/confetti/fx_quest_complete.particlefx +++ /dev/null @@ -1,636 +0,0 @@ -emitters { - mode: PLAY_MODE_ONCE - duration: 0.2 - space: EMISSION_SPACE_WORLD - position { - x: -200.0 - } - rotation { - z: 0.25881904 - w: 0.9659258 - } - tile_source: "/core/particles/confetti/fx_confetti.atlas" - animation: "fx_confetti" - material: "/builtins/materials/particlefx.material" - max_particle_count: 128 - type: EMITTER_TYPE_CIRCLE - start_delay: 0.2 - properties { - key: EMITTER_KEY_SPAWN_RATE - points { - y: 60.0 - } - } - properties { - key: EMITTER_KEY_SIZE_X - points { - y: 60.0 - } - } - properties { - key: EMITTER_KEY_SIZE_Y - points { - y: 60.0 - } - } - properties { - key: EMITTER_KEY_PARTICLE_LIFE_TIME - points { - y: 2.0 - } - } - properties { - key: EMITTER_KEY_PARTICLE_SPEED - points { - y: 900.0 - } - spread: 200.0 - } - properties { - key: EMITTER_KEY_PARTICLE_SIZE - points { - y: 40.0 - } - spread: 20.0 - } - properties { - key: EMITTER_KEY_PARTICLE_RED - points { - y: 0.9 - } - spread: 0.1 - } - properties { - key: EMITTER_KEY_PARTICLE_GREEN - points { - y: 0.8 - } - spread: 0.1 - } - properties { - key: EMITTER_KEY_PARTICLE_BLUE - points { - y: 0.1 - } - } - properties { - key: EMITTER_KEY_PARTICLE_ALPHA - points { - y: 1.0 - } - } - properties { - key: EMITTER_KEY_PARTICLE_ROTATION - points { - y: 0.0 - } - spread: 360.0 - } - properties { - key: EMITTER_KEY_PARTICLE_STRETCH_FACTOR_X - points { - y: 0.0 - } - spread: 0.2 - } - properties { - key: EMITTER_KEY_PARTICLE_STRETCH_FACTOR_Y - points { - y: 0.0 - } - spread: 0.2 - } - properties { - key: EMITTER_KEY_PARTICLE_ANGULAR_VELOCITY - points { - y: 20.0 - } - } - particle_properties { - key: PARTICLE_KEY_SCALE - points { - y: 1.0 - } - } - particle_properties { - key: PARTICLE_KEY_RED - points { - y: 1.0 - } - } - particle_properties { - key: PARTICLE_KEY_GREEN - points { - y: 1.0 - } - } - particle_properties { - key: PARTICLE_KEY_BLUE - points { - y: 1.0 - } - } - particle_properties { - key: PARTICLE_KEY_ALPHA - points { - y: 0.0 - t_x: 0.07194582 - t_y: 0.99740857 - } - points { - x: 0.11320755 - y: 0.99277455 - t_x: 0.99418455 - t_y: 0.10768964 - } - points { - x: 0.7112546 - y: 0.555656 - t_x: 0.5694311 - t_y: -0.82203907 - } - points { - x: 1.0 - y: 0.0072254334 - t_x: 0.4737472 - t_y: -0.8806609 - } - } - particle_properties { - key: PARTICLE_KEY_ROTATION - points { - y: -0.10988372 - t_x: 0.0022114606 - t_y: 0.99999756 - } - points { - x: 1.0 - y: 266.89883 - } - } - particle_properties { - key: PARTICLE_KEY_ANGULAR_VELOCITY - points { - y: 1.0 - } - } - modifiers { - type: MODIFIER_TYPE_DRAG - properties { - key: MODIFIER_KEY_MAGNITUDE - points { - y: 1.5 - } - } - } - modifiers { - type: MODIFIER_TYPE_ACCELERATION - rotation { - z: -0.25881904 - w: 0.9659258 - } - properties { - key: MODIFIER_KEY_MAGNITUDE - points { - y: -400.0 - } - } - } - modifiers { - type: MODIFIER_TYPE_VORTEX - rotation { - z: 0.25881904 - w: 0.9659258 - } - properties { - key: MODIFIER_KEY_MAGNITUDE - points { - y: 100.0 - } - } - properties { - key: MODIFIER_KEY_MAX_DISTANCE - points { - y: 400.0 - } - } - } -} -emitters { - id: "emitter1" - mode: PLAY_MODE_ONCE - duration: 0.2 - space: EMISSION_SPACE_WORLD - tile_source: "/core/particles/confetti/fx_confetti.atlas" - animation: "fx_confetti" - material: "/builtins/materials/particlefx.material" - max_particle_count: 128 - type: EMITTER_TYPE_CIRCLE - properties { - key: EMITTER_KEY_SPAWN_RATE - points { - y: 40.0 - } - } - properties { - key: EMITTER_KEY_SIZE_X - points { - y: 60.0 - } - } - properties { - key: EMITTER_KEY_SIZE_Y - points { - y: 60.0 - } - } - properties { - key: EMITTER_KEY_PARTICLE_LIFE_TIME - points { - y: 2.0 - } - } - properties { - key: EMITTER_KEY_PARTICLE_SPEED - points { - y: 900.0 - } - spread: 200.0 - } - properties { - key: EMITTER_KEY_PARTICLE_SIZE - points { - y: 40.0 - } - spread: 20.0 - } - properties { - key: EMITTER_KEY_PARTICLE_RED - points { - y: 0.8 - } - spread: 0.1 - } - properties { - key: EMITTER_KEY_PARTICLE_GREEN - points { - y: 0.15 - } - } - properties { - key: EMITTER_KEY_PARTICLE_BLUE - points { - y: 0.8 - } - spread: 0.1 - } - properties { - key: EMITTER_KEY_PARTICLE_ALPHA - points { - y: 1.0 - } - } - properties { - key: EMITTER_KEY_PARTICLE_ROTATION - points { - y: 0.0 - } - spread: 360.0 - } - properties { - key: EMITTER_KEY_PARTICLE_STRETCH_FACTOR_X - points { - y: 0.0 - } - spread: 0.2 - } - properties { - key: EMITTER_KEY_PARTICLE_STRETCH_FACTOR_Y - points { - y: 0.0 - } - spread: 0.2 - } - properties { - key: EMITTER_KEY_PARTICLE_ANGULAR_VELOCITY - points { - y: 20.0 - } - } - particle_properties { - key: PARTICLE_KEY_SCALE - points { - y: 1.0 - } - } - particle_properties { - key: PARTICLE_KEY_RED - points { - y: 1.0 - } - } - particle_properties { - key: PARTICLE_KEY_GREEN - points { - y: 1.0 - } - } - particle_properties { - key: PARTICLE_KEY_BLUE - points { - y: 1.0 - } - } - particle_properties { - key: PARTICLE_KEY_ALPHA - points { - y: 0.0 - t_x: 0.07194582 - t_y: 0.99740857 - } - points { - x: 0.11320755 - y: 0.99277455 - t_x: 0.99418455 - t_y: 0.10768964 - } - points { - x: 0.7112546 - y: 0.555656 - t_x: 0.5694311 - t_y: -0.82203907 - } - points { - x: 1.0 - y: 0.0072254334 - t_x: 0.4737472 - t_y: -0.8806609 - } - } - particle_properties { - key: PARTICLE_KEY_ROTATION - points { - y: -0.10988372 - t_x: 0.0022114606 - t_y: 0.99999756 - } - points { - x: 1.0 - y: 266.89883 - } - } - particle_properties { - key: PARTICLE_KEY_ANGULAR_VELOCITY - points { - y: 1.0 - } - } - modifiers { - type: MODIFIER_TYPE_ACCELERATION - properties { - key: MODIFIER_KEY_MAGNITUDE - points { - y: -400.0 - } - } - } - modifiers { - type: MODIFIER_TYPE_DRAG - properties { - key: MODIFIER_KEY_MAGNITUDE - points { - y: 1.5 - } - } - } - modifiers { - type: MODIFIER_TYPE_VORTEX - properties { - key: MODIFIER_KEY_MAGNITUDE - points { - y: -200.0 - } - } - properties { - key: MODIFIER_KEY_MAX_DISTANCE - points { - y: 400.0 - } - } - } -} -emitters { - id: "emitter2" - mode: PLAY_MODE_ONCE - duration: 0.2 - space: EMISSION_SPACE_WORLD - position { - x: 200.0 - } - rotation { - z: -0.25881904 - w: 0.9659258 - } - tile_source: "/core/particles/confetti/fx_confetti.atlas" - animation: "fx_confetti" - material: "/builtins/materials/particlefx.material" - max_particle_count: 128 - type: EMITTER_TYPE_CIRCLE - start_delay: 0.2 - properties { - key: EMITTER_KEY_SPAWN_RATE - points { - y: 60.0 - } - } - properties { - key: EMITTER_KEY_SIZE_X - points { - y: 60.0 - } - } - properties { - key: EMITTER_KEY_SIZE_Y - points { - y: 60.0 - } - } - properties { - key: EMITTER_KEY_PARTICLE_LIFE_TIME - points { - y: 2.0 - } - } - properties { - key: EMITTER_KEY_PARTICLE_SPEED - points { - y: 900.0 - } - spread: 200.0 - } - properties { - key: EMITTER_KEY_PARTICLE_SIZE - points { - y: 40.0 - } - spread: 20.0 - } - properties { - key: EMITTER_KEY_PARTICLE_RED - points { - y: 0.2 - } - } - properties { - key: EMITTER_KEY_PARTICLE_GREEN - points { - y: 0.8 - } - spread: 0.1 - } - properties { - key: EMITTER_KEY_PARTICLE_BLUE - points { - y: 0.9 - } - spread: 0.1 - } - properties { - key: EMITTER_KEY_PARTICLE_ALPHA - points { - y: 1.0 - } - } - properties { - key: EMITTER_KEY_PARTICLE_ROTATION - points { - y: 0.0 - } - spread: 360.0 - } - properties { - key: EMITTER_KEY_PARTICLE_STRETCH_FACTOR_X - points { - y: 0.0 - } - spread: 0.2 - } - properties { - key: EMITTER_KEY_PARTICLE_STRETCH_FACTOR_Y - points { - y: 0.0 - } - spread: 0.2 - } - properties { - key: EMITTER_KEY_PARTICLE_ANGULAR_VELOCITY - points { - y: 20.0 - } - } - particle_properties { - key: PARTICLE_KEY_SCALE - points { - y: 1.0 - } - } - particle_properties { - key: PARTICLE_KEY_RED - points { - y: 1.0 - } - } - particle_properties { - key: PARTICLE_KEY_GREEN - points { - y: 1.0 - } - } - particle_properties { - key: PARTICLE_KEY_BLUE - points { - y: 1.0 - } - } - particle_properties { - key: PARTICLE_KEY_ALPHA - points { - y: 0.0 - t_x: 0.07194582 - t_y: 0.99740857 - } - points { - x: 0.11320755 - y: 0.99277455 - t_x: 0.99418455 - t_y: 0.10768964 - } - points { - x: 0.7112546 - y: 0.555656 - t_x: 0.5694311 - t_y: -0.82203907 - } - points { - x: 1.0 - y: 0.0072254334 - t_x: 0.4737472 - t_y: -0.8806609 - } - } - particle_properties { - key: PARTICLE_KEY_ROTATION - points { - y: -0.10988372 - t_x: 0.0022114606 - t_y: 0.99999756 - } - points { - x: 1.0 - y: 266.89883 - } - } - particle_properties { - key: PARTICLE_KEY_ANGULAR_VELOCITY - points { - y: 1.0 - } - } - modifiers { - type: MODIFIER_TYPE_ACCELERATION - rotation { - z: 0.25881904 - w: 0.9659258 - } - properties { - key: MODIFIER_KEY_MAGNITUDE - points { - y: -400.0 - } - } - } - modifiers { - type: MODIFIER_TYPE_DRAG - properties { - key: MODIFIER_KEY_MAGNITUDE - points { - y: 1.5 - } - } - } - modifiers { - type: MODIFIER_TYPE_VORTEX - properties { - key: MODIFIER_KEY_MAGNITUDE - points { - y: 100.0 - } - } - properties { - key: MODIFIER_KEY_MAX_DISTANCE - points { - y: 400.0 - } - } - } -} diff --git a/core/utils/editor_only_label-df.material b/core/utils/editor_only_label-df.material deleted file mode 100644 index 0105560..0000000 --- a/core/utils/editor_only_label-df.material +++ /dev/null @@ -1,7 +0,0 @@ -name: "label" -vertex_program: "/builtins/fonts/font-df.vp" -fragment_program: "/builtins/fonts/font-df.fp" -vertex_constants { - name: "view_proj" - type: CONSTANT_TYPE_VIEWPROJ -} diff --git a/core/utils/editor_only_sprite.material b/core/utils/editor_only_sprite.material deleted file mode 100644 index a980168..0000000 --- a/core/utils/editor_only_sprite.material +++ /dev/null @@ -1,25 +0,0 @@ -name: "sprite" -tags: "editor" -vertex_program: "/builtins/materials/sprite.vp" -fragment_program: "/builtins/materials/sprite.fp" -vertex_constants { - name: "view_proj" - type: CONSTANT_TYPE_VIEWPROJ -} -fragment_constants { - name: "tint" - type: CONSTANT_TYPE_USER - value { - x: 1.0 - y: 1.0 - z: 1.0 - w: 1.0 - } -} -samplers { - name: "texture_sampler" - wrap_u: WRAP_MODE_CLAMP_TO_EDGE - wrap_v: WRAP_MODE_CLAMP_TO_EDGE - filter_min: FILTER_MODE_MIN_DEFAULT - filter_mag: FILTER_MODE_MAG_DEFAULT -} diff --git a/core/utils/play_particle.script b/core/utils/play_particle.script deleted file mode 100644 index b7f8c58..0000000 --- a/core/utils/play_particle.script +++ /dev/null @@ -1,10 +0,0 @@ -go.property("particle", hash("")) -go.property("lifetime", 5) - -function init(self) - local particle_url = msg.url(nil, nil, self.particle) - particlefx.play(particle_url) - timer.delay(self.lifetime, false, function() - go.delete() - end) -end diff --git a/core/utils/set_sprite.script b/core/utils/set_sprite.script deleted file mode 100644 index 1ae8c9c..0000000 --- a/core/utils/set_sprite.script +++ /dev/null @@ -1,14 +0,0 @@ -local EMPTY_HASH = hash("") -go.property("image_id", hash("")) -go.property("sprite_url", msg.url("#sprite")) -go.property("scale", vmath.vector3(1, 1, 1)) - -function init(self) - if self.image_id and self.image_id ~= EMPTY_HASH then - sprite.play_flipbook(self.sprite_url, self.image_id) - end - - if self.scale.x ~= 1 or self.scale.y ~= 1 then - go.set(self.sprite_url, "scale", self.scale) - end -end diff --git a/game.project b/game.project index f7b228f..15d69fb 100644 --- a/game.project +++ b/game.project @@ -18,7 +18,6 @@ title = Decore version = 2 publisher = Insality developer = Maksim Tuprikov, Insality -custom_resources = /resources dependencies#0 = https://github.com/britzl/deftest/archive/master.zip dependencies#1 = https://github.com/Insality/defold-tweener/archive/refs/tags/3.zip dependencies#2 = https://github.com/subsoap/defos/archive/refs/tags/v2.8.3.zip @@ -53,9 +52,6 @@ max_instances = 32000 [gui] max_count = 2048 -[native_extension] -app_manifest = /examples/manifest.appmanifest - [event] use_xpcall = 1 From 04e59441ecfcc07b899bd204fc71db9de46cd8db Mon Sep 17 00:00:00 2001 From: Insality Date: Mon, 22 Dec 2025 19:32:15 +0200 Subject: [PATCH 6/6] Update docs, annotations --- README.md | 34 +-- USE_CASES.md | 35 +++ decore/decore.lua | 2 +- decore/internal/decore_annotations.lua | 33 --- decore/internal/system_decore.lua | 11 +- game.project | 1 + system/game_object/game_object.version | 1 + system/game_object/game_object_command.lua | 46 +++ system/game_object/game_object_system.lua | 309 +++++++++++++++++++++ system/transform/transform.version | 1 + system/transform/transform_command.lua | 121 ++++++++ system/transform/transform_system.lua | 187 +++++++++++++ 12 files changed, 723 insertions(+), 58 deletions(-) create mode 100644 USE_CASES.md delete mode 100644 decore/internal/decore_annotations.lua create mode 100644 system/game_object/game_object.version create mode 100644 system/game_object/game_object_command.lua create mode 100644 system/game_object/game_object_system.lua create mode 100644 system/transform/transform.version create mode 100644 system/transform/transform_command.lua create mode 100644 system/transform/transform_system.lua diff --git a/README.md b/README.md index e32c88e..31c4d88 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Add in your `game.project` dependencies: ``` -https://github.com/Insality/decore/archive/refs/tags/2.zip +https://github.com/Insality/decore/archive/refs/tags/3.zip ``` ### Basic Usage @@ -47,39 +47,24 @@ function update(self, dt) self.world:update(dt) end - function on_input(self, action_id, action) + -- Systems can be accessed via world, if registered return self.world.input:on_input(action_id, action) end - function final(self) self.world:clearEntities() self.world:clearSystems() end ``` -Look at system examples to get more information about how to use systems. -[System examples](https://github.com/Insality/asset-store/tree/main/system/Insality) - -## Entities prefab Example +## Examples +Look at next examples to get more information about how to use the library: +- [System examples](https://github.com/Insality/asset-store/tree/main/system/Insality) - System examples +- [Entity example](https://github.com/Insality/cosmic-dash-jam-2025/blob/main/entity/player/player_entity.lua) - Entity example +- [Shooting Circles](https://github.com/Insality/shooting_circles) - Game Example +- [Cosmic Dash](https://github.com/Insality/cosmic-dash-jam-2025) - Game Example -```lua --- /entity/player/player_entity.lua ----@diagnostic disable: missing-fields ----@type entity -return { - transform = { size_x = 128, size_y = 128 }, - game_object = { factory_url = "/entities#player" }, - field = { solid = true }, - player = true, - panthera = { animation_path = require("entity.player.player_panthera"), default_animation = "idle", is_loop = true, play_on_start = "idle" }, - player_visual = true, - field_movable = true, - user_controlled = true, -} - -``` ## Quick API Reference @@ -114,11 +99,14 @@ decore.create_component(component_id, [component_pack_id]) decore.apply_component(entity, component_id, [component_data]) decore.apply_components(entity, [components]) +-- Find entities decore.find_entities(world, component_id, [component_value]) +-- Debug functions decore.print_loaded_packs_debug_info() decore.print_loaded_systems_debug_info(world) +-- Logging decore.set_logger([logger_instance]) decore.get_logger([name], [level]) ``` diff --git a/USE_CASES.md b/USE_CASES.md new file mode 100644 index 0000000..091addc --- /dev/null +++ b/USE_CASES.md @@ -0,0 +1,35 @@ +# Use Cases + +This section illustrates practical examples of how to use the Decore module in your Defold game development projects. + +## Global world module + +Often for convenience, we can create a Lua module file which will be used as a global world module. + +```lua +-- /game/world.lua + +--- Use this module to get the latest created world instance +---@class world +local M = {} +local METATABLE = { __index = nil } + +---@param world world +function M.set_world(world) + METATABLE.__index = world +end + +return setmetatable(M, METATABLE) +``` + +```lua +-- Game script +local decore = require("decore.decore") +local world = require("game.world") + +function init(self) + self.world = decore.new_world(...) + -- Set a world after creation to have access to it from any script later + world.set_world(self.world) +end +``` diff --git a/decore/decore.lua b/decore/decore.lua index 1e83a8e..2442897 100644 --- a/decore/decore.lua +++ b/decore/decore.lua @@ -189,7 +189,7 @@ end ---Register components pack to decore components ----@param components_data decore.components_pack_data +---@param components_data decore.components_data ---@return boolean function M.register_components(components_data) local pack_id = components_data.pack_id diff --git a/decore/internal/decore_annotations.lua b/decore/internal/decore_annotations.lua deleted file mode 100644 index eb69e5f..0000000 --- a/decore/internal/decore_annotations.lua +++ /dev/null @@ -1,33 +0,0 @@ ----@class action ----@field action_id hash|nil - ---- JSON file scheme for entities data ----@class decore.entities_pack_data ----@field pack_id string ----@field entities table - ---- JSON file scheme for components data ----@class decore.components_pack_data ----@field pack_id string ----@field components table - ---- JSON file scheme for worlds data ----@class decore.worlds_pack_data ----@field pack_id string ----@field worlds table - ----@class decore.world.instance_id ----@field world_id string|nil ----@field pack_id string|nil - ----@class decore.entities_pack_data.instance ----@field prefab_id string|nil ----@field pack_id string|nil ----@field components table|nil - ----@class decore.world.instance ----@field included_worlds decore.world.instance_id[]|nil ----@field entities decore.entities_pack_data.instance[] - - - diff --git a/decore/internal/system_decore.lua b/decore/internal/system_decore.lua index 6e451c4..cb37d64 100644 --- a/decore/internal/system_decore.lua +++ b/decore/internal/system_decore.lua @@ -3,12 +3,21 @@ local decore_data = require("decore.internal.decore_data") local ecs = require("decore.internal.ecs") +---@class decore.entity_prefab_data +---@field prefab_id string|nil +---@field pack_id string|nil +---@field components table|nil + +---@class decore.components_data +---@field pack_id string +---@field components table + ---@class entity ---@field id number|nil Unique entity id, autofilled by decore.create_entity ---@field prefab_id string|nil The entity id from decore collections, autofilled by decore.create_entity ---@field pack_id string|nil The entity id from decore collections, autofilled by decore.create_entity ---@field parent_prefab_id string|nil The parent prefab_id, used for prefab inheritance ----@field child_instancies decore.entities_pack_data.instance[]|nil The child instances to spawn on entity creation +---@field child_instancies decore.entity_prefab_data[]|nil The child instances to spawn on entity creation ---@field parent_id number|nil The parent id ---@field children_ids number[]|nil The children ids diff --git a/game.project b/game.project index 15d69fb..4698686 100644 --- a/game.project +++ b/game.project @@ -26,6 +26,7 @@ dependencies#4 = https://github.com/AGulev/drawpixels/archive/refs/tags/2.0.1.zi dependencies#5 = https://github.com/Insality/defold-event/archive/refs/tags/11.zip dependencies#6 = https://github.com/Insality/druid/archive/refs/heads/develop.zip dependencies#7 = https://github.com/Insality/panthera/archive/refs/tags/runtime.4.zip +dependencies#8 = https://github.com/Insality/asset-store/archive/refs/heads/main.zip [library] include_dirs = decore diff --git a/system/game_object/game_object.version b/system/game_object/game_object.version new file mode 100644 index 0000000..c197978 --- /dev/null +++ b/system/game_object/game_object.version @@ -0,0 +1 @@ +Insality:game_object@1 \ No newline at end of file diff --git a/system/game_object/game_object_command.lua b/system/game_object/game_object_command.lua new file mode 100644 index 0000000..6be2ae0 --- /dev/null +++ b/system/game_object/game_object_command.lua @@ -0,0 +1,46 @@ +---@class world +---@field game_object system.game_object.command + +---@class system.game_object.command +---@field game_object system.game_object +local M = {} + + +---@return system.game_object.command +function M.create(game_object) + return setmetatable({ game_object = game_object }, { __index = M }) +end + + +---@param entity entity +function M:refresh_transform(entity) + assert(entity.game_object, "Entity should have game_object component") + assert(entity.transform, "Entity should have transform component") + ---@cast entity entity.game_object + self.game_object:refresh_transform(entity) +end + + +---@param entity entity +---@param enabled boolean +function M:set_enabled(entity, enabled) + assert(entity.game_object, "Entity has no game_object component") + + for _, game_object in pairs(entity.game_object.object) do + if enabled then + msg.post(game_object, "enable") + else + msg.post(game_object, "disable") + end + end +end + + +---@param id string|hash +---@return entity|nil +function M:get_entity(id) + return self.game_object.root_to_entity[id] +end + + +return M diff --git a/system/game_object/game_object_system.lua b/system/game_object/game_object_system.lua new file mode 100644 index 0000000..95b26a2 --- /dev/null +++ b/system/game_object/game_object_system.lua @@ -0,0 +1,309 @@ +local decore = require("decore.decore") +local command_game_object = require("system.game_object.game_object_command") + +---@class entity +---@field game_object component.game_object|nil +---@field hidden boolean|nil + +---@class entity.game_object: entity +---@field game_object component.game_object +---@field transform component.transform +---@field hidden boolean|nil + +-- { is_factory = true, factory_url = "/entities#window_settings" } +---@class component.game_object +---@field root string|hash +---@field object table +---@field sprite_url string|nil # "sprite" as default one. Will try be pick up from the root +---@field factory_url string|nil +---@field is_slice9 boolean|nil +---@field slice9_offset vector3|nil +---@field remove_delay number|nil +---@field is_factory boolean|nil +---@field runtime_created boolean|nil +---@field object_scheme table|nil Ex: { "root", "circle", "cross" }. Required to set a struct of children game objects to fill inside object field. Used only if entity spawned from the collection with entitty.script +decore.register_component("game_object") +decore.register_component("hidden", false) + + +---@class system.game_object: system +---@field entities entity.game_object[] +---@field root_to_entity table +local M = {} + +M.DEBUG_PANEL_UPDATE_MEMORY_LIMIT = 2048 +M.DEBUG_PANEL_POSTWRAP_MEMORY_LIMIT = 2048 +M.MSG_INIT_ENTITY = hash("init_entity") + +local TEMP_VECTOR = vmath.vector3(0, 0, 0) +local TEMP_QUAT = vmath.quat(0, 0, 0, 1) +local VECTOR3_ONE = vmath.vector3(1, 1, 1) +local ROOT_URL = hash("/root") +local HASH_POSITION = hash("position") +local HASH_SIZE = hash("size") +local HASH_SCALE = hash("scale") +local HASH_EULER_Z = hash("euler.z") +local sin = math.sin +local cos = math.cos +local rad = math.rad + +---@return system.game_object +function M.create() + local self = decore.system(M, "game_object") + self.filter = decore.ecs.requireAll("game_object", "transform", decore.ecs.rejectAll("hidden")) + + self.root_to_entity = {} + + return self +end + + +function M:onAddToWorld() + self.world.game_object = command_game_object.create(self) +end + + +function M:postWrap() + self.world.event_bus:process("transform_event", self.process_transform_event, self) +end + + +---@param entity entity.game_object +function M:onAdd(entity) + local is_already_exists = entity.game_object.root or entity.game_object.object + if is_already_exists then + self:refresh_root(entity) + return + end + + local object = self:create_object(entity) + local root = object[ROOT_URL] + entity.game_object.root = root + entity.game_object.object = object + entity.game_object.runtime_created = true + self:refresh_root(entity) +end + + +---@param entity entity.game_object +function M:onRemove(entity) + local remove_delay = entity.game_object.remove_delay + + if not remove_delay then + self:remove_entity(entity) + else + timer.delay(remove_delay, false, function() + self:remove_entity(entity) + end) + end +end + + +---@param entity entity.game_object +function M:remove_entity(entity) + local root = entity.game_object.root + if root then + self.root_to_entity[root] = nil + + if go.exists(root)then + go.delete(root, false) + entity.game_object.root = nil + end + end + + local object = entity.game_object.object + if object then + for key, node in pairs(object) do + local related_entity = self.root_to_entity[node] + if related_entity then + self.world:removeEntity(related_entity) + else + -- TODO: it removes also a childs of the related entity + -- And I can get errors like panthera trying to play on deleted object + -- Right before it will be deleted with upper removeEntity + if go.exists(node) then + go.delete(node, false) + object[key] = nil + end + end + end + end +end + + +---@param events system.transform.event[] +function M:process_transform_event(events) + for i = 1, #events do + local event = events[i] + local entity = event.entity + local transform = entity.transform + local game_object = entity.game_object + + if not self.indices[entity] or not game_object then + -- skip + else + local root = game_object.root + if root then + local delay = event.delay or 0 + + if event.is_position_changed then + TEMP_VECTOR.x = transform.position_x + TEMP_VECTOR.y = transform.position_y + --TEMP_VECTOR.z = transform.position_z + TEMP_VECTOR.z = self:get_position_z(transform) + if event.animate_time then + local easing = event.easing or go.EASING_OUTSINE + go.animate(root, HASH_POSITION, go.PLAYBACK_ONCE_FORWARD, TEMP_VECTOR, easing, event.animate_time, delay) + else + go.set_position(TEMP_VECTOR, root) + end + end + + if event.is_rotation_changed then + if event.animate_time then + local easing = event.easing or go.EASING_OUTSINE + go.animate(root, HASH_EULER_Z, go.PLAYBACK_ONCE_FORWARD, transform.rotation, easing, event.animate_time, delay) + else + go.set(root, HASH_EULER_Z, transform.rotation) + end + end + + if event.is_scale_changed then + TEMP_VECTOR.x = transform.scale_x + TEMP_VECTOR.y = transform.scale_y + TEMP_VECTOR.z = transform.scale_z + if event.animate_time then + local easing = event.easing or go.EASING_OUTSINE + go.animate(root, HASH_SCALE, go.PLAYBACK_ONCE_FORWARD, TEMP_VECTOR, easing, event.animate_time, delay) + else + go.set_scale(TEMP_VECTOR, root) + end + end + + if game_object.is_slice9 then + local component_url = M.get_component_url(entity, game_object.sprite_url or "/root#sprite") + + TEMP_VECTOR.x = transform.size_x + TEMP_VECTOR.y = transform.size_y + if event.animate_time then + local easing = event.easing or go.EASING_OUTSINE + go.animate(component_url, HASH_SIZE, go.PLAYBACK_ONCE_FORWARD, TEMP_VECTOR, easing, event.animate_time, delay) + else + go.set(component_url, HASH_SIZE, TEMP_VECTOR) + end + end + end + end + end +end + + +function M:refresh_transform(entity) + local root = entity.game_object.root + if not root then + return + end + + go.set_position(entity.transform.position, root) + go.set_scale(entity.transform.scale, root) + + TEMP_QUAT.z = sin(rad(entity.transform.rotation) * 0.5) + TEMP_QUAT.w = cos(rad(entity.transform.rotation) * 0.5) + go.set_rotation(TEMP_QUAT, root) +end + + +local PROPERTIES = { [ROOT_URL] = { is_spawn_by_entity = true } } +---@param entity entity.game_object +---@return table +function M:create_object(entity) + TEMP_VECTOR.x = entity.transform.position_x + TEMP_VECTOR.y = entity.transform.position_y + TEMP_VECTOR.z = self:get_position_z(entity.transform) + + if entity.game_object.is_factory then + local object = factory.create(entity.game_object.factory_url, TEMP_VECTOR, nil, PROPERTIES[ROOT_URL], entity.transform.scale_x) + return { [ROOT_URL] = object } + else + return collectionfactory.create(entity.game_object.factory_url, TEMP_VECTOR, nil, PROPERTIES, entity.transform.scale_x) + end +end + + +---@param t component.transform +---@return number +function M:get_position_z(t) + return -t.position_y / 10000 + t.position_x / 100000 + t.position_z / 10 +end + + +---@param entity entity.game_object +function M:refresh_root(entity) + local root = entity.game_object.root + if not root then + return + end + + if entity.game_object.is_slice9 then + local sprite_id = entity.game_object.sprite_url or "sprite" + local component_url = M.get_component_url(entity, sprite_id) + local slice9_offset = entity.game_object.slice9_offset + TEMP_VECTOR.x = entity.transform.size_x + (slice9_offset and slice9_offset.x or 0) + TEMP_VECTOR.y = entity.transform.size_y + (slice9_offset and slice9_offset.y or 0) + go.set(component_url, HASH_SIZE, TEMP_VECTOR) + go.set(root, HASH_SCALE, VECTOR3_ONE) + else + TEMP_VECTOR.x = entity.transform.scale_x + TEMP_VECTOR.y = entity.transform.scale_y + TEMP_VECTOR.z = entity.transform.scale_z + go.set(root, HASH_SCALE, TEMP_VECTOR) + end + + go.set(root, HASH_EULER_Z, entity.transform.rotation) + + TEMP_VECTOR.x = entity.transform.position_x + TEMP_VECTOR.y = entity.transform.position_y + TEMP_VECTOR.z = self:get_position_z(entity.transform) + go.set(root, HASH_POSITION, TEMP_VECTOR) + + self.root_to_entity[root] = entity +end + + +---Split string by separator +---@param s string +---@param sep string +function M.split(s, sep) + sep = sep or "%s" + local t = {} + local i = 1 + for str in string.gmatch(s, "([^" .. sep .. "]+)") do + t[i] = str + i = i + 1 + end + return t +end + + +---@param entity entity +---@param sprite_url string +function M.get_component_url(entity, sprite_url) + local splitted = M.split(sprite_url, "#") + local object_id, component_id = splitted[1], splitted[2] or "sprite" + -- If target starts with #, then it's a component id + if string.sub(sprite_url, 1, 1) == "#" then + object_id = nil + component_id = splitted[1] + end + + local target_url = msg.url(nil, object_id, component_id) + if entity.game_object and entity.game_object.object then + local object_url = entity.game_object.object[object_id] + target_url = msg.url(nil, object_url, component_id) + end + + return target_url +end + + +return M diff --git a/system/transform/transform.version b/system/transform/transform.version new file mode 100644 index 0000000..f99521e --- /dev/null +++ b/system/transform/transform.version @@ -0,0 +1 @@ +Insality:transform@1 \ No newline at end of file diff --git a/system/transform/transform_command.lua b/system/transform/transform_command.lua new file mode 100644 index 0000000..88e7616 --- /dev/null +++ b/system/transform/transform_command.lua @@ -0,0 +1,121 @@ +---@class world +---@field transform system.transform.command + +---@class system.transform.command +---@field private transform system.transform +local M = {} + + +---@param transform system.transform +---@return system.transform.command +function M.create(transform) + return setmetatable({ transform = transform }, { __index = M }) +end + + +---@param entity entity +---@param x number|nil +---@param y number|nil +---@param z number|nil +function M:set_position(entity, x, y, z) + assert(entity.transform, "Entity does not have a transform component.") + ---@cast entity entity.transform + self.transform:set_position(entity, x, y, z) +end + + +---@param entity entity +---@param x number|nil +---@param y number|nil +---@param z number|nil +function M:add_position(entity, x, y, z) + local t = entity.transform + assert(t, "Entity does not have a transform component.") + ---@cast entity entity.transform + + x = x and t.position_x + x + y = y and t.position_y + y + z = z and t.position_z + z + self.transform:set_position(entity, x, y, z) +end + + +---@param entity entity +---@param x number|nil +---@param y number|nil +---@param z number|nil +function M:set_scale(entity, x, y, z) + assert(entity.transform, "Entity does not have a transform component.") + ---@cast entity entity.transform + self.transform:set_scale(entity, x, y, z) +end + + +---@param entity entity +---@param x number|nil +---@param y number|nil +---@param z number|nil +function M:set_size(entity, x, y, z) + assert(entity.transform, "Entity does not have a transform component.") + ---@cast entity entity.transform + self.transform:set_size(entity, x, y, z) +end + + +function M:set_rotation(entity, rotation) + assert(entity.transform, "Entity does not have a transform component.") + ---@cast entity entity.transform + self.transform:set_rotation(entity, rotation) +end + + +---@param entity entity +---@param animate_time number|nil +---@param easing userdata|nil +---@param delay number|nil +---@param callback function|nil +function M:set_animate_time(entity, animate_time, easing, delay, callback) + assert(entity.transform, "Entity does not have a transform component.") + ---@cast entity entity.transform + self.transform:set_animate_time(entity, animate_time, easing, delay, callback) +end + + +---Return node borders relative to the current node parent +---@param entity entity +---@return number, number, number, number @left, top, right, bottom +function M:get_transform_borders(entity) + local t = entity.transform --[[@as component.transform]] + + local left = t.position_x - t.size_x * 0.5 + local top = t.position_y + t.size_y * 0.5 + local right = t.position_x + t.size_x * 0.5 + local bottom = t.position_y - t.size_y * 0.5 + + return left, top, right, bottom +end + + +---Check if two entities are overlapping +---@param entity1 entity +---@param entity2 entity +---@return boolean +function M:is_overlap(entity1, entity2) + local left1, right1, top1, bottom1 = self:get_transform_borders(entity1) + local left2, right2, top2, bottom2 = self:get_transform_borders(entity2) + + return left1 < right2 and right1 > left2 and top1 > bottom2 and bottom1 < top2 +end + + +---@param entity entity +---@param x number +---@param y number +---@return boolean +function M:pick_entity(entity, x, y) + local left, top, right, bottom = self:get_transform_borders(entity) + return x > left and x < right and y > bottom and y < top +end + + +return M diff --git a/system/transform/transform_system.lua b/system/transform/transform_system.lua new file mode 100644 index 0000000..c9ff966 --- /dev/null +++ b/system/transform/transform_system.lua @@ -0,0 +1,187 @@ +local decore = require("decore.decore") +local transform_command = require("system.transform.transform_command") + +---@class entity +---@field transform component.transform? + +---@class entity.transform: entity +---@field transform component.transform + +---@class component.transform +---@field position_x number The position x +---@field position_y number The position y +---@field position_z number The position z +---@field size_x number The size x +---@field size_y number The size y +---@field size_z number The size z +---@field scale_x number The scale x +---@field scale_y number The scale y +---@field scale_z number The scale z +---@field rotation number The rotation +---@field is_animated boolean Whether the transform is animated +decore.register_component("transform", { + position_x = 0, + position_y = 0, + position_z = 0, + size_x = 1, + size_y = 1, + size_z = 1, + scale_x = 1, + scale_y = 1, + scale_z = 1, + rotation = 0, +}) + +---@class system.transform.event +---@field entity entity.transform The entity that was changed. +---@field is_position_changed boolean|nil If true, the position was changed. +---@field is_scale_changed boolean|nil If true, the scale was changed. +---@field is_rotation_changed boolean|nil If true, the rotation was changed. +---@field is_size_changed boolean|nil If true, the size was changed. +---@field animate_time number|nil If true, the time it took to animate the transform. +---@field easing userdata|nil The easing function used for the animation. +---@field delay number|nil The delay before the animation starts. +---@field callback function|nil The callback function to call when the animation is complete. + +---@class system.transform: system +---@field entities entity.transform[] +local M = {} + + +---@return system.transform +function M.create() + return decore.system(M, "transform", "transform") +end + + +function M:onAddToWorld() + self.world.transform = transform_command.create(self) + self.world.event_bus:set_merge_policy("transform_event", self.event_merge_policy) +end + + +---@param entity entity.transform +---@param x number|nil +---@param y number|nil +---@param z number|nil +function M:set_position(entity, x, y, z) + local t = entity.transform + local is_changed = (x and t.position_x ~= x) or (y and t.position_y ~= y) or (z and t.position_z ~= z) + + t.position_x = x or t.position_x + t.position_y = y or t.position_y + t.position_z = z or t.position_z + + if is_changed then + self.world.event_bus:trigger("transform_event", { + entity = entity, + is_position_changed = true, + }) + end +end + + +---@param entity entity.transform +---@param x number|nil +---@param y number|nil +---@param z number|nil +function M:set_scale(entity, x, y, z) + local t = entity.transform + local is_changed = (x and t.scale_x ~= x) or (y and t.scale_y ~= y) or (z and t.scale_z ~= z) + + t.scale_x = x or t.scale_x + t.scale_y = y or t.scale_y + t.scale_z = z or t.scale_z + + if is_changed then + self.world.event_bus:trigger("transform_event", { + entity = entity, + is_scale_changed = true, + }) + end +end + + +---@param entity entity.transform +---@param x number|nil +---@param y number|nil +---@param z number|nil +function M:set_size(entity, x, y, z) + local t = entity.transform + local is_changed = (x and t.size_x ~= x) or (y and t.size_y ~= y) or (z and t.size_z ~= z) + + t.size_x = x or t.size_x + t.size_y = y or t.size_y + t.size_z = z or t.size_z + + if is_changed then + self.world.event_bus:trigger("transform_event", { + entity = entity, + is_size_changed = true, + }) + end +end + + +---@param entity entity.transform +---@param rotation number In degrees +function M:set_rotation(entity, rotation) + local t = entity.transform + local is_changed = t.rotation ~= rotation + t.rotation = rotation or t.rotation + + if is_changed then + self.world.event_bus:trigger("transform_event", { + entity = entity, + is_rotation_changed = true, + }) + end +end + + +---@param entity entity.transform +---@param animate_time number|nil +---@param easing userdata|nil +---@param delay number|nil +---@param callback function|nil +function M:set_animate_time(entity, animate_time, easing, delay, callback) + self.world.event_bus:trigger("transform_event", { + entity = entity, + animate_time = animate_time, + easing = easing, + delay = delay, + callback = callback + }) +end + + +---@param new_event system.transform.event +---@param events system.transform.event[] +---@param entity_map table +---@return boolean is_merged +function M.event_merge_policy(new_event, events, entity_map) + local entity = new_event.entity + if not entity then + return false + end + + local existing_events = entity_map[entity] + if existing_events and #existing_events > 0 then + -- Merge with the last event for this entity + local existing_event = existing_events[#existing_events] + existing_event.is_position_changed = new_event.is_position_changed or existing_event.is_position_changed + existing_event.is_scale_changed = new_event.is_scale_changed or existing_event.is_scale_changed + existing_event.is_rotation_changed = new_event.is_rotation_changed or existing_event.is_rotation_changed + existing_event.is_size_changed = new_event.is_size_changed or existing_event.is_size_changed + existing_event.animate_time = new_event.animate_time or existing_event.animate_time + existing_event.easing = new_event.easing or existing_event.easing + existing_event.delay = new_event.delay or existing_event.delay + existing_event.callback = new_event.callback or existing_event.callback + return true + end + + return false +end + + +return M