A library for parsing Tiled maps.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add object template support (#5)

* bump version

* add object template support

Move Object into its own module (src/object.zig) and add support for
.tj template files. When an object references a template, field values
and properties from the template are applied as defaults; instance values
always take precedence. Instance properties with the same key as a
template property correctly free the template allocation before replacing
it.

* fix property_type default, add optimize build option

- Property.property_type defaults to null to avoid requiring callers to
set it explicitly
- Expose standardOptimizeOption in the build so the module respects
release modes
- Remove empty propertytype strings from test fixture
- Bump version to 0.2.4

* add tests for object template field inheritance and property merging

Test that template field values are applied when absent from the instance
(height, rotation), that instance values take precedence (width, name,
visible), and that properties from both sources are merged with instance
winning on key conflicts. Adds dedicated fixture files to cover the
property-override case not exercised by the existing map.tmj.

authored by

Kent Smith and committed by
GitHub
c9651616 cfeb42bc

+467 -1130
+3
build.zig
··· 1 1 pub fn build(b: *std.Build) void { 2 2 const target = b.standardTargetOptions(.{}); 3 + const optimize = b.standardOptimizeOption(.{}); 4 + 3 5 const coverage = b.option(bool, "coverage", "Generate a coverage report with kcov") orelse false; 4 6 const coverage_requested = coverage and b.graph.host.result.os.tag == .linux; 5 7 6 8 const mod = b.addModule("tmz", .{ 7 9 .root_source_file = b.path("src/root.zig"), 8 10 .target = target, 11 + .optimize = optimize, 9 12 }); 10 13 11 14 const mod_tests = b.addTest(.{
+1 -1
build.zig.zon
··· 1 1 .{ 2 2 .name = .tmz, 3 - .version = "0.2.2", 3 + .version = "0.2.4", 4 4 .fingerprint = 0xcc3a94566dc4ab5d, // Changing this has security and trust implications. 5 5 .minimum_zig_version = "0.15.1", 6 6 .dependencies = .{},
+1 -1
src/Property.zig
··· 1 1 /// https://doc.mapeditor.org/en/stable/reference/json-map-format/#property 2 2 name: []const u8, 3 - property_type: ?[]const u8, 3 + property_type: ?[]const u8 = null, 4 4 type: Type = .string, 5 5 value: Value, 6 6
+1 -130
src/layer.zig
··· 357 357 } 358 358 }; 359 359 360 - /// https://doc.mapeditor.org/en/stable/reference/json-map-format/#object 361 - pub const Object = struct { 362 - gid: ?u32 = null, 363 - height: f32, 364 - id: u32, 365 - name: []const u8, 366 - point: ?bool = null, 367 - polygon: ?[]Point = null, 368 - polyline: ?[]Point = null, 369 - properties: std.StringHashMapUnmanaged(Property), 370 - rotation: f32, 371 - template: ?[]const u8 = null, 372 - text: ?Text = null, 373 - class: ?[]const u8 = null, 374 - visible: bool, 375 - width: f32, 376 - x: f32, 377 - y: f32, 378 - 379 - type: Type, 380 - 381 - pub const Type = enum { 382 - rectangle, 383 - ellipse, 384 - polygon, 385 - polyline, 386 - tile, 387 - text, 388 - }; 389 - 390 - pub fn fromJson(allocator: Allocator, json_object: JsonObject) !Object { 391 - var properties: std.StringHashMapUnmanaged(Property) = .empty; 392 - 393 - if (json_object.properties) |props| { 394 - for (props) |property| { 395 - const prop = try Property.fromJson(allocator, property); 396 - try properties.put(allocator, prop.name, prop); 397 - } 398 - } 399 - 400 - const object: Object = .{ 401 - .gid = json_object.gid, 402 - .height = json_object.height, 403 - .id = json_object.id, 404 - .name = try allocator.dupe(u8, json_object.name), 405 - .point = json_object.point, 406 - .class = if (json_object.type) |class| try allocator.dupe(u8, class) else null, 407 - .rotation = json_object.rotation, 408 - .visible = json_object.visible, 409 - .width = json_object.width, 410 - .x = json_object.x, 411 - .y = json_object.y, 412 - .properties = properties, 413 - 414 - .type = set_type: { 415 - if (json_object.gid) |_| { 416 - break :set_type .tile; 417 - } 418 - if (json_object.ellipse) |_| { 419 - break :set_type .ellipse; 420 - } 421 - if (json_object.polygon) |_| { 422 - break :set_type .polygon; 423 - } 424 - if (json_object.polyline) |_| { 425 - break :set_type .polyline; 426 - } 427 - if (json_object.text) |_| { 428 - break :set_type .text; 429 - } 430 - break :set_type .rectangle; 431 - }, 432 - }; 433 - return object; 434 - } 435 - 436 - pub fn deinit(self: *Object, allocator: Allocator) void { 437 - allocator.free(self.name); 438 - if (self.class) |class| allocator.free(class); 439 - 440 - var properties_it = self.properties.valueIterator(); 441 - while (properties_it.next()) |value_ptr| { 442 - value_ptr.*.deinit(allocator); 443 - } 444 - self.properties.deinit(allocator); 445 - } 446 - 447 - const JsonObject = struct { 448 - ellipse: ?bool = null, 449 - gid: ?u32 = null, 450 - height: f32, 451 - id: u32, 452 - name: []const u8, 453 - point: ?bool = null, 454 - polygon: ?[]Point = null, 455 - polyline: ?[]Point = null, 456 - properties: ?[]Property = null, 457 - rotation: f32, 458 - template: ?[]const u8 = null, 459 - text: ?Text = null, 460 - type: ?[]const u8 = null, 461 - visible: bool, 462 - width: f32, 463 - x: f32, 464 - y: f32, 465 - }; 466 - }; 467 - 468 - /// https://doc.mapeditor.org/en/stable/reference/json-map-format/#point 469 - pub const Point = struct { 470 - x: f32, 471 - y: f32, 472 - }; 473 - 474 - /// https://doc.mapeditor.org/en/stable/reference/json-map-format/#text 475 - pub const Text = struct { 476 - bold: ?bool = null, 477 - color: ?[]const u8 = null, 478 - fontfamily: []const u8 = "sans-serif", 479 - halign: enum { center, right, justify, left } = .left, 480 - italic: bool = false, 481 - kerning: bool = true, 482 - pixelsize: u32 = 16, 483 - strikeout: bool = false, 484 - text: []const u8, 485 - underline: bool = false, 486 - valign: enum { center, bottom, top } = .top, 487 - wrap: bool = false, 488 - }; 489 - 490 360 // Decode base64 data (and optionally decompress) into a slice of u32 Global Tile Ids allocated on the heap, caller owns slice 491 361 fn parseBase64Data(allocator: Allocator, base64_data: []const u8, size: usize, compression: Compression) []u32 { 492 362 // var arena = std.heap.ArenaAllocator.init(allocator); ··· 552 422 const tmz = @import("root.zig"); 553 423 const Color = tmz.Color; 554 424 const Property = tmz.Property; 425 + const Object = tmz.Object; 555 426 556 427 const std = @import("std"); 557 428 const base64_decoder = std.base64.standard.Decoder;
+193
src/object.zig
··· 1 + /// https://doc.mapeditor.org/en/stable/reference/json-map-format/#object 2 + pub const Object = struct { 3 + gid: ?u32 = null, 4 + height: f32, 5 + id: u32, 6 + name: []const u8, 7 + point: ?bool = null, 8 + polygon: ?[]Point = null, 9 + polyline: ?[]Point = null, 10 + properties: std.StringHashMapUnmanaged(Property), 11 + rotation: f32, 12 + template: ?[]const u8 = null, 13 + text: ?Text = null, 14 + class: ?[]const u8 = null, 15 + visible: bool, 16 + width: f32, 17 + x: f32, 18 + y: f32, 19 + 20 + type: Type, 21 + 22 + pub const Type = enum { 23 + rectangle, 24 + ellipse, 25 + polygon, 26 + polyline, 27 + tile, 28 + text, 29 + }; 30 + 31 + pub fn fromJson(allocator: Allocator, json_obj: JsonObject) !Object { 32 + var properties: std.StringHashMapUnmanaged(Property) = .empty; 33 + var json_object = json_obj; 34 + 35 + if (json_object.template) |template| { 36 + var arena = std.heap.ArenaAllocator.init(allocator); 37 + defer arena.deinit(); 38 + const arena_allocator = arena.allocator(); 39 + 40 + const file = try std.fs.cwd().openFile(template, .{}); 41 + defer file.close(); 42 + 43 + var reader: std.fs.File.Reader = .initStreaming(file, &.{}); 44 + 45 + var out: std.Io.Writer.Allocating = .init(arena_allocator); 46 + defer out.deinit(); 47 + 48 + _ = try reader.interface.streamRemaining(&out.writer); 49 + 50 + const json = try out.toOwnedSlice(); 51 + 52 + const parsed_value = try std.json.parseFromSliceLeaky(std.json.Value, arena_allocator, json, .{ .ignore_unknown_fields = true }); 53 + const json_template = try std.json.parseFromValueLeaky(JsonObject.Template, arena_allocator, parsed_value, .{ .ignore_unknown_fields = true }); 54 + 55 + const json_template_object = json_template.object; 56 + 57 + if (json_template_object.properties) |props| { 58 + for (props) |property| { 59 + const prop = try Property.fromJson(allocator, property); 60 + try properties.put(allocator, prop.name, prop); 61 + } 62 + } 63 + json_object.applyTemplate(json_template_object); 64 + } 65 + 66 + if (json_object.properties) |props| { 67 + for (props) |property| { 68 + const prop = try Property.fromJson(allocator, property); 69 + if (properties.fetchRemove(prop.name)) |entry| { 70 + var old = entry.value; 71 + old.deinit(allocator); 72 + } 73 + try properties.put(allocator, prop.name, prop); 74 + } 75 + } 76 + 77 + const object: Object = .{ 78 + .gid = json_object.gid, 79 + .height = json_object.height.?, 80 + .id = json_object.id.?, 81 + .name = try allocator.dupe(u8, json_object.name.?), 82 + .point = json_object.point, 83 + .class = if (json_object.type) |class| try allocator.dupe(u8, class) else null, 84 + .rotation = json_object.rotation.?, 85 + .visible = json_object.visible.?, 86 + .width = json_object.width.?, 87 + .x = json_object.x.?, 88 + .y = json_object.y.?, 89 + .properties = properties, 90 + 91 + .type = set_type: { 92 + if (json_object.gid) |_| { 93 + break :set_type .tile; 94 + } 95 + if (json_object.ellipse) |_| { 96 + break :set_type .ellipse; 97 + } 98 + if (json_object.polygon) |_| { 99 + break :set_type .polygon; 100 + } 101 + if (json_object.polyline) |_| { 102 + break :set_type .polyline; 103 + } 104 + if (json_object.text) |_| { 105 + break :set_type .text; 106 + } 107 + break :set_type .rectangle; 108 + }, 109 + }; 110 + 111 + return object; 112 + } 113 + 114 + pub fn deinit(self: *Object, allocator: Allocator) void { 115 + allocator.free(self.name); 116 + if (self.class) |class| allocator.free(class); 117 + 118 + var properties_it = self.properties.valueIterator(); 119 + while (properties_it.next()) |value_ptr| { 120 + value_ptr.*.deinit(allocator); 121 + } 122 + self.properties.deinit(allocator); 123 + } 124 + 125 + pub const JsonObject = struct { 126 + pub const Template = struct { 127 + object: JsonObject, 128 + type: ?[]const u8, 129 + }; 130 + 131 + ellipse: ?bool = null, 132 + gid: ?u32 = null, 133 + height: ?f32 = null, 134 + id: ?u32 = null, 135 + name: ?[]const u8 = null, 136 + opacity: ?f32 = null, 137 + point: ?bool = null, 138 + polygon: ?[]Point = null, 139 + polyline: ?[]Point = null, 140 + properties: ?[]Property = null, 141 + rotation: ?f32 = null, 142 + template: ?[]const u8 = null, 143 + text: ?Text = null, 144 + type: ?[]const u8 = null, 145 + visible: ?bool = null, 146 + width: ?f32 = null, 147 + x: ?f32 = null, 148 + y: ?f32 = null, 149 + 150 + pub fn applyTemplate(self: *JsonObject, template: JsonObject) void { 151 + if (self.ellipse == null) self.ellipse = if (template.ellipse) |ellipse| ellipse else null; 152 + if (self.id == null) self.id = if (template.id) |id| id else null; 153 + if (self.gid == null) self.gid = if (template.gid) |gid| gid else null; 154 + if (self.height == null) self.height = if (template.height) |height| height else null; 155 + if (self.opacity == null) self.opacity = if (template.opacity) |opacity| opacity else null; 156 + if (self.point == null) self.point = if (template.point) |point| point else null; 157 + if (self.polygon == null) self.polygon = if (template.polygon) |polygon| polygon else null; 158 + if (self.polyline == null) self.polyline = if (template.polyline) |polyline| polyline else null; 159 + if (self.rotation == null) self.rotation = if (template.rotation) |rotation| rotation else null; 160 + if (self.text == null) self.text = if (template.text) |text| text else null; 161 + if (self.type == null) self.type = if (template.type) |t| t else null; 162 + if (self.visible == null) self.visible = if (template.visible) |visible| visible else null; 163 + if (self.width == null) self.width = if (template.width) |width| width else null; 164 + } 165 + }; 166 + }; 167 + 168 + /// https://doc.mapeditor.org/en/stable/reference/json-map-format/#point 169 + pub const Point = struct { 170 + x: f32, 171 + y: f32, 172 + }; 173 + 174 + /// https://doc.mapeditor.org/en/stable/reference/json-map-format/#text 175 + pub const Text = struct { 176 + bold: ?bool = null, 177 + color: ?[]const u8 = null, 178 + fontfamily: []const u8 = "sans-serif", 179 + halign: enum { center, right, justify, left } = .left, 180 + italic: bool = false, 181 + kerning: bool = true, 182 + pixelsize: u32 = 16, 183 + strikeout: bool = false, 184 + text: []const u8, 185 + underline: bool = false, 186 + valign: enum { center, bottom, top } = .top, 187 + wrap: bool = false, 188 + }; 189 + 190 + const Property = @import("root.zig").Property; 191 + 192 + const std = @import("std"); 193 + const Allocator = std.mem.Allocator;
+1 -1
src/root.zig
··· 1 1 pub const Map = @import("Map.zig"); 2 2 pub const Layer = layer.Layer; 3 - pub const Object = layer.Object; 3 + pub const Object = @import("object.zig").Object; 4 4 pub const Tileset = tileset.Tileset; 5 5 pub const Tile = tileset.Tile; 6 6 pub const Property = @import("Property.zig");
+125 -995
test/map.tmj
··· 1 - { 2 - "backgroundcolor": "#aabbccdd", 3 - "class": "bar", 4 - "compressionlevel": -1, 5 - "height": 30, 6 - "infinite": false, 7 - "layers": [ 8 - { 9 - "class": "bar", 10 - "data": [ 11 - 1, 12 - 0, 13 - 0, 14 - 0, 15 - 0, 16 - 0, 17 - 0, 18 - 0, 19 - 0, 20 - 0, 21 - 0, 22 - 0, 23 - 0, 24 - 0, 25 - 0, 26 - 0, 27 - 0, 28 - 0, 29 - 0, 30 - 0, 31 - 0, 32 - 0, 33 - 0, 34 - 0, 35 - 0, 36 - 0, 37 - 0, 38 - 0, 39 - 0, 40 - 0, 41 - 0, 42 - 0, 43 - 0, 44 - 0, 45 - 0, 46 - 0, 47 - 0, 48 - 0, 49 - 0, 50 - 0, 51 - 0, 52 - 0, 53 - 0, 54 - 0, 55 - 0, 56 - 0, 57 - 0, 58 - 0, 59 - 0, 60 - 0, 61 - 0, 62 - 0, 63 - 0, 64 - 0, 65 - 0, 66 - 0, 67 - 0, 68 - 0, 69 - 0, 70 - 0, 71 - 0, 72 - 0, 73 - 0, 74 - 0, 75 - 0, 76 - 0, 77 - 0, 78 - 0, 79 - 0, 80 - 0, 81 - 0, 82 - 0, 83 - 0, 84 - 0, 85 - 0, 86 - 0, 87 - 0, 88 - 0, 89 - 0, 90 - 0, 91 - 0, 92 - 0, 93 - 0, 94 - 0, 95 - 0, 96 - 0, 97 - 0, 98 - 0, 99 - 0, 100 - 0, 101 - 0, 102 - 0, 103 - 0, 104 - 0, 105 - 0, 106 - 0, 107 - 0, 108 - 0, 109 - 0, 110 - 0, 111 - 0, 112 - 0, 113 - 0, 114 - 0, 115 - 0, 116 - 0, 117 - 0, 118 - 0, 119 - 0, 120 - 0, 121 - 0, 122 - 0, 123 - 0, 124 - 0, 125 - 0, 126 - 0, 127 - 0, 128 - 0, 129 - 0, 130 - 0, 131 - 0, 132 - 0, 133 - 0, 134 - 0, 135 - 0, 136 - 0, 137 - 0, 138 - 0, 139 - 0, 140 - 0, 141 - 0, 142 - 0, 143 - 0, 144 - 0, 145 - 0, 146 - 0, 147 - 0, 148 - 0, 149 - 0, 150 - 0, 151 - 0, 152 - 0, 153 - 0, 154 - 0, 155 - 0, 156 - 0, 157 - 0, 158 - 0, 159 - 0, 160 - 0, 161 - 0, 162 - 0, 163 - 0, 164 - 0, 165 - 0, 166 - 0, 167 - 0, 168 - 0, 169 - 0, 170 - 0, 171 - 0, 172 - 0, 173 - 0, 174 - 0, 175 - 0, 176 - 0, 177 - 0, 178 - 0, 179 - 0, 180 - 0, 181 - 0, 182 - 0, 183 - 0, 184 - 0, 185 - 0, 186 - 0, 187 - 0, 188 - 0, 189 - 0, 190 - 0, 191 - 0, 192 - 0, 193 - 0, 194 - 0, 195 - 0, 196 - 0, 197 - 0, 198 - 0, 199 - 0, 200 - 0, 201 - 0, 202 - 0, 203 - 0, 204 - 0, 205 - 0, 206 - 0, 207 - 0, 208 - 0, 209 - 0, 210 - 0, 211 - 0, 212 - 0, 213 - 0, 214 - 0, 215 - 0, 216 - 0, 217 - 0, 218 - 0, 219 - 0, 220 - 0, 221 - 0, 222 - 0, 223 - 0, 224 - 0, 225 - 0, 226 - 0, 227 - 0, 228 - 0, 229 - 0, 230 - 0, 231 - 0, 232 - 0, 233 - 0, 234 - 0, 235 - 0, 236 - 0, 237 - 0, 238 - 0, 239 - 0, 240 - 0, 241 - 0, 242 - 0, 243 - 0, 244 - 0, 245 - 0, 246 - 0, 247 - 0, 248 - 0, 249 - 0, 250 - 0, 251 - 0, 252 - 0, 253 - 0, 254 - 0, 255 - 0, 256 - 0, 257 - 0, 258 - 0, 259 - 0, 260 - 0, 261 - 0, 262 - 0, 263 - 0, 264 - 0, 265 - 0, 266 - 0, 267 - 0, 268 - 0, 269 - 0, 270 - 0, 271 - 0, 272 - 0, 273 - 0, 274 - 0, 275 - 0, 276 - 0, 277 - 0, 278 - 0, 279 - 0, 280 - 0, 281 - 0, 282 - 0, 283 - 0, 284 - 0, 285 - 0, 286 - 0, 287 - 0, 288 - 0, 289 - 0, 290 - 0, 291 - 0, 292 - 0, 293 - 0, 294 - 0, 295 - 0, 296 - 0, 297 - 0, 298 - 0, 299 - 0, 300 - 0, 301 - 0, 302 - 0, 303 - 0, 304 - 0, 305 - 0, 306 - 0, 307 - 0, 308 - 0, 309 - 0, 310 - 0, 311 - 0, 312 - 0, 313 - 0, 314 - 0, 315 - 0, 316 - 0, 317 - 0, 318 - 0, 319 - 0, 320 - 0, 321 - 0, 322 - 0, 323 - 0, 324 - 0, 325 - 0, 326 - 0, 327 - 0, 328 - 0, 329 - 0, 330 - 0, 331 - 0, 332 - 0, 333 - 0, 334 - 0, 335 - 0, 336 - 0, 337 - 0, 338 - 0, 339 - 0, 340 - 0, 341 - 0, 342 - 0, 343 - 0, 344 - 0, 345 - 0, 346 - 0, 347 - 0, 348 - 0, 349 - 0, 350 - 0, 351 - 0, 352 - 0, 353 - 0, 354 - 0, 355 - 0, 356 - 0, 357 - 0, 358 - 0, 359 - 0, 360 - 0, 361 - 0, 362 - 0, 363 - 0, 364 - 0, 365 - 0, 366 - 0, 367 - 0, 368 - 0, 369 - 0, 370 - 0, 371 - 0, 372 - 0, 373 - 0, 374 - 0, 375 - 0, 376 - 0, 377 - 0, 378 - 0, 379 - 0, 380 - 0, 381 - 0, 382 - 0, 383 - 0, 384 - 0, 385 - 0, 386 - 0, 387 - 0, 388 - 0, 389 - 0, 390 - 0, 391 - 0, 392 - 0, 393 - 0, 394 - 0, 395 - 0, 396 - 0, 397 - 0, 398 - 0, 399 - 0, 400 - 0, 401 - 0, 402 - 0, 403 - 0, 404 - 0, 405 - 0, 406 - 0, 407 - 0, 408 - 0, 409 - 0, 410 - 0, 411 - 0, 412 - 0, 413 - 0, 414 - 0, 415 - 0, 416 - 0, 417 - 0, 418 - 0, 419 - 0, 420 - 0, 421 - 0, 422 - 0, 423 - 0, 424 - 0, 425 - 0, 426 - 0, 427 - 0, 428 - 0, 429 - 0, 430 - 0, 431 - 0, 432 - 0, 433 - 0, 434 - 0, 435 - 0, 436 - 0, 437 - 0, 438 - 0, 439 - 0, 440 - 0, 441 - 0, 442 - 0, 443 - 0, 444 - 0, 445 - 0, 446 - 0, 447 - 0, 448 - 0, 449 - 0, 450 - 0, 451 - 0, 452 - 0, 453 - 0, 454 - 0, 455 - 0, 456 - 0, 457 - 0, 458 - 0, 459 - 0, 460 - 0, 461 - 0, 462 - 0, 463 - 0, 464 - 0, 465 - 0, 466 - 0, 467 - 0, 468 - 0, 469 - 0, 470 - 0, 471 - 0, 472 - 0, 473 - 0, 474 - 0, 475 - 0, 476 - 0, 477 - 0, 478 - 0, 479 - 0, 480 - 0, 481 - 0, 482 - 0, 483 - 0, 484 - 0, 485 - 0, 486 - 0, 487 - 0, 488 - 0, 489 - 0, 490 - 0, 491 - 0, 492 - 0, 493 - 0, 494 - 0, 495 - 0, 496 - 0, 497 - 0, 498 - 0, 499 - 0, 500 - 0, 501 - 0, 502 - 0, 503 - 0, 504 - 0, 505 - 0, 506 - 0, 507 - 0, 508 - 0, 509 - 0, 510 - 0, 511 - 0, 512 - 0, 513 - 0, 514 - 0, 515 - 0, 516 - 0, 517 - 0, 518 - 0, 519 - 0, 520 - 0, 521 - 0, 522 - 0, 523 - 0, 524 - 0, 525 - 0, 526 - 0, 527 - 0, 528 - 0, 529 - 0, 530 - 0, 531 - 0, 532 - 0, 533 - 0, 534 - 0, 535 - 0, 536 - 0, 537 - 0, 538 - 0, 539 - 0, 540 - 0, 541 - 0, 542 - 0, 543 - 0, 544 - 0, 545 - 0, 546 - 0, 547 - 0, 548 - 0, 549 - 0, 550 - 0, 551 - 0, 552 - 0, 553 - 0, 554 - 0, 555 - 0, 556 - 0, 557 - 0, 558 - 0, 559 - 0, 560 - 0, 561 - 0, 562 - 0, 563 - 0, 564 - 0, 565 - 0, 566 - 0, 567 - 0, 568 - 0, 569 - 0, 570 - 0, 571 - 0, 572 - 0, 573 - 0, 574 - 0, 575 - 0, 576 - 0, 577 - 0, 578 - 0, 579 - 0, 580 - 0, 581 - 0, 582 - 0, 583 - 0, 584 - 0, 585 - 0, 586 - 0, 587 - 0, 588 - 0, 589 - 0, 590 - 0, 591 - 0, 592 - 0, 593 - 0, 594 - 0, 595 - 0, 596 - 0, 597 - 0, 598 - 0, 599 - 0, 600 - 0, 601 - 0, 602 - 0, 603 - 0, 604 - 0, 605 - 0, 606 - 0, 607 - 0, 608 - 0, 609 - 0, 610 - 0, 611 - 0, 612 - 0, 613 - 0, 614 - 0, 615 - 0, 616 - 0, 617 - 0, 618 - 0, 619 - 0, 620 - 0, 621 - 0, 622 - 0, 623 - 0, 624 - 0, 625 - 0, 626 - 0, 627 - 0, 628 - 0, 629 - 0, 630 - 0, 631 - 0, 632 - 0, 633 - 0, 634 - 0, 635 - 0, 636 - 0, 637 - 0, 638 - 0, 639 - 0, 640 - 0, 641 - 0, 642 - 0, 643 - 0, 644 - 0, 645 - 0, 646 - 0, 647 - 0, 648 - 0, 649 - 0, 650 - 0, 651 - 0, 652 - 0, 653 - 0, 654 - 0, 655 - 0, 656 - 0, 657 - 0, 658 - 0, 659 - 0, 660 - 0, 661 - 0, 662 - 0, 663 - 0, 664 - 0, 665 - 0, 666 - 0, 667 - 0, 668 - 0, 669 - 0, 670 - 0, 671 - 0, 672 - 0, 673 - 0, 674 - 0, 675 - 0, 676 - 0, 677 - 0, 678 - 0, 679 - 0, 680 - 0, 681 - 0, 682 - 0, 683 - 0, 684 - 0, 685 - 0, 686 - 0, 687 - 0, 688 - 0, 689 - 0, 690 - 0, 691 - 0, 692 - 0, 693 - 0, 694 - 0, 695 - 0, 696 - 0, 697 - 0, 698 - 0, 699 - 0, 700 - 0, 701 - 0, 702 - 0, 703 - 0, 704 - 0, 705 - 0, 706 - 0, 707 - 0, 708 - 0, 709 - 0, 710 - 0, 711 - 0, 712 - 0, 713 - 0, 714 - 0, 715 - 0, 716 - 0, 717 - 0, 718 - 0, 719 - 0, 720 - 0, 721 - 0, 722 - 0, 723 - 0, 724 - 0, 725 - 0, 726 - 0, 727 - 0, 728 - 0, 729 - 0, 730 - 0, 731 - 0, 732 - 0, 733 - 0, 734 - 0, 735 - 0, 736 - 0, 737 - 0, 738 - 0, 739 - 0, 740 - 0, 741 - 0, 742 - 0, 743 - 0, 744 - 0, 745 - 0, 746 - 0, 747 - 0, 748 - 0, 749 - 0, 750 - 0, 751 - 0, 752 - 0, 753 - 0, 754 - 0, 755 - 0, 756 - 0, 757 - 0, 758 - 0, 759 - 0, 760 - 0, 761 - 0, 762 - 0, 763 - 0, 764 - 0, 765 - 0, 766 - 0, 767 - 0, 768 - 0, 769 - 0, 770 - 0, 771 - 0, 772 - 0, 773 - 0, 774 - 0, 775 - 0, 776 - 0, 777 - 0, 778 - 0, 779 - 0, 780 - 0, 781 - 0, 782 - 0, 783 - 0, 784 - 0, 785 - 0, 786 - 0, 787 - 0, 788 - 0, 789 - 0, 790 - 0, 791 - 0, 792 - 0, 793 - 0, 794 - 0, 795 - 0, 796 - 0, 797 - 0, 798 - 0, 799 - 0, 800 - 0, 801 - 0, 802 - 0, 803 - 0, 804 - 0, 805 - 0, 806 - 0, 807 - 0, 808 - 0, 809 - 0, 810 - 0, 811 - 0, 812 - 0, 813 - 0, 814 - 0, 815 - 0, 816 - 0, 817 - 0, 818 - 0, 819 - 0, 820 - 0, 821 - 0, 822 - 0, 823 - 0, 824 - 0, 825 - 0, 826 - 0, 827 - 0, 828 - 0, 829 - 0, 830 - 0, 831 - 0, 832 - 0, 833 - 0, 834 - 0, 835 - 0, 836 - 0, 837 - 0, 838 - 0, 839 - 0, 840 - 0, 841 - 0, 842 - 0, 843 - 0, 844 - 0, 845 - 0, 846 - 0, 847 - 0, 848 - 0, 849 - 0, 850 - 0, 851 - 0, 852 - 0, 853 - 0, 854 - 0, 855 - 0, 856 - 0, 857 - 0, 858 - 0, 859 - 0, 860 - 0, 861 - 0, 862 - 0, 863 - 0, 864 - 0, 865 - 0, 866 - 0, 867 - 0, 868 - 0, 869 - 0, 870 - 0, 871 - 0, 872 - 0, 873 - 0, 874 - 0, 875 - 0, 876 - 0, 877 - 0, 878 - 0, 879 - 0, 880 - 0, 881 - 0, 882 - 0, 883 - 0, 884 - 0, 885 - 0, 886 - 0, 887 - 0, 888 - 0, 889 - 0, 890 - 0, 891 - 0, 892 - 0, 893 - 0, 894 - 0, 895 - 0, 896 - 0, 897 - 0, 898 - 0, 899 - 0, 900 - 0, 901 - 0, 902 - 0, 903 - 0, 904 - 0, 905 - 0, 906 - 0, 907 - 0, 908 - 0, 909 - 0, 910 - 0 911 - ], 912 - "height": 30, 913 - "id": 1, 914 - "name": "Tile Layer 1", 915 - "opacity": 1, 916 - "type": "tilelayer", 917 - "visible": true, 918 - "width": 30, 919 - "x": 0, 920 - "y": 0 921 - }, 922 - { 923 - "draworder": "topdown", 924 - "id": 2, 925 - "name": "Object Layer 1", 926 - "objects": [ 1 + { "backgroundcolor":"#aabbccdd", 2 + "class":"bar", 3 + "compressionlevel":-1, 4 + "height":30, 5 + "infinite":false, 6 + "layers":[ 927 7 { 928 - "height": 19, 929 - "id": 1, 930 - "name": "hello", 931 - "rotation": 0, 932 - "text": { 933 - "text": "Hello World", 934 - "wrap": true 935 - }, 936 - "type": "hello_world", 937 - "visible": true, 938 - "width": 91.4375, 939 - "x": 70, 940 - "y": 44 941 - }, 8 + "class":"bar", 9 + "data":[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 39 + "height":30, 40 + "id":1, 41 + "name":"Tile Layer 1", 42 + "opacity":1, 43 + "type":"tilelayer", 44 + "visible":true, 45 + "width":30, 46 + "x":0, 47 + "y":0 48 + }, 942 49 { 943 - "height": 84.2105263157895, 944 - "id": 2, 945 - "name": "", 946 - "rotation": 0, 947 - "type": "", 948 - "visible": true, 949 - "width": 100.250626566416, 950 - "x": 214.53634085213, 951 - "y": 172.431077694236 952 - } 953 - ], 954 - "opacity": 1, 955 - "type": "objectgroup", 956 - "visible": true, 957 - "x": 0, 958 - "y": 0 959 - }, 960 - { 961 - "id": 3, 962 - "image": "", 963 - "name": "Image Layer 1", 964 - "opacity": 1, 965 - "type": "imagelayer", 966 - "visible": true, 967 - "x": 0, 968 - "y": 0 969 - } 970 - ], 971 - "nextlayerid": 4, 972 - "nextobjectid": 3, 973 - "orientation": "orthogonal", 974 - "renderorder": "right-down", 975 - "tiledversion": "1.11.2", 976 - "tileheight": 16, 977 - "tilesets": [ 978 - { 979 - "firstgid": 1, 980 - "source": "source_tileset.tsj" 981 - }, 982 - { 983 - "firstgid": 5, 984 - "source": "images_tileset.tsj" 985 - } 986 - ], 987 - "tilewidth": 16, 988 - "type": "map", 989 - "version": "1.10", 990 - "width": 30, 991 - "properties": [ 992 - { 993 - "name": "test_property", 994 - "type": "string", 995 - "value": "test_value" 996 - } 997 - ] 50 + "draworder":"topdown", 51 + "id":2, 52 + "name":"Object Layer 1", 53 + "objects":[ 54 + { 55 + "height":19, 56 + "id":1, 57 + "name":"hello", 58 + "opacity":1, 59 + "rotation":0, 60 + "text": 61 + { 62 + "text":"Hello World", 63 + "wrap":true 64 + }, 65 + "type":"hello_world", 66 + "visible":true, 67 + "width":91.4375, 68 + "x":70, 69 + "y":44 70 + }, 71 + { 72 + "height":84, 73 + "id":2, 74 + "name":"template_instance", 75 + "properties":[ 76 + { 77 + "name":"instance_prop", 78 + "type":"bool", 79 + "value":true 80 + }], 81 + "template":"object_template.tj", 82 + "width":80, 83 + "visible": true, 84 + "x":246, 85 + "y":161 86 + }], 87 + "opacity":1, 88 + "type":"objectgroup", 89 + "visible":true, 90 + "x":0, 91 + "y":0 92 + }, 93 + { 94 + "id":3, 95 + "image":"", 96 + "name":"Image Layer 1", 97 + "opacity":1, 98 + "type":"imagelayer", 99 + "visible":true, 100 + "x":0, 101 + "y":0 102 + }], 103 + "nextlayerid":4, 104 + "nextobjectid":3, 105 + "orientation":"orthogonal", 106 + "properties":[ 107 + { 108 + "name":"test_property", 109 + "type":"string", 110 + "value":"test_value" 111 + }], 112 + "renderorder":"right-down", 113 + "tiledversion":"1.12.1", 114 + "tileheight":16, 115 + "tilesets":[ 116 + { 117 + "firstgid":1, 118 + "source":"source_tileset.tsj" 119 + }, 120 + { 121 + "firstgid":5, 122 + "source":"images_tileset.tsj" 123 + }], 124 + "tilewidth":16, 125 + "type":"map", 126 + "version":"1.10", 127 + "width":30 998 128 }
+38
test/map_template_override.tmj
··· 1 + { 2 + "height": 10, 3 + "width": 10, 4 + "infinite": false, 5 + "nextlayerid": 3, 6 + "nextobjectid": 4, 7 + "orientation": "orthogonal", 8 + "tileheight": 16, 9 + "tilewidth": 16, 10 + "tiledversion": "1.12.1", 11 + "version": "1.10", 12 + "layers": [ 13 + { 14 + "id": 2, 15 + "type": "objectgroup", 16 + "draworder": "topdown", 17 + "name": "Objects", 18 + "opacity": 1, 19 + "visible": true, 20 + "x": 0, 21 + "y": 0, 22 + "objects": [ 23 + { 24 + "id": 3, 25 + "name": "override_test", 26 + "template": "object_template_override.tj", 27 + "x": 10.0, 28 + "y": 20.0, 29 + "visible": true, 30 + "width": 150.0, 31 + "properties": [ 32 + {"name": "shared_key", "type": "string", "value": "from_instance"} 33 + ] 34 + } 35 + ] 36 + } 37 + ] 38 + }
+27
test/maps.zig
··· 9 9 defer map.deinit(allocator); 10 10 11 11 try baseTests(map); 12 + try templateObjectTests(map); 12 13 } 13 14 14 15 test "initFromFile" { ··· 138 139 try expectEqualStrings("test_property", prop.name); 139 140 try expectEqual(.string, prop.type); 140 141 try expectEqualStrings("test_value", prop.value.string); 142 + } 143 + 144 + fn templateObjectTests(map: Map) !void { 145 + const obj = map.findObject("template_instance").?; 146 + 147 + // Instance overrides template's name "oh" 148 + try expectEqualStrings("template_instance", obj.name); 149 + // Instance width=80 overrides template width=100 150 + try expectEqual(80.0, obj.width); 151 + // height not in instance → inherited from template (84) 152 + try expectEqual(84.0, obj.height); 153 + // rotation not in instance → inherited from template (0) 154 + try expectEqual(0.0, obj.rotation); 155 + try expectEqual(true, obj.visible); 156 + try expectEqual(246.0, obj.x); 157 + try expectEqual(161.0, obj.y); 158 + try expectEqual(.rectangle, obj.type); 159 + 160 + // Both template and instance properties present 161 + try expectEqual(2, obj.properties.size); 162 + const template_prop = obj.properties.get("test").?; 163 + try expectEqual(.bool, template_prop.type); 164 + try expectEqual(true, template_prop.value.bool); 165 + const instance_prop = obj.properties.get("instance_prop").?; 166 + try expectEqual(.bool, instance_prop.type); 167 + try expectEqual(true, instance_prop.value.bool); 141 168 } 142 169 143 170 test "findObject" {
+20
test/object_template.tj
··· 1 + { 2 + "object": { 3 + "height": 84, 4 + "id": 2, 5 + "name": "oh", 6 + "opacity": 1, 7 + "properties": [ 8 + { 9 + "name": "test", 10 + "type": "bool", 11 + "value": true 12 + } 13 + ], 14 + "rotation": 0, 15 + "type": "", 16 + "visible": true, 17 + "width": 100 18 + }, 19 + "type": "template" 20 + }
+16
test/object_template_override.tj
··· 1 + { 2 + "object": { 3 + "height": 50, 4 + "id": 1, 5 + "name": "base", 6 + "rotation": 45.0, 7 + "type": "", 8 + "visible": false, 9 + "width": 200, 10 + "properties": [ 11 + {"name": "shared_key", "type": "string", "value": "from_template"}, 12 + {"name": "template_only", "type": "int", "value": 99} 13 + ] 14 + }, 15 + "type": "template" 16 + }
+40
test/objects.zig
··· 1 + test "object template: instance overrides template fields and properties" { 2 + try changeTestDir(); 3 + const allocator = std.testing.allocator; 4 + 5 + var map = try Map.initFromFile(allocator, "map_template_override.tmj"); 6 + defer map.deinit(allocator); 7 + 8 + const obj = map.findObject("override_test").?; 9 + 10 + // width: instance 150 overrides template 200 11 + try expectEqual(150.0, obj.width); 12 + // height: not in instance, inherited from template (50) 13 + try expectEqual(50.0, obj.height); 14 + // rotation: not in instance, inherited from template (45.0) 15 + try expectEqual(45.0, obj.rotation); 16 + // visible: instance true overrides template false 17 + try expectEqual(true, obj.visible); 18 + // name: instance overrides template "base" 19 + try expectEqualStrings("override_test", obj.name); 20 + try expectEqual(.rectangle, obj.type); 21 + 22 + // shared_key: instance "from_instance" wins over template "from_template" 23 + const shared = obj.properties.get("shared_key").?; 24 + try expectEqual(.string, shared.type); 25 + try expectEqualStrings("from_instance", shared.value.string); 26 + 27 + // template_only: flows through from template (no instance override) 28 + const tmpl_only = obj.properties.get("template_only").?; 29 + try expectEqual(.int, tmpl_only.type); 30 + try expectEqual(99, tmpl_only.value.int); 31 + 32 + try expectEqual(2, obj.properties.size); 33 + } 34 + 35 + const tmz = @import("tmz"); 36 + const Map = tmz.Map; 37 + const changeTestDir = @import("tests.zig").changeTestDir; 38 + const std = @import("std"); 39 + const expectEqual = std.testing.expectEqual; 40 + const expectEqualStrings = std.testing.expectEqualStrings;
-2
test/properties.json
··· 1 1 [ 2 2 { 3 3 "name": "name", 4 - "propertytype": "", 5 4 "type": "string", 6 5 "value": "game" 7 6 }, 8 7 { 9 8 "name": "width", 10 - "propertytype": "", 11 9 "type": "int", 12 10 "value": 640 13 11 },
+1
test/tests.zig
··· 3 3 _ = @import("layers.zig"); 4 4 _ = @import("tilesets.zig"); 5 5 _ = @import("properties.zig"); 6 + _ = @import("objects.zig"); 6 7 } 7 8 8 9 pub fn changeTestDir() !void {