├── .DS_Store ├── lib ├── .DS_Store ├── ldtk_renderer.rb └── ldtk_bridge.rb ├── assets ├── tileset.png ├── four_four_tileset.png └── level │ └── png │ ├── Level1__FgDeco.png │ ├── Level1__Tiles.png │ ├── Level2__FgDeco.png │ ├── Level2__Tiles.png │ ├── Level1__Background.png │ ├── Level1__IntGrid-int.png │ ├── Level2__Background.png │ └── Level2__IntGrid-int.png ├── app └── main.rb ├── LICENSE └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleB0xes/LDtkBridge/HEAD/.DS_Store -------------------------------------------------------------------------------- /lib/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleB0xes/LDtkBridge/HEAD/lib/.DS_Store -------------------------------------------------------------------------------- /assets/tileset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleB0xes/LDtkBridge/HEAD/assets/tileset.png -------------------------------------------------------------------------------- /assets/four_four_tileset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleB0xes/LDtkBridge/HEAD/assets/four_four_tileset.png -------------------------------------------------------------------------------- /app/main.rb: -------------------------------------------------------------------------------- 1 | require 'lib/ldtk_bridge' 2 | 3 | def tick args 4 | args.outputs.background_color = [0, 0, 0] 5 | end 6 | -------------------------------------------------------------------------------- /assets/level/png/Level1__FgDeco.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleB0xes/LDtkBridge/HEAD/assets/level/png/Level1__FgDeco.png -------------------------------------------------------------------------------- /assets/level/png/Level1__Tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleB0xes/LDtkBridge/HEAD/assets/level/png/Level1__Tiles.png -------------------------------------------------------------------------------- /assets/level/png/Level2__FgDeco.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleB0xes/LDtkBridge/HEAD/assets/level/png/Level2__FgDeco.png -------------------------------------------------------------------------------- /assets/level/png/Level2__Tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleB0xes/LDtkBridge/HEAD/assets/level/png/Level2__Tiles.png -------------------------------------------------------------------------------- /assets/level/png/Level1__Background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleB0xes/LDtkBridge/HEAD/assets/level/png/Level1__Background.png -------------------------------------------------------------------------------- /assets/level/png/Level1__IntGrid-int.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleB0xes/LDtkBridge/HEAD/assets/level/png/Level1__IntGrid-int.png -------------------------------------------------------------------------------- /assets/level/png/Level2__Background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleB0xes/LDtkBridge/HEAD/assets/level/png/Level2__Background.png -------------------------------------------------------------------------------- /assets/level/png/Level2__IntGrid-int.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleB0xes/LDtkBridge/HEAD/assets/level/png/Level2__IntGrid-int.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Nicolas Henriot 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LDtkBridge 2 | A bridge between LDtk and DragonRuby 3 | 4 | 5 | ⚠️ construction in progress ⚠️ 6 | 7 | Documentation in progress too ! 8 | 9 | 10 | ## Usage 11 | 12 | ### LDtk project init 13 | First you need to init your map 14 | 15 | ```ruby 16 | my_world = LDtk.create_world('path_to_your_file', 'your_ldtk_file.ldtk') 17 | ``` 18 | my_world is a `Hash` and has this structure : 19 | ```ruby 20 | { 21 | folder: 'path_to_your_file/', 22 | levels: {}, # Hash with all levels 23 | definitions: {} # some useful information about tilesets 24 | } 25 | ``` 26 | 27 | ### Loading Levels and Layers 28 | #### Level 29 | If in your LDtk file, your level is named "Level_1" in LDtk, LDtkBridge converts it to a symbol `:Level_1`. 30 | 31 | This level can be load like this 32 | 33 | ```ruby 34 | my_level = LDtk.level(my_world, :Level_1) 35 | ``` 36 | 37 | Each level is a hash with keys : 38 | ```ruby 39 | { 40 | identifier: # Symbol name of the level 41 | uid: # Integer Unique integer identifier 42 | iid: # String Unique instance identifier 43 | px_hei: # Integer height of the level in px, 44 | px_wid: # Integer width of the level in px, 45 | world_x: # Integer x position of the level in the world in px, 46 | world_y: # Integer y position of the level in the world in px, 47 | layers: # Hash hash of layer with layer identifier as key 48 | field: # Array array of field hash` 49 | } 50 | ``` 51 | 52 | #### Layers 53 | In the same way, you can load a layer named "Ground" in "Level_1" with 54 | 55 | ```ruby 56 | my_layer = LDtk.layer(my_world, :Level_1, :Ground) 57 | ``` 58 | Each layer is a hash with keys 59 | ```ruby 60 | { 61 | cell_width: # Integer width in cells 62 | cell_height: # Integer height in cells 63 | grid_size: # Integer cell size in px 64 | identifier # Symbol name of the layer 65 | type: # Symbol Layer type 66 | # (possible values: :IntGrid,:Entities, :Tiles or :AutoLayer) 67 | iid: # String Unique instance identifier 68 | tileset_def_uid: # Integer definition id of the layer's tileset 69 | tileset_rel_path: # String relative path of tileset 70 | grid_tiles # Array array of tiles's hash (dr friendly) 71 | entities # Array array of entity's hash 72 | int_grid # Array array of int in 'dr-friendly' orientation 73 | } 74 | ``` 75 | 76 | ### Tile Layer 77 | When the layer is a tiles layer (id `type: :Tiles), `grid_tiles` key link to an array of tile. 78 | LDtkBridge make DR friendly tiles (plus some useful fields). So tiles have this format : 79 | ```ruby 80 | { 81 | x: 82 | y: 83 | w: 84 | h: 85 | source_x: 86 | source_y: 87 | source_w: 88 | source_h: 89 | flip_horizontally: 90 | flip_vertically: 91 | 92 | tile_id: 93 | enum_tags 94 | } 95 | ``` 96 | ### Entities Layer 97 | 98 | ### Int Grid Layer 99 | 100 | -------------------------------------------------------------------------------- /lib/ldtk_renderer.rb: -------------------------------------------------------------------------------- 1 | module LDtk 2 | DEFAULT_CAMERA = { x: 0, y: 0, scale: 4 } 3 | 4 | # Render 5 | def self.render_layer(world, level_name, layer_name, camera = DEFAULT_CAMERA) 6 | layer = layer(world, level_name, layer_name) 7 | tileset_path = world.folder + layer.tileset_rel_path 8 | $gtk.args.outputs.sprites << layer.grid_tiles.map do |tile| 9 | 10 | { 11 | x: (tile.x - camera.x) * camera.scale, 12 | y: (tile.y - camera.y) * camera.scale, 13 | w: tile.w * camera.scale, 14 | h: tile.h * camera.scale, 15 | path: tileset_path, 16 | source_x: tile.source_x, 17 | source_y: tile.source_y, 18 | source_w: tile.source_w, 19 | source_h: tile.source_h, 20 | flip_horizontally: tile.flip_horizontally, 21 | flip_vertically: tile.flip_vertically 22 | } 23 | end 24 | end 25 | 26 | def self.layer_as_rt(world, layer, rt_name) 27 | $gtk.args.render_target(rt_name).width = layer.cell_width * layer.grid_size 28 | $gtk.args.render_target(rt_name).height = layer.cell_height * layer.grid_size 29 | 30 | tileset_path = world.folder + layer.tileset_rel_path 31 | 32 | $gtk.args.render_target(rt_name).sprites << layer.grid_tiles.map do |tile| 33 | 34 | { 35 | x: tile.x, 36 | y: tile.y, 37 | w: tile.w, 38 | h: tile.h, 39 | 40 | path: tileset_path, 41 | source_x: tile.source_x, 42 | source_y: tile.source_y, 43 | source_w: tile.source_w, 44 | source_h: tile.source_h, 45 | flip_horizontally: tile.flip_horizontally, 46 | flip_vertically: tile.flip_vertically 47 | } 48 | end 49 | end 50 | 51 | 52 | # Render a layer in static_sprite with draw override 53 | # link to a camera 54 | def self.render_layer_as_static(layer, camera = DEFAULT_CAMERA) 55 | tileset_path = @folder + layer.tileset_rel_path 56 | $gtk.args.outputs.static_sprites << layer.grid_tiles.map do |tile| 57 | LDtkTile.new(tile, tileset_path, camera) 58 | end 59 | end 60 | 61 | def self.get_tiles(layer, scale=1, dx=0, dy=0) 62 | tileset_path = @folder + layer.tileset_rel_path 63 | 64 | layer.grid_tiles.map do |tile| 65 | { 66 | x: (tile.x - dx) * scale, 67 | y: (tile.y - dy) * scale, 68 | w: tile.w * scale, 69 | h: tile.w * scale, 70 | path: tileset_path, 71 | source_x: tile.source_x, 72 | source_y: tile.source_y, 73 | source_w: tile.source_w, 74 | source_h: tile.source_h, 75 | flip_horizontally: tile.flip_horizontally, 76 | flip_vertically: tile.flip_vertically 77 | } 78 | end 79 | end 80 | 81 | def self.render_debug_int_grid(world, level_name, layer_name, camera = DEFAULT_CAMERA) 82 | layer = layer(world, level_name, layer_name) 83 | color_definition = { 84 | 1 => { r: 255, g: 0, b: 0 }, 85 | 2 => { r: 0, g: 0, b: 0 }, 86 | 3 => { r: 0, g: 0, b: 255 }, 87 | 4 => { r: 255, g: 255, b: 0 } 88 | } 89 | 90 | $gtk.args.outputs.primitives << layer[:int_grid].map.with_index do |value, index| 91 | grid_size = layer[:grid_size] 92 | if value != 0 93 | x = index % layer[:cell_width] 94 | y = (index / layer[:cell_width]).to_i 95 | 96 | { 97 | x: (x * grid_size - camera.x) * camera.scale, 98 | y: (y * grid_size - camera.y) * camera.scale, 99 | w: grid_size * camera.scale, 100 | h: grid_size * camera.scale, 101 | path: :pixel, 102 | source_x: 0, 103 | source_y: 0, 104 | source_w: 1, 105 | source_h: 1, 106 | a: 125 107 | }.merge(color_definition[value]).sprite 108 | end 109 | end 110 | end 111 | 112 | def self.drawable_entities(world, level_name, layer_name, camera = DEFAULT_CAMERA) 113 | # cell_width = world.levels[level_name].layers[layer_name].cell_width 114 | # cell_height = world.levels[level_name].layers[layer_name].cell_height 115 | entities = world.levels[level_name].layers[layer_name].entities 116 | 117 | entities.select { |e| e.key?(:tile) && e[:tile].key?(:tileset_uid) } 118 | .map do |e| 119 | { 120 | x: e.pos.x * camera.scale, 121 | y: e.pos.y * camera.scale, 122 | w: e.tile.w * camera.scale, 123 | h: e.tile.h * camera.scale, 124 | source_x: e.tile.x, 125 | source_y: e.tile.y, 126 | source_w: e.tile.w, 127 | source_h: e.tile.h, 128 | path: "#{world.folder}#{world.definitions.tilesets[e.tile.tileset_uid].rel_path}" 129 | } 130 | end 131 | end 132 | end 133 | 134 | 135 | 136 | class LDtkTile 137 | attr_sprite 138 | 139 | def initialize(tile, path, camera) 140 | @camera = camera 141 | @x = tile.x 142 | @y = tile.y 143 | @w = tile.w 144 | @h = tile.w 145 | @path = path 146 | @source_x = tile.source_x 147 | @source_y = tile.source_y 148 | @source_w = tile.source_w 149 | @source_h = tile.source_h 150 | @flip_horizontally = tile.flip_horizontally 151 | @flip_vertically = tile.flip_vertically 152 | end 153 | 154 | def draw_override ffi_draw 155 | scale = @camera.scale 156 | ffi_draw.draw_sprite_3( 157 | (@x.to_i - @camera.x) * scale, (@y.to_i - @camera.y) * scale, @w * scale, @h * scale, 158 | @path, 159 | 0, 160 | @a, 255, 255, 255, 161 | nil, nil, nil, nil, 162 | @flip_horizontally, @flip_vertically, 163 | nil, nil, 164 | @source_x, @source_y, @source_w, @source_h 165 | ) 166 | # The argument order for ffi_draw.draw_sprite_4 is: 167 | # x, y, w, h, 168 | # path, 169 | # angle, 170 | # alpha, red_saturation, green_saturation, blue_saturation 171 | # tile_x, tile_y, tile_w, tile_h, 172 | # flip_horizontally, flip_vertically, 173 | # angle_anchor_x, angle_anchor_y, 174 | # source_x, source_y, source_w, source_h, 175 | # blendmode_enum 176 | end 177 | end 178 | -------------------------------------------------------------------------------- /lib/ldtk_bridge.rb: -------------------------------------------------------------------------------- 1 | module LDtk 2 | @folder = '' 3 | @definitions = {} 4 | 5 | # Return a Hash with hash tilesets and hash levels 6 | def self.load_world(folder, file_name) 7 | @folder = folder 8 | 9 | ldtk_file = $gtk.parse_json_file(folder + '/' + file_name) 10 | 11 | @definitions = create_definitions(ldtk_file['defs']) 12 | 13 | # Transform raw data in DR friendly hash 14 | levels = create_levels(ldtk_file['levels'], @definitions) 15 | 16 | { 17 | folder: folder + '/', 18 | levels: levels, 19 | definitions: @definitions 20 | } 21 | end 22 | 23 | # Definitions contains : 24 | # - A more practical Hash of tilesets data with uid as keys 25 | # - Other stuffs...later 26 | def self.create_definitions(definitions) 27 | all_tilesets = Hash[definitions['tilesets'].map{ |t| 28 | [ 29 | t['uid'], 30 | { uid: t['uid'], rel_path: t['relPath'], px_wid: t['pxWid'], px_hei: t['pxHei'], enum_tags: tileset_enum_tag(t['enumTags'])} 31 | ]} 32 | ] 33 | 34 | 35 | { tilesets: all_tilesets } 36 | end 37 | 38 | def self.tileset_enum_tag(enum_tags) 39 | enum_tags.map do |t| 40 | { 41 | enum_id: t['enumValueId'].to_sym, 42 | tile_ids: t['tileIds'] 43 | } 44 | 45 | end 46 | 47 | end 48 | 49 | # each level have field 50 | # { 51 | # identifier: `symbol`, name of the level 52 | # px_hei: `int`, height of the level in px, 53 | # px_wid: `int`, width of the level in px, 54 | # world_x: `int`, x position of the level in the world in px, 55 | # world_y: `int`, y position of the level in the world in px, 56 | # layers: `hash of layer hash` 57 | # field_instances: `array of field hash` 58 | # } 59 | def self.create_levels(levels, defs) 60 | all_levels = {} 61 | levels.each do |l| 62 | layers = self.create_layers(l["layerInstances"], defs) 63 | level_fields = Hash[l['fieldInstances'].map do |f| 64 | value = f['__value'] 65 | 66 | ### Just transform hash value with symbol keys instead 67 | ### string keys 68 | if value.is_a?(Hash) 69 | value = value.transform_keys(&:to_sym) 70 | end 71 | [f['__identifier'].to_sym, value] 72 | end 73 | ] 74 | level = { 75 | identifier: l["identifier"].to_sym, 76 | px_hei: l['pxHei'], 77 | px_wid: l['pxWid'], 78 | world_x: l['worldX'], 79 | world_y: l['worldY'], 80 | uid: l['uid'], 81 | iid: l['iid'], 82 | layers: Hash[layers.map{|layer| [layer[:identifier], layer]}], 83 | fields: level_fields 84 | } 85 | all_levels[level[:identifier]] = level 86 | end 87 | 88 | all_levels 89 | end 90 | 91 | # each layer have field 92 | # { 93 | # cell_width: `int`, width in cells 94 | # cell_height: `int`, height in cells 95 | # grid_size: `int` cell size in px 96 | # identifier `symbol` name of the layer 97 | # type: `symbol` Layer type (possible values: :IntGrid, 98 | # # :Entities, :Tiles or :AutoLayer) 99 | # tileset_def_uid: `int` definition id of the layer's tileset 100 | # tileset_rel_path: `string` relative path of tileset 101 | # grid_tiles `array` array of tiles's hash (dr friendly) 102 | # int_grid 'array' array of int in 'dr-friendly' orientation 103 | # } 104 | def self.create_layers(layers, defs) 105 | all_layers = [] 106 | 107 | layers.each do |l| 108 | layer = { 109 | cell_width: l['__cWid'], 110 | cell_height: l['__cHei'], 111 | grid_size: l['__gridSize'], 112 | identifier: l['__identifier'].to_sym, 113 | type: l['__type'].to_sym, 114 | iid: l['iid'], 115 | tileset_def_uid: l['__tilesetDefUid'], 116 | tileset_rel_path: l['__tilesetRelPath'], 117 | grid_tiles: [], 118 | entities: [], 119 | int_grid: [], 120 | 121 | } 122 | 123 | 124 | # ----------------------------- 125 | # Generate the grid tiles layer 126 | # ----------------------------- 127 | layer[:grid_tiles] = l['gridTiles'].map do |tile| 128 | position = { x: 0, y: 0 } 129 | position.x = tile['px'][0] 130 | position.y = layer.cell_height * layer.grid_size - tile['px'][1] - layer.grid_size 131 | 132 | source = { x: 0, y: 0 } 133 | source.x = tile['src'][0] 134 | source.y = defs[:tilesets][layer[:tileset_def_uid]][:px_hei] - tile['src'][1] - layer[:grid_size] 135 | 136 | flip_h = tile['f'] == 1 || tile['f'] == 3 137 | flip_v = tile['f'] == 2 || tile['f'] == 3 138 | tile_id = tile['t'] 139 | 140 | 141 | # Add all tags of tile 142 | tags = [] 143 | defs.tilesets[layer[:tileset_def_uid]][:enum_tags].each do |v| 144 | tags.push(v[:enum_id]) if v[:tile_ids].include?(tile_id) 145 | end 146 | 147 | # Create the tile 148 | create_dr_friendly_tile(position,source, layer[:grid_size], flip_h, flip_v).merge({tile_id: tile_id, enum_tags: tags}) 149 | end 150 | 151 | # ------------------------- 152 | # generate the entity layer 153 | # ------------------------- 154 | layer[:entities] = l['entityInstances'].map do |e| 155 | entity = { 156 | iid: e['iid'], 157 | identifier: e['__identifier'].to_sym, 158 | grid_pos: { x: e['__grid'][0] , y: (layer[:cell_height] - 1 - e['__grid'][1] )}, 159 | pos: { x: e['px'][0] - e['__pivot'][0] * layer[:grid_size], y: layer[:cell_height] * layer[:grid_size] - e['height'] + e['__pivot'][1] * layer[:grid_size] - e['px'][1]}, 160 | 161 | size: { w: e['width'], h: e['height'] }, 162 | pivot: { x: e['__pivot'][0], y: e['__pivot'][1] }, 163 | tile: {} 164 | } 165 | 166 | ## Format the value of fields 167 | entity[:fields] = Hash[e['fieldInstances'].map do |f| 168 | value = f['__value'] 169 | 170 | ### Just transform hash value with symbol keys instead 171 | ### string keys 172 | value = value.transform_keys(&:to_sym) if value.is_a?(Hash) 173 | [f['__identifier'].to_sym, value] 174 | end 175 | ] 176 | entity[:fields_type] = Hash[e['fieldInstances'].map do |f| 177 | [f['__identifier'].to_sym, f['__type']] 178 | end 179 | ] 180 | 181 | 182 | # In some case, fields needs to be rewrite 183 | entity[:fields].each do |k, v| 184 | if entity[:fields_type][k] == 'Array' 185 | entity[:fields][k] = v.map do |point| 186 | { 187 | cx: point['cx'], 188 | cy: layer.cell_height - point['cy'] - 1 189 | } 190 | end 191 | 192 | # Perhaps other specific type later... 193 | end 194 | end 195 | 196 | # Set the tile rectangle if exist 197 | if e['__tile'] 198 | entity[:tile] = { 199 | x: e['__tile']['x'], 200 | y: defs[:tilesets][e['__tile']['tilesetUid']][:px_hei] - e['__tile']['y'] - e['__tile']['h'], 201 | w: e['__tile']['w'], 202 | h: e['__tile']['h'], 203 | tileset_uid: e['__tile']['tilesetUid'] 204 | } 205 | end 206 | entity 207 | end 208 | 209 | # ------------------------------- 210 | # generate the integer grid layer 211 | # ------------------------------- 212 | if layer[:type] == :IntGrid 213 | layer[:int_grid] = Array.new(l['__cWid'] * l['__cHei'], 0) 214 | l['intGridCsv'].each.with_index do |value, index| 215 | x = index % l['__cWid'] 216 | y = (l['__cHei'] - 1) - (index / l['__cWid']).to_i 217 | layer[:int_grid][x + y * l['__cWid']] = value 218 | end 219 | end 220 | 221 | all_layers.push(layer) 222 | end 223 | 224 | all_layers 225 | end 226 | 227 | def self.create_dr_friendly_tile(position, source, grid_size, flip_h, flip_v) 228 | { 229 | x: position.x, y: position.y, 230 | w: grid_size, h: grid_size, 231 | source_x: source.x, source_y: source.y, 232 | source_w: grid_size, source_h: grid_size, 233 | flip_horizontally: flip_h, flip_vertically: flip_v 234 | } 235 | end 236 | 237 | # Return the level's size 238 | # @return {w: width, h:height} [Hash] 239 | # @param level [LDtk.Level] 240 | def self.level_size(level) 241 | { w: level[:px_wid], h: level[:px_hei] } 242 | end 243 | 244 | # Return the relative path of a tileset 245 | # @return [String] 246 | # @param world [Hash] LDtkBridge's world data 247 | # @param uid [Integer] uid of the tileset 248 | def self.tileset_path(world, uid) 249 | world.folder + world.definitions[:tilesets][uid][:rel_path] 250 | end 251 | 252 | # return all named entities 253 | def self.all(layer, entity_identifier) 254 | layer[:entities].select { |e| e.identifier == entity_identifier } 255 | end 256 | 257 | def self.entities(layer) 258 | layer[:entities] 259 | end 260 | 261 | def self.get_int(layer, position_x, position_y) 262 | layer[:int_grid][position_x + position_y * layer[:cell_width]] 263 | end 264 | 265 | def self.level(world, level_name) 266 | world.levels[level_name] 267 | end 268 | 269 | def self.layer(world, level_name, layer_name) 270 | world.levels[level_name].layers[layer_name] 271 | end 272 | 273 | def self.level_field(world, level_name, field_name) 274 | world.levels[level_name].fields[field_name] 275 | end 276 | 277 | def self.entity_rect(entity) 278 | { 279 | x: entity.pos.x, 280 | y: entity.pos.y, 281 | w: entity[:size].w, 282 | h: entity[:size].h 283 | } 284 | 285 | end 286 | 287 | 288 | def self.entity_ref_field(world, entity_ref) 289 | level_identifier = world.levels 290 | .select { |_k, v| v.iid == entity_ref[:levelIid] } 291 | .values 292 | .first 293 | .identifier 294 | 295 | layer_identifier = world.levels[level_identifier] 296 | .layers 297 | .select { |_k, v| v.iid == entity_ref[:layerIid] } 298 | .values 299 | .first 300 | .identifier 301 | 302 | entity = world.levels[level_identifier] 303 | .layers[layer_identifier] 304 | .entities 305 | .select { |e| e.iid == entity_ref[:entityIid] } 306 | .first 307 | 308 | { 309 | level: level_identifier, 310 | layer: layer_identifier, 311 | entity: entity 312 | } 313 | 314 | end 315 | 316 | def self.tile_enum(world, tile) 317 | world.definitions[tile] 318 | 319 | end 320 | end 321 | --------------------------------------------------------------------------------