From 8a18d321b33b9ef14dd472c3f1c3586f30402cbb Mon Sep 17 00:00:00 2001 From: Louis Pearson Date: Thu, 11 Aug 2022 00:42:48 -0600 Subject: [PATCH] Add direction enum, improve wire loading/saving --- assets/maps/wired.ldtk | 208 ++++++++++++++++++++--------------------- src/game.zig | 95 +++++++++++++------ src/world.zig | 45 ++++++++- tools/LDtkImport.zig | 149 +++++++++++++++++++++-------- 4 files changed, 321 insertions(+), 176 deletions(-) diff --git a/assets/maps/wired.ldtk b/assets/maps/wired.ldtk index 89ef287..9f5d954 100644 --- a/assets/maps/wired.ldtk +++ b/assets/maps/wired.ldtk @@ -5036,6 +5036,101 @@ "defUid": 60, "px": [120,136], "fieldInstances": [] + }, + { + "__identifier": "Wire", + "__grid": [18,5], + "__pivot": [0.5,0.5], + "__tags": [], + "__tile": null, + "__smartColor": "#BC2634", + "iid": "69613910-02f0-11ed-9529-d355f437215d", + "width": 8, + "height": 8, + "defUid": 93, + "px": [148,44], + "fieldInstances": [ + { "__identifier": "Point", "__value": [{ "cx": 12, "cy": 5 }], "__type": "Array", "__tile": null, "defUid": 94, "realEditorValues": [{ + "id": "V_String", + "params": ["12,5"] + }] }, + { "__identifier": "Anchor", "__value": [ true, false ], "__type": "Array", "__tile": null, "defUid": 98, "realEditorValues": [ null, { + "id": "V_Bool", + "params": [ false ] + } ] } + ] + }, + { + "__identifier": "Wire", + "__grid": [18,10], + "__pivot": [0.5,0.5], + "__tags": [], + "__tile": null, + "__smartColor": "#BC2634", + "iid": "725b0b40-02f0-11ed-9529-479e26eec218", + "width": 8, + "height": 8, + "defUid": 93, + "px": [148,84], + "fieldInstances": [ + { "__identifier": "Point", "__value": [ { "cx": 12, "cy": 11 }, { "cx": 6, "cy": 11 } ], "__type": "Array", "__tile": null, "defUid": 94, "realEditorValues": [ { + "id": "V_String", + "params": ["12,11"] + }, { + "id": "V_String", + "params": ["6,11"] + } ] }, + { "__identifier": "Anchor", "__value": [ true, false ], "__type": "Array", "__tile": null, "defUid": 98, "realEditorValues": [ { + "id": "V_Bool", + "params": [ true ] + }, { + "id": "V_Bool", + "params": [ false ] + } ] } + ] + }, + { + "__identifier": "Wire", + "__grid": [11,10], + "__pivot": [0.5,0.5], + "__tags": [], + "__tile": null, + "__smartColor": "#BC2634", + "iid": "7c02a9a0-02f0-11ed-9529-2549e7fc0619", + "width": 8, + "height": 8, + "defUid": 93, + "px": [92,84], + "fieldInstances": [ + { "__identifier": "Point", "__value": [{ "cx": 16, "cy": 11 }], "__type": "Array", "__tile": null, "defUid": 94, "realEditorValues": [{ + "id": "V_String", + "params": ["16,11"] + }] }, + { "__identifier": "Anchor", "__value": [ true, false ], "__type": "Array", "__tile": null, "defUid": 98, "realEditorValues": [ null, { + "id": "V_Bool", + "params": [ false ] + } ] } + ] + }, + { + "__identifier": "Wire", + "__grid": [12,13], + "__pivot": [0.5,0.5], + "__tags": [], + "__tile": null, + "__smartColor": "#BC2634", + "iid": "852141e0-02f0-11ed-9529-25cb29c77f46", + "width": 8, + "height": 8, + "defUid": 93, + "px": [100,108], + "fieldInstances": [ + { "__identifier": "Point", "__value": [{ "cx": 15, "cy": 13 }], "__type": "Array", "__tile": null, "defUid": 94, "realEditorValues": [{ + "id": "V_String", + "params": ["15,13"] + }] }, + { "__identifier": "Anchor", "__value": [ true, true ], "__type": "Array", "__tile": null, "defUid": 98, "realEditorValues": [ null, null ] } + ] } ] }, @@ -5063,8 +5158,8 @@ 1,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0, 0,10,0,0,0,0,0,10,0,0,0,0,0,2,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, 0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,6,13,13,13,13,13,1,13,13,13,13,1,0, - 0,0,0,0,1,0,1,0,0,0,0,1,1,6,1,1,1,0,1,0,0,0,0,2,1,0,1,1,0,0,0,10,0,0,0, - 0,10,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,3,0,1,6,1,1, + 0,0,0,0,1,0,1,0,0,0,0,1,1,6,1,1,1,0,1,0,0,0,0,0,1,0,1,1,0,0,0,10,0,0,0, + 0,10,0,1,0,0,0,0,2,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,3,0,1,6,1,1, 1,1,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,2,12,0,10,0,0,0,9, 0,1,0,0,0,0,1,6,1,0,0,0,12,12,0,12,0,0,0,1,0,1,0,0,0,0,1,0,3,1,1,1,6,1,0, 12,0,0,0,1,0,1,0,0,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,3,0,1,0, @@ -5094,7 +5189,7 @@ { "px": [64,64], "src": [112,48], "f": 0, "t": 110, "d": [41,168] }, { "px": [48,144], "src": [56,48], "f": 0, "t": 103, "d": [42,366] }, { "px": [64,144], "src": [56,48], "f": 0, "t": 103, "d": [42,368] }, - { "px": [152,72], "src": [88,48], "f": 0, "t": 107, "d": [43,199] }, + { "px": [152,80], "src": [88,48], "f": 0, "t": 107, "d": [43,219] }, { "px": [8,8], "src": [48,48], "f": 0, "t": 102, "d": [44,21] }, { "px": [24,24], "src": [48,48], "f": 0, "t": 102, "d": [44,63] }, { "px": [32,24], "src": [48,48], "f": 0, "t": 102, "d": [44,64] }, @@ -5121,8 +5216,8 @@ { "px": [152,64], "src": [72,48], "f": 0, "t": 105, "d": [45,179] }, { "px": [8,72], "src": [72,48], "f": 0, "t": 105, "d": [45,181] }, { "px": [104,72], "src": [72,48], "f": 0, "t": 105, "d": [45,193] }, + { "px": [152,72], "src": [72,48], "f": 0, "t": 105, "d": [45,199] }, { "px": [104,80], "src": [72,48], "f": 0, "t": 105, "d": [45,213] }, - { "px": [152,80], "src": [72,48], "f": 0, "t": 105, "d": [45,219] }, { "px": [16,88], "src": [72,48], "f": 0, "t": 105, "d": [45,222] }, { "px": [104,88], "src": [72,48], "f": 0, "t": 105, "d": [45,233] }, { "px": [104,96], "src": [72,48], "f": 0, "t": 105, "d": [45,253] }, @@ -5148,7 +5243,7 @@ { "px": [48,80], "src": [0,8], "f": 0, "t": 16, "d": [129,206] }, { "px": [88,80], "src": [0,8], "f": 0, "t": 16, "d": [129,211] }, { "px": [144,40], "src": [16,8], "f": 0, "t": 18, "d": [59,118] }, - { "px": [144,72], "src": [16,8], "f": 0, "t": 18, "d": [59,198] }, + { "px": [144,80], "src": [16,8], "f": 0, "t": 18, "d": [59,218] }, { "px": [96,104], "src": [24,8], "f": 0, "t": 19, "d": [56,272] }, { "px": [64,120], "src": [80,8], "f": 0, "t": 26, "d": [53,308] }, { "px": [152,88], "src": [96,8], "f": 0, "t": 28, "d": [78,239] }, @@ -5363,109 +5458,6 @@ { "px": [48,128], "src": [32,56], "f": 0, "t": 116, "d": [22,326] }, { "px": [56,8], "src": [8,56], "f": 0, "t": 113, "d": [23,27] }, { "px": [120,128], "src": [8,56], "f": 0, "t": 113, "d": [23,335] }, - { "px": [0,0], "src": [120,56], "f": 0, "t": 127, "d": [161,0] }, - { "px": [8,0], "src": [120,56], "f": 0, "t": 127, "d": [161,1] }, - { "px": [16,0], "src": [120,56], "f": 0, "t": 127, "d": [161,2] }, - { "px": [56,0], "src": [120,56], "f": 0, "t": 127, "d": [161,7] }, - { "px": [80,0], "src": [120,56], "f": 0, "t": 127, "d": [161,10] }, - { "px": [88,0], "src": [120,56], "f": 0, "t": 127, "d": [161,11] }, - { "px": [96,0], "src": [120,56], "f": 0, "t": 127, "d": [161,12] }, - { "px": [104,0], "src": [120,56], "f": 0, "t": 127, "d": [161,13] }, - { "px": [112,0], "src": [120,56], "f": 0, "t": 127, "d": [161,14] }, - { "px": [120,0], "src": [120,56], "f": 0, "t": 127, "d": [161,15] }, - { "px": [128,0], "src": [120,56], "f": 0, "t": 127, "d": [161,16] }, - { "px": [136,0], "src": [120,56], "f": 0, "t": 127, "d": [161,17] }, - { "px": [144,0], "src": [120,56], "f": 0, "t": 127, "d": [161,18] }, - { "px": [152,0], "src": [120,56], "f": 0, "t": 127, "d": [161,19] }, - { "px": [0,8], "src": [120,56], "f": 0, "t": 127, "d": [161,20] }, - { "px": [8,8], "src": [120,56], "f": 0, "t": 127, "d": [161,21] }, - { "px": [88,8], "src": [120,56], "f": 0, "t": 127, "d": [161,31] }, - { "px": [96,8], "src": [120,56], "f": 0, "t": 127, "d": [161,32] }, - { "px": [104,8], "src": [120,56], "f": 0, "t": 127, "d": [161,33] }, - { "px": [112,8], "src": [120,56], "f": 0, "t": 127, "d": [161,34] }, - { "px": [120,8], "src": [120,56], "f": 0, "t": 127, "d": [161,35] }, - { "px": [128,8], "src": [120,56], "f": 0, "t": 127, "d": [161,36] }, - { "px": [136,8], "src": [120,56], "f": 0, "t": 127, "d": [161,37] }, - { "px": [144,8], "src": [120,56], "f": 0, "t": 127, "d": [161,38] }, - { "px": [152,8], "src": [120,56], "f": 0, "t": 127, "d": [161,39] }, - { "px": [0,16], "src": [120,56], "f": 0, "t": 127, "d": [161,40] }, - { "px": [8,16], "src": [120,56], "f": 0, "t": 127, "d": [161,41] }, - { "px": [88,16], "src": [120,56], "f": 0, "t": 127, "d": [161,51] }, - { "px": [96,16], "src": [120,56], "f": 0, "t": 127, "d": [161,52] }, - { "px": [104,16], "src": [120,56], "f": 0, "t": 127, "d": [161,53] }, - { "px": [112,16], "src": [120,56], "f": 0, "t": 127, "d": [161,54] }, - { "px": [120,16], "src": [120,56], "f": 0, "t": 127, "d": [161,55] }, - { "px": [128,16], "src": [120,56], "f": 0, "t": 127, "d": [161,56] }, - { "px": [136,16], "src": [120,56], "f": 0, "t": 127, "d": [161,57] }, - { "px": [144,16], "src": [120,56], "f": 0, "t": 127, "d": [161,58] }, - { "px": [152,16], "src": [120,56], "f": 0, "t": 127, "d": [161,59] }, - { "px": [0,24], "src": [120,56], "f": 0, "t": 127, "d": [161,60] }, - { "px": [8,24], "src": [120,56], "f": 0, "t": 127, "d": [161,61] }, - { "px": [152,24], "src": [120,56], "f": 0, "t": 127, "d": [161,79] }, - { "px": [0,32], "src": [120,56], "f": 0, "t": 127, "d": [161,80] }, - { "px": [8,32], "src": [120,56], "f": 0, "t": 127, "d": [161,81] }, - { "px": [0,40], "src": [120,56], "f": 0, "t": 127, "d": [161,100] }, - { "px": [8,40], "src": [120,56], "f": 0, "t": 127, "d": [161,101] }, - { "px": [0,48], "src": [120,56], "f": 0, "t": 127, "d": [161,120] }, - { "px": [8,48], "src": [120,56], "f": 0, "t": 127, "d": [161,121] }, - { "px": [152,48], "src": [120,56], "f": 0, "t": 127, "d": [161,139] }, - { "px": [0,56], "src": [120,56], "f": 0, "t": 127, "d": [161,140] }, - { "px": [8,56], "src": [120,56], "f": 0, "t": 127, "d": [161,141] }, - { "px": [56,56], "src": [120,56], "f": 0, "t": 127, "d": [161,147] }, - { "px": [64,56], "src": [120,56], "f": 0, "t": 127, "d": [161,148] }, - { "px": [72,56], "src": [120,56], "f": 0, "t": 127, "d": [161,149] }, - { "px": [80,56], "src": [120,56], "f": 0, "t": 127, "d": [161,150] }, - { "px": [88,56], "src": [120,56], "f": 0, "t": 127, "d": [161,151] }, - { "px": [96,56], "src": [120,56], "f": 0, "t": 127, "d": [161,152] }, - { "px": [104,56], "src": [120,56], "f": 0, "t": 127, "d": [161,153] }, - { "px": [112,56], "src": [120,56], "f": 0, "t": 127, "d": [161,154] }, - { "px": [120,56], "src": [120,56], "f": 0, "t": 127, "d": [161,155] }, - { "px": [128,56], "src": [120,56], "f": 0, "t": 127, "d": [161,156] }, - { "px": [136,56], "src": [120,56], "f": 0, "t": 127, "d": [161,157] }, - { "px": [144,56], "src": [120,56], "f": 0, "t": 127, "d": [161,158] }, - { "px": [152,56], "src": [120,56], "f": 0, "t": 127, "d": [161,159] }, - { "px": [0,64], "src": [120,56], "f": 0, "t": 127, "d": [161,160] }, - { "px": [8,64], "src": [120,56], "f": 0, "t": 127, "d": [161,161] }, - { "px": [152,64], "src": [120,56], "f": 0, "t": 127, "d": [161,179] }, - { "px": [0,72], "src": [120,56], "f": 0, "t": 127, "d": [161,180] }, - { "px": [8,72], "src": [120,56], "f": 0, "t": 127, "d": [161,181] }, - { "px": [0,80], "src": [120,56], "f": 0, "t": 127, "d": [161,200] }, - { "px": [8,80], "src": [120,56], "f": 0, "t": 127, "d": [161,201] }, - { "px": [0,88], "src": [120,56], "f": 0, "t": 127, "d": [161,220] }, - { "px": [8,88], "src": [120,56], "f": 0, "t": 127, "d": [161,221] }, - { "px": [0,96], "src": [120,56], "f": 0, "t": 127, "d": [161,240] }, - { "px": [8,96], "src": [120,56], "f": 0, "t": 127, "d": [161,241] }, - { "px": [152,96], "src": [120,56], "f": 0, "t": 127, "d": [161,259] }, - { "px": [0,104], "src": [120,56], "f": 0, "t": 127, "d": [161,260] }, - { "px": [8,104], "src": [120,56], "f": 0, "t": 127, "d": [161,261] }, - { "px": [56,104], "src": [120,56], "f": 0, "t": 127, "d": [161,267] }, - { "px": [64,104], "src": [120,56], "f": 0, "t": 127, "d": [161,268] }, - { "px": [72,104], "src": [120,56], "f": 0, "t": 127, "d": [161,269] }, - { "px": [80,104], "src": [120,56], "f": 0, "t": 127, "d": [161,270] }, - { "px": [136,104], "src": [120,56], "f": 0, "t": 127, "d": [161,277] }, - { "px": [144,104], "src": [120,56], "f": 0, "t": 127, "d": [161,278] }, - { "px": [152,104], "src": [120,56], "f": 0, "t": 127, "d": [161,279] }, - { "px": [0,112], "src": [120,56], "f": 0, "t": 127, "d": [161,280] }, - { "px": [8,112], "src": [120,56], "f": 0, "t": 127, "d": [161,281] }, - { "px": [72,112], "src": [120,56], "f": 0, "t": 127, "d": [161,289] }, - { "px": [152,112], "src": [120,56], "f": 0, "t": 127, "d": [161,299] }, - { "px": [0,120], "src": [120,56], "f": 0, "t": 127, "d": [161,300] }, - { "px": [8,120], "src": [120,56], "f": 0, "t": 127, "d": [161,301] }, - { "px": [152,144], "src": [120,56], "f": 0, "t": 127, "d": [161,379] }, - { "px": [48,152], "src": [120,56], "f": 0, "t": 127, "d": [161,386] }, - { "px": [56,152], "src": [120,56], "f": 0, "t": 127, "d": [161,387] }, - { "px": [64,152], "src": [120,56], "f": 0, "t": 127, "d": [161,388] }, - { "px": [72,152], "src": [120,56], "f": 0, "t": 127, "d": [161,389] }, - { "px": [80,152], "src": [120,56], "f": 0, "t": 127, "d": [161,390] }, - { "px": [88,152], "src": [120,56], "f": 0, "t": 127, "d": [161,391] }, - { "px": [96,152], "src": [120,56], "f": 0, "t": 127, "d": [161,392] }, - { "px": [104,152], "src": [120,56], "f": 0, "t": 127, "d": [161,393] }, - { "px": [112,152], "src": [120,56], "f": 0, "t": 127, "d": [161,394] }, - { "px": [120,152], "src": [120,56], "f": 0, "t": 127, "d": [161,395] }, - { "px": [128,152], "src": [120,56], "f": 0, "t": 127, "d": [161,396] }, - { "px": [136,152], "src": [120,56], "f": 0, "t": 127, "d": [161,397] }, - { "px": [144,152], "src": [120,56], "f": 0, "t": 127, "d": [161,398] }, - { "px": [152,152], "src": [120,56], "f": 0, "t": 127, "d": [161,399] }, { "px": [40,144], "src": [8,16], "f": 0, "t": 33, "d": [125,365] }, { "px": [24,64], "src": [24,16], "f": 0, "t": 35, "d": [126,163] }, { "px": [24,80], "src": [24,16], "f": 0, "t": 35, "d": [126,203] }, diff --git a/src/game.zig b/src/game.zig index 80e1af2..e6b9855 100644 --- a/src/game.zig +++ b/src/game.zig @@ -50,6 +50,19 @@ const Wire = struct { node.pos = b + @splat(2, @intToFloat(f32, i)) * size / @splat(2, @intToFloat(f32, this.nodes.len)); } } + + pub fn addInline(this: *@This(), div: usize, point: Vec2f) !void { + const divf = @splat(2, @intToFloat(f32, div)); + var last = this.end().pos; + const dist = point - last; + const chunk = dist / divf; + var i: usize = 0; + while (i < div) : (i += 1) { + const next = last + chunk; + last = next; + try this.nodes.append(Pos.init(next)); + } + } }; const Player = struct { @@ -202,6 +215,11 @@ const playerAnim = pac: { break :pac animArr.slice(); }; +fn posFromWorld(coord: world.Coordinate) Vec2f { + const tile_size = Vec2{ 8, 8 }; + return util.vec2ToVec2f(coord.toVec2() * tile_size); +} + fn loadLevel(lvl: usize) !void { fba.reset(); map.clear(); @@ -236,24 +254,39 @@ fn loadLevel(lvl: usize) !void { const coord0 = wire[0].coord.subC(levelc); const coord1 = wire[1].coord.subC(levelc); w4.tracef("---- Wire [%d, %d] (%d, %d), (%d, %d)", wireArr[0], wireArr[1], coord0.val[0], coord0.val[1], coord1.val[0], coord1.val[1]); - const p1 = util.vec2ToVec2f(coord0.toVec2() * tile_size + Vec2{ 4, 4 }); - const p2 = util.vec2ToVec2f(coord1.toVec2() * tile_size + Vec2{ 4, 4 }); var w = try wires.addOne(); _ = try w.nodes.resize(0); - // const divisions = wire.divisions; - const divisions = 10; - var i: usize = 0; - while (i <= divisions) : (i += 1) { - try w.nodes.append(Pos.init(p1)); + const divisions = 7; + var last_coord: world.Coordinate = undefined; + for (wireSlice) |world_wire| { + switch (world_wire) { + .Begin => |coord| { + last_coord = coord.subC(levelc); + w4.tracef("\t start (%d, %d)", last_coord.val[0], last_coord.val[1]); + try w.nodes.append(Pos.init(posFromWorld(last_coord) + Vec2f{ 4, 4 })); + }, + .BeginPinned => |coord| { + last_coord = coord.subC(levelc); + w4.tracef("\t start [a] (%d, %d)", last_coord.val[0], last_coord.val[1]); + try w.nodes.append(Pos.init(posFromWorld(last_coord) + Vec2f{ 4, 4 })); + }, + .Point => |offset| { + last_coord = last_coord.addOffset(offset); + w4.tracef("\t point (%d, %d) = last + (%d, %d)", last_coord.val[0], last_coord.val[1], offset[0], offset[1]); + try w.addInline(divisions, posFromWorld(last_coord) + Vec2f{ 4, 4 }); + }, + .PointPinned => |offset| { + last_coord = last_coord.addOffset(offset); + w4.tracef("\t point (%d, %d) = last + (%d, %d)", last_coord.val[0], last_coord.val[1], offset[0], offset[1]); + try w.addInline(divisions, posFromWorld(last_coord) + Vec2f{ 4, 4 }); + }, + .End => break, + } } - w.begin().pos = p1; - w.end().pos = p2; w.begin().pinned = wire[0].anchored; w.end().pinned = wire[1].anchored; - - w.straighten(); } } @@ -313,27 +346,37 @@ fn moveLevel(direction: enum { L, R, U, D }) !void { // Save wires back into database const levelc = world.Coordinate.fromWorld(level.world_x, level.world_y); while (wires.popOrNull()) |*w| { + var wire: [10]world.Wire = undefined; + + // Are the ends anchored? const aStart = w.begin().pinned; - const aEnd = w.begin().pinned; + const divby = @splat(2, @as(f32, 8)); const wstart = world.Coordinate.fromVec2f(w.begin().pos / divby).addC(levelc); - const offset = w.end().pos - w.begin().pos; - const end = world.Coordinate.fromVec2f(offset / divby).toOffset(); - var wire: [3]world.Wire = undefined; - if (aStart) { - wire[0] = .{ .BeginPinned = wstart }; - } else { - wire[0] = .{ .Begin = wstart }; + + w4.tracef("[moveLevel] new wire (%d,%d)", wstart.val[0], wstart.val[1]); + wire[0] = if (aStart) .{ .BeginPinned = wstart } else .{ .Begin = wstart }; + var idx: usize = 1; + + var last_pos = w.begin().pos; + for (w.nodes.constSlice()) |point, i| { + if (i == 0) continue; + const length = util.lengthf(point.pos - last_pos) / 8; + if (i % 8 == 0 or length > 6 or i == w.nodes.constSlice().len - 1) { + const diff = point.pos - last_pos; + const offset = world.Coordinate.fromVec2f(diff / divby).toOffset(); + wire[idx] = if (point.pinned) .{ .PointPinned = offset } else .{ .Point = offset }; + idx += 1; + last_pos = point.pos; + w4.tracef("\t offset (%d,%d)", offset[0], offset[1]); + } } - if (aEnd) { - wire[1] = .{ .PointPinned = end }; - } else { - wire[1] = .{ .Point = end }; - } + wire[idx] = .End; + idx += 1; - wire[2] = .End; - db.addWire(&wire); + w4.tracef("\t finished, length %d", idx); + db.addWire(wire[0..idx]); } // TODO: Figure out the more principled way for checking boundaries diff --git a/src/world.zig b/src/world.zig index dcb5a78..9ddd389 100644 --- a/src/world.zig +++ b/src/world.zig @@ -117,6 +117,15 @@ pub const CircuitType = enum(u5) { Diode = 11, Conduit_Vertical = 12, Conduit_Horizontal = 13, + + pub fn canConnect(circuit: CircuitType, side: Direction) bool { + return switch (circuit) { + .None => false, + .Conduit_Vertical => (side != .East and side != .West), + .Conduit_Horizontal => (side != .North and side != .South), + else => true, + }; + } }; pub const TileData = union(enum) { @@ -263,6 +272,33 @@ pub const Coordinate = struct { } }; +pub const Direction = enum { + North, + West, + East, + South, + + pub const each = [_]Direction{ .North, .West, .East, .South }; + + pub fn toOffset(dir: Direction) [2]i16 { + return switch (dir) { + .North => .{ 0, -1 }, + .West => .{ -1, 0 }, + .East => .{ 1, 0 }, + .South => .{ 0, 1 }, + }; + } + + pub fn getOpposite(dir: Direction) Direction { + return switch (dir) { + .North => .South, + .West => .East, + .East => .West, + .South => .North, + }; + } +}; + pub const Level = struct { world_x: i8, world_y: i8, @@ -332,6 +368,11 @@ pub const Level = struct { return tiles[i]; } + pub fn getCircuit(level: Level, globalc: Coord) ?CircuitType { + const tile = level.getTile(globalc) orelse return null; + return tile.getCircuit(); + } + pub fn getJoin(level: Level, which: usize) ?Coordinate { const tiles = level.tiles orelse return null; var joinCount: usize = 0; @@ -563,11 +604,11 @@ pub const Wire = union(enum) { try coord.write(writer); }, .Point => |point| { - const byte = @bitCast(u8, @intCast(i8, point[0])) | @bitCast(u8, @intCast(i8, point[1])) << 4; + const byte = (@bitCast(u8, @intCast(i8, point[0])) & 0b0000_1111) | (@bitCast(u8, @intCast(i8, point[1])) & 0b1111_0000) << 4; try writer.writeByte(byte); }, .PointPinned => |point| { - const byte = @bitCast(u8, @intCast(i8, point[0])) | @bitCast(u8, @intCast(i8, point[1])) << 4; + const byte = (@bitCast(u8, @intCast(i8, point[0])) & 0b0000_1111) | (@bitCast(u8, @intCast(i8, point[1])) & 0b1111_0000) << 4; try writer.writeByte(byte); }, .End => {}, diff --git a/tools/LDtkImport.zig b/tools/LDtkImport.zig index 012478c..f822e77 100644 --- a/tools/LDtkImport.zig +++ b/tools/LDtkImport.zig @@ -3,6 +3,9 @@ const std = @import("std"); const LDtk = @import("../deps/zig-ldtk/src/LDtk.zig"); const world = @import("../src/world.zig"); +const Coord = world.Coordinate; +const Dir = world.Direction; + const KB = 1024; const MB = 1024 * KB; @@ -71,15 +74,15 @@ fn make(step: *std.build.Step) !void { .wires = &wires, }); - for (parsed_level.tiles.?) |tile, i| { - if (tile == .tile) { - std.log.warn("{:0>2}: {}", .{ i, tile.tile }); - } else if (tile == .flags) { - std.log.warn("{:0>2}: {s} {s}", .{ i, @tagName(tile.flags.solid), @tagName(tile.flags.circuit) }); - } else { - std.log.warn("{:0>2}: {}", .{ i, tile }); - } - } + // for (parsed_level.tiles.?) |tile, i| { + // if (tile == .tile) { + // std.log.warn("{:0>2}: {}", .{ i, tile.tile }); + // } else if (tile == .flags) { + // std.log.warn("{:0>2}: {s} {s}", .{ i, @tagName(tile.flags.solid), @tagName(tile.flags.circuit) }); + // } else { + // std.log.warn("{:0>2}: {}", .{ i, tile }); + // } + // } try levels.append(parsed_level); } @@ -188,11 +191,11 @@ fn parseLevel(opt: struct { kind_opt = .Trapdoor; } - const levelc = world.Coordinate.fromWorld(world_x, world_y); + const levelc = Coord.fromWorld(world_x, world_y); // Parsing code for wire entities. They're a little more complex // than the rest if (kind_opt) |kind| { - const entc = world.Coordinate.init(.{ + const entc = Coord.init(.{ @intCast(i16, entity.__grid[0]), @intCast(i16, entity.__grid[1]), }); @@ -202,28 +205,28 @@ fn parseLevel(opt: struct { if (is_wire) { var anchor1 = false; var anchor2 = false; - const p1_c = world.Coordinate.init(.{ - @intCast(i16, entity.__grid[0]), - @intCast(i16, entity.__grid[1]), - }); - var p2_c = world.Coordinate.init(.{ + const p1_c = Coord.init(.{ @intCast(i16, entity.__grid[0]), @intCast(i16, entity.__grid[1]), }); + std.log.warn("[parseLevel:wire] {}", .{ p1_c }); + var points: []Coord = undefined; for (entity.fieldInstances) |field| { if (std.mem.eql(u8, field.__identifier, "Anchor")) { const anchors = field.__value.Array.items; anchor1 = anchors[0].Bool; anchor2 = anchors[1].Bool; } else if (std.mem.eql(u8, field.__identifier, "Point")) { - const end = field.__value.Array.items.len - 1; - const endpoint = field.__value.Array.items[end]; - const x = endpoint.Object.get("cx").?; - const y = endpoint.Object.get("cy").?; - p2_c.val = .{ - @intCast(i16, x.Integer), - @intCast(i16, y.Integer), - }; + points = try allocator.alloc(Coord, field.__value.Array.items.len); + for (field.__value.Array.items) |point, i| { + const x = point.Object.get("cx").?; + const y = point.Object.get("cy").?; + std.log.warn("\t{} {}", .{ x.Integer, y.Integer }); + points[i] = Coord.init(.{ + @intCast(i16, x.Integer), + @intCast(i16, y.Integer), + }); + } } } @@ -233,11 +236,21 @@ fn parseLevel(opt: struct { try wires.append(.{ .Begin = p1_c.addC(levelc) }); } - if (anchor2) { - try wires.append(.{ .PointPinned = p2_c.subC(p1_c).toOffset() }); - } else { - try wires.append(.{ .Point = p2_c.subC(p1_c).toOffset() }); + std.log.warn("\tConverting to wire nodes", .{}); + var last_point = p1_c; + for (points) |point, i| { + const offset = point.subC(last_point).toOffset(); + std.log.warn("\toffset: {} {}", .{ offset[0], offset[1] }); + last_point = point; + if (i == points.len - 1) { + if (anchor2) { + try wires.append(.{ .PointPinned = offset }); + continue; + } + } + try wires.append(.{ .Point = offset }); } + try wires.append(.End); } } @@ -317,17 +330,30 @@ fn parseLevel(opt: struct { } pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayList(world.CircuitNode) { - const Coord = world.Coordinate; const SearchItem = struct { coord: Coord, last_coord: ?Coord = null, last_node: world.NodeID, + + fn next(current: @This(), current_node: world.NodeID, offset: [2]i16) @This() { + return @This(){ + .coord = current.coord.add(offset), + .last_coord = current.coord, + .last_node = current_node, + }; + } }; const Queue = std.TailQueue(SearchItem); const Node = Queue.Node; var nodes = std.ArrayList(world.CircuitNode).init(alloc); + var node_input_dir = std.ArrayList(Dir).init(alloc); + defer node_input_dir.deinit(); + + var source_node = std.ArrayList(world.NodeID).init(alloc); + defer source_node.deinit(); + var sources = Queue{}; var sockets = Queue{}; @@ -390,11 +416,15 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL var next_node = last_node; const tile = level.getTile(coord) orelse continue; - // std.log.warn("[buildCircuit] {} [{}] {}", .{ coord, node.data.last_node, tile }); if (tile != .flags) continue; const flags = tile.flags; + const dir = if (last_node != std.math.maxInt(world.NodeID)) + getInputDirection(coord, nodes.items[last_node].coord) + else + .South; + switch (flags.circuit) { .Conduit => { // Collects from two other nodes. Intersections will need to be stored so when @@ -411,6 +441,8 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL .kind = .{ .Socket = null }, .coord = coord, }); + try node_input_dir.append(dir); + try source_node.append(last_node); }, .Plug => { // Plugs by their nature end a conduit path, so don't add @@ -419,6 +451,8 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL .kind = .{ .Plug = last_node }, .coord = coord, }); + try node_input_dir.append(dir); + try source_node.append(last_node); continue; }, .Outlet => { @@ -427,6 +461,8 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL .kind = .{ .Outlet = last_node }, .coord = coord, }); + try node_input_dir.append(dir); + try source_node.append(last_node); }, .Switch_Off => { // Add switch @@ -438,6 +474,33 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL } }, .coord = coord, }); + try node_input_dir.append(dir); + try source_node.append(last_node); + + // Loop over sides, check if they are connected, and add a + // switch outlet if so + for (Dir.each) |side| { + const next_coord = coord.add(side.toOffset()); + if (level.getCircuit(next_coord)) |circuit| { + if (circuit.canConnect(side.getOpposite()) and side != dir) { + const outlet = @intCast(world.NodeID, nodes.items.len); + const which = if (side == .North or side == .South) @as(u8, 1) else @as(u8, 0); + try nodes.append(.{ + .kind = .{ .SwitchOutlet = .{ + .source = next_node, + .which = which, + } }, + .coord = next_coord, + }); + try node_input_dir.append(side); + try source_node.append(next_node); + + const outlet_search = try alloc.create(Node); + outlet_search.* = .{ .data = node.data.next(outlet, side.toOffset()) }; + bfs_queue.append(outlet_search); + } + } + } }, .Switch_On => { // Add switch @@ -449,6 +512,8 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL } }, .coord = coord, }); + try node_input_dir.append(dir); + try source_node.append(last_node); }, .Join => { const last_coord = node.data.last_coord.?; @@ -461,6 +526,8 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL .kind = .{ .Join = last_node }, .coord = coord, }); + try node_input_dir.append(dir); + try source_node.append(last_node); } }, .And => { @@ -469,6 +536,8 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL .kind = .{ .And = .{ std.math.maxInt(world.NodeID), std.math.maxInt(world.NodeID) } }, .coord = coord, }); + try node_input_dir.append(dir); + try source_node.append(last_node); }, .Xor => { next_node = @intCast(world.NodeID, nodes.items.len); @@ -476,6 +545,8 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL .kind = .{ .Xor = .{ std.math.maxInt(world.NodeID), std.math.maxInt(world.NodeID) } }, .coord = coord, }); + try node_input_dir.append(dir); + try source_node.append(last_node); }, .Diode => { // TODO @@ -523,12 +594,13 @@ pub fn buildCircuit(alloc: std.mem.Allocator, levels: []world.Level) !std.ArrayL .Source => {}, .And => { const neighbors = try findNeighbors(alloc, levels, nodes.items, i); + defer neighbors.deinit(); + std.log.warn("[{}]: Found {} neighbors", .{ i, neighbors.items.len }); - for (neighbors.items) |node, a| { - std.log.warn("\tNeighbor {}: [{}] {}", .{ a, node.id, node.side }); - // if (node.side == .North) linkNeighbor(nodes.items[node]); - if (node.side == .West) nodes.items[i].kind.And[0] = node.id; - if (node.side == .East) nodes.items[i].kind.And[1] = node.id; + for (neighbors.items) |neighbor, a| { + std.log.warn("\tNeighbor {}: [{}] {}", .{ a, neighbor.id, neighbor.side }); + if (neighbor.side == .West) nodes.items[i].kind.And[0] = neighbor.id; + if (neighbor.side == .East) nodes.items[i].kind.And[1] = neighbor.id; } }, .Xor => {}, @@ -555,7 +627,6 @@ fn findNeighbors( nodes: []world.CircuitNode, index: usize, ) !std.ArrayList(Neighbor) { - const Coord = world.Coordinate; var visited = std.AutoHashMap(Coord, void).init(alloc); defer visited.deinit(); @@ -651,9 +722,7 @@ fn findNeighbors( return neighbors; } -const Dir = enum { North, West, East, South }; - -fn getInputDirection(coord: world.Coordinate, last_coord: world.Coordinate) Dir { +fn getInputDirection(coord: Coord, last_coord: Coord) Dir { if (last_coord.eq(coord.add(.{ 0, -1 }))) { return .North; } else if (last_coord.eq(coord.add(.{ -1, 0 }))) { @@ -672,7 +741,7 @@ fn getLevel(levels: []world.Level, x: i8, y: i8) ?world.Level { return null; } -fn getNode(nodes: []world.CircuitNode, coord: world.Coordinate) ?world.NodeID { +fn getNode(nodes: []world.CircuitNode, coord: Coord) ?world.NodeID { for (nodes) |node, i| { if (node.coord.eq(coord)) return @intCast(world.NodeID, i); }