diff --git a/res/ui/building_icons/display.png b/res/ui/building_icons/display.png new file mode 100644 index 00000000..14c48d7e Binary files /dev/null and b/res/ui/building_icons/display.png differ diff --git a/res_built/atlas/atlas0_hq.json b/res_built/atlas/atlas0_hq.json index 81803977..2aa01927 100644 --- a/res_built/atlas/atlas0_hq.json +++ b/res_built/atlas/atlas0_hq.json @@ -2,7 +2,7 @@ "sprites/belt/built/forward_0.png": { - "frame": {"x":1523,"y":704,"w":116,"h":144}, + "frame": {"x":440,"y":742,"w":116,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":0,"w":116,"h":144}, @@ -10,7 +10,7 @@ }, "sprites/belt/built/forward_1.png": { - "frame": {"x":1791,"y":731,"w":116,"h":144}, + "frame": {"x":1925,"y":1008,"w":116,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":0,"w":116,"h":144}, @@ -18,7 +18,7 @@ }, "sprites/belt/built/forward_2.png": { - "frame": {"x":1163,"y":1541,"w":116,"h":144}, + "frame": {"x":1540,"y":1139,"w":116,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":0,"w":116,"h":144}, @@ -26,7 +26,7 @@ }, "sprites/belt/built/forward_3.png": { - "frame": {"x":1283,"y":1670,"w":116,"h":144}, + "frame": {"x":1803,"y":1118,"w":116,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":0,"w":116,"h":144}, @@ -34,7 +34,7 @@ }, "sprites/belt/built/forward_4.png": { - "frame": {"x":1403,"y":1684,"w":116,"h":144}, + "frame": {"x":1923,"y":1156,"w":116,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":0,"w":116,"h":144}, @@ -42,7 +42,7 @@ }, "sprites/belt/built/forward_5.png": { - "frame": {"x":3,"y":1832,"w":116,"h":144}, + "frame": {"x":1801,"y":1266,"w":116,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":0,"w":116,"h":144}, @@ -50,7 +50,7 @@ }, "sprites/belt/built/forward_6.png": { - "frame": {"x":3,"y":1644,"w":116,"h":144}, + "frame": {"x":1921,"y":1304,"w":116,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":0,"w":116,"h":144}, @@ -58,7 +58,7 @@ }, "sprites/belt/built/forward_7.png": { - "frame": {"x":123,"y":1832,"w":116,"h":144}, + "frame": {"x":432,"y":1334,"w":116,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":0,"w":116,"h":144}, @@ -66,7 +66,7 @@ }, "sprites/belt/built/forward_8.png": { - "frame": {"x":123,"y":1644,"w":116,"h":144}, + "frame": {"x":430,"y":1482,"w":116,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":0,"w":116,"h":144}, @@ -74,7 +74,7 @@ }, "sprites/belt/built/forward_9.png": { - "frame": {"x":243,"y":1832,"w":116,"h":144}, + "frame": {"x":141,"y":1877,"w":116,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":0,"w":116,"h":144}, @@ -82,7 +82,7 @@ }, "sprites/belt/built/forward_10.png": { - "frame": {"x":1779,"y":1027,"w":116,"h":144}, + "frame": {"x":438,"y":890,"w":116,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":0,"w":116,"h":144}, @@ -90,7 +90,7 @@ }, "sprites/belt/built/forward_11.png": { - "frame": {"x":1510,"y":1000,"w":116,"h":144}, + "frame": {"x":438,"y":1038,"w":116,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":0,"w":116,"h":144}, @@ -98,7 +98,7 @@ }, "sprites/belt/built/forward_12.png": { - "frame": {"x":1770,"y":1175,"w":116,"h":144}, + "frame": {"x":1542,"y":991,"w":116,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":0,"w":116,"h":144}, @@ -106,7 +106,7 @@ }, "sprites/belt/built/forward_13.png": { - "frame": {"x":925,"y":1336,"w":116,"h":144}, + "frame": {"x":437,"y":1186,"w":116,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":0,"w":116,"h":144}, @@ -114,7 +114,7 @@ }, "sprites/belt/built/left_0.png": { - "frame": {"x":1912,"y":590,"w":130,"h":130}, + "frame": {"x":145,"y":1475,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":14,"w":130,"h":130}, @@ -122,7 +122,7 @@ }, "sprites/belt/built/left_1.png": { - "frame": {"x":1912,"y":724,"w":130,"h":130}, + "frame": {"x":3,"y":1575,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":14,"w":130,"h":130}, @@ -130,7 +130,7 @@ }, "sprites/belt/built/left_2.png": { - "frame": {"x":1189,"y":1139,"w":130,"h":130}, + "frame": {"x":279,"y":1493,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":14,"w":130,"h":130}, @@ -138,7 +138,7 @@ }, "sprites/belt/built/left_3.png": { - "frame": {"x":1045,"y":1242,"w":130,"h":130}, + "frame": {"x":137,"y":1609,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":14,"w":130,"h":130}, @@ -146,7 +146,7 @@ }, "sprites/belt/built/left_4.png": { - "frame": {"x":1323,"y":1268,"w":130,"h":130}, + "frame": {"x":3,"y":1709,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":14,"w":130,"h":130}, @@ -154,7 +154,7 @@ }, "sprites/belt/built/left_5.png": { - "frame": {"x":1179,"y":1273,"w":130,"h":130}, + "frame": {"x":271,"y":1627,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":14,"w":130,"h":130}, @@ -162,7 +162,7 @@ }, "sprites/belt/built/left_6.png": { - "frame": {"x":1457,"y":1282,"w":130,"h":130}, + "frame": {"x":137,"y":1743,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":14,"w":130,"h":130}, @@ -170,7 +170,7 @@ }, "sprites/belt/built/left_7.png": { - "frame": {"x":1313,"y":1402,"w":130,"h":130}, + "frame": {"x":3,"y":1843,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":14,"w":130,"h":130}, @@ -178,7 +178,7 @@ }, "sprites/belt/built/left_8.png": { - "frame": {"x":1591,"y":1287,"w":130,"h":130}, + "frame": {"x":1379,"y":1274,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":14,"w":130,"h":130}, @@ -186,7 +186,7 @@ }, "sprites/belt/built/left_9.png": { - "frame": {"x":1447,"y":1416,"w":130,"h":130}, + "frame": {"x":1513,"y":1287,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":14,"w":130,"h":130}, @@ -194,7 +194,7 @@ }, "sprites/belt/built/left_10.png": { - "frame": {"x":1911,"y":858,"w":130,"h":130}, + "frame": {"x":1076,"y":1288,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":14,"w":130,"h":130}, @@ -202,7 +202,7 @@ }, "sprites/belt/built/left_11.png": { - "frame": {"x":1358,"y":1134,"w":130,"h":130}, + "frame": {"x":927,"y":1295,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":14,"w":130,"h":130}, @@ -210,7 +210,7 @@ }, "sprites/belt/built/left_12.png": { - "frame": {"x":1492,"y":1148,"w":130,"h":130}, + "frame": {"x":786,"y":1363,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":14,"w":130,"h":130}, @@ -218,7 +218,7 @@ }, "sprites/belt/built/left_13.png": { - "frame": {"x":1890,"y":1285,"w":130,"h":130}, + "frame": {"x":552,"y":1416,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":14,"w":130,"h":130}, @@ -226,7 +226,7 @@ }, "sprites/belt/built/right_0.png": { - "frame": {"x":1725,"y":1323,"w":130,"h":130}, + "frame": {"x":1647,"y":1294,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":14,"w":130,"h":130}, @@ -234,7 +234,7 @@ }, "sprites/belt/built/right_1.png": { - "frame": {"x":1581,"y":1421,"w":130,"h":130}, + "frame": {"x":1781,"y":1414,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":14,"w":130,"h":130}, @@ -242,7 +242,7 @@ }, "sprites/belt/built/right_2.png": { - "frame": {"x":1045,"y":1376,"w":130,"h":130}, + "frame": {"x":1344,"y":1408,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":14,"w":130,"h":130}, @@ -250,7 +250,7 @@ }, "sprites/belt/built/right_3.png": { - "frame": {"x":1179,"y":1407,"w":130,"h":130}, + "frame": {"x":1195,"y":1520,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":14,"w":130,"h":130}, @@ -258,7 +258,7 @@ }, "sprites/belt/built/right_4.png": { - "frame": {"x":1313,"y":1536,"w":130,"h":130}, + "frame": {"x":1054,"y":1556,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":14,"w":130,"h":130}, @@ -266,7 +266,7 @@ }, "sprites/belt/built/right_5.png": { - "frame": {"x":1447,"y":1550,"w":130,"h":130}, + "frame": {"x":1478,"y":1421,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":14,"w":130,"h":130}, @@ -274,7 +274,7 @@ }, "sprites/belt/built/right_6.png": { - "frame": {"x":1581,"y":1555,"w":130,"h":130}, + "frame": {"x":1329,"y":1542,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":14,"w":130,"h":130}, @@ -282,7 +282,7 @@ }, "sprites/belt/built/right_7.png": { - "frame": {"x":1715,"y":1591,"w":130,"h":130}, + "frame": {"x":1188,"y":1654,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":14,"w":130,"h":130}, @@ -290,7 +290,7 @@ }, "sprites/belt/built/right_8.png": { - "frame": {"x":627,"y":1450,"w":130,"h":130}, + "frame": {"x":1612,"y":1428,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":14,"w":130,"h":130}, @@ -298,7 +298,7 @@ }, "sprites/belt/built/right_9.png": { - "frame": {"x":761,"y":1470,"w":130,"h":130}, + "frame": {"x":1463,"y":1555,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":14,"w":130,"h":130}, @@ -306,7 +306,7 @@ }, "sprites/belt/built/right_10.png": { - "frame": {"x":1859,"y":1419,"w":130,"h":130}, + "frame": {"x":1915,"y":1452,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":14,"w":130,"h":130}, @@ -314,7 +314,7 @@ }, "sprites/belt/built/right_11.png": { - "frame": {"x":1715,"y":1457,"w":130,"h":130}, + "frame": {"x":1210,"y":1386,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":14,"w":130,"h":130}, @@ -322,7 +322,7 @@ }, "sprites/belt/built/right_12.png": { - "frame": {"x":1849,"y":1553,"w":130,"h":130}, + "frame": {"x":1061,"y":1422,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":14,"w":130,"h":130}, @@ -330,7 +330,7 @@ }, "sprites/belt/built/right_13.png": { - "frame": {"x":791,"y":1336,"w":130,"h":130}, + "frame": {"x":920,"y":1429,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":14,"w":130,"h":130}, @@ -338,7 +338,7 @@ }, "sprites/blueprints/belt_left.png": { - "frame": {"x":895,"y":1484,"w":130,"h":130}, + "frame": {"x":1322,"y":1676,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":14,"w":130,"h":130}, @@ -346,7 +346,7 @@ }, "sprites/blueprints/belt_right.png": { - "frame": {"x":1029,"y":1510,"w":130,"h":130}, + "frame": {"x":1746,"y":1548,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":14,"w":130,"h":130}, @@ -354,7 +354,7 @@ }, "sprites/blueprints/belt_top.png": { - "frame": {"x":243,"y":1644,"w":116,"h":144}, + "frame": {"x":261,"y":1877,"w":116,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":0,"w":116,"h":144}, @@ -362,7 +362,7 @@ }, "sprites/blueprints/constant_signal.png": { - "frame": {"x":961,"y":977,"w":105,"h":127}, + "frame": {"x":1937,"y":759,"w":105,"h":127}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":20,"y":0,"w":105,"h":127}, @@ -378,15 +378,23 @@ }, "sprites/blueprints/cutter.png": { - "frame": {"x":295,"y":299,"w":256,"h":144}, + "frame": {"x":847,"y":298,"w":256,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":17,"y":0,"w":256,"h":144}, "sourceSize": {"w":288,"h":144} }, +"sprites/blueprints/display.png": +{ + "frame": {"x":1597,"y":1562,"w":128,"h":136}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":8,"y":8,"w":128,"h":136}, + "sourceSize": {"w":144,"h":144} +}, "sprites/blueprints/filter.png": { - "frame": {"x":847,"y":298,"w":268,"h":144}, + "frame": {"x":1107,"y":556,"w":268,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":16,"y":0,"w":268,"h":144}, @@ -394,7 +402,7 @@ }, "sprites/blueprints/lever.png": { - "frame": {"x":403,"y":887,"w":110,"h":116}, + "frame": {"x":1823,"y":732,"w":110,"h":116}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":17,"y":17,"w":110,"h":116}, @@ -402,7 +410,7 @@ }, "sprites/blueprints/logic_gate-not.png": { - "frame": {"x":1512,"y":852,"w":123,"h":144}, + "frame": {"x":1545,"y":843,"w":123,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":11,"y":0,"w":123,"h":144}, @@ -410,7 +418,7 @@ }, "sprites/blueprints/logic_gate-or.png": { - "frame": {"x":1074,"y":852,"w":144,"h":123}, + "frame": {"x":560,"y":888,"w":144,"h":123}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":144,"h":123}, @@ -418,7 +426,7 @@ }, "sprites/blueprints/logic_gate-transistor.png": { - "frame": {"x":402,"y":1138,"w":101,"h":144}, + "frame": {"x":1820,"y":970,"w":101,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":101,"h":144}, @@ -426,7 +434,7 @@ }, "sprites/blueprints/logic_gate-xor.png": { - "frame": {"x":665,"y":741,"w":144,"h":143}, + "frame": {"x":1101,"y":852,"w":144,"h":143}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":144,"h":143}, @@ -434,7 +442,7 @@ }, "sprites/blueprints/logic_gate.png": { - "frame": {"x":813,"y":741,"w":144,"h":133}, + "frame": {"x":824,"y":885,"w":144,"h":133}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":144,"h":133}, @@ -442,7 +450,7 @@ }, "sprites/blueprints/miner-chainable.png": { - "frame": {"x":1639,"y":994,"w":136,"h":143}, + "frame": {"x":150,"y":1035,"w":136,"h":143}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":136,"h":143}, @@ -450,7 +458,7 @@ }, "sprites/blueprints/miner.png": { - "frame": {"x":1906,"y":992,"w":136,"h":143}, + "frame": {"x":150,"y":1182,"w":136,"h":143}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":136,"h":143}, @@ -458,7 +466,7 @@ }, "sprites/blueprints/mixer.png": { - "frame": {"x":1118,"y":556,"w":261,"h":144}, + "frame": {"x":1676,"y":584,"w":261,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":13,"y":0,"w":261,"h":144}, @@ -498,7 +506,7 @@ }, "sprites/blueprints/rotater-ccw.png": { - "frame": {"x":517,"y":974,"w":143,"h":144}, + "frame": {"x":3,"y":1035,"w":143,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":143,"h":144}, @@ -506,7 +514,7 @@ }, "sprites/blueprints/rotater-fl.png": { - "frame": {"x":255,"y":1171,"w":142,"h":144}, + "frame": {"x":1396,"y":979,"w":142,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":142,"h":144}, @@ -514,7 +522,7 @@ }, "sprites/blueprints/rotater.png": { - "frame": {"x":813,"y":1049,"w":143,"h":144}, + "frame": {"x":290,"y":1060,"w":143,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":143,"h":144}, @@ -522,7 +530,7 @@ }, "sprites/blueprints/splitter-compact-inverse.png": { - "frame": {"x":3,"y":1182,"w":142,"h":138}, + "frame": {"x":1249,"y":988,"w":142,"h":138}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":2,"w":142,"h":138}, @@ -530,7 +538,7 @@ }, "sprites/blueprints/splitter-compact.png": { - "frame": {"x":1367,"y":992,"w":139,"h":138}, + "frame": {"x":1094,"y":1146,"w":139,"h":138}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":2,"w":139,"h":138}, @@ -538,7 +546,7 @@ }, "sprites/blueprints/splitter.png": { - "frame": {"x":295,"y":447,"w":256,"h":144}, + "frame": {"x":295,"y":299,"w":256,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":17,"y":0,"w":256,"h":144}, @@ -546,7 +554,7 @@ }, "sprites/blueprints/stacker.png": { - "frame": {"x":1383,"y":556,"w":261,"h":144}, + "frame": {"x":295,"y":594,"w":261,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":13,"y":0,"w":261,"h":144}, @@ -554,7 +562,7 @@ }, "sprites/blueprints/trash-storage.png": { - "frame": {"x":263,"y":595,"w":250,"h":288}, + "frame": {"x":847,"y":593,"w":250,"h":288}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":21,"y":0,"w":250,"h":288}, @@ -562,7 +570,7 @@ }, "sprites/blueprints/trash.png": { - "frame": {"x":1079,"y":704,"w":144,"h":144}, + "frame": {"x":292,"y":742,"w":144,"h":144}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":144,"h":144}, @@ -570,7 +578,7 @@ }, "sprites/blueprints/underground_belt_entry-tier2.png": { - "frame": {"x":658,"y":1205,"w":138,"h":125}, + "frame": {"x":3,"y":1330,"w":138,"h":125}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":19,"w":138,"h":125}, @@ -578,7 +586,7 @@ }, "sprites/blueprints/underground_belt_entry.png": { - "frame": {"x":507,"y":1249,"w":138,"h":112}, + "frame": {"x":3,"y":1459,"w":138,"h":112}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":32,"w":138,"h":112}, @@ -586,7 +594,7 @@ }, "sprites/blueprints/underground_belt_exit-tier2.png": { - "frame": {"x":1046,"y":1126,"w":139,"h":112}, + "frame": {"x":558,"y":1185,"w":139,"h":112}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":139,"h":112}, @@ -594,7 +602,7 @@ }, "sprites/blueprints/underground_belt_exit.png": { - "frame": {"x":649,"y":1334,"w":138,"h":112}, + "frame": {"x":1237,"y":1270,"w":138,"h":112}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":138,"h":112}, @@ -602,7 +610,7 @@ }, "sprites/blueprints/wire-cross.png": { - "frame": {"x":1227,"y":704,"w":144,"h":144}, + "frame": {"x":3,"y":887,"w":144,"h":144}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":144,"h":144}, @@ -610,7 +618,7 @@ }, "sprites/blueprints/wire-split.png": { - "frame": {"x":813,"y":878,"w":144,"h":82}, + "frame": {"x":801,"y":1022,"w":144,"h":82}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":62,"w":144,"h":82}, @@ -618,7 +626,7 @@ }, "sprites/blueprints/wire-turn.png": { - "frame": {"x":960,"y":1108,"w":82,"h":82}, + "frame": {"x":706,"y":1036,"w":82,"h":82}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":62,"y":62,"w":82,"h":82}, @@ -626,15 +634,23 @@ }, "sprites/blueprints/wire.png": { - "frame": {"x":2014,"y":245,"w":20,"h":144}, + "frame": {"x":1107,"y":151,"w":20,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":62,"y":0,"w":20,"h":144}, "sourceSize": {"w":144,"h":144} }, +"sprites/blueprints/wire_tunnel-coating.png": +{ + "frame": {"x":255,"y":677,"w":33,"h":135}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":55,"y":4,"w":33,"h":135}, + "sourceSize": {"w":144,"h":144} +}, "sprites/blueprints/wire_tunnel.png": { - "frame": {"x":811,"y":1197,"w":138,"h":135}, + "frame": {"x":290,"y":1208,"w":138,"h":135}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":4,"w":138,"h":135}, @@ -642,7 +658,7 @@ }, "sprites/buildings/belt_left.png": { - "frame": {"x":1912,"y":590,"w":130,"h":130}, + "frame": {"x":145,"y":1475,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":14,"w":130,"h":130}, @@ -650,7 +666,7 @@ }, "sprites/buildings/belt_right.png": { - "frame": {"x":1725,"y":1323,"w":130,"h":130}, + "frame": {"x":1647,"y":1294,"w":130,"h":130}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":14,"w":130,"h":130}, @@ -658,7 +674,7 @@ }, "sprites/buildings/belt_top.png": { - "frame": {"x":1523,"y":704,"w":116,"h":144}, + "frame": {"x":440,"y":742,"w":116,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":0,"w":116,"h":144}, @@ -666,7 +682,7 @@ }, "sprites/buildings/constant_signal.png": { - "frame": {"x":403,"y":1007,"w":104,"h":127}, + "frame": {"x":1941,"y":628,"w":104,"h":127}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":20,"y":0,"w":104,"h":127}, @@ -682,15 +698,23 @@ }, "sprites/buildings/cutter.png": { - "frame": {"x":819,"y":594,"w":256,"h":143}, + "frame": {"x":847,"y":446,"w":256,"h":143}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":17,"y":0,"w":256,"h":143}, "sourceSize": {"w":288,"h":144} }, +"sprites/buildings/display.png": +{ + "frame": {"x":1545,"y":704,"w":126,"h":135}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":9,"w":126,"h":135}, + "sourceSize": {"w":144,"h":144} +}, "sprites/buildings/filter.png": { - "frame": {"x":847,"y":446,"w":267,"h":144}, + "frame": {"x":1379,"y":556,"w":267,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":17,"y":0,"w":267,"h":144}, @@ -706,7 +730,7 @@ }, "sprites/buildings/lever.png": { - "frame": {"x":961,"y":741,"w":109,"h":114}, + "frame": {"x":1823,"y":852,"w":109,"h":114}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":18,"y":18,"w":109,"h":114}, @@ -714,7 +738,7 @@ }, "sprites/buildings/logic_gate-not.png": { - "frame": {"x":1780,"y":879,"w":122,"h":144}, + "frame": {"x":972,"y":885,"w":122,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":12,"y":0,"w":122,"h":144}, @@ -722,7 +746,7 @@ }, "sprites/buildings/logic_gate-or.png": { - "frame": {"x":511,"y":1122,"w":143,"h":123}, + "frame": {"x":1396,"y":852,"w":143,"h":123}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":143,"h":123}, @@ -730,7 +754,7 @@ }, "sprites/buildings/logic_gate-transistor.png": { - "frame": {"x":151,"y":1034,"w":100,"h":144}, + "frame": {"x":151,"y":887,"w":100,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":100,"h":144}, @@ -738,7 +762,7 @@ }, "sprites/buildings/logic_gate-xor.png": { - "frame": {"x":664,"y":1058,"w":143,"h":143}, + "frame": {"x":3,"y":1183,"w":143,"h":143}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":143,"h":143}, @@ -746,7 +770,7 @@ }, "sprites/buildings/logic_gate.png": { - "frame": {"x":255,"y":1035,"w":143,"h":132}, + "frame": {"x":1249,"y":852,"w":143,"h":132}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":143,"h":132}, @@ -754,7 +778,7 @@ }, "sprites/buildings/miner-chainable.png": { - "frame": {"x":1630,"y":1141,"w":136,"h":142}, + "frame": {"x":150,"y":1329,"w":136,"h":142}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":136,"h":142}, @@ -762,7 +786,7 @@ }, "sprites/buildings/miner.png": { - "frame": {"x":1899,"y":1139,"w":136,"h":142}, + "frame": {"x":290,"y":1347,"w":136,"h":142}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":136,"h":142}, @@ -770,7 +794,7 @@ }, "sprites/buildings/mixer.png": { - "frame": {"x":1648,"y":584,"w":260,"h":143}, + "frame": {"x":560,"y":594,"w":260,"h":143}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":0,"w":260,"h":143}, @@ -810,7 +834,7 @@ }, "sprites/buildings/rotater-ccw.png": { - "frame": {"x":3,"y":1324,"w":141,"h":143}, + "frame": {"x":1098,"y":999,"w":141,"h":143}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":0,"w":141,"h":143}, @@ -818,7 +842,7 @@ }, "sprites/buildings/rotater-fl.png": { - "frame": {"x":1222,"y":852,"w":141,"h":143}, + "frame": {"x":949,"y":1033,"w":141,"h":143}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":141,"h":143}, @@ -826,7 +850,7 @@ }, "sprites/buildings/rotater.png": { - "frame": {"x":1070,"y":979,"w":141,"h":143}, + "frame": {"x":1395,"y":1127,"w":141,"h":143}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":0,"w":141,"h":143}, @@ -834,7 +858,7 @@ }, "sprites/buildings/splitter-compact-inverse.png": { - "frame": {"x":1367,"y":852,"w":141,"h":136}, + "frame": {"x":1243,"y":1130,"w":141,"h":136}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":3,"w":141,"h":136}, @@ -842,7 +866,7 @@ }, "sprites/buildings/splitter-compact.png": { - "frame": {"x":1215,"y":999,"w":139,"h":136}, + "frame": {"x":792,"y":1108,"w":139,"h":136}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":3,"w":139,"h":136}, @@ -850,7 +874,7 @@ }, "sprites/buildings/splitter.png": { - "frame": {"x":3,"y":595,"w":256,"h":143}, + "frame": {"x":295,"y":447,"w":256,"h":143}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":17,"y":0,"w":256,"h":143}, @@ -858,7 +882,7 @@ }, "sprites/buildings/stacker.png": { - "frame": {"x":555,"y":594,"w":260,"h":143}, + "frame": {"x":560,"y":741,"w":260,"h":143}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":0,"w":260,"h":143}, @@ -866,7 +890,7 @@ }, "sprites/buildings/trash-storage.png": { - "frame": {"x":3,"y":742,"w":248,"h":288}, + "frame": {"x":3,"y":595,"w":248,"h":288}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":22,"y":0,"w":248,"h":288}, @@ -874,7 +898,7 @@ }, "sprites/buildings/trash.png": { - "frame": {"x":1375,"y":704,"w":144,"h":144}, + "frame": {"x":1101,"y":704,"w":144,"h":144}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":144,"h":144}, @@ -882,7 +906,7 @@ }, "sprites/buildings/underground_belt_entry-tier2.png": { - "frame": {"x":3,"y":1471,"w":137,"h":124}, + "frame": {"x":1660,"y":1166,"w":137,"h":124}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":20,"w":137,"h":124}, @@ -890,7 +914,7 @@ }, "sprites/buildings/underground_belt_entry.png": { - "frame": {"x":486,"y":1365,"w":137,"h":111}, + "frame": {"x":935,"y":1180,"w":137,"h":111}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":33,"w":137,"h":111}, @@ -898,7 +922,7 @@ }, "sprites/buildings/underground_belt_exit-tier2.png": { - "frame": {"x":233,"y":1457,"w":137,"h":111}, + "frame": {"x":786,"y":1248,"w":137,"h":111}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":137,"h":111}, @@ -906,7 +930,7 @@ }, "sprites/buildings/underground_belt_exit.png": { - "frame": {"x":1639,"y":879,"w":137,"h":111}, + "frame": {"x":557,"y":1301,"w":137,"h":111}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":137,"h":111}, @@ -914,7 +938,7 @@ }, "sprites/buildings/wire-cross.png": { - "frame": {"x":1643,"y":731,"w":144,"h":144}, + "frame": {"x":1249,"y":704,"w":144,"h":144}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":144,"h":144}, @@ -922,7 +946,7 @@ }, "sprites/buildings/wire-split.png": { - "frame": {"x":665,"y":888,"w":144,"h":81}, + "frame": {"x":290,"y":890,"w":144,"h":81}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":63,"w":144,"h":81}, @@ -930,7 +954,7 @@ }, "sprites/buildings/wire-turn.png": { - "frame": {"x":960,"y":1194,"w":81,"h":81}, + "frame": {"x":706,"y":1122,"w":81,"h":81}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":63,"y":63,"w":81,"h":81}, @@ -938,15 +962,23 @@ }, "sprites/buildings/wire.png": { - "frame": {"x":1975,"y":294,"w":18,"h":144}, + "frame": {"x":2027,"y":184,"w":18,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":63,"y":0,"w":18,"h":144}, "sourceSize": {"w":144,"h":144} }, +"sprites/buildings/wire_tunnel-coating.png": +{ + "frame": {"x":255,"y":816,"w":31,"h":134}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":56,"y":5,"w":31,"h":134}, + "sourceSize": {"w":144,"h":144} +}, "sprites/buildings/wire_tunnel.png": { - "frame": {"x":242,"y":1319,"w":137,"h":134}, + "frame": {"x":1662,"y":1028,"w":137,"h":134}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":5,"w":137,"h":134}, @@ -954,7 +986,7 @@ }, "sprites/debug/acceptor_slot.png": { - "frame": {"x":1997,"y":294,"w":12,"h":12}, + "frame": {"x":1107,"y":447,"w":12,"h":12}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, @@ -962,7 +994,7 @@ }, "sprites/debug/ejector_slot.png": { - "frame": {"x":1997,"y":310,"w":12,"h":12}, + "frame": {"x":1107,"y":463,"w":12,"h":12}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":12,"h":12}, @@ -970,15 +1002,15 @@ }, "sprites/misc/hub_direction_indicator.png": { - "frame": {"x":2014,"y":184,"w":24,"h":24}, + "frame": {"x":1975,"y":184,"w":48,"h":48}, "rotated": false, "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":24,"h":24}, - "sourceSize": {"w":24,"h":24} + "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, + "sourceSize": {"w":48,"h":48} }, "sprites/misc/slot_bad_arrow.png": { - "frame": {"x":1975,"y":227,"w":35,"h":35}, + "frame": {"x":255,"y":638,"w":35,"h":35}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":2,"w":35,"h":35}, @@ -986,7 +1018,7 @@ }, "sprites/misc/slot_good_arrow.png": { - "frame": {"x":1975,"y":184,"w":35,"h":39}, + "frame": {"x":255,"y":595,"w":35,"h":39}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":0,"w":35,"h":39}, @@ -994,7 +1026,7 @@ }, "sprites/misc/storage_overlay.png": { - "frame": {"x":149,"y":1282,"w":89,"h":44}, + "frame": {"x":708,"y":988,"w":89,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":89,"h":44}, @@ -1002,31 +1034,87 @@ }, "sprites/misc/waypoint.png": { - "frame": {"x":1975,"y":266,"w":20,"h":24}, + "frame": {"x":48,"y":1977,"w":38,"h":48}, "rotated": false, "trimmed": true, - "spriteSourceSize": {"x":2,"y":0,"w":20,"h":24}, - "sourceSize": {"w":24,"h":24} + "spriteSourceSize": {"x":5,"y":0,"w":38,"h":48}, + "sourceSize": {"w":48,"h":48} }, "sprites/wires/boolean_false.png": { - "frame": {"x":2014,"y":212,"w":22,"h":29}, + "frame": {"x":255,"y":954,"w":31,"h":41}, "rotated": false, "trimmed": true, - "spriteSourceSize": {"x":14,"y":11,"w":22,"h":29}, + "spriteSourceSize": {"x":9,"y":5,"w":31,"h":41}, "sourceSize": {"w":48,"h":48} }, "sprites/wires/boolean_true.png": { - "frame": {"x":1997,"y":541,"w":17,"h":30}, + "frame": {"x":1650,"y":556,"w":22,"h":41}, "rotated": false, "trimmed": true, - "spriteSourceSize": {"x":14,"y":10,"w":17,"h":30}, + "spriteSourceSize": {"x":11,"y":5,"w":22,"h":41}, "sourceSize": {"w":48,"h":48} }, +"sprites/wires/display/blue.png": +{ + "frame": {"x":1975,"y":288,"w":47,"h":47}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":1,"w":47,"h":47}, + "sourceSize": {"w":49,"h":49} +}, +"sprites/wires/display/cyan.png": +{ + "frame": {"x":1975,"y":339,"w":47,"h":47}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":1,"w":47,"h":47}, + "sourceSize": {"w":49,"h":49} +}, +"sprites/wires/display/green.png": +{ + "frame": {"x":1975,"y":390,"w":47,"h":47}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":1,"w":47,"h":47}, + "sourceSize": {"w":49,"h":49} +}, +"sprites/wires/display/purple.png": +{ + "frame": {"x":1975,"y":441,"w":47,"h":47}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":1,"w":47,"h":47}, + "sourceSize": {"w":49,"h":49} +}, +"sprites/wires/display/red.png": +{ + "frame": {"x":1975,"y":492,"w":47,"h":47}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":1,"w":47,"h":47}, + "sourceSize": {"w":49,"h":49} +}, +"sprites/wires/display/white.png": +{ + "frame": {"x":1975,"y":543,"w":47,"h":47}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":1,"w":47,"h":47}, + "sourceSize": {"w":49,"h":49} +}, +"sprites/wires/display/yellow.png": +{ + "frame": {"x":90,"y":1977,"w":47,"h":47}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":1,"w":47,"h":47}, + "sourceSize": {"w":49,"h":49} +}, "sprites/wires/lever_on.png": { - "frame": {"x":961,"y":859,"w":109,"h":114}, + "frame": {"x":1936,"y":890,"w":109,"h":114}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":18,"y":18,"w":109,"h":114}, @@ -1048,9 +1136,25 @@ "spriteSourceSize": {"x":44,"y":0,"w":60,"h":67}, "sourceSize": {"w":144,"h":144} }, +"sprites/wires/network_conflict.png": +{ + "frame": {"x":271,"y":1810,"w":47,"h":44}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":2,"w":47,"h":44}, + "sourceSize": {"w":48,"h":48} +}, +"sprites/wires/network_empty.png": +{ + "frame": {"x":3,"y":1977,"w":41,"h":48}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":5,"y":0,"w":41,"h":48}, + "sourceSize": {"w":48,"h":48} +}, "sprites/wires/overlay_tile.png": { - "frame": {"x":149,"y":1182,"w":96,"h":96}, + "frame": {"x":708,"y":888,"w":96,"h":96}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":96}, @@ -1058,7 +1162,7 @@ }, "sprites/wires/sets/color_cross.png": { - "frame": {"x":517,"y":741,"w":144,"h":144}, + "frame": {"x":1397,"y":704,"w":144,"h":144}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":144,"h":144}, @@ -1066,7 +1170,7 @@ }, "sprites/wires/sets/color_forward.png": { - "frame": {"x":1997,"y":393,"w":18,"h":144}, + "frame": {"x":2026,"y":332,"w":18,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":63,"y":0,"w":18,"h":144}, @@ -1074,7 +1178,7 @@ }, "sprites/wires/sets/color_split.png": { - "frame": {"x":813,"y":964,"w":144,"h":81}, + "frame": {"x":290,"y":975,"w":144,"h":81}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":63,"w":144,"h":81}, @@ -1082,7 +1186,7 @@ }, "sprites/wires/sets/color_turn.png": { - "frame": {"x":401,"y":1286,"w":81,"h":81}, + "frame": {"x":701,"y":1207,"w":81,"h":81}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":63,"y":63,"w":81,"h":81}, @@ -1090,7 +1194,7 @@ }, "sprites/wires/sets/conflict_cross.png": { - "frame": {"x":255,"y":887,"w":144,"h":144}, + "frame": {"x":1675,"y":732,"w":144,"h":144}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":144,"h":144}, @@ -1098,7 +1202,7 @@ }, "sprites/wires/sets/conflict_forward.png": { - "frame": {"x":1975,"y":442,"w":18,"h":144}, + "frame": {"x":2026,"y":480,"w":18,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":63,"y":0,"w":18,"h":144}, @@ -1106,7 +1210,7 @@ }, "sprites/wires/sets/conflict_split.png": { - "frame": {"x":517,"y":889,"w":144,"h":81}, + "frame": {"x":558,"y":1015,"w":144,"h":81}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":63,"w":144,"h":81}, @@ -1114,7 +1218,7 @@ }, "sprites/wires/sets/conflict_turn.png": { - "frame": {"x":148,"y":1330,"w":81,"h":81}, + "frame": {"x":701,"y":1292,"w":81,"h":81}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":63,"y":63,"w":81,"h":81}, @@ -1122,7 +1226,7 @@ }, "sprites/wires/sets/regular_cross.png": { - "frame": {"x":1643,"y":731,"w":144,"h":144}, + "frame": {"x":1249,"y":704,"w":144,"h":144}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":144,"h":144}, @@ -1130,7 +1234,7 @@ }, "sprites/wires/sets/regular_forward.png": { - "frame": {"x":1975,"y":294,"w":18,"h":144}, + "frame": {"x":2027,"y":184,"w":18,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":63,"y":0,"w":18,"h":144}, @@ -1138,7 +1242,7 @@ }, "sprites/wires/sets/regular_split.png": { - "frame": {"x":665,"y":888,"w":144,"h":81}, + "frame": {"x":290,"y":890,"w":144,"h":81}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":63,"w":144,"h":81}, @@ -1146,7 +1250,7 @@ }, "sprites/wires/sets/regular_turn.png": { - "frame": {"x":960,"y":1194,"w":81,"h":81}, + "frame": {"x":706,"y":1122,"w":81,"h":81}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":63,"y":63,"w":81,"h":81}, @@ -1154,7 +1258,7 @@ }, "sprites/wires/sets/shape_cross.png": { - "frame": {"x":3,"y":1034,"w":144,"h":144}, + "frame": {"x":1672,"y":880,"w":144,"h":144}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":144,"h":144}, @@ -1162,7 +1266,7 @@ }, "sprites/wires/sets/shape_forward.png": { - "frame": {"x":2019,"y":393,"w":18,"h":144}, + "frame": {"x":1107,"y":299,"w":18,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":63,"y":0,"w":18,"h":144}, @@ -1170,7 +1274,7 @@ }, "sprites/wires/sets/shape_split.png": { - "frame": {"x":665,"y":973,"w":144,"h":81}, + "frame": {"x":558,"y":1100,"w":144,"h":81}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":63,"w":144,"h":81}, @@ -1178,11 +1282,19 @@ }, "sprites/wires/sets/shape_turn.png": { - "frame": {"x":148,"y":1415,"w":81,"h":81}, + "frame": {"x":698,"y":1377,"w":81,"h":81}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":63,"y":63,"w":81,"h":81}, "sourceSize": {"w":144,"h":144} +}, +"sprites/wires/wires_preview.png": +{ + "frame": {"x":1975,"y":236,"w":48,"h":48}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, + "sourceSize": {"w":48,"h":48} }}, "meta": { "app": "https://www.codeandweb.com/texturepacker", @@ -1191,6 +1303,6 @@ "format": "RGBA8888", "size": {"w":2048,"h":2048}, "scale": "0.75", - "smartupdate": "$TexturePacker:SmartUpdate:495f928ed9092c817f98f68825f1d4ae:98823415164aea829cee3223195c589c:908b89f5ca8ff73e331a35a3b14d0604$" + "smartupdate": "$TexturePacker:SmartUpdate:876f0711b44fa7bbab8d2539e9651766:ff01f850e086ef31c114b036c3a32e6d:908b89f5ca8ff73e331a35a3b14d0604$" } } diff --git a/res_built/atlas/atlas0_hq.png b/res_built/atlas/atlas0_hq.png index 48d831a5..ef9ab78b 100644 Binary files a/res_built/atlas/atlas0_hq.png and b/res_built/atlas/atlas0_hq.png differ diff --git a/res_built/atlas/atlas0_lq.json b/res_built/atlas/atlas0_lq.json index 59321600..3896c6fd 100644 --- a/res_built/atlas/atlas0_lq.json +++ b/res_built/atlas/atlas0_lq.json @@ -2,7 +2,7 @@ "sprites/belt/built/forward_0.png": { - "frame": {"x":466,"y":439,"w":40,"h":48}, + "frame": {"x":415,"y":463,"w":40,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":40,"h":48}, @@ -10,7 +10,7 @@ }, "sprites/belt/built/forward_1.png": { - "frame": {"x":466,"y":491,"w":40,"h":48}, + "frame": {"x":195,"y":828,"w":40,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":40,"h":48}, @@ -18,7 +18,7 @@ }, "sprites/belt/built/forward_2.png": { - "frame": {"x":283,"y":832,"w":40,"h":48}, + "frame": {"x":3,"y":959,"w":40,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":40,"h":48}, @@ -26,7 +26,7 @@ }, "sprites/belt/built/forward_3.png": { - "frame": {"x":327,"y":846,"w":40,"h":48}, + "frame": {"x":448,"y":705,"w":40,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":40,"h":48}, @@ -34,7 +34,7 @@ }, "sprites/belt/built/forward_4.png": { - "frame": {"x":371,"y":857,"w":40,"h":48}, + "frame": {"x":394,"y":752,"w":40,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":40,"h":48}, @@ -42,7 +42,7 @@ }, "sprites/belt/built/forward_5.png": { - "frame": {"x":3,"y":700,"w":40,"h":48}, + "frame": {"x":342,"y":762,"w":40,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":40,"h":48}, @@ -50,7 +50,7 @@ }, "sprites/belt/built/forward_6.png": { - "frame": {"x":47,"y":700,"w":40,"h":48}, + "frame": {"x":289,"y":795,"w":40,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":40,"h":48}, @@ -58,7 +58,7 @@ }, "sprites/belt/built/forward_7.png": { - "frame": {"x":91,"y":743,"w":40,"h":48}, + "frame": {"x":239,"y":836,"w":40,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":40,"h":48}, @@ -66,7 +66,7 @@ }, "sprites/belt/built/forward_8.png": { - "frame": {"x":135,"y":781,"w":40,"h":48}, + "frame": {"x":191,"y":880,"w":40,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":40,"h":48}, @@ -74,7 +74,7 @@ }, "sprites/belt/built/forward_9.png": { - "frame": {"x":179,"y":815,"w":40,"h":48}, + "frame": {"x":143,"y":883,"w":40,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":40,"h":48}, @@ -82,7 +82,7 @@ }, "sprites/belt/built/forward_10.png": { - "frame": {"x":419,"y":609,"w":40,"h":48}, + "frame": {"x":147,"y":831,"w":40,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":40,"h":48}, @@ -90,7 +90,7 @@ }, "sprites/belt/built/forward_11.png": { - "frame": {"x":416,"y":661,"w":40,"h":48}, + "frame": {"x":99,"y":862,"w":40,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":40,"h":48}, @@ -98,7 +98,7 @@ }, "sprites/belt/built/forward_12.png": { - "frame": {"x":195,"y":763,"w":40,"h":48}, + "frame": {"x":51,"y":870,"w":40,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":40,"h":48}, @@ -106,7 +106,7 @@ }, "sprites/belt/built/forward_13.png": { - "frame": {"x":239,"y":787,"w":40,"h":48}, + "frame": {"x":3,"y":907,"w":40,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":40,"h":48}, @@ -114,7 +114,7 @@ }, "sprites/belt/built/left_0.png": { - "frame": {"x":326,"y":399,"w":44,"h":44}, + "frame": {"x":3,"y":667,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":4,"w":44,"h":44}, @@ -122,7 +122,7 @@ }, "sprites/belt/built/left_1.png": { - "frame": {"x":326,"y":447,"w":44,"h":44}, + "frame": {"x":208,"y":636,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":4,"w":44,"h":44}, @@ -130,7 +130,7 @@ }, "sprites/belt/built/left_2.png": { - "frame": {"x":207,"y":667,"w":44,"h":44}, + "frame": {"x":256,"y":640,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":4,"w":44,"h":44}, @@ -138,7 +138,7 @@ }, "sprites/belt/built/left_3.png": { - "frame": {"x":309,"y":688,"w":44,"h":44}, + "frame": {"x":202,"y":684,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":4,"w":44,"h":44}, @@ -146,7 +146,7 @@ }, "sprites/belt/built/left_4.png": { - "frame": {"x":255,"y":691,"w":44,"h":44}, + "frame": {"x":150,"y":687,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":4,"w":44,"h":44}, @@ -154,7 +154,7 @@ }, "sprites/belt/built/left_5.png": { - "frame": {"x":357,"y":702,"w":44,"h":44}, + "frame": {"x":99,"y":718,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":4,"w":44,"h":44}, @@ -162,7 +162,7 @@ }, "sprites/belt/built/left_6.png": { - "frame": {"x":303,"y":736,"w":44,"h":44}, + "frame": {"x":51,"y":726,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":4,"w":44,"h":44}, @@ -170,7 +170,7 @@ }, "sprites/belt/built/left_7.png": { - "frame": {"x":405,"y":713,"w":44,"h":44}, + "frame": {"x":3,"y":763,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":4,"w":44,"h":44}, @@ -178,7 +178,7 @@ }, "sprites/belt/built/left_8.png": { - "frame": {"x":351,"y":750,"w":44,"h":44}, + "frame": {"x":304,"y":651,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":4,"w":44,"h":44}, @@ -186,7 +186,7 @@ }, "sprites/belt/built/left_9.png": { - "frame": {"x":453,"y":739,"w":44,"h":44}, + "frame": {"x":250,"y":688,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":4,"w":44,"h":44}, @@ -194,7 +194,7 @@ }, "sprites/belt/built/left_10.png": { - "frame": {"x":326,"y":495,"w":44,"h":44}, + "frame": {"x":154,"y":639,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":4,"w":44,"h":44}, @@ -202,7 +202,7 @@ }, "sprites/belt/built/left_11.png": { - "frame": {"x":465,"y":543,"w":44,"h":44}, + "frame": {"x":102,"y":670,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":4,"w":44,"h":44}, @@ -210,7 +210,7 @@ }, "sprites/belt/built/left_12.png": { - "frame": {"x":465,"y":591,"w":44,"h":44}, + "frame": {"x":51,"y":678,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":4,"w":44,"h":44}, @@ -218,7 +218,7 @@ }, "sprites/belt/built/left_13.png": { - "frame": {"x":463,"y":639,"w":44,"h":44}, + "frame": {"x":3,"y":715,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":4,"w":44,"h":44}, @@ -226,7 +226,7 @@ }, "sprites/belt/built/right_0.png": { - "frame": {"x":399,"y":761,"w":44,"h":44}, + "frame": {"x":198,"y":732,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":4,"w":44,"h":44}, @@ -234,7 +234,7 @@ }, "sprites/belt/built/right_1.png": { - "frame": {"x":447,"y":787,"w":44,"h":44}, + "frame": {"x":147,"y":735,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":4,"w":44,"h":44}, @@ -242,7 +242,7 @@ }, "sprites/belt/built/right_2.png": { - "frame": {"x":195,"y":715,"w":44,"h":44}, + "frame": {"x":298,"y":699,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":4,"w":44,"h":44}, @@ -250,7 +250,7 @@ }, "sprites/belt/built/right_3.png": { - "frame": {"x":243,"y":739,"w":44,"h":44}, + "frame": {"x":246,"y":736,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":4,"w":44,"h":44}, @@ -258,7 +258,7 @@ }, "sprites/belt/built/right_4.png": { - "frame": {"x":291,"y":784,"w":44,"h":44}, + "frame": {"x":195,"y":780,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":4,"w":44,"h":44}, @@ -266,7 +266,7 @@ }, "sprites/belt/built/right_5.png": { - "frame": {"x":339,"y":798,"w":44,"h":44}, + "frame": {"x":147,"y":783,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":4,"w":44,"h":44}, @@ -274,7 +274,7 @@ }, "sprites/belt/built/right_6.png": { - "frame": {"x":387,"y":809,"w":44,"h":44}, + "frame": {"x":99,"y":814,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":4,"w":44,"h":44}, @@ -282,7 +282,7 @@ }, "sprites/belt/built/right_7.png": { - "frame": {"x":435,"y":835,"w":44,"h":44}, + "frame": {"x":51,"y":822,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":4,"w":44,"h":44}, @@ -290,7 +290,7 @@ }, "sprites/belt/built/right_8.png": { - "frame": {"x":3,"y":652,"w":44,"h":44}, + "frame": {"x":3,"y":859,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":4,"w":44,"h":44}, @@ -298,7 +298,7 @@ }, "sprites/belt/built/right_9.png": { - "frame": {"x":51,"y":652,"w":44,"h":44}, + "frame": {"x":400,"y":704,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":4,"w":44,"h":44}, @@ -306,7 +306,7 @@ }, "sprites/belt/built/right_10.png": { - "frame": {"x":3,"y":604,"w":44,"h":44}, + "frame": {"x":99,"y":766,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":4,"w":44,"h":44}, @@ -314,7 +314,7 @@ }, "sprites/belt/built/right_11.png": { - "frame": {"x":51,"y":604,"w":44,"h":44}, + "frame": {"x":51,"y":774,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":4,"w":44,"h":44}, @@ -322,7 +322,7 @@ }, "sprites/belt/built/right_12.png": { - "frame": {"x":99,"y":647,"w":44,"h":44}, + "frame": {"x":3,"y":811,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":4,"w":44,"h":44}, @@ -330,7 +330,7 @@ }, "sprites/belt/built/right_13.png": { - "frame": {"x":147,"y":685,"w":44,"h":44}, + "frame": {"x":352,"y":666,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":4,"w":44,"h":44}, @@ -338,7 +338,7 @@ }, "sprites/blueprints/belt_left.png": { - "frame": {"x":99,"y":695,"w":44,"h":44}, + "frame": {"x":346,"y":714,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":4,"w":44,"h":44}, @@ -346,7 +346,7 @@ }, "sprites/blueprints/belt_right.png": { - "frame": {"x":147,"y":733,"w":44,"h":44}, + "frame": {"x":294,"y":747,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":4,"w":44,"h":44}, @@ -354,7 +354,7 @@ }, "sprites/blueprints/belt_top.png": { - "frame": {"x":223,"y":839,"w":40,"h":48}, + "frame": {"x":95,"y":914,"w":40,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":40,"h":48}, @@ -362,7 +362,7 @@ }, "sprites/blueprints/constant_signal.png": { - "frame": {"x":426,"y":411,"w":36,"h":43}, + "frame": {"x":329,"y":390,"w":36,"h":43}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":6,"y":0,"w":36,"h":43}, @@ -378,12 +378,20 @@ }, "sprites/blueprints/cutter.png": { - "frame": {"x":95,"y":296,"w":87,"h":48}, + "frame": {"x":187,"y":315,"w":87,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":87,"h":48}, "sourceSize": {"w":96,"h":48} }, +"sprites/blueprints/display.png": +{ + "frame": {"x":106,"y":620,"w":44,"h":46}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":44,"h":46}, + "sourceSize": {"w":48,"h":48} +}, "sprites/blueprints/filter.png": { "frame": {"x":3,"y":244,"w":91,"h":48}, @@ -394,7 +402,7 @@ }, "sprites/blueprints/lever.png": { - "frame": {"x":267,"y":884,"w":38,"h":40}, + "frame": {"x":470,"y":257,"w":38,"h":40}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":5,"w":38,"h":40}, @@ -402,7 +410,7 @@ }, "sprites/blueprints/logic_gate-not.png": { - "frame": {"x":467,"y":335,"w":42,"h":48}, + "frame": {"x":243,"y":784,"w":42,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":0,"w":42,"h":48}, @@ -410,7 +418,7 @@ }, "sprites/blueprints/logic_gate-or.png": { - "frame": {"x":159,"y":519,"w":48,"h":42}, + "frame": {"x":55,"y":500,"w":48,"h":42}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":42}, @@ -418,7 +426,7 @@ }, "sprites/blueprints/logic_gate-transistor.png": { - "frame": {"x":426,"y":505,"w":35,"h":48}, + "frame": {"x":144,"y":448,"w":35,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":35,"h":48}, @@ -434,7 +442,7 @@ }, "sprites/blueprints/logic_gate.png": { - "frame": {"x":3,"y":504,"w":48,"h":45}, + "frame": {"x":384,"y":566,"w":48,"h":45}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":45}, @@ -442,7 +450,7 @@ }, "sprites/blueprints/miner-chainable.png": { - "frame": {"x":263,"y":597,"w":47,"h":48}, + "frame": {"x":462,"y":345,"w":47,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":47,"h":48}, @@ -450,7 +458,7 @@ }, "sprites/blueprints/miner.png": { - "frame": {"x":314,"y":636,"w":47,"h":48}, + "frame": {"x":211,"y":584,"w":47,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":47,"h":48}, @@ -506,7 +514,7 @@ }, "sprites/blueprints/rotater-fl.png": { - "frame": {"x":274,"y":399,"w":48,"h":48}, + "frame": {"x":285,"y":263,"w":48,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, @@ -514,7 +522,7 @@ }, "sprites/blueprints/rotater.png": { - "frame": {"x":374,"y":411,"w":48,"h":48}, + "frame": {"x":460,"y":397,"w":48,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, @@ -522,7 +530,7 @@ }, "sprites/blueprints/splitter-compact-inverse.png": { - "frame": {"x":274,"y":451,"w":48,"h":48}, + "frame": {"x":183,"y":367,"w":48,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, @@ -530,7 +538,7 @@ }, "sprites/blueprints/splitter-compact.png": { - "frame": {"x":3,"y":553,"w":47,"h":47}, + "frame": {"x":364,"y":615,"w":47,"h":47}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":47,"h":47}, @@ -538,7 +546,7 @@ }, "sprites/blueprints/splitter.png": { - "frame": {"x":186,"y":315,"w":87,"h":48}, + "frame": {"x":278,"y":338,"w":87,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":87,"h":48}, @@ -546,7 +554,7 @@ }, "sprites/blueprints/stacker.png": { - "frame": {"x":374,"y":307,"w":89,"h":48}, + "frame": {"x":369,"y":307,"w":89,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":0,"w":89,"h":48}, @@ -554,7 +562,7 @@ }, "sprites/blueprints/trash-storage.png": { - "frame": {"x":285,"y":299,"w":85,"h":96}, + "frame": {"x":94,"y":348,"w":85,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":6,"y":0,"w":85,"h":96}, @@ -562,7 +570,7 @@ }, "sprites/blueprints/trash.png": { - "frame": {"x":374,"y":463,"w":48,"h":48}, + "frame": {"x":277,"y":390,"w":48,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, @@ -570,7 +578,7 @@ }, "sprites/blueprints/underground_belt_entry-tier2.png": { - "frame": {"x":107,"y":504,"w":48,"h":43}, + "frame": {"x":3,"y":500,"w":48,"h":43}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":5,"w":48,"h":43}, @@ -578,7 +586,7 @@ }, "sprites/blueprints/underground_belt_entry.png": { - "frame": {"x":211,"y":519,"w":48,"h":38}, + "frame": {"x":159,"y":523,"w":48,"h":38}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":10,"w":48,"h":38}, @@ -586,7 +594,7 @@ }, "sprites/blueprints/underground_belt_exit-tier2.png": { - "frame": {"x":263,"y":555,"w":48,"h":38}, + "frame": {"x":211,"y":542,"w":48,"h":38}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":38}, @@ -594,7 +602,7 @@ }, "sprites/blueprints/underground_belt_exit.png": { - "frame": {"x":315,"y":594,"w":48,"h":38}, + "frame": {"x":263,"y":546,"w":48,"h":38}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":38}, @@ -602,7 +610,7 @@ }, "sprites/blueprints/wire-cross.png": { - "frame": {"x":374,"y":515,"w":48,"h":48}, + "frame": {"x":183,"y":419,"w":48,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, @@ -610,7 +618,7 @@ }, "sprites/blueprints/wire-split.png": { - "frame": {"x":285,"y":263,"w":48,"h":28}, + "frame": {"x":315,"y":567,"w":48,"h":28}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":20,"w":48,"h":28}, @@ -618,7 +626,7 @@ }, "sprites/blueprints/wire-turn.png": { - "frame": {"x":479,"y":187,"w":28,"h":28}, + "frame": {"x":479,"y":129,"w":28,"h":28}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":20,"y":20,"w":28,"h":28}, @@ -626,15 +634,23 @@ }, "sprites/blueprints/wire.png": { - "frame": {"x":500,"y":64,"w":8,"h":48}, + "frame": {"x":357,"y":286,"w":8,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":20,"y":0,"w":8,"h":48}, "sourceSize": {"w":48,"h":48} }, +"sprites/blueprints/wire_tunnel-coating.png": +{ + "frame": {"x":235,"y":458,"w":13,"h":47}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":17,"y":0,"w":13,"h":47}, + "sourceSize": {"w":48,"h":48} +}, "sprites/blueprints/wire_tunnel.png": { - "frame": {"x":315,"y":543,"w":48,"h":47}, + "frame": {"x":384,"y":515,"w":48,"h":47}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":47}, @@ -642,7 +658,7 @@ }, "sprites/buildings/belt_left.png": { - "frame": {"x":326,"y":399,"w":44,"h":44}, + "frame": {"x":3,"y":667,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":4,"w":44,"h":44}, @@ -650,7 +666,7 @@ }, "sprites/buildings/belt_right.png": { - "frame": {"x":399,"y":761,"w":44,"h":44}, + "frame": {"x":198,"y":732,"w":44,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":4,"w":44,"h":44}, @@ -658,7 +674,7 @@ }, "sprites/buildings/belt_top.png": { - "frame": {"x":466,"y":439,"w":40,"h":48}, + "frame": {"x":415,"y":463,"w":40,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":40,"h":48}, @@ -666,7 +682,7 @@ }, "sprites/buildings/constant_signal.png": { - "frame": {"x":426,"y":458,"w":36,"h":43}, + "frame": {"x":235,"y":411,"w":36,"h":43}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":6,"y":0,"w":36,"h":43}, @@ -682,12 +698,20 @@ }, "sprites/buildings/cutter.png": { - "frame": {"x":3,"y":348,"w":87,"h":48}, + "frame": {"x":369,"y":359,"w":87,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":87,"h":48}, "sourceSize": {"w":96,"h":48} }, +"sprites/buildings/display.png": +{ + "frame": {"x":54,"y":628,"w":44,"h":46}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":44,"h":46}, + "sourceSize": {"w":48,"h":48} +}, "sprites/buildings/filter.png": { "frame": {"x":191,"y":263,"w":90,"h":48}, @@ -706,7 +730,7 @@ }, "sprites/buildings/lever.png": { - "frame": {"x":309,"y":898,"w":38,"h":40}, + "frame": {"x":470,"y":301,"w":38,"h":40}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":5,"w":38,"h":40}, @@ -714,7 +738,7 @@ }, "sprites/buildings/logic_gate-not.png": { - "frame": {"x":466,"y":387,"w":43,"h":48}, + "frame": {"x":466,"y":653,"w":43,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":0,"w":43,"h":48}, @@ -722,7 +746,7 @@ }, "sprites/buildings/logic_gate-or.png": { - "frame": {"x":107,"y":551,"w":48,"h":42}, + "frame": {"x":107,"y":500,"w":48,"h":42}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":42}, @@ -730,7 +754,7 @@ }, "sprites/buildings/logic_gate-transistor.png": { - "frame": {"x":426,"y":557,"w":35,"h":48}, + "frame": {"x":421,"y":411,"w":35,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":35,"h":48}, @@ -738,7 +762,7 @@ }, "sprites/buildings/logic_gate-xor.png": { - "frame": {"x":3,"y":400,"w":48,"h":48}, + "frame": {"x":92,"y":448,"w":48,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, @@ -746,7 +770,7 @@ }, "sprites/buildings/logic_gate.png": { - "frame": {"x":55,"y":504,"w":48,"h":45}, + "frame": {"x":436,"y":604,"w":48,"h":45}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":45}, @@ -754,7 +778,7 @@ }, "sprites/buildings/miner-chainable.png": { - "frame": {"x":365,"y":650,"w":47,"h":48}, + "frame": {"x":262,"y":588,"w":47,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":47,"h":48}, @@ -762,7 +786,7 @@ }, "sprites/buildings/miner.png": { - "frame": {"x":460,"y":687,"w":47,"h":48}, + "frame": {"x":313,"y":599,"w":47,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":47,"h":48}, @@ -770,7 +794,7 @@ }, "sprites/buildings/mixer.png": { - "frame": {"x":374,"y":359,"w":88,"h":48}, + "frame": {"x":3,"y":296,"w":88,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":88,"h":48}, @@ -802,7 +826,7 @@ }, "sprites/buildings/painter.png": { - "frame": {"x":375,"y":255,"w":96,"h":48}, + "frame": {"x":370,"y":255,"w":96,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":48}, @@ -810,7 +834,7 @@ }, "sprites/buildings/rotater-ccw.png": { - "frame": {"x":55,"y":400,"w":48,"h":48}, + "frame": {"x":369,"y":411,"w":48,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, @@ -818,7 +842,7 @@ }, "sprites/buildings/rotater-fl.png": { - "frame": {"x":107,"y":400,"w":48,"h":48}, + "frame": {"x":460,"y":449,"w":48,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, @@ -826,7 +850,7 @@ }, "sprites/buildings/rotater.png": { - "frame": {"x":3,"y":452,"w":48,"h":48}, + "frame": {"x":275,"y":442,"w":48,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, @@ -834,7 +858,7 @@ }, "sprites/buildings/splitter-compact-inverse.png": { - "frame": {"x":367,"y":567,"w":48,"h":47}, + "frame": {"x":436,"y":553,"w":48,"h":47}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":47}, @@ -842,7 +866,7 @@ }, "sprites/buildings/splitter-compact.png": { - "frame": {"x":54,"y":553,"w":47,"h":47}, + "frame": {"x":415,"y":653,"w":47,"h":47}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":47,"h":47}, @@ -850,7 +874,7 @@ }, "sprites/buildings/splitter.png": { - "frame": {"x":94,"y":348,"w":87,"h":48}, + "frame": {"x":3,"y":348,"w":87,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":87,"h":48}, @@ -858,7 +882,7 @@ }, "sprites/buildings/stacker.png": { - "frame": {"x":3,"y":296,"w":88,"h":48}, + "frame": {"x":95,"y":296,"w":88,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":88,"h":48}, @@ -866,7 +890,7 @@ }, "sprites/buildings/trash-storage.png": { - "frame": {"x":185,"y":367,"w":85,"h":96}, + "frame": {"x":3,"y":400,"w":85,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":6,"y":0,"w":85,"h":96}, @@ -874,7 +898,7 @@ }, "sprites/buildings/trash.png": { - "frame": {"x":55,"y":452,"w":48,"h":48}, + "frame": {"x":183,"y":471,"w":48,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, @@ -882,7 +906,7 @@ }, "sprites/buildings/underground_belt_entry-tier2.png": { - "frame": {"x":156,"y":597,"w":47,"h":42}, + "frame": {"x":3,"y":579,"w":47,"h":42}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":6,"w":47,"h":42}, @@ -890,7 +914,7 @@ }, "sprites/buildings/underground_belt_entry.png": { - "frame": {"x":207,"y":625,"w":47,"h":38}, + "frame": {"x":3,"y":625,"w":47,"h":38}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":10,"w":47,"h":38}, @@ -898,7 +922,7 @@ }, "sprites/buildings/underground_belt_exit-tier2.png": { - "frame": {"x":156,"y":643,"w":47,"h":38}, + "frame": {"x":106,"y":578,"w":47,"h":38}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":47,"h":38}, @@ -906,7 +930,7 @@ }, "sprites/buildings/underground_belt_exit.png": { - "frame": {"x":258,"y":649,"w":47,"h":38}, + "frame": {"x":157,"y":597,"w":47,"h":38}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":47,"h":38}, @@ -914,7 +938,7 @@ }, "sprites/buildings/wire-cross.png": { - "frame": {"x":107,"y":452,"w":48,"h":48}, + "frame": {"x":268,"y":494,"w":48,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, @@ -922,7 +946,7 @@ }, "sprites/buildings/wire-split.png": { - "frame": {"x":367,"y":618,"w":48,"h":28}, + "frame": {"x":55,"y":546,"w":48,"h":28}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":20,"w":48,"h":28}, @@ -930,7 +954,7 @@ }, "sprites/buildings/wire-turn.png": { - "frame": {"x":343,"y":195,"w":28,"h":28}, + "frame": {"x":479,"y":161,"w":28,"h":28}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":20,"y":20,"w":28,"h":28}, @@ -938,15 +962,23 @@ }, "sprites/buildings/wire.png": { - "frame": {"x":487,"y":79,"w":8,"h":48}, + "frame": {"x":327,"y":457,"w":8,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":20,"y":0,"w":8,"h":48}, "sourceSize": {"w":48,"h":48} }, +"sprites/buildings/wire_tunnel-coating.png": +{ + "frame": {"x":252,"y":492,"w":12,"h":46}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":18,"y":1,"w":12,"h":46}, + "sourceSize": {"w":48,"h":48} +}, "sprites/buildings/wire_tunnel.png": { - "frame": {"x":105,"y":597,"w":47,"h":46}, + "frame": {"x":55,"y":578,"w":47,"h":46}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":47,"h":46}, @@ -954,7 +986,7 @@ }, "sprites/debug/acceptor_slot.png": { - "frame": {"x":504,"y":30,"w":4,"h":4}, + "frame": {"x":379,"y":55,"w":4,"h":4}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":4,"h":4}, @@ -962,7 +994,7 @@ }, "sprites/debug/ejector_slot.png": { - "frame": {"x":504,"y":38,"w":4,"h":4}, + "frame": {"x":379,"y":63,"w":4,"h":4}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":4,"h":4}, @@ -970,15 +1002,15 @@ }, "sprites/misc/hub_direction_indicator.png": { - "frame": {"x":479,"y":131,"w":8,"h":8}, + "frame": {"x":487,"y":30,"w":16,"h":16}, "rotated": false, "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":8,"h":8}, - "sourceSize": {"w":8,"h":8} + "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, + "sourceSize": {"w":16,"h":16} }, "sprites/misc/slot_bad_arrow.png": { - "frame": {"x":487,"y":30,"w":13,"h":13}, + "frame": {"x":252,"y":458,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -986,7 +1018,7 @@ }, "sprites/misc/slot_good_arrow.png": { - "frame": {"x":487,"y":47,"w":13,"h":13}, + "frame": {"x":252,"y":475,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -994,7 +1026,7 @@ }, "sprites/misc/storage_overlay.png": { - "frame": {"x":479,"y":168,"w":30,"h":15}, + "frame": {"x":479,"y":110,"w":30,"h":15}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":30,"h":15}, @@ -1002,31 +1034,87 @@ }, "sprites/misc/waypoint.png": { - "frame": {"x":479,"y":143,"w":8,"h":8}, + "frame": {"x":349,"y":437,"w":14,"h":16}, "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":8,"h":8}, - "sourceSize": {"w":8,"h":8} + "trimmed": true, + "spriteSourceSize": {"x":1,"y":0,"w":14,"h":16}, + "sourceSize": {"w":16,"h":16} }, "sprites/wires/boolean_false.png": { - "frame": {"x":487,"y":64,"w":9,"h":11}, + "frame": {"x":235,"y":509,"w":12,"h":15}, "rotated": false, "trimmed": true, - "spriteSourceSize": {"x":4,"y":3,"w":9,"h":11}, + "spriteSourceSize": {"x":2,"y":1,"w":12,"h":15}, "sourceSize": {"w":16,"h":16} }, "sprites/wires/boolean_true.png": { - "frame": {"x":499,"y":283,"w":7,"h":12}, + "frame": {"x":357,"y":267,"w":9,"h":15}, "rotated": false, "trimmed": true, - "spriteSourceSize": {"x":4,"y":2,"w":7,"h":12}, + "spriteSourceSize": {"x":3,"y":1,"w":9,"h":15}, + "sourceSize": {"w":16,"h":16} +}, +"sprites/wires/display/blue.png": +{ + "frame": {"x":487,"y":50,"w":16,"h":16}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, + "sourceSize": {"w":16,"h":16} +}, +"sprites/wires/display/cyan.png": +{ + "frame": {"x":487,"y":70,"w":16,"h":16}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, + "sourceSize": {"w":16,"h":16} +}, +"sprites/wires/display/green.png": +{ + "frame": {"x":487,"y":90,"w":16,"h":16}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, + "sourceSize": {"w":16,"h":16} +}, +"sprites/wires/display/purple.png": +{ + "frame": {"x":337,"y":267,"w":16,"h":16}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, + "sourceSize": {"w":16,"h":16} +}, +"sprites/wires/display/red.png": +{ + "frame": {"x":337,"y":287,"w":16,"h":16}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, + "sourceSize": {"w":16,"h":16} +}, +"sprites/wires/display/white.png": +{ + "frame": {"x":337,"y":307,"w":16,"h":16}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, + "sourceSize": {"w":16,"h":16} +}, +"sprites/wires/display/yellow.png": +{ + "frame": {"x":278,"y":315,"w":16,"h":16}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, "sourceSize": {"w":16,"h":16} }, "sprites/wires/lever_on.png": { - "frame": {"x":3,"y":942,"w":38,"h":40}, + "frame": {"x":235,"y":367,"w":38,"h":40}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":5,"w":38,"h":40}, @@ -1034,7 +1122,7 @@ }, "sprites/wires/logical_acceptor.png": { - "frame": {"x":343,"y":259,"w":23,"h":36}, + "frame": {"x":343,"y":227,"w":23,"h":36}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":13,"y":0,"w":23,"h":36}, @@ -1048,6 +1136,22 @@ "spriteSourceSize": {"x":14,"y":0,"w":22,"h":23}, "sourceSize": {"w":48,"h":48} }, +"sprites/wires/network_conflict.png": +{ + "frame": {"x":298,"y":315,"w":16,"h":16}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, + "sourceSize": {"w":16,"h":16} +}, +"sprites/wires/network_empty.png": +{ + "frame": {"x":318,"y":315,"w":15,"h":16}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":0,"w":15,"h":16}, + "sourceSize": {"w":16,"h":16} +}, "sprites/wires/overlay_tile.png": { "frame": {"x":343,"y":159,"w":32,"h":32}, @@ -1058,7 +1162,7 @@ }, "sprites/wires/sets/color_cross.png": { - "frame": {"x":159,"y":467,"w":48,"h":48}, + "frame": {"x":363,"y":463,"w":48,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, @@ -1066,7 +1170,7 @@ }, "sprites/wires/sets/color_forward.png": { - "frame": {"x":499,"y":116,"w":8,"h":48}, + "frame": {"x":339,"y":457,"w":8,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":20,"y":0,"w":8,"h":48}, @@ -1074,7 +1178,7 @@ }, "sprites/wires/sets/color_split.png": { - "frame": {"x":211,"y":561,"w":48,"h":28}, + "frame": {"x":3,"y":547,"w":48,"h":28}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":20,"w":48,"h":28}, @@ -1082,7 +1186,7 @@ }, "sprites/wires/sets/color_turn.png": { - "frame": {"x":475,"y":219,"w":28,"h":28}, + "frame": {"x":479,"y":193,"w":28,"h":28}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":20,"y":20,"w":28,"h":28}, @@ -1090,7 +1194,7 @@ }, "sprites/wires/sets/conflict_cross.png": { - "frame": {"x":211,"y":467,"w":48,"h":48}, + "frame": {"x":459,"y":501,"w":48,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, @@ -1098,7 +1202,7 @@ }, "sprites/wires/sets/conflict_forward.png": { - "frame": {"x":475,"y":283,"w":8,"h":48}, + "frame": {"x":351,"y":457,"w":8,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":20,"y":0,"w":8,"h":48}, @@ -1106,7 +1210,7 @@ }, "sprites/wires/sets/conflict_split.png": { - "frame": {"x":159,"y":565,"w":48,"h":28}, + "frame": {"x":107,"y":546,"w":48,"h":28}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":20,"w":48,"h":28}, @@ -1114,7 +1218,7 @@ }, "sprites/wires/sets/conflict_turn.png": { - "frame": {"x":475,"y":251,"w":28,"h":28}, + "frame": {"x":343,"y":195,"w":28,"h":28}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":20,"y":20,"w":28,"h":28}, @@ -1122,7 +1226,7 @@ }, "sprites/wires/sets/regular_cross.png": { - "frame": {"x":107,"y":452,"w":48,"h":48}, + "frame": {"x":268,"y":494,"w":48,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, @@ -1130,7 +1234,7 @@ }, "sprites/wires/sets/regular_forward.png": { - "frame": {"x":487,"y":79,"w":8,"h":48}, + "frame": {"x":327,"y":457,"w":8,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":20,"y":0,"w":8,"h":48}, @@ -1138,7 +1242,7 @@ }, "sprites/wires/sets/regular_split.png": { - "frame": {"x":367,"y":618,"w":48,"h":28}, + "frame": {"x":55,"y":546,"w":48,"h":28}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":20,"w":48,"h":28}, @@ -1146,7 +1250,7 @@ }, "sprites/wires/sets/regular_turn.png": { - "frame": {"x":343,"y":195,"w":28,"h":28}, + "frame": {"x":479,"y":161,"w":28,"h":28}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":20,"y":20,"w":28,"h":28}, @@ -1154,7 +1258,7 @@ }, "sprites/wires/sets/shape_cross.png": { - "frame": {"x":263,"y":503,"w":48,"h":48}, + "frame": {"x":332,"y":515,"w":48,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, @@ -1162,7 +1266,7 @@ }, "sprites/wires/sets/shape_forward.png": { - "frame": {"x":487,"y":283,"w":8,"h":48}, + "frame": {"x":320,"y":509,"w":8,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":20,"y":0,"w":8,"h":48}, @@ -1170,7 +1274,7 @@ }, "sprites/wires/sets/shape_split.png": { - "frame": {"x":211,"y":593,"w":48,"h":28}, + "frame": {"x":159,"y":565,"w":48,"h":28}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":20,"w":48,"h":28}, @@ -1178,11 +1282,19 @@ }, "sprites/wires/sets/shape_turn.png": { - "frame": {"x":343,"y":227,"w":28,"h":28}, + "frame": {"x":475,"y":225,"w":28,"h":28}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":20,"y":20,"w":28,"h":28}, "sourceSize": {"w":48,"h":48} +}, +"sprites/wires/wires_preview.png": +{ + "frame": {"x":329,"y":437,"w":16,"h":16}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, + "sourceSize": {"w":16,"h":16} }}, "meta": { "app": "https://www.codeandweb.com/texturepacker", @@ -1191,6 +1303,6 @@ "format": "RGBA8888", "size": {"w":512,"h":1024}, "scale": "0.25", - "smartupdate": "$TexturePacker:SmartUpdate:495f928ed9092c817f98f68825f1d4ae:98823415164aea829cee3223195c589c:908b89f5ca8ff73e331a35a3b14d0604$" + "smartupdate": "$TexturePacker:SmartUpdate:876f0711b44fa7bbab8d2539e9651766:ff01f850e086ef31c114b036c3a32e6d:908b89f5ca8ff73e331a35a3b14d0604$" } } diff --git a/res_built/atlas/atlas0_lq.png b/res_built/atlas/atlas0_lq.png index 2013c80c..c6b4be55 100644 Binary files a/res_built/atlas/atlas0_lq.png and b/res_built/atlas/atlas0_lq.png differ diff --git a/res_built/atlas/atlas0_mq.json b/res_built/atlas/atlas0_mq.json index 97d9b26c..89809854 100644 --- a/res_built/atlas/atlas0_mq.json +++ b/res_built/atlas/atlas0_mq.json @@ -2,7 +2,7 @@ "sprites/belt/built/forward_0.png": { - "frame": {"x":372,"y":1385,"w":78,"h":96}, + "frame": {"x":943,"y":816,"w":78,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":0,"w":78,"h":96}, @@ -10,7 +10,7 @@ }, "sprites/belt/built/forward_1.png": { - "frame": {"x":276,"y":1429,"w":78,"h":96}, + "frame": {"x":439,"y":803,"w":78,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":0,"w":78,"h":96}, @@ -18,7 +18,7 @@ }, "sprites/belt/built/forward_2.png": { - "frame": {"x":942,"y":1324,"w":78,"h":96}, + "frame": {"x":850,"y":1402,"w":78,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":0,"w":78,"h":96}, @@ -26,7 +26,7 @@ }, "sprites/belt/built/forward_3.png": { - "frame": {"x":759,"y":1354,"w":78,"h":96}, + "frame": {"x":932,"y":1402,"w":78,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":0,"w":78,"h":96}, @@ -34,7 +34,7 @@ }, "sprites/belt/built/forward_4.png": { - "frame": {"x":655,"y":1362,"w":78,"h":96}, + "frame": {"x":751,"y":1431,"w":78,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":0,"w":78,"h":96}, @@ -42,7 +42,7 @@ }, "sprites/belt/built/forward_5.png": { - "frame": {"x":555,"y":1459,"w":78,"h":96}, + "frame": {"x":650,"y":1480,"w":78,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":0,"w":78,"h":96}, @@ -50,7 +50,7 @@ }, "sprites/belt/built/forward_6.png": { - "frame": {"x":454,"y":1469,"w":78,"h":96}, + "frame": {"x":551,"y":1494,"w":78,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":0,"w":78,"h":96}, @@ -58,7 +58,7 @@ }, "sprites/belt/built/forward_7.png": { - "frame": {"x":358,"y":1485,"w":78,"h":96}, + "frame": {"x":454,"y":1522,"w":78,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":0,"w":78,"h":96}, @@ -66,7 +66,7 @@ }, "sprites/belt/built/forward_8.png": { - "frame": {"x":267,"y":1529,"w":78,"h":96}, + "frame": {"x":358,"y":1553,"w":78,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":0,"w":78,"h":96}, @@ -74,7 +74,7 @@ }, "sprites/belt/built/forward_9.png": { - "frame": {"x":176,"y":1598,"w":78,"h":96}, + "frame": {"x":267,"y":1554,"w":78,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":0,"w":78,"h":96}, @@ -82,7 +82,7 @@ }, "sprites/belt/built/forward_10.png": { - "frame": {"x":185,"y":1498,"w":78,"h":96}, + "frame": {"x":276,"y":1454,"w":78,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":0,"w":78,"h":96}, @@ -90,7 +90,7 @@ }, "sprites/belt/built/forward_11.png": { - "frame": {"x":94,"y":1530,"w":78,"h":96}, + "frame": {"x":185,"y":1499,"w":78,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":0,"w":78,"h":96}, @@ -98,7 +98,7 @@ }, "sprites/belt/built/forward_12.png": { - "frame": {"x":3,"y":1607,"w":78,"h":96}, + "frame": {"x":94,"y":1514,"w":78,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":0,"w":78,"h":96}, @@ -106,7 +106,7 @@ }, "sprites/belt/built/forward_13.png": { - "frame": {"x":860,"y":1324,"w":78,"h":96}, + "frame": {"x":3,"y":1585,"w":78,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":0,"w":78,"h":96}, @@ -114,7 +114,7 @@ }, "sprites/belt/built/left_0.png": { - "frame": {"x":496,"y":1096,"w":87,"h":87}, + "frame": {"x":196,"y":1135,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":9,"w":87,"h":87}, @@ -122,7 +122,7 @@ }, "sprites/belt/built/left_1.png": { - "frame": {"x":395,"y":1112,"w":87,"h":87}, + "frame": {"x":99,"y":1150,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":9,"w":87,"h":87}, @@ -130,7 +130,7 @@ }, "sprites/belt/built/left_2.png": { - "frame": {"x":793,"y":990,"w":87,"h":87}, + "frame": {"x":287,"y":1181,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":9,"w":87,"h":87}, @@ -138,7 +138,7 @@ }, "sprites/belt/built/left_3.png": { - "frame": {"x":884,"y":1051,"w":87,"h":87}, + "frame": {"x":190,"y":1226,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":9,"w":87,"h":87}, @@ -146,7 +146,7 @@ }, "sprites/belt/built/left_4.png": { - "frame": {"x":793,"y":1081,"w":87,"h":87}, + "frame": {"x":94,"y":1241,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":9,"w":87,"h":87}, @@ -154,7 +154,7 @@ }, "sprites/belt/built/left_5.png": { - "frame": {"x":691,"y":1089,"w":87,"h":87}, + "frame": {"x":3,"y":1312,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":9,"w":87,"h":87}, @@ -162,7 +162,7 @@ }, "sprites/belt/built/left_6.png": { - "frame": {"x":587,"y":1177,"w":87,"h":87}, + "frame": {"x":674,"y":1207,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":9,"w":87,"h":87}, @@ -170,7 +170,7 @@ }, "sprites/belt/built/left_7.png": { - "frame": {"x":486,"y":1187,"w":87,"h":87}, + "frame": {"x":577,"y":1217,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":9,"w":87,"h":87}, @@ -178,7 +178,7 @@ }, "sprites/belt/built/left_8.png": { - "frame": {"x":386,"y":1203,"w":87,"h":87}, + "frame": {"x":478,"y":1246,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":9,"w":87,"h":87}, @@ -186,7 +186,7 @@ }, "sprites/belt/built/left_9.png": { - "frame": {"x":287,"y":1247,"w":87,"h":87}, + "frame": {"x":378,"y":1271,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":9,"w":87,"h":87}, @@ -194,7 +194,7 @@ }, "sprites/belt/built/left_10.png": { - "frame": {"x":295,"y":1156,"w":87,"h":87}, + "frame": {"x":3,"y":1221,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":9,"w":87,"h":87}, @@ -202,7 +202,7 @@ }, "sprites/belt/built/left_11.png": { - "frame": {"x":196,"y":1225,"w":87,"h":87}, + "frame": {"x":583,"y":1126,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":9,"w":87,"h":87}, @@ -210,7 +210,7 @@ }, "sprites/belt/built/left_12.png": { - "frame": {"x":99,"y":1257,"w":87,"h":87}, + "frame": {"x":486,"y":1155,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":9,"w":87,"h":87}, @@ -218,7 +218,7 @@ }, "sprites/belt/built/left_13.png": { - "frame": {"x":3,"y":1334,"w":87,"h":87}, + "frame": {"x":387,"y":1180,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":9,"w":87,"h":87}, @@ -226,7 +226,7 @@ }, "sprites/belt/built/right_0.png": { - "frame": {"x":190,"y":1316,"w":87,"h":87}, + "frame": {"x":281,"y":1272,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":9,"w":87,"h":87}, @@ -234,7 +234,7 @@ }, "sprites/belt/built/right_1.png": { - "frame": {"x":94,"y":1348,"w":87,"h":87}, + "frame": {"x":185,"y":1317,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":9,"w":87,"h":87}, @@ -242,7 +242,7 @@ }, "sprites/belt/built/right_2.png": { - "frame": {"x":577,"y":1268,"w":87,"h":87}, + "frame": {"x":569,"y":1308,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":9,"w":87,"h":87}, @@ -250,7 +250,7 @@ }, "sprites/belt/built/right_3.png": { - "frame": {"x":477,"y":1278,"w":87,"h":87}, + "frame": {"x":469,"y":1337,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":9,"w":87,"h":87}, @@ -258,7 +258,7 @@ }, "sprites/belt/built/right_4.png": { - "frame": {"x":378,"y":1294,"w":87,"h":87}, + "frame": {"x":372,"y":1362,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":9,"w":87,"h":87}, @@ -266,7 +266,7 @@ }, "sprites/belt/built/right_5.png": { - "frame": {"x":281,"y":1338,"w":87,"h":87}, + "frame": {"x":276,"y":1363,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":9,"w":87,"h":87}, @@ -274,7 +274,7 @@ }, "sprites/belt/built/right_6.png": { - "frame": {"x":185,"y":1407,"w":87,"h":87}, + "frame": {"x":185,"y":1408,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":9,"w":87,"h":87}, @@ -282,7 +282,7 @@ }, "sprites/belt/built/right_7.png": { - "frame": {"x":94,"y":1439,"w":87,"h":87}, + "frame": {"x":94,"y":1423,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":9,"w":87,"h":87}, @@ -290,7 +290,7 @@ }, "sprites/belt/built/right_8.png": { - "frame": {"x":3,"y":1516,"w":87,"h":87}, + "frame": {"x":3,"y":1494,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":9,"w":87,"h":87}, @@ -298,7 +298,7 @@ }, "sprites/belt/built/right_9.png": { - "frame": {"x":873,"y":1233,"w":87,"h":87}, + "frame": {"x":856,"y":1311,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":9,"w":87,"h":87}, @@ -306,7 +306,7 @@ }, "sprites/belt/built/right_10.png": { - "frame": {"x":3,"y":1425,"w":87,"h":87}, + "frame": {"x":94,"y":1332,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":9,"w":87,"h":87}, @@ -314,7 +314,7 @@ }, "sprites/belt/built/right_11.png": { - "frame": {"x":884,"y":1142,"w":87,"h":87}, + "frame": {"x":3,"y":1403,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":9,"w":87,"h":87}, @@ -322,7 +322,7 @@ }, "sprites/belt/built/right_12.png": { - "frame": {"x":782,"y":1172,"w":87,"h":87}, + "frame": {"x":765,"y":1249,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":9,"w":87,"h":87}, @@ -330,7 +330,7 @@ }, "sprites/belt/built/right_13.png": { - "frame": {"x":678,"y":1180,"w":87,"h":87}, + "frame": {"x":668,"y":1298,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":9,"w":87,"h":87}, @@ -338,7 +338,7 @@ }, "sprites/blueprints/belt_left.png": { - "frame": {"x":769,"y":1263,"w":87,"h":87}, + "frame": {"x":759,"y":1340,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":9,"w":87,"h":87}, @@ -346,7 +346,7 @@ }, "sprites/blueprints/belt_right.png": { - "frame": {"x":668,"y":1271,"w":87,"h":87}, + "frame": {"x":660,"y":1389,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":9,"w":87,"h":87}, @@ -354,7 +354,7 @@ }, "sprites/blueprints/belt_top.png": { - "frame": {"x":85,"y":1630,"w":78,"h":96}, + "frame": {"x":176,"y":1599,"w":78,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":0,"w":78,"h":96}, @@ -378,12 +378,20 @@ }, "sprites/blueprints/cutter.png": { - "frame": {"x":752,"y":594,"w":172,"h":96}, + "frame": {"x":745,"y":594,"w":172,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":11,"y":0,"w":172,"h":96}, "sourceSize": {"w":192,"h":96} }, +"sprites/blueprints/display.png": +{ + "frame": {"x":560,"y":1399,"w":86,"h":91}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":5,"y":5,"w":86,"h":91}, + "sourceSize": {"w":96,"h":96} +}, "sprites/blueprints/filter.png": { "frame": {"x":569,"y":303,"w":180,"h":96}, @@ -394,7 +402,7 @@ }, "sprites/blueprints/lever.png": { - "frame": {"x":449,"y":703,"w":74,"h":78}, + "frame": {"x":946,"y":734,"w":74,"h":78}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":11,"y":11,"w":74,"h":78}, @@ -402,7 +410,7 @@ }, "sprites/blueprints/logic_gate-not.png": { - "frame": {"x":568,"y":1359,"w":83,"h":96}, + "frame": {"x":367,"y":1453,"w":83,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":7,"y":0,"w":83,"h":96}, @@ -410,7 +418,7 @@ }, "sprites/blueprints/logic_gate-or.png": { - "frame": {"x":203,"y":866,"w":96,"h":82}, + "frame": {"x":521,"y":991,"w":96,"h":82}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":82}, @@ -418,7 +426,7 @@ }, "sprites/blueprints/logic_gate-transistor.png": { - "frame": {"x":826,"y":890,"w":68,"h":96}, + "frame": {"x":449,"y":703,"w":68,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":68,"h":96}, @@ -426,7 +434,7 @@ }, "sprites/blueprints/logic_gate-xor.png": { - "frame": {"x":903,"y":751,"w":96,"h":96}, + "frame": {"x":3,"y":674,"w":96,"h":96}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":96}, @@ -434,7 +442,7 @@ }, "sprites/blueprints/logic_gate.png": { - "frame": {"x":103,"y":774,"w":96,"h":89}, + "frame": {"x":335,"y":902,"w":96,"h":89}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":89}, @@ -442,7 +450,7 @@ }, "sprites/blueprints/miner-chainable.png": { - "frame": {"x":602,"y":978,"w":92,"h":96}, + "frame": {"x":929,"y":916,"w":92,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":0,"w":92,"h":96}, @@ -450,7 +458,7 @@ }, "sprites/blueprints/miner.png": { - "frame": {"x":500,"y":996,"w":92,"h":96}, + "frame": {"x":100,"y":1050,"w":92,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":0,"w":92,"h":96}, @@ -498,7 +506,7 @@ }, "sprites/blueprints/rotater-ccw.png": { - "frame": {"x":903,"y":851,"w":96,"h":96}, + "frame": {"x":103,"y":674,"w":96,"h":96}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":96}, @@ -506,7 +514,7 @@ }, "sprites/blueprints/rotater-fl.png": { - "frame": {"x":303,"y":890,"w":95,"h":96}, + "frame": {"x":921,"y":1016,"w":95,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":95,"h":96}, @@ -514,7 +522,7 @@ }, "sprites/blueprints/rotater.png": { - "frame": {"x":3,"y":674,"w":96,"h":96}, + "frame": {"x":203,"y":674,"w":96,"h":96}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":96}, @@ -522,7 +530,7 @@ }, "sprites/blueprints/splitter-compact-inverse.png": { - "frame": {"x":503,"y":899,"w":95,"h":93}, + "frame": {"x":820,"y":1152,"w":95,"h":93}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":1,"w":95,"h":93}, @@ -530,7 +538,7 @@ }, "sprites/blueprints/splitter-compact.png": { - "frame": {"x":928,"y":654,"w":93,"h":93}, + "frame": {"x":101,"y":874,"w":93,"h":93}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":1,"w":93,"h":93}, @@ -562,7 +570,7 @@ }, "sprites/blueprints/trash.png": { - "frame": {"x":103,"y":674,"w":96,"h":96}, + "frame": {"x":349,"y":703,"w":96,"h":96}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":96}, @@ -570,7 +578,7 @@ }, "sprites/blueprints/underground_belt_entry-tier2.png": { - "frame": {"x":3,"y":1089,"w":93,"h":84}, + "frame": {"x":198,"y":969,"w":93,"h":84}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":12,"w":93,"h":84}, @@ -578,7 +586,7 @@ }, "sprites/blueprints/underground_belt_entry.png": { - "frame": {"x":3,"y":1177,"w":93,"h":75}, + "frame": {"x":100,"y":971,"w":93,"h":75}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":21,"w":93,"h":75}, @@ -586,7 +594,7 @@ }, "sprites/blueprints/underground_belt_exit-tier2.png": { - "frame": {"x":302,"y":990,"w":94,"h":75}, + "frame": {"x":3,"y":874,"w":94,"h":75}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":0,"w":94,"h":75}, @@ -594,7 +602,7 @@ }, "sprites/blueprints/underground_belt_exit.png": { - "frame": {"x":602,"y":899,"w":93,"h":75}, + "frame": {"x":3,"y":1048,"w":93,"h":75}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":0,"w":93,"h":75}, @@ -602,7 +610,7 @@ }, "sprites/blueprints/wire-cross.png": { - "frame": {"x":203,"y":674,"w":96,"h":96}, + "frame": {"x":521,"y":799,"w":96,"h":96}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":96}, @@ -610,7 +618,7 @@ }, "sprites/blueprints/wire-split.png": { - "frame": {"x":103,"y":867,"w":96,"h":55}, + "frame": {"x":721,"y":990,"w":96,"h":55}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":41,"w":96,"h":55}, @@ -632,9 +640,17 @@ "spriteSourceSize": {"x":41,"y":0,"w":14,"h":96}, "sourceSize": {"w":96,"h":96} }, +"sprites/blueprints/wire_tunnel-coating.png": +{ + "frame": {"x":921,"y":594,"w":23,"h":91}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":36,"y":2,"w":23,"h":91}, + "sourceSize": {"w":96,"h":96} +}, "sprites/blueprints/wire_tunnel.png": { - "frame": {"x":202,"y":1052,"w":93,"h":91}, + "frame": {"x":3,"y":953,"w":93,"h":91}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":2,"w":93,"h":91}, @@ -642,7 +658,7 @@ }, "sprites/buildings/belt_left.png": { - "frame": {"x":496,"y":1096,"w":87,"h":87}, + "frame": {"x":196,"y":1135,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":9,"w":87,"h":87}, @@ -650,7 +666,7 @@ }, "sprites/buildings/belt_right.png": { - "frame": {"x":190,"y":1316,"w":87,"h":87}, + "frame": {"x":281,"y":1272,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":9,"w":87,"h":87}, @@ -658,7 +674,7 @@ }, "sprites/buildings/belt_top.png": { - "frame": {"x":372,"y":1385,"w":78,"h":96}, + "frame": {"x":943,"y":816,"w":78,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":0,"w":78,"h":96}, @@ -688,6 +704,14 @@ "spriteSourceSize": {"x":11,"y":0,"w":171,"h":96}, "sourceSize": {"w":192,"h":96} }, +"sprites/buildings/display.png": +{ + "frame": {"x":463,"y":1428,"w":84,"h":90}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":6,"w":84,"h":90}, + "sourceSize": {"w":96,"h":96} +}, "sprites/buildings/filter.png": { "frame": {"x":569,"y":403,"w":179,"h":96}, @@ -714,7 +738,7 @@ }, "sprites/buildings/logic_gate-not.png": { - "frame": {"x":469,"y":1369,"w":82,"h":96}, + "frame": {"x":435,"y":903,"w":82,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":8,"y":0,"w":82,"h":96}, @@ -722,7 +746,7 @@ }, "sprites/buildings/logic_gate-or.png": { - "frame": {"x":303,"y":803,"w":96,"h":83}, + "frame": {"x":621,"y":923,"w":96,"h":83}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":83}, @@ -730,7 +754,7 @@ }, "sprites/buildings/logic_gate-transistor.png": { - "frame": {"x":3,"y":1707,"w":68,"h":96}, + "frame": {"x":621,"y":799,"w":68,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":68,"h":96}, @@ -738,7 +762,7 @@ }, "sprites/buildings/logic_gate-xor.png": { - "frame": {"x":3,"y":774,"w":96,"h":95}, + "frame": {"x":339,"y":803,"w":96,"h":95}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":95}, @@ -746,7 +770,7 @@ }, "sprites/buildings/logic_gate.png": { - "frame": {"x":203,"y":774,"w":96,"h":88}, + "frame": {"x":521,"y":899,"w":96,"h":88}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":88}, @@ -754,7 +778,7 @@ }, "sprites/buildings/miner-chainable.png": { - "frame": {"x":698,"y":990,"w":91,"h":95}, + "frame": {"x":391,"y":1081,"w":91,"h":95}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":0,"w":91,"h":95}, @@ -762,7 +786,7 @@ }, "sprites/buildings/miner.png": { - "frame": {"x":596,"y":1078,"w":91,"h":95}, + "frame": {"x":292,"y":1082,"w":91,"h":95}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":0,"w":91,"h":95}, @@ -810,7 +834,7 @@ }, "sprites/buildings/rotater-ccw.png": { - "frame": {"x":203,"y":952,"w":95,"h":96}, + "frame": {"x":821,"y":1052,"w":95,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":95,"h":96}, @@ -818,7 +842,7 @@ }, "sprites/buildings/rotater-fl.png": { - "frame": {"x":103,"y":984,"w":95,"h":96}, + "frame": {"x":721,"y":1107,"w":95,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":95,"h":96}, @@ -826,7 +850,7 @@ }, "sprites/buildings/rotater.png": { - "frame": {"x":3,"y":989,"w":95,"h":96}, + "frame": {"x":920,"y":1116,"w":95,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":95,"h":96}, @@ -834,7 +858,7 @@ }, "sprites/buildings/splitter-compact-inverse.png": { - "frame": {"x":402,"y":923,"w":94,"h":91}, + "frame": {"x":919,"y":1216,"w":94,"h":91}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":2,"w":94,"h":91}, @@ -842,7 +866,7 @@ }, "sprites/buildings/splitter-compact.png": { - "frame": {"x":102,"y":1084,"w":93,"h":91}, + "frame": {"x":198,"y":874,"w":93,"h":91}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":2,"w":93,"h":91}, @@ -866,7 +890,7 @@ }, "sprites/buildings/trash-storage.png": { - "frame": {"x":733,"y":694,"w":166,"h":192}, + "frame": {"x":736,"y":694,"w":166,"h":192}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":0,"w":166,"h":192}, @@ -874,7 +898,7 @@ }, "sprites/buildings/trash.png": { - "frame": {"x":349,"y":703,"w":96,"h":96}, + "frame": {"x":729,"y":890,"w":96,"h":96}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":96}, @@ -882,7 +906,7 @@ }, "sprites/buildings/underground_belt_entry-tier2.png": { - "frame": {"x":299,"y":1069,"w":92,"h":83}, + "frame": {"x":295,"y":995,"w":92,"h":83}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":13,"w":92,"h":83}, @@ -890,7 +914,7 @@ }, "sprites/buildings/underground_belt_entry.png": { - "frame": {"x":199,"y":1147,"w":92,"h":74}, + "frame": {"x":196,"y":1057,"w":92,"h":74}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":22,"w":92,"h":74}, @@ -898,7 +922,7 @@ }, "sprites/buildings/underground_belt_exit-tier2.png": { - "frame": {"x":100,"y":1179,"w":92,"h":74}, + "frame": {"x":391,"y":1003,"w":92,"h":74}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":0,"w":92,"h":74}, @@ -906,7 +930,7 @@ }, "sprites/buildings/underground_belt_exit.png": { - "frame": {"x":3,"y":1256,"w":92,"h":74}, + "frame": {"x":487,"y":1077,"w":92,"h":74}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":0,"w":92,"h":74}, @@ -914,7 +938,7 @@ }, "sprites/buildings/wire-cross.png": { - "frame": {"x":526,"y":799,"w":96,"h":96}, + "frame": {"x":829,"y":894,"w":96,"h":96}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":96}, @@ -922,7 +946,7 @@ }, "sprites/buildings/wire-split.png": { - "frame": {"x":3,"y":873,"w":96,"h":54}, + "frame": {"x":621,"y":1010,"w":96,"h":54}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":42,"w":96,"h":54}, @@ -944,9 +968,17 @@ "spriteSourceSize": {"x":42,"y":0,"w":12,"h":96}, "sourceSize": {"w":96,"h":96} }, +"sprites/buildings/wire_tunnel-coating.png": +{ + "frame": {"x":921,"y":689,"w":21,"h":90}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":37,"y":3,"w":21,"h":90}, + "sourceSize": {"w":96,"h":96} +}, "sprites/buildings/wire_tunnel.png": { - "frame": {"x":400,"y":1018,"w":92,"h":90}, + "frame": {"x":3,"y":1127,"w":92,"h":90}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":3,"w":92,"h":90}, @@ -970,15 +1002,15 @@ }, "sprites/misc/hub_direction_indicator.png": { - "frame": {"x":717,"y":561,"w":16,"h":16}, + "frame": {"x":693,"y":851,"w":32,"h":32}, "rotated": false, "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, - "sourceSize": {"w":16,"h":16} + "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, + "sourceSize": {"w":32,"h":32} }, "sprites/misc/slot_bad_arrow.png": { - "frame": {"x":717,"y":533,"w":24,"h":24}, + "frame": {"x":717,"y":605,"w":24,"h":24}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":24,"h":24}, @@ -986,7 +1018,7 @@ }, "sprites/misc/slot_good_arrow.png": { - "frame": {"x":717,"y":503,"w":24,"h":26}, + "frame": {"x":717,"y":575,"w":24,"h":26}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":24,"h":26}, @@ -1002,31 +1034,87 @@ }, "sprites/misc/waypoint.png": { - "frame": {"x":928,"y":594,"w":14,"h":16}, + "frame": {"x":717,"y":539,"w":26,"h":32}, "rotated": false, "trimmed": true, - "spriteSourceSize": {"x":1,"y":0,"w":14,"h":16}, - "sourceSize": {"w":16,"h":16} + "spriteSourceSize": {"x":3,"y":0,"w":26,"h":32}, + "sourceSize": {"w":32,"h":32} }, "sprites/wires/boolean_false.png": { - "frame": {"x":717,"y":581,"w":15,"h":20}, + "frame": {"x":717,"y":633,"w":21,"h":28}, "rotated": false, "trimmed": true, - "spriteSourceSize": {"x":9,"y":7,"w":15,"h":20}, + "spriteSourceSize": {"x":6,"y":3,"w":21,"h":28}, "sourceSize": {"w":32,"h":32} }, "sprites/wires/boolean_true.png": { - "frame": {"x":928,"y":614,"w":12,"h":21}, + "frame": {"x":717,"y":665,"w":15,"h":28}, "rotated": false, "trimmed": true, - "spriteSourceSize": {"x":9,"y":6,"w":12,"h":21}, + "spriteSourceSize": {"x":7,"y":3,"w":15,"h":28}, "sourceSize": {"w":32,"h":32} }, +"sprites/wires/display/blue.png": +{ + "frame": {"x":699,"y":703,"w":33,"h":33}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":33,"h":33}, + "sourceSize": {"w":33,"h":33} +}, +"sprites/wires/display/cyan.png": +{ + "frame": {"x":699,"y":740,"w":33,"h":33}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":33,"h":33}, + "sourceSize": {"w":33,"h":33} +}, +"sprites/wires/display/green.png": +{ + "frame": {"x":699,"y":777,"w":33,"h":33}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":33,"h":33}, + "sourceSize": {"w":33,"h":33} +}, +"sprites/wires/display/purple.png": +{ + "frame": {"x":906,"y":783,"w":33,"h":33}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":33,"h":33}, + "sourceSize": {"w":33,"h":33} +}, +"sprites/wires/display/red.png": +{ + "frame": {"x":906,"y":820,"w":33,"h":33}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":33,"h":33}, + "sourceSize": {"w":33,"h":33} +}, +"sprites/wires/display/white.png": +{ + "frame": {"x":906,"y":857,"w":33,"h":33}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":33,"h":33}, + "sourceSize": {"w":33,"h":33} +}, +"sprites/wires/display/yellow.png": +{ + "frame": {"x":693,"y":814,"w":33,"h":33}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":33,"h":33}, + "sourceSize": {"w":33,"h":33} +}, "sprites/wires/lever_on.png": { - "frame": {"x":449,"y":785,"w":73,"h":76}, + "frame": {"x":948,"y":654,"w":73,"h":76}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":12,"y":12,"w":73,"h":76}, @@ -1048,6 +1136,22 @@ "spriteSourceSize": {"x":29,"y":0,"w":41,"h":45}, "sourceSize": {"w":96,"h":96} }, +"sprites/wires/network_conflict.png": +{ + "frame": {"x":303,"y":798,"w":32,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":1,"w":32,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/wires/network_empty.png": +{ + "frame": {"x":717,"y":503,"w":28,"h":32}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":3,"y":0,"w":28,"h":32}, + "sourceSize": {"w":32,"h":32} +}, "sprites/wires/overlay_tile.png": { "frame": {"x":955,"y":3,"w":64,"h":64}, @@ -1058,7 +1162,7 @@ }, "sprites/wires/sets/color_cross.png": { - "frame": {"x":626,"y":799,"w":96,"h":96}, + "frame": {"x":3,"y":774,"w":96,"h":96}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":96}, @@ -1074,7 +1178,7 @@ }, "sprites/wires/sets/color_split.png": { - "frame": {"x":103,"y":926,"w":96,"h":54}, + "frame": {"x":821,"y":994,"w":96,"h":54}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":42,"w":96,"h":54}, @@ -1090,7 +1194,7 @@ }, "sprites/wires/sets/conflict_cross.png": { - "frame": {"x":726,"y":890,"w":96,"h":96}, + "frame": {"x":103,"y":774,"w":96,"h":96}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":96}, @@ -1098,7 +1202,7 @@ }, "sprites/wires/sets/conflict_forward.png": { - "frame": {"x":736,"y":581,"w":12,"h":96}, + "frame": {"x":303,"y":832,"w":12,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":42,"y":0,"w":12,"h":96}, @@ -1106,7 +1210,7 @@ }, "sprites/wires/sets/conflict_split.png": { - "frame": {"x":3,"y":931,"w":96,"h":54}, + "frame": {"x":721,"y":1049,"w":96,"h":54}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":42,"w":96,"h":54}, @@ -1122,7 +1226,7 @@ }, "sprites/wires/sets/regular_cross.png": { - "frame": {"x":526,"y":799,"w":96,"h":96}, + "frame": {"x":829,"y":894,"w":96,"h":96}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":96}, @@ -1138,7 +1242,7 @@ }, "sprites/wires/sets/regular_split.png": { - "frame": {"x":3,"y":873,"w":96,"h":54}, + "frame": {"x":621,"y":1010,"w":96,"h":54}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":42,"w":96,"h":54}, @@ -1154,7 +1258,7 @@ }, "sprites/wires/sets/shape_cross.png": { - "frame": {"x":898,"y":951,"w":96,"h":96}, + "frame": {"x":203,"y":774,"w":96,"h":96}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":96}, @@ -1162,7 +1266,7 @@ }, "sprites/wires/sets/shape_forward.png": { - "frame": {"x":717,"y":605,"w":12,"h":96}, + "frame": {"x":319,"y":832,"w":12,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":42,"y":0,"w":12,"h":96}, @@ -1170,7 +1274,7 @@ }, "sprites/wires/sets/shape_split.png": { - "frame": {"x":403,"y":865,"w":96,"h":54}, + "frame": {"x":621,"y":1068,"w":96,"h":54}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":42,"w":96,"h":54}, @@ -1183,6 +1287,14 @@ "trimmed": true, "spriteSourceSize": {"x":42,"y":42,"w":54,"h":54}, "sourceSize": {"w":96,"h":96} +}, +"sprites/wires/wires_preview.png": +{ + "frame": {"x":693,"y":887,"w":32,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, + "sourceSize": {"w":32,"h":32} }}, "meta": { "app": "https://www.codeandweb.com/texturepacker", @@ -1191,6 +1303,6 @@ "format": "RGBA8888", "size": {"w":1024,"h":2048}, "scale": "0.5", - "smartupdate": "$TexturePacker:SmartUpdate:495f928ed9092c817f98f68825f1d4ae:98823415164aea829cee3223195c589c:908b89f5ca8ff73e331a35a3b14d0604$" + "smartupdate": "$TexturePacker:SmartUpdate:876f0711b44fa7bbab8d2539e9651766:ff01f850e086ef31c114b036c3a32e6d:908b89f5ca8ff73e331a35a3b14d0604$" } } diff --git a/res_built/atlas/atlas0_mq.png b/res_built/atlas/atlas0_mq.png index d8755e05..e05b73e3 100644 Binary files a/res_built/atlas/atlas0_mq.png and b/res_built/atlas/atlas0_mq.png differ diff --git a/res_raw/atlas.tps b/res_raw/atlas.tps index f1206df5..28809d93 100644 --- a/res_raw/atlas.tps +++ b/res_raw/atlas.tps @@ -258,6 +258,7 @@ sprites/belt/built/right_8.png sprites/belt/built/right_9.png sprites/blueprints/constant_signal.png + sprites/blueprints/display.png sprites/blueprints/lever.png sprites/blueprints/logic_gate-not.png sprites/blueprints/logic_gate-or.png @@ -278,6 +279,7 @@ sprites/blueprints/underground_belt_exit.png sprites/blueprints/wire_tunnel.png sprites/buildings/constant_signal.png + sprites/buildings/display.png sprites/buildings/lever.png sprites/buildings/logic_gate-not.png sprites/buildings/logic_gate-or.png @@ -531,6 +533,7 @@ sprites/wires/boolean_false.png sprites/wires/boolean_true.png + sprites/wires/wires_preview.png pivotPoint 0.5,0.5 @@ -545,6 +548,27 @@ scale9FromFile + sprites/wires/display/blue.png + sprites/wires/display/cyan.png + sprites/wires/display/green.png + sprites/wires/display/purple.png + sprites/wires/display/red.png + sprites/wires/display/white.png + sprites/wires/display/yellow.png + + pivotPoint + 0.5,0.5 + spriteScale + 1 + scale9Enabled + + scale9Borders + 11,11,22,22 + scale9Paddings + 11,11,22,22 + scale9FromFile + + fileList diff --git a/res_raw/sprites/blueprints/display.png b/res_raw/sprites/blueprints/display.png new file mode 100644 index 00000000..bb0319f2 Binary files /dev/null and b/res_raw/sprites/blueprints/display.png differ diff --git a/res_raw/sprites/blueprints/wire_tunnel-coating.png b/res_raw/sprites/blueprints/wire_tunnel-coating.png new file mode 100644 index 00000000..381e628a Binary files /dev/null and b/res_raw/sprites/blueprints/wire_tunnel-coating.png differ diff --git a/res_raw/sprites/buildings/display.png b/res_raw/sprites/buildings/display.png new file mode 100644 index 00000000..ec7b95d9 Binary files /dev/null and b/res_raw/sprites/buildings/display.png differ diff --git a/res_raw/sprites/buildings/wire_tunnel-coating.png b/res_raw/sprites/buildings/wire_tunnel-coating.png new file mode 100644 index 00000000..aed6c8cb Binary files /dev/null and b/res_raw/sprites/buildings/wire_tunnel-coating.png differ diff --git a/res_raw/sprites/wires/boolean_false.png b/res_raw/sprites/wires/boolean_false.png index 796c1f83..757b6cd9 100644 Binary files a/res_raw/sprites/wires/boolean_false.png and b/res_raw/sprites/wires/boolean_false.png differ diff --git a/res_raw/sprites/wires/boolean_true.png b/res_raw/sprites/wires/boolean_true.png index dca45273..f63bae3f 100644 Binary files a/res_raw/sprites/wires/boolean_true.png and b/res_raw/sprites/wires/boolean_true.png differ diff --git a/res_raw/sprites/wires/display/blue.png b/res_raw/sprites/wires/display/blue.png new file mode 100644 index 00000000..c131ed84 Binary files /dev/null and b/res_raw/sprites/wires/display/blue.png differ diff --git a/res_raw/sprites/wires/display/cyan.png b/res_raw/sprites/wires/display/cyan.png new file mode 100644 index 00000000..9cc20690 Binary files /dev/null and b/res_raw/sprites/wires/display/cyan.png differ diff --git a/res_raw/sprites/wires/display/green.png b/res_raw/sprites/wires/display/green.png new file mode 100644 index 00000000..cf390a5b Binary files /dev/null and b/res_raw/sprites/wires/display/green.png differ diff --git a/res_raw/sprites/wires/display/purple.png b/res_raw/sprites/wires/display/purple.png new file mode 100644 index 00000000..1285a151 Binary files /dev/null and b/res_raw/sprites/wires/display/purple.png differ diff --git a/res_raw/sprites/wires/display/red.png b/res_raw/sprites/wires/display/red.png new file mode 100644 index 00000000..fabf484c Binary files /dev/null and b/res_raw/sprites/wires/display/red.png differ diff --git a/res_raw/sprites/wires/display/white.png b/res_raw/sprites/wires/display/white.png new file mode 100644 index 00000000..0430f042 Binary files /dev/null and b/res_raw/sprites/wires/display/white.png differ diff --git a/res_raw/sprites/wires/display/yellow.png b/res_raw/sprites/wires/display/yellow.png new file mode 100644 index 00000000..02018a71 Binary files /dev/null and b/res_raw/sprites/wires/display/yellow.png differ diff --git a/res_raw/sprites/wires/network_conflict.png b/res_raw/sprites/wires/network_conflict.png new file mode 100644 index 00000000..4b941bc0 Binary files /dev/null and b/res_raw/sprites/wires/network_conflict.png differ diff --git a/res_raw/sprites/wires/network_empty.png b/res_raw/sprites/wires/network_empty.png new file mode 100644 index 00000000..1f8d1c36 Binary files /dev/null and b/res_raw/sprites/wires/network_empty.png differ diff --git a/res_raw/sprites/wires/wires_preview.png b/res_raw/sprites/wires/wires_preview.png new file mode 100644 index 00000000..03c9d0ef Binary files /dev/null and b/res_raw/sprites/wires/wires_preview.png differ diff --git a/src/css/common.scss b/src/css/common.scss index b8f02d40..368af699 100644 --- a/src/css/common.scss +++ b/src/css/common.scss @@ -318,9 +318,9 @@ input { canvas { pointer-events: all; - image-rendering: pixelated; + // image-rendering: pixelated; // &.smoothed { - // } + // }1 // &.unsmoothed { // } letter-spacing: 0 !important; diff --git a/src/css/icons.scss b/src/css/icons.scss index 01e28b62..38c32fd5 100644 --- a/src/css/icons.scss +++ b/src/css/icons.scss @@ -1,5 +1,5 @@ $buildings: belt, cutter, miner, mixer, painter, rotater, splitter, stacker, trash, underground_belt, wire, - constant_signal, logic_gate, lever, filter, wire_tunnel; + constant_signal, logic_gate, lever, filter, wire_tunnel, display; @each $building in $buildings { [data-icon="building_icons/#{$building}.png"] { diff --git a/src/js/core/buffer_maintainer.js b/src/js/core/buffer_maintainer.js index c28c0ee5..3d466f14 100644 --- a/src/js/core/buffer_maintainer.js +++ b/src/js/core/buffer_maintainer.js @@ -13,7 +13,7 @@ import { round1Digit } from "./utils"; const logger = createLogger("buffers"); -const bufferGcDurationSeconds = 10; +const bufferGcDurationSeconds = 5; export class BufferMaintainer { /** @@ -27,6 +27,31 @@ export class BufferMaintainer { this.iterationIndex = 1; this.lastIteration = 0; + + this.root.signals.gameFrameStarted.add(this.update, this); + } + + /** + * Returns the buffer stats + */ + getStats() { + let stats = { + rootKeys: 0, + subKeys: 0, + vramBytes: 0, + }; + this.cache.forEach((subCache, key) => { + ++stats.rootKeys; + + subCache.forEach((cacheEntry, subKey) => { + ++stats.subKeys; + + const canvas = cacheEntry.canvas; + stats.vramBytes += canvas.width * canvas.height * 4; + }); + }); + + return stats; } /** diff --git a/src/js/core/config.js b/src/js/core/config.js index 3f6362c1..8d30aebb 100644 --- a/src/js/core/config.js +++ b/src/js/core/config.js @@ -53,6 +53,8 @@ export const globalConfig = { beltSpeedItemsPerSecond: 2, minerSpeedItemsPerSecond: 0, // COMPUTED + defaultItemDiameter: 20, + itemSpacingOnBelts: 0.63, wiresSpeedItemsPerSecond: 6, diff --git a/src/js/core/config.local.js b/src/js/core/config.local.js index 7bf9a002..ec62d3c1 100644 --- a/src/js/core/config.local.js +++ b/src/js/core/config.local.js @@ -95,5 +95,14 @@ export default { // Whether to items / s instead of items / m in stats // detailedStatistics: true, // ----------------------------------------------------------------------------------- + // Shows detailed information about which atlas is used + // showAtlasInfo: true, + // ----------------------------------------------------------------------------------- + // Renders the rotation of all wires + // renderWireRotations: true, + // ----------------------------------------------------------------------------------- + // Renders information about wire networks + // renderWireNetworkInfos: true, + // ----------------------------------------------------------------------------------- /* dev:end */ }; diff --git a/src/js/core/draw_parameters.js b/src/js/core/draw_parameters.js index 649c89b0..71971ed1 100644 --- a/src/js/core/draw_parameters.js +++ b/src/js/core/draw_parameters.js @@ -22,9 +22,5 @@ export class DrawParameters { // FIXME: Not really nice /** @type {GameRoot} */ this.root = root; - - if (G_IS_DEV && globalConfig.debug.testClipping) { - this.visibleRect = this.visibleRect.expandedInAllDirections(-100); - } } } diff --git a/src/js/core/draw_utils.js b/src/js/core/draw_utils.js index ea5b70c2..d5183cfb 100644 --- a/src/js/core/draw_utils.js +++ b/src/js/core/draw_utils.js @@ -3,6 +3,12 @@ * @typedef {import("./draw_parameters").DrawParameters} DrawParameters */ +import { globalConfig } from "./config"; +import { createLogger } from "./logging"; +import { Rectangle } from "./rectangle"; + +const logger = createLogger("draw_utils"); + export function initDrawUtils() { CanvasRenderingContext2D.prototype.beginRoundedRect = function (x, y, w, h, r) { this.beginPath(); @@ -52,9 +58,64 @@ export function initDrawUtils() { * @param {number=} param0.offsetY */ export function drawRotatedSprite({ parameters, sprite, x, y, angle, size, offsetX = 0, offsetY = 0 }) { + if (angle === 0) { + sprite.drawCachedCentered(parameters, x + offsetX, y + offsetY, size); + return; + } + parameters.context.translate(x, y); parameters.context.rotate(angle); sprite.drawCachedCentered(parameters, offsetX, offsetY, size, false); parameters.context.rotate(-angle); parameters.context.translate(-x, -y); } + +let warningsShown = 0; + +/** + * Draws a sprite with clipping + * @param {object} param0 + * @param {DrawParameters} param0.parameters + * @param {HTMLCanvasElement} param0.sprite + * @param {number} param0.x + * @param {number} param0.y + * @param {number} param0.w + * @param {number} param0.h + * @param {number} param0.originalW + * @param {number} param0.originalH + */ +export function drawSpriteClipped({ parameters, sprite, x, y, w, h, originalW, originalH }) { + const rect = new Rectangle(x, y, w, h); + const intersection = rect.getIntersection(parameters.visibleRect); + if (!intersection) { + // Clipped + if (++warningsShown % 200 === 1) { + logger.warn( + "Sprite drawn clipped but it's not on screen - perform culling before (", + warningsShown, + "warnings)" + ); + } + if (G_IS_DEV && globalConfig.debug.testClipping) { + parameters.context.fillStyle = "yellow"; + parameters.context.fillRect(x, y, w, h); + } + return; + } + + parameters.context.drawImage( + sprite, + + // src pos and size + ((intersection.x - x) / w) * originalW, + ((intersection.y - y) / h) * originalH, + (originalW * intersection.w) / w, + (originalH * intersection.h) / h, + + // dest pos and size + intersection.x, + intersection.y, + intersection.w, + intersection.h + ); +} diff --git a/src/js/core/rectangle.js b/src/js/core/rectangle.js index 6b4315aa..1cbfdc27 100644 --- a/src/js/core/rectangle.js +++ b/src/js/core/rectangle.js @@ -376,24 +376,33 @@ export class Rectangle { ); } + /** + * Good for printing stuff + */ + toString() { + return ( + "[x:" + + round2Digits(this.x) + + "| y:" + + round2Digits(this.y) + + "| w:" + + round2Digits(this.w) + + "| h:" + + round2Digits(this.h) + + "]" + ); + } + /** * Returns a new recangle in tile space which includes all tiles which are visible in this rect - * @param {boolean=} includeHalfTiles * @returns {Rectangle} */ - toTileCullRectangle(includeHalfTiles = true) { - let scaled = this.allScaled(1.0 / globalConfig.tileSize); - - if (includeHalfTiles) { - // Increase rectangle size - scaled = Rectangle.fromTRBL( - Math.floor(scaled.y), - Math.ceil(scaled.right()), - Math.ceil(scaled.bottom()), - Math.floor(scaled.x) - ); - } - - return scaled; + toTileCullRectangle() { + return new Rectangle( + Math.floor(this.x / globalConfig.tileSize), + Math.floor(this.y / globalConfig.tileSize), + Math.ceil(this.w / globalConfig.tileSize), + Math.ceil(this.h / globalConfig.tileSize) + ); } } diff --git a/src/js/core/sprites.js b/src/js/core/sprites.js index 6d9ed5af..bdcc65b4 100644 --- a/src/js/core/sprites.js +++ b/src/js/core/sprites.js @@ -4,7 +4,7 @@ import { round3Digits } from "./utils"; const floorSpriteCoordinates = false; -export const ORIGINAL_SPRITE_SCALE = "0.5"; +export const ORIGINAL_SPRITE_SCALE = "0.75"; export class BaseSprite { /** diff --git a/src/js/core/stale_area_detector.js b/src/js/core/stale_area_detector.js new file mode 100644 index 00000000..f8e77f0c --- /dev/null +++ b/src/js/core/stale_area_detector.js @@ -0,0 +1,50 @@ +import { createLogger } from "./logging"; +import { Rectangle } from "./rectangle"; +import { globalConfig } from "./config"; + +const logger = createLogger("stale_areas"); + +export class StaleAreaDetector { + /** + * + * @param {object} param0 + * @param {import("../game/root").GameRoot} param0.root + * @param {string} param0.name The name for reference + * @param {(Rectangle) => void} param0.recomputeMethod Method which recomputes the given area + */ + constructor({ root, name, recomputeMethod }) { + this.root = root; + this.name = name; + this.recomputeMethod = recomputeMethod; + + /** @type {Rectangle} */ + this.staleArea = null; + } + + /** + * Invalidates the given area + * @param {Rectangle} area + */ + invalidate(area) { + // logger.log(this.name, "invalidated", area.toString()); + if (this.staleArea) { + this.staleArea = this.staleArea.getUnion(area); + } else { + this.staleArea = area.clone(); + } + } + + /** + * Updates the stale area + */ + update() { + if (this.staleArea) { + logger.log(this.name, "is recomputing", this.staleArea.toString()); + if (G_IS_DEV && globalConfig.debug.renderChanges) { + this.root.hud.parts.changesDebugger.renderChange(this.name, this.staleArea, "#fd145b"); + } + this.recomputeMethod(this.staleArea); + this.staleArea = null; + } + } +} diff --git a/src/js/core/utils.js b/src/js/core/utils.js index 51198df3..4cb41087 100644 --- a/src/js/core/utils.js +++ b/src/js/core/utils.js @@ -713,3 +713,12 @@ export function rotateDirectionalObject(obj, rotation) { left: queue[3], }; } + +/** + * Modulo which works for negative numbers + * @param {number} n + * @param {number} m + */ +export function safeModulo(n, m) { + return ((n % m) + m) % m; +} diff --git a/src/js/core/vector.js b/src/js/core/vector.js index 4c624c8a..50fa9433 100644 --- a/src/js/core/vector.js +++ b/src/js/core/vector.js @@ -1,4 +1,5 @@ import { globalConfig } from "./config"; +import { safeModulo } from "./utils"; const tileSize = globalConfig.tileSize; const halfTileSize = globalConfig.halfTileSize; @@ -287,6 +288,15 @@ export class Vector { return dx * dx + dy * dy; } + /** + * Returns x % f, y % f + * @param {number} f + * @returns {Vector} new vector + */ + modScalar(f) { + return new Vector(safeModulo(this.x, f), safeModulo(this.y, f)); + } + /** * Computes and returns the center between both points * @param {Vector} v diff --git a/src/js/game/base_item.js b/src/js/game/base_item.js index 09389b32..cbd89e7f 100644 --- a/src/js/game/base_item.js +++ b/src/js/game/base_item.js @@ -1,12 +1,9 @@ +import { globalConfig } from "../core/config"; import { DrawParameters } from "../core/draw_parameters"; import { BasicSerializableObject } from "../savegame/serialization"; -/** @enum {string} */ -export const enumItemType = { - shape: "shape", - color: "color", - boolean: "boolean", -}; +/** @type {ItemType[]} **/ +export const itemTypes = ["shape", "color", "boolean"]; /** * Class for items on belts etc. Not an entity for performance reasons @@ -25,10 +22,10 @@ export class BaseItem extends BasicSerializableObject { return {}; } - /** @returns {enumItemType} */ + /** @returns {ItemType} **/ getItemType() { abstract; - return ""; + return "shape"; } /** @@ -59,9 +56,24 @@ export class BaseItem extends BasicSerializableObject { * @param {number} x * @param {number} y * @param {DrawParameters} parameters - * @param {number=} size + * @param {number=} diameter */ - draw(x, y, parameters, size) {} + drawItemCenteredClipped(x, y, parameters, diameter = globalConfig.defaultItemDiameter) { + if (parameters.visibleRect.containsCircle(x, y, diameter / 2)) { + this.drawItemCenteredImpl(x, y, parameters, diameter); + } + } + + /** + * INTERNAL + * @param {number} x + * @param {number} y + * @param {DrawParameters} parameters + * @param {number=} diameter + */ + drawItemCenteredImpl(x, y, parameters, diameter = globalConfig.defaultItemDiameter) { + abstract; + } getBackgroundColorAsResource() { abstract; diff --git a/src/js/game/belt_path.js b/src/js/game/belt_path.js index 99511d5a..0e4b7b79 100644 --- a/src/js/game/belt_path.js +++ b/src/js/game/belt_path.js @@ -8,7 +8,7 @@ import { BasicSerializableObject, types } from "../savegame/serialization"; import { BaseItem } from "./base_item"; import { Entity } from "./entity"; import { typeItemSingleton } from "./item_resolver"; -import { enumLayer, GameRoot } from "./root"; +import { GameRoot } from "./root"; const logger = createLogger("belt_path"); @@ -203,7 +203,7 @@ export class BeltPath extends BasicSerializableObject { const targetEntity = this.root.map.getLayerContentXY( ejectSlotTargetWsTile.x, ejectSlotTargetWsTile.y, - enumLayer.regular + "regular" ); if (targetEntity) { @@ -1194,9 +1194,13 @@ export class BeltPath extends BasicSerializableObject { const worldPos = staticComp.localTileToWorld(localPos).toWorldSpaceCenterOfTile(); const distanceAndItem = this.items[currentItemIndex]; - if (parameters.visibleRect.containsCircle(worldPos.x, worldPos.y, 10)) { - distanceAndItem[_item].draw(worldPos.x, worldPos.y, parameters); - } + + distanceAndItem[_item].drawItemCenteredClipped( + worldPos.x, + worldPos.y, + parameters, + globalConfig.defaultItemDiameter + ); // Check for the next item currentItemPos += distanceAndItem[_nextDistance]; diff --git a/src/js/game/blueprint.js b/src/js/game/blueprint.js index ecffdc31..ccbbc248 100644 --- a/src/js/game/blueprint.js +++ b/src/js/game/blueprint.js @@ -3,7 +3,7 @@ import { Loader } from "../core/loader"; import { createLogger } from "../core/logging"; import { Vector } from "../core/vector"; import { Entity } from "./entity"; -import { GameRoot, enumLayer } from "./root"; +import { GameRoot } from "./root"; import { findNiceIntegerValue } from "../core/utils"; import { blueprintShape } from "./upgrades"; import { globalConfig } from "../core/config"; @@ -20,11 +20,11 @@ export class Blueprint { /** * Returns the layer of this blueprint - * @returns {enumLayer} + * @returns {Layer} */ get layer() { if (this.entities.length === 0) { - return enumLayer.regular; + return "regular"; } return this.entities[0].layer; } @@ -92,7 +92,7 @@ export class Blueprint { parameters.context.globalAlpha = 1; } - staticComp.drawSpriteOnFullEntityBounds(parameters, staticComp.getBlueprintSprite(), 0, newPos); + staticComp.drawSpriteOnBoundsClipped(parameters, staticComp.getBlueprintSprite(), 0, newPos); } parameters.context.globalAlpha = 1; } diff --git a/src/js/game/buildings/belt_base.js b/src/js/game/buildings/belt_base.js index 059591ce..1aafa9e1 100644 --- a/src/js/game/buildings/belt_base.js +++ b/src/js/game/buildings/belt_base.js @@ -89,7 +89,7 @@ export class MetaBeltBaseBuilding extends MetaBuilding { * @param {Vector} param0.tile * @param {number} param0.rotation * @param {string} param0.variant - * @param {string} param0.layer + * @param {Layer} param0.layer * @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array }} */ computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) { diff --git a/src/js/game/buildings/constant_signal.js b/src/js/game/buildings/constant_signal.js index 5c88248f..93e0ab36 100644 --- a/src/js/game/buildings/constant_signal.js +++ b/src/js/game/buildings/constant_signal.js @@ -2,7 +2,7 @@ import { enumDirection, Vector } from "../../core/vector"; import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; import { Entity } from "../entity"; import { MetaBuilding } from "../meta_building"; -import { enumLayer, GameRoot } from "../root"; +import { GameRoot } from "../root"; import { ConstantSignalComponent } from "../components/constant_signal"; export class MetaConstantSignalBuilding extends MetaBuilding { @@ -22,8 +22,9 @@ export class MetaConstantSignalBuilding extends MetaBuilding { return true; } + /** @returns {"wires"} **/ getLayer() { - return enumLayer.wires; + return "wires"; } getDimensions() { diff --git a/src/js/game/buildings/cutter.js b/src/js/game/buildings/cutter.js index bfe142d5..739f4a05 100644 --- a/src/js/game/buildings/cutter.js +++ b/src/js/game/buildings/cutter.js @@ -8,7 +8,6 @@ import { Entity } from "../entity"; import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; import { GameRoot } from "../root"; import { enumHubGoalRewards } from "../tutorial_goals"; -import { enumItemType } from "../base_item"; /** @enum {string} */ export const enumCutterVariants = { quad: "quad" }; @@ -82,7 +81,7 @@ export class MetaCutterBuilding extends MetaBuilding { { pos: new Vector(0, 0), directions: [enumDirection.bottom], - filter: enumItemType.shape, + filter: "shape", }, ], }) diff --git a/src/js/game/buildings/display.js b/src/js/game/buildings/display.js new file mode 100644 index 00000000..9c072af9 --- /dev/null +++ b/src/js/game/buildings/display.js @@ -0,0 +1,51 @@ +import { enumDirection, Vector } from "../../core/vector"; +import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; +import { Entity } from "../entity"; +import { MetaBuilding } from "../meta_building"; +import { GameRoot } from "../root"; +import { DisplayComponent } from "../components/display"; + +export class MetaDisplayBuilding extends MetaBuilding { + constructor() { + super("display"); + } + + getSilhouetteColor() { + return "#aaaaaa"; + } + + /** + * @param {GameRoot} root + */ + getIsUnlocked(root) { + // @todo + return true; + } + + getDimensions() { + return new Vector(1, 1); + } + + getShowWiresLayerPreview() { + return true; + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) { + entity.addComponent( + new WiredPinsComponent({ + slots: [ + { + pos: new Vector(0, 0), + direction: enumDirection.bottom, + type: enumPinSlotType.logicalAcceptor, + }, + ], + }) + ); + entity.addComponent(new DisplayComponent()); + } +} diff --git a/src/js/game/buildings/filter.js b/src/js/game/buildings/filter.js index 084d30e0..5637a21a 100644 --- a/src/js/game/buildings/filter.js +++ b/src/js/game/buildings/filter.js @@ -29,6 +29,10 @@ export class MetaFilterBuilding extends MetaBuilding { return new Vector(2, 1); } + getShowWiresLayerPreview() { + return true; + } + /** * Creates the entity at the given location * @param {Entity} entity diff --git a/src/js/game/buildings/hub.js b/src/js/game/buildings/hub.js index c11c9197..b9929b31 100644 --- a/src/js/game/buildings/hub.js +++ b/src/js/game/buildings/hub.js @@ -1,5 +1,4 @@ import { enumDirection, Vector } from "../../core/vector"; -import { enumItemType } from "../base_item"; import { HubComponent } from "../components/hub"; import { ItemAcceptorComponent } from "../components/item_acceptor"; import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor"; @@ -68,72 +67,72 @@ export class MetaHubBuilding extends MetaBuilding { { pos: new Vector(0, 0), directions: [enumDirection.top, enumDirection.left], - filter: enumItemType.shape, + filter: "shape", }, { pos: new Vector(1, 0), directions: [enumDirection.top], - filter: enumItemType.shape, + filter: "shape", }, { pos: new Vector(2, 0), directions: [enumDirection.top], - filter: enumItemType.shape, + filter: "shape", }, { pos: new Vector(3, 0), directions: [enumDirection.top, enumDirection.right], - filter: enumItemType.shape, + filter: "shape", }, { pos: new Vector(0, 3), directions: [enumDirection.bottom, enumDirection.left], - filter: enumItemType.shape, + filter: "shape", }, { pos: new Vector(1, 3), directions: [enumDirection.bottom], - filter: enumItemType.shape, + filter: "shape", }, { pos: new Vector(2, 3), directions: [enumDirection.bottom], - filter: enumItemType.shape, + filter: "shape", }, { pos: new Vector(3, 3), directions: [enumDirection.bottom, enumDirection.right], - filter: enumItemType.shape, + filter: "shape", }, { pos: new Vector(0, 1), directions: [enumDirection.left], - filter: enumItemType.shape, + filter: "shape", }, { pos: new Vector(0, 2), directions: [enumDirection.left], - filter: enumItemType.shape, + filter: "shape", }, { pos: new Vector(0, 3), directions: [enumDirection.left], - filter: enumItemType.shape, + filter: "shape", }, { pos: new Vector(3, 1), directions: [enumDirection.right], - filter: enumItemType.shape, + filter: "shape", }, { pos: new Vector(3, 2), directions: [enumDirection.right], - filter: enumItemType.shape, + filter: "shape", }, { pos: new Vector(3, 3), directions: [enumDirection.right], - filter: enumItemType.shape, + filter: "shape", }, ], }) diff --git a/src/js/game/buildings/lever.js b/src/js/game/buildings/lever.js index 596c4e0d..e7e35888 100644 --- a/src/js/game/buildings/lever.js +++ b/src/js/game/buildings/lever.js @@ -35,6 +35,10 @@ export class MetaLeverBuilding extends MetaBuilding { return null; } + getShowWiresLayerPreview() { + return true; + } + /** * Creates the entity at the given location * @param {Entity} entity diff --git a/src/js/game/buildings/logic_gate.js b/src/js/game/buildings/logic_gate.js index bc9c7777..e07db3ea 100644 --- a/src/js/game/buildings/logic_gate.js +++ b/src/js/game/buildings/logic_gate.js @@ -2,7 +2,7 @@ import { enumDirection, Vector } from "../../core/vector"; import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; import { Entity } from "../entity"; import { MetaBuilding, defaultBuildingVariant } from "../meta_building"; -import { enumLayer, GameRoot } from "../root"; +import { GameRoot } from "../root"; import { enumLogicGateType, LogicGateComponent } from "../components/logic_gate"; /** @enum {string} */ @@ -39,8 +39,9 @@ export class MetaLogicGateBuilding extends MetaBuilding { return true; } + /** @returns {"wires"} **/ getLayer() { - return enumLayer.wires; + return "wires"; } getDimensions() { diff --git a/src/js/game/buildings/mixer.js b/src/js/game/buildings/mixer.js index a2b35280..cbde309e 100644 --- a/src/js/game/buildings/mixer.js +++ b/src/js/game/buildings/mixer.js @@ -1,7 +1,6 @@ import { formatItemsPerSecond } from "../../core/utils"; import { enumDirection, Vector } from "../../core/vector"; import { T } from "../../translations"; -import { enumItemType } from "../base_item"; import { ItemAcceptorComponent } from "../components/item_acceptor"; import { ItemEjectorComponent } from "../components/item_ejector"; import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor"; @@ -63,12 +62,12 @@ export class MetaMixerBuilding extends MetaBuilding { { pos: new Vector(0, 0), directions: [enumDirection.bottom], - filter: enumItemType.color, + filter: "color", }, { pos: new Vector(1, 0), directions: [enumDirection.bottom], - filter: enumItemType.color, + filter: "color", }, ], }) diff --git a/src/js/game/buildings/painter.js b/src/js/game/buildings/painter.js index ed64f75c..78a1f015 100644 --- a/src/js/game/buildings/painter.js +++ b/src/js/game/buildings/painter.js @@ -8,7 +8,6 @@ import { Entity } from "../entity"; import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; import { GameRoot } from "../root"; import { enumHubGoalRewards } from "../tutorial_goals"; -import { enumItemType } from "../base_item"; import { WiredPinsComponent, enumPinSlotType } from "../components/wired_pins"; import { ProcessingRequirementComponent } from "../components/processing_requirement"; @@ -100,12 +99,12 @@ export class MetaPainterBuilding extends MetaBuilding { { pos: new Vector(0, 0), directions: [enumDirection.left], - filter: enumItemType.shape, + filter: "shape", }, { pos: new Vector(1, 0), directions: [enumDirection.top], - filter: enumItemType.color, + filter: "color", }, ], }) @@ -133,14 +132,14 @@ export class MetaPainterBuilding extends MetaBuilding { { pos: new Vector(0, 0), directions: [enumDirection.left], - filter: enumItemType.shape, + filter: "shape", }, { pos: new Vector(1, 0), directions: [ variant === defaultBuildingVariant ? enumDirection.top : enumDirection.bottom, ], - filter: enumItemType.color, + filter: "color", }, ]); @@ -163,17 +162,17 @@ export class MetaPainterBuilding extends MetaBuilding { { pos: new Vector(0, 0), directions: [enumDirection.left], - filter: enumItemType.shape, + filter: "shape", }, { pos: new Vector(0, 1), directions: [enumDirection.left], - filter: enumItemType.shape, + filter: "shape", }, { pos: new Vector(1, 0), directions: [enumDirection.top], - filter: enumItemType.color, + filter: "color", }, ]); @@ -223,27 +222,27 @@ export class MetaPainterBuilding extends MetaBuilding { { pos: new Vector(0, 0), directions: [enumDirection.left], - filter: enumItemType.shape, + filter: "shape", }, { pos: new Vector(0, 0), directions: [enumDirection.bottom], - filter: enumItemType.color, + filter: "color", }, { pos: new Vector(1, 0), directions: [enumDirection.bottom], - filter: enumItemType.color, + filter: "color", }, { pos: new Vector(2, 0), directions: [enumDirection.bottom], - filter: enumItemType.color, + filter: "color", }, { pos: new Vector(3, 0), directions: [enumDirection.bottom], - filter: enumItemType.color, + filter: "color", }, ]); diff --git a/src/js/game/buildings/rotater.js b/src/js/game/buildings/rotater.js index 45bb97b9..c278ef0d 100644 --- a/src/js/game/buildings/rotater.js +++ b/src/js/game/buildings/rotater.js @@ -8,7 +8,6 @@ import { Entity } from "../entity"; import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; import { GameRoot } from "../root"; import { enumHubGoalRewards } from "../tutorial_goals"; -import { enumItemType } from "../base_item"; /** @enum {string} */ export const enumRotaterVariants = { ccw: "ccw", fl: "fl" }; @@ -89,7 +88,7 @@ export class MetaRotaterBuilding extends MetaBuilding { { pos: new Vector(0, 0), directions: [enumDirection.bottom], - filter: enumItemType.shape, + filter: "shape", }, ], }) diff --git a/src/js/game/buildings/stacker.js b/src/js/game/buildings/stacker.js index 91ac3f62..40a9c5ae 100644 --- a/src/js/game/buildings/stacker.js +++ b/src/js/game/buildings/stacker.js @@ -8,7 +8,6 @@ import { Entity } from "../entity"; import { MetaBuilding } from "../meta_building"; import { GameRoot } from "../root"; import { enumHubGoalRewards } from "../tutorial_goals"; -import { enumItemType } from "../base_item"; export class MetaStackerBuilding extends MetaBuilding { constructor() { @@ -63,12 +62,12 @@ export class MetaStackerBuilding extends MetaBuilding { { pos: new Vector(0, 0), directions: [enumDirection.bottom], - filter: enumItemType.shape, + filter: "shape", }, { pos: new Vector(1, 0), directions: [enumDirection.bottom], - filter: enumItemType.shape, + filter: "shape", }, ], }) diff --git a/src/js/game/buildings/underground_belt.js b/src/js/game/buildings/underground_belt.js index 4b647c13..155b69e2 100644 --- a/src/js/game/buildings/underground_belt.js +++ b/src/js/game/buildings/underground_belt.js @@ -5,7 +5,7 @@ import { ItemEjectorComponent } from "../components/item_ejector"; import { enumUndergroundBeltMode, UndergroundBeltComponent } from "../components/underground_belt"; import { Entity } from "../entity"; import { MetaBuilding, defaultBuildingVariant } from "../meta_building"; -import { GameRoot, enumLayer } from "../root"; +import { GameRoot } from "../root"; import { globalConfig } from "../../core/config"; import { enumHubGoalRewards } from "../tutorial_goals"; import { formatItemsPerSecond, generateMatrixRotations } from "../../core/utils"; @@ -171,7 +171,7 @@ export class MetaUndergroundBeltBuilding extends MetaBuilding { * @param {Vector} param0.tile * @param {number} param0.rotation * @param {string} param0.variant - * @param {string} param0.layer + * @param {Layer} param0.layer * @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array }} */ computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) { @@ -190,7 +190,7 @@ export class MetaUndergroundBeltBuilding extends MetaBuilding { tile = tile.addScalars(searchVector.x, searchVector.y); /* WIRES: FIXME */ - const contents = root.map.getTileContent(tile, enumLayer.regular); + const contents = root.map.getTileContent(tile, "regular"); if (contents) { const undergroundComp = contents.components.UndergroundBelt; if (undergroundComp && undergroundComp.tier === tier) { diff --git a/src/js/game/buildings/wire.js b/src/js/game/buildings/wire.js index fa6e23a3..59c9cb7d 100644 --- a/src/js/game/buildings/wire.js +++ b/src/js/game/buildings/wire.js @@ -5,7 +5,7 @@ import { SOUNDS } from "../../platform/sound"; import { enumWireType, WireComponent } from "../components/wire"; import { Entity } from "../entity"; import { MetaBuilding } from "../meta_building"; -import { enumLayer, GameRoot } from "../root"; +import { GameRoot } from "../root"; export const arrayWireRotationVariantToType = [ enumWireType.regular, @@ -50,8 +50,9 @@ export class MetaWireBuilding extends MetaBuilding { return true; } + /** @returns {"wires"} **/ getLayer() { - return enumLayer.wires; + return "wires"; } getSprite() { diff --git a/src/js/game/buildings/wire_tunnel.js b/src/js/game/buildings/wire_tunnel.js index 9dd570e6..f885abc6 100644 --- a/src/js/game/buildings/wire_tunnel.js +++ b/src/js/game/buildings/wire_tunnel.js @@ -1,8 +1,19 @@ import { Vector } from "../../core/vector"; import { Entity } from "../entity"; -import { MetaBuilding } from "../meta_building"; -import { GameRoot, enumLayer } from "../root"; +import { MetaBuilding, defaultBuildingVariant } from "../meta_building"; +import { GameRoot } from "../root"; import { WireTunnelComponent } from "../components/wire_tunnel"; +import { generateMatrixRotations } from "../../core/utils"; + +/** @enum {string} */ +export const enumWireTunnelVariants = { + coating: "coating", +}; + +const wireTunnelOverlayMatrices = { + [defaultBuildingVariant]: generateMatrixRotations([0, 1, 0, 1, 1, 1, 0, 1, 0]), + [enumWireTunnelVariants.coating]: generateMatrixRotations([0, 1, 0, 0, 1, 0, 0, 1, 0]), +}; export class MetaWireTunnelBuilding extends MetaBuilding { constructor() { @@ -10,7 +21,7 @@ export class MetaWireTunnelBuilding extends MetaBuilding { } getSilhouetteColor() { - return "#25fff2"; + return "#777a86"; } /** @@ -21,16 +32,40 @@ export class MetaWireTunnelBuilding extends MetaBuilding { return true; } - getIsRotateable() { - return false; + /** + * + * @param {number} rotation + * @param {number} rotationVariant + * @param {string} variant + * @param {Entity} entity + */ + getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) { + return wireTunnelOverlayMatrices[variant][rotation]; + } + + getIsRotateable(variant) { + return variant !== defaultBuildingVariant; } getDimensions() { return new Vector(1, 1); } + getAvailableVariants() { + return [defaultBuildingVariant, enumWireTunnelVariants.coating]; + } + + /** @returns {"wires"} **/ getLayer() { - return enumLayer.wires; + return "wires"; + } + + getRotateAutomaticallyWhilePlacing() { + return true; + } + + getStayInPlacementMode() { + return true; } /** @@ -38,6 +73,15 @@ export class MetaWireTunnelBuilding extends MetaBuilding { * @param {Entity} entity */ setupEntityComponents(entity) { - entity.addComponent(new WireTunnelComponent()); + entity.addComponent(new WireTunnelComponent({})); + } + + /** + * @param {Entity} entity + * @param {number} rotationVariant + * @param {string} variant + */ + updateVariants(entity, rotationVariant, variant) { + entity.components.WireTunnel.multipleDirections = variant === defaultBuildingVariant; } } diff --git a/src/js/game/component_registry.js b/src/js/game/component_registry.js index 23f59834..8237b566 100644 --- a/src/js/game/component_registry.js +++ b/src/js/game/component_registry.js @@ -15,7 +15,11 @@ import { ConstantSignalComponent } from "./components/constant_signal"; import { LogicGateComponent } from "./components/logic_gate"; import { LeverComponent } from "./components/lever"; import { WireTunnelComponent } from "./components/wire_tunnel"; +<<<<<<< HEAD import { ProcessingRequirementComponent } from "./components/processing_requirement"; +======= +import { DisplayComponent } from "./components/display"; +>>>>>>> upstream/master export function initComponentRegistry() { gComponentRegistry.register(StaticMapEntityComponent); @@ -35,6 +39,7 @@ export function initComponentRegistry() { gComponentRegistry.register(LeverComponent); gComponentRegistry.register(WireTunnelComponent); gComponentRegistry.register(ProcessingRequirementComponent); + gComponentRegistry.register(DisplayComponent); // IMPORTANT ^^^^^ UPDATE ENTITY COMPONENT STORAGE AFTERWARDS diff --git a/src/js/game/components/display.js b/src/js/game/components/display.js new file mode 100644 index 00000000..720bf8c7 --- /dev/null +++ b/src/js/game/components/display.js @@ -0,0 +1,11 @@ +import { Component } from "../component"; + +export class DisplayComponent extends Component { + static getId() { + return "Display"; + } + + duplicateWithoutContents() { + return new DisplayComponent(); + } +} diff --git a/src/js/game/components/item_acceptor.js b/src/js/game/components/item_acceptor.js index d4fdaf72..ebd7ae5f 100644 --- a/src/js/game/components/item_acceptor.js +++ b/src/js/game/components/item_acceptor.js @@ -1,12 +1,12 @@ import { enumDirection, enumInvertedDirections, Vector } from "../../core/vector"; import { types } from "../../savegame/serialization"; -import { BaseItem, enumItemType } from "../base_item"; +import { BaseItem } from "../base_item"; import { Component } from "../component"; /** @typedef {{ * pos: Vector, * directions: enumDirection[], - * filter?: enumItemType + * filter?: ItemType * }} ItemAcceptorSlot */ /** @@ -20,7 +20,7 @@ import { Component } from "../component"; /** @typedef {{ * pos: Vector, * directions: enumDirection[], - * filter?: enumItemType + * filter?: ItemType * }} ItemAcceptorSlotConfig */ export class ItemAcceptorComponent extends Component { @@ -74,7 +74,7 @@ export class ItemAcceptorComponent extends Component { pos: slot.pos, directions: slot.directions, - // Which type of item to accept (shape | color | all) @see enumItemType + // Which type of item to accept (shape | color | all) @see ItemType filter: slot.filter, }); } diff --git a/src/js/game/components/static_map_entity.js b/src/js/game/components/static_map_entity.js index 32104c51..3d138e42 100644 --- a/src/js/game/components/static_map_entity.js +++ b/src/js/game/components/static_map_entity.js @@ -162,8 +162,9 @@ export class StaticMapEntityComponent extends Component { * @returns {Vector} */ localTileToWorld(localTile) { - const result = this.applyRotationToVector(localTile); - result.addInplace(this.origin); + const result = localTile.rotateFastMultipleOf90(this.rotation); + result.x += this.origin.x; + result.y += this.origin.y; return result; } @@ -235,7 +236,7 @@ export class StaticMapEntityComponent extends Component { * @param {number=} extrudePixels How many pixels to extrude the sprite * @param {Vector=} overridePosition Whether to drwa the entity at a different location */ - drawSpriteOnFullEntityBounds(parameters, sprite, extrudePixels = 0, overridePosition = null) { + drawSpriteOnBoundsClipped(parameters, sprite, extrudePixels = 0, overridePosition = null) { if (!this.shouldBeDrawn(parameters) && !overridePosition) { return; } @@ -255,8 +256,7 @@ export class StaticMapEntityComponent extends Component { worldX - extrudePixels * size.x, worldY - extrudePixels * size.y, globalConfig.tileSize * size.x + 2 * extrudePixels * size.x, - globalConfig.tileSize * size.y + 2 * extrudePixels * size.y, - false + globalConfig.tileSize * size.y + 2 * extrudePixels * size.y ); } else { const rotationCenterX = worldX + globalConfig.halfTileSize; @@ -264,16 +264,14 @@ export class StaticMapEntityComponent extends Component { parameters.context.translate(rotationCenterX, rotationCenterY); parameters.context.rotate(Math.radians(this.rotation)); - sprite.drawCached( parameters, -globalConfig.halfTileSize - extrudePixels * size.x, -globalConfig.halfTileSize - extrudePixels * size.y, globalConfig.tileSize * size.x + 2 * extrudePixels * size.x, globalConfig.tileSize * size.y + 2 * extrudePixels * size.y, - false + false // no clipping possible here ); - parameters.context.rotate(-Math.radians(this.rotation)); parameters.context.translate(-rotationCenterX, -rotationCenterY); } diff --git a/src/js/game/components/storage.js b/src/js/game/components/storage.js index 34e51f4b..3b32f6a3 100644 --- a/src/js/game/components/storage.js +++ b/src/js/game/components/storage.js @@ -1,5 +1,5 @@ import { types } from "../../savegame/serialization"; -import { BaseItem, enumItemType } from "../base_item"; +import { BaseItem } from "../base_item"; import { Component } from "../component"; import { typeItemSingleton } from "../item_resolver"; import { ColorItem } from "../items/color_item"; @@ -65,11 +65,11 @@ export class StorageComponent extends Component { return false; } - if (itemType === enumItemType.color) { + if (itemType === "color") { return /** @type {ColorItem} */ (this.storedItem).color === /** @type {ColorItem} */ (item).color; } - if (itemType === enumItemType.shape) { + if (itemType === "shape") { return ( /** @type {ShapeItem} */ (this.storedItem).definition.getHash() === /** @type {ShapeItem} */ (item).definition.getHash() diff --git a/src/js/game/components/wire_tunnel.js b/src/js/game/components/wire_tunnel.js index 226630ca..dfb38f1f 100644 --- a/src/js/game/components/wire_tunnel.js +++ b/src/js/game/components/wire_tunnel.js @@ -6,6 +6,21 @@ export class WireTunnelComponent extends Component { } duplicateWithoutContents() { - return new WireTunnelComponent(); + return new WireTunnelComponent({ multipleDirections: this.multipleDirections }); + } + + /** + * @param {object} param0 + * @param {boolean=} param0.multipleDirections + */ + constructor({ multipleDirections = true }) { + super(); + this.multipleDirections = multipleDirections; + + /** + * Linked network, only if its not multiple directions + * @type {Array} + */ + this.linkedNetworks = []; } } diff --git a/src/js/game/components/wired_pins.js b/src/js/game/components/wired_pins.js index 2822ab57..9a19c2b0 100644 --- a/src/js/game/components/wired_pins.js +++ b/src/js/game/components/wired_pins.js @@ -1,6 +1,8 @@ import { enumDirection, Vector } from "../../core/vector"; import { BaseItem } from "../base_item"; import { Component } from "../component"; +import { types } from "../../savegame/serialization"; +import { typeItemSingleton } from "../item_resolver"; /** @enum {string} */ export const enumPinSlotType = { @@ -27,6 +29,16 @@ export class WiredPinsComponent extends Component { return "WiredPins"; } + static getSchema() { + return { + slots: types.array( + types.structured({ + value: types.nullable(typeItemSingleton), + }) + ), + }; + } + /** * * @param {object} param0 diff --git a/src/js/game/core.js b/src/js/game/core.js index 7f28e638..3ca70bb6 100644 --- a/src/js/game/core.js +++ b/src/js/game/core.js @@ -9,7 +9,7 @@ import { DrawParameters } from "../core/draw_parameters"; import { gMetaBuildingRegistry } from "../core/global_registries"; import { createLogger } from "../core/logging"; import { Rectangle } from "../core/rectangle"; -import { randomInt, round2Digits } from "../core/utils"; +import { randomInt, round2Digits, round3Digits } from "../core/utils"; import { Vector } from "../core/vector"; import { Savegame } from "../savegame/savegame"; import { SavegameSerializer } from "../savegame/savegame_serializer"; @@ -26,7 +26,7 @@ import { GameLogic } from "./logic"; import { MapView } from "./map_view"; import { defaultBuildingVariant } from "./meta_building"; import { ProductionAnalytics } from "./production_analytics"; -import { enumLayer, GameRoot } from "./root"; +import { GameRoot } from "./root"; import { ShapeDefinitionManager } from "./shape_definition_manager"; import { SoundProxy } from "./sound_proxy"; import { GameTime } from "./time/game_time"; @@ -329,8 +329,7 @@ export class GameCore { return; } - // Update buffers as the very first - root.buffers.update(); + this.root.signals.gameFrameStarted.dispatch(); root.queue.requireRedraw = false; @@ -369,6 +368,13 @@ export class GameCore { } // Transform to world space + + if (G_IS_DEV && globalConfig.debug.testClipping) { + params.visibleRect = params.visibleRect.expandedInAllDirections( + -200 / this.root.camera.zoomLevel + ); + } + root.camera.transform(context); assert(context.globalAlpha === 1.0, "Global alpha not 1 on frame start"); @@ -383,36 +389,24 @@ export class GameCore { // Map overview root.map.drawOverlay(params); } else { + // Background (grid, resources, etc) root.map.drawBackground(params); - // Underlays for splitters / balancers - systems.beltUnderlays.drawUnderlays(params); - // Belt items systems.belt.drawBeltItems(params); - // Items being ejected / accepted currently (animations) - systems.itemEjector.draw(params); - systems.itemAcceptor.draw(params); - - // Miner & Static map entities + // Miner & Static map entities etc. root.map.drawForeground(params); // HUB Overlay systems.hub.draw(params); - // Storage items - systems.storage.draw(params); - // Green wires overlay root.hud.parts.wiresOverlay.draw(params); - if (this.root.currentLayer === enumLayer.wires) { + if (this.root.currentLayer === "wires") { // Static map entities root.map.drawWiresForegroundLayer(params); - - // pins - systems.wiredPins.draw(params); } } @@ -439,6 +433,9 @@ export class GameCore { params.zoomLevel = 1; params.desiredAtlasScale = ORIGINAL_SPRITE_SCALE; params.visibleRect = new Rectangle(0, 0, this.root.gameWidth, this.root.gameHeight); + if (G_IS_DEV && globalConfig.debug.testClipping) { + params.visibleRect = params.visibleRect.expandedInAllDirections(-200); + } // Draw overlays, those are screen space root.hud.drawOverlays(params); @@ -457,7 +454,7 @@ export class GameCore { if (G_IS_DEV && globalConfig.debug.showAtlasInfo) { context.font = "13px GameFont"; - context.fillStyle = "yellow"; + context.fillStyle = "blue"; context.fillText( "Atlas: " + desiredAtlasScale + @@ -465,9 +462,31 @@ export class GameCore { round2Digits(zoomLevel) + " / Effective Zoom: " + round2Digits(effectiveZoomLevel), - 200, - 20 + 20, + 600 ); + + const stats = this.root.buffers.getStats(); + context.fillText( + "Buffers: " + + stats.rootKeys + + " root keys, " + + stats.subKeys + + " sub keys / buffers / VRAM: " + + round2Digits(stats.vramBytes / (1024 * 1024)) + + " MB", + + 20, + 620 + ); + } + + if (G_IS_DEV && globalConfig.debug.testClipping) { + context.strokeStyle = "red"; + context.lineWidth = 1; + context.beginPath(); + context.rect(200, 200, this.root.gameWidth - 400, this.root.gameHeight - 400); + context.stroke(); } } } diff --git a/src/js/game/entity.js b/src/js/game/entity.js index d3a53e90..ca21a16d 100644 --- a/src/js/game/entity.js +++ b/src/js/game/entity.js @@ -3,7 +3,7 @@ import { DrawParameters } from "../core/draw_parameters"; import { Component } from "./component"; /* typehints:end */ -import { GameRoot, enumLayer } from "./root"; +import { GameRoot } from "./root"; import { globalConfig } from "../core/config"; import { enumDirectionToVector, enumDirectionToAngle } from "../core/vector"; import { BasicSerializableObject, types } from "../savegame/serialization"; @@ -36,8 +36,9 @@ export class Entity extends BasicSerializableObject { /** * On which layer this entity is + * @type {Layer} */ - this.layer = enumLayer.regular; + this.layer = "regular"; /** * Internal entity unique id, set by the @see EntityManager diff --git a/src/js/game/entity_components.js b/src/js/game/entity_components.js index 167eab88..16cf2ad4 100644 --- a/src/js/game/entity_components.js +++ b/src/js/game/entity_components.js @@ -16,6 +16,7 @@ import { LogicGateComponent } from "./components/logic_gate"; import { LeverComponent } from "./components/lever"; import { WireTunnelComponent } from "./components/wire_tunnel"; import { ProcessingRequirementComponent } from "./components/processing_requirement"; +import { DisplayComponent } from "./components/display"; /* typehints:end */ /** @@ -77,6 +78,9 @@ export class EntityComponentStorage { /** @type {ProcessingRequirementComponent} */ this.ProcessingRequirement; + /** @type {DisplayComponent} */ + this.Display; + /* typehints:end */ } } diff --git a/src/js/game/game_system_manager.js b/src/js/game/game_system_manager.js index 863ecefa..afcaf194 100644 --- a/src/js/game/game_system_manager.js +++ b/src/js/game/game_system_manager.js @@ -19,6 +19,7 @@ import { WireSystem } from "./systems/wire"; import { ConstantSignalSystem } from "./systems/constant_signal"; import { LogicGateSystem } from "./systems/logic_gate"; import { LeverSystem } from "./systems/lever"; +import { DisplaySystem } from "./systems/display"; const logger = createLogger("game_system_manager"); @@ -80,6 +81,9 @@ export class GameSystemManager { /** @type {LeverSystem} */ lever: null, + /** @type {DisplaySystem} */ + display: null, + /* typehints:end */ }; this.systemUpdateOrder = []; @@ -137,6 +141,8 @@ export class GameSystemManager { // Wires must be after all gate, signal etc logic! add("wire", WireSystem); + add("display", DisplaySystem); + logger.log("📦 There are", this.systemUpdateOrder.length, "game systems"); } diff --git a/src/js/game/game_system_with_filter.js b/src/js/game/game_system_with_filter.js index ef6dfd52..7b1ffbf0 100644 --- a/src/js/game/game_system_with_filter.js +++ b/src/js/game/game_system_with_filter.js @@ -6,8 +6,7 @@ import { Entity } from "./entity"; import { GameRoot } from "./root"; import { GameSystem } from "./game_system"; import { arrayDelete, arrayDeleteValue } from "../core/utils"; -import { DrawParameters } from "../core/draw_parameters"; -import { globalConfig } from "../core/config"; + export class GameSystemWithFilter extends GameSystem { /** * Constructs a new game system with the given component filter. It will process @@ -35,80 +34,6 @@ export class GameSystemWithFilter extends GameSystem { this.root.signals.bulkOperationFinished.add(this.refreshCaches, this); } - /** - * Calls a function for each matching entity on the screen, useful for drawing them - * @param {DrawParameters} parameters - * @param {function} callback - */ - forEachMatchingEntityOnScreen(parameters, callback) { - const cullRange = parameters.visibleRect.toTileCullRectangle(); - if (this.allEntities.length < 100) { - // So, its much quicker to simply perform per-entity checking - - for (let i = 0; i < this.allEntities.length; ++i) { - const entity = this.allEntities[i]; - if (cullRange.containsRect(entity.components.StaticMapEntity.getTileSpaceBounds())) { - callback(parameters, entity); - } - } - return; - } - - const top = cullRange.top(); - const right = cullRange.right(); - const bottom = cullRange.bottom(); - const left = cullRange.left(); - - const border = 1; - const minY = top - border; - const maxY = bottom + border; - const minX = left - border; - const maxX = right + border - 1; - - const map = this.root.map; - - let seenUids = new Set(); - - const chunkStartX = Math.floor(minX / globalConfig.mapChunkSize); - const chunkStartY = Math.floor(minY / globalConfig.mapChunkSize); - - const chunkEndX = Math.ceil(maxX / globalConfig.mapChunkSize); - const chunkEndY = Math.ceil(maxY / globalConfig.mapChunkSize); - - const requiredComponents = this.requiredComponentIds; - - // Render y from top down for proper blending - for (let chunkX = chunkStartX; chunkX <= chunkEndX; ++chunkX) { - for (let chunkY = chunkStartY; chunkY <= chunkEndY; ++chunkY) { - const chunk = map.getChunk(chunkX, chunkY, false); - if (!chunk) { - continue; - } - - // BIG TODO: CULLING ON AN ENTITY BASIS - - const entities = chunk.containedEntities; - entityLoop: for (let i = 0; i < entities.length; ++i) { - const entity = entities[i]; - - // Avoid drawing twice - if (seenUids.has(entity.uid)) { - continue; - } - - seenUids.add(entity.uid); - - for (let i = 0; i < requiredComponents.length; ++i) { - if (!entity.components[requiredComponents[i]]) { - continue entityLoop; - } - } - callback(parameters, entity); - } - } - } - } - /** * @param {Entity} entity */ diff --git a/src/js/game/hub_goals.js b/src/js/game/hub_goals.js index d946ce51..8adee905 100644 --- a/src/js/game/hub_goals.js +++ b/src/js/game/hub_goals.js @@ -3,7 +3,7 @@ import { clamp, findNiceIntegerValue, randomChoice, randomInt } from "../core/ut import { BasicSerializableObject, types } from "../savegame/serialization"; import { enumColors } from "./colors"; import { enumItemProcessorTypes } from "./components/item_processor"; -import { GameRoot, enumLayer } from "./root"; +import { GameRoot } from "./root"; import { enumSubShape, ShapeDefinition } from "./shape_definition"; import { enumHubGoalRewards, tutorialGoals } from "./tutorial_goals"; import { UPGRADES } from "./upgrades"; diff --git a/src/js/game/hud/hud.js b/src/js/game/hud/hud.js index 1b5ec51a..c1aa146e 100644 --- a/src/js/game/hud/hud.js +++ b/src/js/game/hud/hud.js @@ -42,6 +42,7 @@ import { HUDSandboxController } from "./parts/sandbox_controller"; import { HUDWiresToolbar } from "./parts/wires_toolbar"; import { HUDWireInfo } from "./parts/wire_info"; import { HUDLeverToggle } from "./parts/lever_toggle"; +import { HUDLayerPreview } from "./parts/layer_preview"; export class GameHUD { /** @@ -80,6 +81,7 @@ export class GameHUD { shapeViewer: new HUDShapeViewer(this.root), wiresOverlay: new HUDWiresOverlay(this.root), + layerPreview: new HUDLayerPreview(this.root), // Typing hints /* typehints:start */ diff --git a/src/js/game/hud/parts/blueprint_placer.js b/src/js/game/hud/parts/blueprint_placer.js index 38341395..47bf1363 100644 --- a/src/js/game/hud/parts/blueprint_placer.js +++ b/src/js/game/hud/parts/blueprint_placer.js @@ -11,7 +11,6 @@ import { BaseHUDPart } from "../base_hud_part"; import { DynamicDomAttach } from "../dynamic_dom_attach"; import { Blueprint } from "../../blueprint"; import { SOUNDS } from "../../../platform/sound"; -import { enumLayer } from "../../root"; export class HUDBlueprintPlacer extends BaseHUDPart { createElements(parent) { @@ -60,7 +59,7 @@ export class HUDBlueprintPlacer extends BaseHUDPart { /** * Called when the layer was changed - * @param {enumLayer} layer + * @param {Layer} layer */ onEditModeChanged(layer) { // Check if the layer of the blueprint differs and thus we have to deselect it diff --git a/src/js/game/hud/parts/building_placer.js b/src/js/game/hud/parts/building_placer.js index a6a16aad..461a3431 100644 --- a/src/js/game/hud/parts/building_placer.js +++ b/src/js/game/hud/parts/building_placer.js @@ -18,7 +18,7 @@ import { THEME } from "../../theme"; import { DynamicDomAttach } from "../dynamic_dom_attach"; import { HUDBuildingPlacerLogic } from "./building_placer_logic"; import { makeOffscreenBuffer } from "../../../core/buffer_utils"; -import { enumLayer } from "../../root"; +import { layers } from "../../root"; import { getCodeFromBuildingData } from "../../building_codes"; export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { @@ -61,9 +61,9 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { this.currentInterpolatedCornerTile = new Vector(); this.lockIndicatorSprites = {}; - for (const layerId in enumLayer) { - this.lockIndicatorSprites[layerId] = this.makeLockIndicatorSprite(layerId); - } + layers.forEach(layer => { + this.lockIndicatorSprites[layer] = this.makeLockIndicatorSprite(layer); + }); // @@ -76,7 +76,7 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { /** * Makes the lock indicator sprite for the given layer - * @param {enumLayer} layer + * @param {Layer} layer */ makeLockIndicatorSprite(layer) { const dims = 48; @@ -247,6 +247,31 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { } else { this.drawRegularPlacement(parameters); } + + if (metaBuilding.getShowWiresLayerPreview()) { + this.drawLayerPeek(parameters); + } + } + + /** + * + * @param {DrawParameters} parameters + */ + drawLayerPeek(parameters) { + const mousePosition = this.root.app.mousePosition; + if (!mousePosition) { + // Not on screen + return; + } + + const worldPosition = this.root.camera.screenToWorld(mousePosition); + + // Draw peeker + this.root.hud.parts.layerPreview.renderPreview( + parameters, + worldPosition, + 1 / this.root.camera.zoomLevel + ); } /** @@ -349,7 +374,7 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { // HACK to draw the entity sprite const previewSprite = metaBuilding.getBlueprintSprite(rotationVariant, this.currentVariant.get()); staticComp.origin = worldPos.divideScalar(globalConfig.tileSize).subScalars(0.5, 0.5); - staticComp.drawSpriteOnFullEntityBounds(parameters, previewSprite); + staticComp.drawSpriteOnBoundsClipped(parameters, previewSprite); staticComp.origin = mouseTile; // Draw ejectors diff --git a/src/js/game/hud/parts/building_placer_logic.js b/src/js/game/hud/parts/building_placer_logic.js index b010ff40..898801c0 100644 --- a/src/js/game/hud/parts/building_placer_logic.js +++ b/src/js/game/hud/parts/building_placer_logic.js @@ -12,7 +12,6 @@ import { BaseHUDPart } from "../base_hud_part"; import { SOUNDS } from "../../../platform/sound"; import { MetaMinerBuilding, enumMinerVariants } from "../../buildings/miner"; import { enumHubGoalRewards } from "../../tutorial_goals"; -import { enumLayer } from "../../root"; import { getBuildingDataFromCode, getCodeFromBuildingData } from "../../building_codes"; import { MetaHubBuilding } from "../../buildings/hub"; @@ -133,12 +132,12 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { /** * Called when the edit mode got changed - * @param {enumLayer} editMode + * @param {Layer} layer */ - onEditModeChanged(editMode) { + onEditModeChanged(layer) { const metaBuilding = this.currentMetaBuilding.get(); if (metaBuilding) { - if (metaBuilding.getLayer() !== editMode) { + if (metaBuilding.getLayer() !== layer) { // This layer doesn't fit the edit mode anymore this.currentMetaBuilding.set(null); } diff --git a/src/js/game/hud/parts/buildings_toolbar.js b/src/js/game/hud/parts/buildings_toolbar.js index 5b8bc4fc..8c05ab70 100644 --- a/src/js/game/hud/parts/buildings_toolbar.js +++ b/src/js/game/hud/parts/buildings_toolbar.js @@ -8,10 +8,10 @@ import { MetaSplitterBuilding } from "../../buildings/splitter"; import { MetaStackerBuilding } from "../../buildings/stacker"; import { MetaTrashBuilding } from "../../buildings/trash"; import { MetaUndergroundBeltBuilding } from "../../buildings/underground_belt"; -import { enumLayer } from "../../root"; import { HUDBaseToolbar } from "./base_toolbar"; import { MetaLeverBuilding } from "../../buildings/lever"; import { MetaFilterBuilding } from "../../buildings/filter"; +import { MetaDisplayBuilding } from "../../buildings/display"; const supportedBuildings = [ MetaBeltBaseBuilding, @@ -26,6 +26,7 @@ const supportedBuildings = [ MetaTrashBuilding, MetaLeverBuilding, MetaFilterBuilding, + MetaDisplayBuilding, ]; export class HUDBuildingsToolbar extends HUDBaseToolbar { @@ -33,7 +34,7 @@ export class HUDBuildingsToolbar extends HUDBaseToolbar { super(root, { supportedBuildings, visibilityCondition: () => - !this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === enumLayer.regular, + !this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === "regular", htmlElementId: "ingame_HUD_buildings_toolbar", }); } diff --git a/src/js/game/hud/parts/color_blind_helper.js b/src/js/game/hud/parts/color_blind_helper.js index 46890979..e5572228 100644 --- a/src/js/game/hud/parts/color_blind_helper.js +++ b/src/js/game/hud/parts/color_blind_helper.js @@ -7,8 +7,6 @@ import { DrawParameters } from "../../../core/draw_parameters"; import { THEME } from "../../theme"; import { globalConfig } from "../../../core/config"; import { T } from "../../../translations"; -import { enumItemType } from "../../base_item"; -import { enumLayer } from "../../root"; export class HUDColorBlindHelper extends BaseHUDPart { createElements(parent) { @@ -41,7 +39,7 @@ export class HUDColorBlindHelper extends BaseHUDPart { return null; } - if (this.root.currentLayer !== enumLayer.regular) { + if (this.root.currentLayer !== "regular") { // Not in regular mode return null; } @@ -56,7 +54,7 @@ export class HUDColorBlindHelper extends BaseHUDPart { // Check if the belt has a color item if (beltComp) { const item = beltComp.assignedPath.findItemAtTile(tile); - if (item && item.getItemType() === enumItemType.color) { + if (item && item.getItemType() === "color") { return /** @type {ColorItem} */ (item).color; } } @@ -66,7 +64,7 @@ export class HUDColorBlindHelper extends BaseHUDPart { if (ejectorComp) { for (let i = 0; i < ejectorComp.slots.length; ++i) { const slot = ejectorComp.slots[i]; - if (slot.item && slot.item.getItemType() === enumItemType.color) { + if (slot.item && slot.item.getItemType() === "color") { return /** @type {ColorItem} */ (slot.item).color; } } @@ -74,7 +72,7 @@ export class HUDColorBlindHelper extends BaseHUDPart { } else { // We hovered a lower layer, show the color there const lowerLayer = this.root.map.getLowerLayerContentXY(tile.x, tile.y); - if (lowerLayer && lowerLayer.getItemType() === enumItemType.color) { + if (lowerLayer && lowerLayer.getItemType() === "color") { return /** @type {ColorItem} */ (lowerLayer).color; } } diff --git a/src/js/game/hud/parts/debug_changes.js b/src/js/game/hud/parts/debug_changes.js index 1502afa2..88de84fe 100644 --- a/src/js/game/hud/parts/debug_changes.js +++ b/src/js/game/hud/parts/debug_changes.js @@ -57,7 +57,7 @@ export class HUDChangesDebugger extends BaseHUDPart { for (let i = 0; i < this.changes.length; ++i) { const change = this.changes[i]; parameters.context.fillStyle = change.fillColor; - parameters.context.globalAlpha = 0.5; + parameters.context.globalAlpha = 0.2; parameters.context.fillRect( change.area.x * globalConfig.tileSize, change.area.y * globalConfig.tileSize, diff --git a/src/js/game/hud/parts/layer_preview.js b/src/js/game/hud/parts/layer_preview.js new file mode 100644 index 00000000..36e0ea58 --- /dev/null +++ b/src/js/game/hud/parts/layer_preview.js @@ -0,0 +1,123 @@ +import { freeCanvas, makeOffscreenBuffer } from "../../../core/buffer_utils"; +import { globalConfig } from "../../../core/config"; +import { Loader } from "../../../core/loader"; +import { Vector } from "../../../core/vector"; +import { MapChunkView } from "../../map_chunk_view"; +import { THEME } from "../../theme"; +import { BaseHUDPart } from "../base_hud_part"; + +/** + * Helper class which allows peaking through to the wires layer + */ +export class HUDLayerPreview extends BaseHUDPart { + initialize() { + this.initializeCanvas(); + this.root.signals.aboutToDestruct.add(() => freeCanvas(this.canvas)); + this.root.signals.resized.add(this.initializeCanvas, this); + this.previewOverlay = Loader.getSprite("sprites/wires/wires_preview.png"); + } + + /** + * (re) initializes the canvas + */ + initializeCanvas() { + if (this.canvas) { + freeCanvas(this.canvas); + delete this.canvas; + delete this.context; + } + + // Compute how big the preview should be + this.previewSize = Math.round( + Math.min(1024, Math.min(this.root.gameWidth, this.root.gameHeight) * 0.8) + ); + + const [canvas, context] = makeOffscreenBuffer(this.previewSize, this.previewSize, { + smooth: true, + label: "layerPeeker", + reusable: true, + }); + + context.clearRect(0, 0, this.previewSize, this.previewSize); + this.canvas = canvas; + this.context = context; + } + + /** + * Prepares the canvas to render at the given worldPos and the given camera scale + * + * @param {Vector} worldPos + * @param {number} scale 1 / zoomLevel + */ + prepareCanvasForPreview(worldPos, scale) { + this.context.clearRect(0, 0, this.previewSize, this.previewSize); + this.context.fillStyle = THEME.map.wires.previewColor; + this.context.fillRect(0, 0, this.previewSize, this.previewSize); + + const dimensions = scale * this.previewSize; + + const startWorldX = worldPos.x - dimensions / 2; + const startWorldY = worldPos.y - dimensions / 2; + + const startTileX = Math.floor(startWorldX / globalConfig.tileSize); + const startTileY = Math.floor(startWorldY / globalConfig.tileSize); + const tileDimensions = Math.ceil(dimensions / globalConfig.tileSize); + + this.context.save(); + this.context.scale(1 / scale, 1 / scale); + this.context.translate( + startTileX * globalConfig.tileSize - startWorldX, + startTileY * globalConfig.tileSize - startWorldY + ); + + for (let dx = 0; dx < tileDimensions; ++dx) { + for (let dy = 0; dy < tileDimensions; ++dy) { + const tileX = dx + startTileX; + const tileY = dy + startTileY; + + const content = this.root.map.getLayerContentXY(tileX, tileY, "wires"); + if (content) { + MapChunkView.drawSingleWiresOverviewTile({ + context: this.context, + x: dx * globalConfig.tileSize, + y: dy * globalConfig.tileSize, + entity: content, + tileSizePixels: globalConfig.tileSize, + }); + } + } + } + + this.context.restore(); + this.context.globalCompositeOperation = "destination-in"; + this.previewOverlay.draw(this.context, 0, 0, this.previewSize, this.previewSize); + this.context.globalCompositeOperation = "source-over"; + + return this.canvas; + } + + /** + * Renders the preview at the given position + * @param {import("../../../core/draw_utils").DrawParameters} parameters + * @param {Vector} worldPos + * @param {number} scale 1 / zoomLevel + */ + renderPreview(parameters, worldPos, scale) { + if (this.root.currentLayer !== "regular") { + // Only supporting wires right now + return; + } + + const canvas = this.prepareCanvasForPreview(worldPos, scale); + + parameters.context.globalAlpha = 0.3; + parameters.context.drawImage( + canvas, + worldPos.x - (scale * this.previewSize) / 2, + worldPos.y - (scale * this.previewSize) / 2, + scale * this.previewSize, + scale * this.previewSize + ); + parameters.context.globalAlpha = 1; + } +} diff --git a/src/js/game/hud/parts/lever_toggle.js b/src/js/game/hud/parts/lever_toggle.js index 8458022f..1859f7ab 100644 --- a/src/js/game/hud/parts/lever_toggle.js +++ b/src/js/game/hud/parts/lever_toggle.js @@ -1,7 +1,6 @@ import { STOP_PROPAGATION } from "../../../core/signal"; import { Vector } from "../../../core/vector"; import { enumMouseButton } from "../../camera"; -import { enumLayer } from "../../root"; import { BaseHUDPart } from "../base_hud_part"; export class HUDLeverToggle extends BaseHUDPart { @@ -15,7 +14,7 @@ export class HUDLeverToggle extends BaseHUDPart { */ downPreHandler(pos, button) { const tile = this.root.camera.screenToWorld(pos).toTileSpace(); - const contents = this.root.map.getLayerContentXY(tile.x, tile.y, enumLayer.regular); + const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "regular"); if (contents) { const leverComp = contents.components.Lever; if (leverComp) { diff --git a/src/js/game/hud/parts/sandbox_controller.js b/src/js/game/hud/parts/sandbox_controller.js index 04773019..dd521655 100644 --- a/src/js/game/hud/parts/sandbox_controller.js +++ b/src/js/game/hud/parts/sandbox_controller.js @@ -78,7 +78,7 @@ export class HUDSandboxController extends BaseHUDPart { if (!this.root.hubGoals.storedShapes[blueprintShape]) { this.root.hubGoals.storedShapes[blueprintShape] = 0; } - this.root.hubGoals.storedShapes[blueprintShape] += 1e4; + this.root.hubGoals.storedShapes[blueprintShape] += 1e9; } maxOutAll() { diff --git a/src/js/game/hud/parts/wire_info.js b/src/js/game/hud/parts/wire_info.js index b15804a0..9394dcc2 100644 --- a/src/js/game/hud/parts/wire_info.js +++ b/src/js/game/hud/parts/wire_info.js @@ -1,16 +1,22 @@ -import { BaseHUDPart } from "../base_hud_part"; -import { enumLayer } from "../../root"; import { globalConfig } from "../../../core/config"; +import { MapChunkView } from "../../map_chunk_view"; +import { WireNetwork } from "../../systems/wire"; +import { THEME } from "../../theme"; +import { BaseHUDPart } from "../base_hud_part"; +import { Loader } from "../../../core/loader"; export class HUDWireInfo extends BaseHUDPart { - initialize() {} + initialize() { + this.spriteEmpty = Loader.getSprite("sprites/wires/network_empty.png"); + this.spriteConflict = Loader.getSprite("sprites/wires/network_conflict.png"); + } /** * * @param {import("../../../core/draw_utils").DrawParameters} parameters */ drawOverlays(parameters) { - if (this.root.currentLayer !== enumLayer.wires) { + if (this.root.currentLayer !== "wires") { // Not in the wires layer return; } @@ -21,38 +27,93 @@ export class HUDWireInfo extends BaseHUDPart { return; } - const tile = this.root.camera.screenToWorld(mousePos).toTileSpace(); - const entity = this.root.map.getLayerContentXY(tile.x, tile.y, enumLayer.wires); + const worldPos = this.root.camera.screenToWorld(mousePos); + const tile = worldPos.toTileSpace(); + const entity = this.root.map.getLayerContentXY(tile.x, tile.y, "wires"); - if (entity) { - const wireComp = entity.components.Wire; - if (wireComp) { - const screenTile = this.root.camera.worldToScreen(tile.toWorldSpace()); - parameters.context.fillStyle = "rgba(0, 0, 0, 0.1)"; - parameters.context.fillRect( - screenTile.x, - screenTile.y, - globalConfig.tileSize * this.root.camera.zoomLevel, - globalConfig.tileSize * this.root.camera.zoomLevel + if (!entity) { + // No entity + return; + } + + if ( + !this.root.camera.getIsMapOverlayActive() && + !this.root.logic.getIsEntityIntersectedWithMatrix(entity, worldPos) + ) { + // Detailed intersection check + return; + } + + const networks = this.root.logic.getEntityWireNetworks(entity, tile); + if (networks === null) { + // This entity will never be able to be connected + return; + } + + if (networks.length === 0) { + // No network at all + return; + } + + for (let i = 0; i < networks.length; ++i) { + const network = networks[i]; + this.drawHighlightedNetwork(parameters, network); + } + + if (networks.length === 1) { + const network = networks[0]; + + if (network.valueConflict) { + this.spriteConflict.draw(parameters.context, mousePos.x + 15, mousePos.y - 10, 60, 60); + } else if (!network.currentValue) { + this.spriteEmpty.draw(parameters.context, mousePos.x + 15, mousePos.y - 10, 60, 60); + } else { + network.currentValue.drawItemCenteredClipped( + mousePos.x + 40, + mousePos.y + 10, + parameters, + 60 ); - - parameters.context.font = "25px GameFont"; - const network = wireComp.linkedNetwork; - if (!network) { - parameters.context.fillStyle = "#333"; - parameters.context.fillText("empty", mousePos.x, mousePos.y); - } else { - if (network.valueConflict) { - parameters.context.fillStyle = "#a10"; - parameters.context.fillText("conflict", mousePos.x, mousePos.y); - } else if (!network.currentValue) { - parameters.context.fillStyle = "#333"; - parameters.context.fillText("empty", mousePos.x, mousePos.y); - } else { - network.currentValue.draw(mousePos.x + 20, mousePos.y, parameters, 40); - } - } } } } + + /** + * + * + * @param {import("../../../core/draw_utils").DrawParameters} parameters + * @param {WireNetwork} network + */ + drawHighlightedNetwork(parameters, network) { + parameters.context.globalAlpha = 0.5; + + for (let i = 0; i < network.wires.length; ++i) { + const wire = network.wires[i]; + const staticComp = wire.components.StaticMapEntity; + const screenTile = this.root.camera.worldToScreen(staticComp.origin.toWorldSpace()); + MapChunkView.drawSingleWiresOverviewTile({ + context: parameters.context, + x: screenTile.x, + y: screenTile.y, + entity: wire, + tileSizePixels: globalConfig.tileSize * this.root.camera.zoomLevel, + overrideColor: THEME.map.wires.highlightColor, + }); + } + + for (let i = 0; i < network.tunnels.length; ++i) { + const tunnel = network.tunnels[i]; + const staticComp = tunnel.components.StaticMapEntity; + const screenTile = this.root.camera.worldToScreen(staticComp.origin.toWorldSpace()); + MapChunkView.drawSingleWiresOverviewTile({ + context: parameters.context, + x: screenTile.x, + y: screenTile.y, + entity: tunnel, + tileSizePixels: globalConfig.tileSize * this.root.camera.zoomLevel, + overrideColor: THEME.map.wires.highlightColor, + }); + } + parameters.context.globalAlpha = 1; + } } diff --git a/src/js/game/hud/parts/wires_overlay.js b/src/js/game/hud/parts/wires_overlay.js index 10652e11..364002fd 100644 --- a/src/js/game/hud/parts/wires_overlay.js +++ b/src/js/game/hud/parts/wires_overlay.js @@ -2,7 +2,6 @@ import { makeOffscreenBuffer } from "../../../core/buffer_utils"; import { globalConfig } from "../../../core/config"; import { DrawParameters } from "../../../core/draw_parameters"; import { KEYMAPPINGS } from "../../key_action_mapper"; -import { enumLayer } from "../../root"; import { THEME } from "../../theme"; import { BaseHUDPart } from "../base_hud_part"; import { Loader } from "../../../core/loader"; @@ -26,10 +25,10 @@ export class HUDWiresOverlay extends BaseHUDPart { * Switches between layers */ switchLayers() { - if (this.root.currentLayer === enumLayer.regular) { - this.root.currentLayer = enumLayer.wires; + if (this.root.currentLayer === "regular") { + this.root.currentLayer = "wires"; } else { - this.root.currentLayer = enumLayer.regular; + this.root.currentLayer = "regular"; } this.root.signals.editModeChanged.dispatch(this.root.currentLayer); } @@ -51,7 +50,7 @@ export class HUDWiresOverlay extends BaseHUDPart { } update() { - const desiredAlpha = this.root.currentLayer === enumLayer.wires ? 1.0 : 0.0; + const desiredAlpha = this.root.currentLayer === "wires" ? 1.0 : 0.0; this.currentAlpha = lerp(this.currentAlpha, desiredAlpha, 0.12); } diff --git a/src/js/game/hud/parts/wires_toolbar.js b/src/js/game/hud/parts/wires_toolbar.js index 35ed21e8..542218e2 100644 --- a/src/js/game/hud/parts/wires_toolbar.js +++ b/src/js/game/hud/parts/wires_toolbar.js @@ -1,4 +1,3 @@ -import { enumLayer } from "../../root"; import { HUDBaseToolbar } from "./base_toolbar"; import { MetaWireBuilding } from "../../buildings/wire"; import { MetaConstantSignalBuilding } from "../../buildings/constant_signal"; @@ -19,7 +18,7 @@ export class HUDWiresToolbar extends HUDBaseToolbar { super(root, { supportedBuildings, visibilityCondition: () => - !this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === enumLayer.wires, + !this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === "wires", htmlElementId: "ingame_HUD_wires_toolbar", }); } diff --git a/src/js/game/hud/trailer_maker.js b/src/js/game/hud/trailer_maker.js index cb81aab3..8655def4 100644 --- a/src/js/game/hud/trailer_maker.js +++ b/src/js/game/hud/trailer_maker.js @@ -1,4 +1,4 @@ -import { GameRoot, enumLayer } from "../root"; +import { GameRoot } from "../root"; import { globalConfig } from "../../core/config"; import { Vector, mixVector } from "../../core/vector"; import { lerp } from "../../core/utils"; diff --git a/src/js/game/items/boolean_item.js b/src/js/game/items/boolean_item.js index 8da1d449..38422398 100644 --- a/src/js/game/items/boolean_item.js +++ b/src/js/game/items/boolean_item.js @@ -1,7 +1,8 @@ import { DrawParameters } from "../../core/draw_parameters"; import { Loader } from "../../core/loader"; import { types } from "../../savegame/serialization"; -import { BaseItem, enumItemType } from "../base_item"; +import { BaseItem } from "../base_item"; +import { globalConfig } from "../../core/config"; export class BooleanItem extends BaseItem { static getId() { @@ -20,8 +21,9 @@ export class BooleanItem extends BaseItem { this.value = data; } + /** @returns {"boolean"} **/ getItemType() { - return enumItemType.boolean; + return "boolean"; } /** @@ -42,17 +44,17 @@ export class BooleanItem extends BaseItem { /** * @param {number} x * @param {number} y - * @param {number} size + * @param {number} diameter * @param {DrawParameters} parameters */ - draw(x, y, parameters, size = 12) { + drawItemCenteredImpl(x, y, parameters, diameter = globalConfig.defaultItemDiameter) { let sprite; if (this.value) { sprite = Loader.getSprite("sprites/wires/boolean_true.png"); } else { sprite = Loader.getSprite("sprites/wires/boolean_false.png"); } - sprite.drawCachedCentered(parameters, x, y, size * 1.5); + sprite.drawCachedCentered(parameters, x, y, diameter); } } diff --git a/src/js/game/items/color_item.js b/src/js/game/items/color_item.js index 009b310b..19d26286 100644 --- a/src/js/game/items/color_item.js +++ b/src/js/game/items/color_item.js @@ -2,9 +2,10 @@ import { globalConfig } from "../../core/config"; import { smoothenDpi } from "../../core/dpi_manager"; import { DrawParameters } from "../../core/draw_parameters"; import { types } from "../../savegame/serialization"; -import { BaseItem, enumItemType } from "../base_item"; +import { BaseItem } from "../base_item"; import { enumColors, enumColorsToHexCode } from "../colors"; import { THEME } from "../theme"; +import { drawSpriteClipped } from "../../core/draw_utils"; export class ColorItem extends BaseItem { static getId() { @@ -23,8 +24,9 @@ export class ColorItem extends BaseItem { this.color = data; } + /** @returns {"color"} **/ getItemType() { - return enumItemType.color; + return "color"; } /** @@ -50,26 +52,36 @@ export class ColorItem extends BaseItem { /** * @param {number} x * @param {number} y - * @param {number} size + * @param {number} diameter * @param {DrawParameters} parameters */ - draw(x, y, parameters, size = 12) { + drawItemCenteredImpl(x, y, parameters, diameter = globalConfig.defaultItemDiameter) { if (!this.bufferGenerator) { this.bufferGenerator = this.internalGenerateColorBuffer.bind(this); } + const realDiameter = diameter * 0.6; const dpi = smoothenDpi(globalConfig.shapesSharpness * parameters.zoomLevel); - - const key = size + "/" + dpi; + const key = realDiameter + "/" + dpi + "/" + this.color; const canvas = parameters.root.buffers.getForKey({ - key, - subKey: this.color, - w: size, - h: size, + key: "coloritem", + subKey: key, + w: realDiameter, + h: realDiameter, dpi, redrawMethod: this.bufferGenerator, }); - parameters.context.drawImage(canvas, x - size / 2, y - size / 2, size, size); + + drawSpriteClipped({ + parameters, + sprite: canvas, + x: x - realDiameter / 2, + y: y - realDiameter / 2, + w: realDiameter, + h: realDiameter, + originalW: realDiameter * dpi, + originalH: realDiameter * dpi, + }); } /** * diff --git a/src/js/game/items/shape_item.js b/src/js/game/items/shape_item.js index 44a0a3ad..d99a7251 100644 --- a/src/js/game/items/shape_item.js +++ b/src/js/game/items/shape_item.js @@ -1,8 +1,9 @@ import { DrawParameters } from "../../core/draw_parameters"; import { types } from "../../savegame/serialization"; -import { BaseItem, enumItemType } from "../base_item"; +import { BaseItem } from "../base_item"; import { ShapeDefinition } from "../shape_definition"; import { THEME } from "../theme"; +import { globalConfig } from "../../core/config"; export class ShapeItem extends BaseItem { static getId() { @@ -21,8 +22,9 @@ export class ShapeItem extends BaseItem { this.definition = ShapeDefinition.fromShortKey(data); } + /** @returns {"shape"} **/ getItemType() { - return enumItemType.shape; + return "shape"; } /** @@ -52,9 +54,9 @@ export class ShapeItem extends BaseItem { * @param {number} x * @param {number} y * @param {DrawParameters} parameters - * @param {number=} size + * @param {number=} diameter */ - draw(x, y, parameters, size) { - this.definition.draw(x, y, parameters, size); + drawItemCenteredImpl(x, y, parameters, diameter = globalConfig.defaultItemDiameter) { + this.definition.drawCentered(x, y, parameters, diameter); } } diff --git a/src/js/game/key_action_mapper.js b/src/js/game/key_action_mapper.js index a5b05811..f88e5a22 100644 --- a/src/js/game/key_action_mapper.js +++ b/src/js/game/key_action_mapper.js @@ -55,12 +55,14 @@ export const KEYMAPPINGS = { painter: { keyCode: key("9") }, trash: { keyCode: key("0") }, + lever: { keyCode: key("L") }, + filter: { keyCode: key("B") }, + display: { keyCode: key("N") }, + wire: { keyCode: key("1") }, wire_tunnel: { keyCode: key("2") }, constant_signal: { keyCode: key("3") }, logic_gate: { keyCode: key("4") }, - lever: { keyCode: key("5") }, - filter: { keyCode: key("6") }, }, placement: { diff --git a/src/js/game/logic.js b/src/js/game/logic.js index 28690baf..ce4d18a5 100644 --- a/src/js/game/logic.js +++ b/src/js/game/logic.js @@ -1,23 +1,20 @@ import { createLogger } from "../core/logging"; import { STOP_PROPAGATION } from "../core/signal"; import { round2Digits } from "../core/utils"; -import { - enumDirection, - enumDirectionToAngle, - enumDirectionToVector, - enumInvertedDirections, - Vector, -} from "../core/vector"; +import { enumDirection, enumDirectionToVector, enumInvertedDirections, Vector } from "../core/vector"; +import { getBuildingDataFromCode } from "./building_codes"; import { Entity } from "./entity"; import { MetaBuilding } from "./meta_building"; -import { enumLayer, GameRoot } from "./root"; +import { GameRoot } from "./root"; +import { WireNetwork } from "./systems/wire"; +import { globalConfig } from "../core/config"; +import { CHUNK_OVERLAY_RES } from "./map_chunk_view"; const logger = createLogger("ingame/logic"); /** @enum {number} */ export const enumWireEdgeFlag = { empty: 0, - filled: 1, connected: 2, }; @@ -215,15 +212,97 @@ export class GameLogic { return false; } - if (neighbourStatus === enumWireEdgeFlag.filled) { - return true; - } - if (neighbourStatus === enumWireEdgeFlag.connected) { return true; } } + /** + * Returns all wire networks this entity participates in on the given tile + * @param {Entity} entity + * @param {Vector} tile + * @returns {Array|null} Null if the entity is never able to be connected at the given tile + */ + getEntityWireNetworks(entity, tile) { + let canConnectAtAll = false; + + /** @type {Set} */ + const networks = new Set(); + + const staticComp = entity.components.StaticMapEntity; + const wireComp = entity.components.Wire; + if (wireComp) { + canConnectAtAll = true; + if (wireComp.linkedNetwork) { + networks.add(wireComp.linkedNetwork); + } + } + + const tunnelComp = entity.components.WireTunnel; + if (tunnelComp) { + canConnectAtAll = true; + for (let i = 0; i < tunnelComp.linkedNetworks.length; ++i) { + networks.add(tunnelComp.linkedNetworks[i]); + } + } + + const pinsComp = entity.components.WiredPins; + if (pinsComp) { + const slots = pinsComp.slots; + for (let i = 0; i < slots.length; ++i) { + const slot = slots[i]; + const slotLocalPos = staticComp.localTileToWorld(slot.pos); + if (slotLocalPos.equals(tile)) { + canConnectAtAll = true; + if (slot.linkedNetwork) { + networks.add(slot.linkedNetwork); + } + } + } + } + + if (!canConnectAtAll) { + return null; + } + + return Array.from(networks); + } + + /** + * Returns if the entities tile *and* his overlay matrix is intersected + * @param {Entity} entity + * @param {Vector} worldPos + */ + getIsEntityIntersectedWithMatrix(entity, worldPos) { + const staticComp = entity.components.StaticMapEntity; + const tile = worldPos.toTileSpace(); + + if (!staticComp.getTileSpaceBounds().containsPoint(tile.x, tile.y)) { + // No intersection at all + return; + } + + const data = getBuildingDataFromCode(staticComp.code); + const overlayMatrix = data.metaInstance.getSpecialOverlayRenderMatrix( + staticComp.rotation, + data.rotationVariant, + data.variant, + entity + ); + // Always the same + if (!overlayMatrix) { + return true; + } + + const localPosition = worldPos + .divideScalar(globalConfig.tileSize) + .modScalar(1) + .multiplyScalar(CHUNK_OVERLAY_RES) + .floor(); + + return !!overlayMatrix[localPosition.x + localPosition.y * 3]; + } + /** * Gets the flag at the given tile * @param {Vector} tile @@ -268,15 +347,26 @@ export class GameLogic { } // Now check if there's a connectable wire - const targetEntity = this.root.map.getTileContent(tile, enumLayer.wires); + const targetEntity = this.root.map.getTileContent(tile, "wires"); if (!targetEntity) { return enumWireEdgeFlag.empty; } + const targetStaticComp = targetEntity.components.StaticMapEntity; + // Check if its a crossing const wireTunnelComp = targetEntity.components.WireTunnel; if (wireTunnelComp) { - return enumWireEdgeFlag.filled; + // Check if the crossing is connected + if (wireTunnelComp.multipleDirections) { + return enumWireEdgeFlag.connected; + } else { + // Its a coating, check if it matches the direction + const referenceDirection = targetStaticComp.localDirectionToWorld(enumDirection.top); + return referenceDirection === edge || enumInvertedDirections[referenceDirection] === edge + ? enumWireEdgeFlag.connected + : enumWireEdgeFlag.empty; + } } // Check if its a wire @@ -285,15 +375,9 @@ export class GameLogic { return enumWireEdgeFlag.empty; } - const refAngle = enumDirectionToAngle[edge]; - const refRotation = targetEntity.components.StaticMapEntity.originalRotation; - const canConnectRemotely = refRotation === refAngle || (refRotation + 180) % 360 === refAngle; - - // Check if the wire points towards the right direction - if (!canConnectRemotely) { - // Seems its not the right direction - well, still its filled - return enumWireEdgeFlag.filled; - } + // const refAngle = enumDirectionToAngle[edge]; + // const refRotation = targetEntity.components.StaticMapEntity.originalRotation; + // const canConnectRemotely = refRotation === refAngle || (refRotation + 180) % 360 === refAngle; // Actually connected return enumWireEdgeFlag.connected; @@ -317,7 +401,7 @@ export class GameLogic { continue; } - const entity = this.root.map.getLayerContentXY(tile.x + dx, tile.y + dy, enumLayer.regular); + const entity = this.root.map.getLayerContentXY(tile.x + dx, tile.y + dy, "regular"); if (entity) { let ejectorSlots = []; let acceptorSlots = []; diff --git a/src/js/game/map.js b/src/js/game/map.js index b0992627..5ff51ce8 100644 --- a/src/js/game/map.js +++ b/src/js/game/map.js @@ -1,13 +1,10 @@ -import { GameRoot, enumLayer } from "./root"; import { globalConfig } from "../core/config"; import { Vector } from "../core/vector"; -import { Entity } from "./entity"; -import { createLogger } from "../core/logging"; -import { BaseItem } from "./base_item"; -import { MapChunkView } from "./map_chunk_view"; import { BasicSerializableObject, types } from "../savegame/serialization"; - -const logger = createLogger("map"); +import { BaseItem } from "./base_item"; +import { Entity } from "./entity"; +import { MapChunkView } from "./map_chunk_view"; +import { GameRoot } from "./root"; export class BaseMap extends BasicSerializableObject { static getId() { @@ -97,7 +94,7 @@ export class BaseMap extends BasicSerializableObject { /** * Returns the tile content of a given tile * @param {Vector} tile - * @param {enumLayer} layer + * @param {Layer} layer * @returns {Entity} Entity or null */ getTileContent(tile, layer) { @@ -122,7 +119,7 @@ export class BaseMap extends BasicSerializableObject { * Returns the tile content of a given tile * @param {number} x * @param {number} y - * @param {enumLayer} layer + * @param {Layer} layer * @returns {Entity} Entity or null */ getLayerContentXY(x, y, layer) { @@ -147,7 +144,7 @@ export class BaseMap extends BasicSerializableObject { /** * Checks if the tile is used * @param {Vector} tile - * @param {enumLayer} layer + * @param {Layer} layer * @returns {boolean} */ isTileUsed(tile, layer) { @@ -162,7 +159,7 @@ export class BaseMap extends BasicSerializableObject { * Checks if the tile is used * @param {number} x * @param {number} y - * @param {enumLayer} layer + * @param {Layer} layer * @returns {boolean} */ isTileUsedXY(x, y, layer) { diff --git a/src/js/game/map_chunk.js b/src/js/game/map_chunk.js index 189a62cd..54af1125 100644 --- a/src/js/game/map_chunk.js +++ b/src/js/game/map_chunk.js @@ -7,8 +7,9 @@ import { BaseItem } from "./base_item"; import { enumColors } from "./colors"; import { Entity } from "./entity"; import { COLOR_ITEM_SINGLETONS } from "./items/color_item"; -import { enumLayer, GameRoot } from "./root"; +import { GameRoot } from "./root"; import { enumSubShape } from "./shape_definition"; +import { Rectangle } from "../core/rectangle"; const logger = createLogger("map_chunk"); @@ -26,25 +27,54 @@ export class MapChunk { this.tileX = x * globalConfig.mapChunkSize; this.tileY = y * globalConfig.mapChunkSize; - /** @type {Array>} */ + /** + * Stores the contents of the lower (= map resources) layer + * @type {Array>} + */ + this.lowerLayer = make2DUndefinedArray(globalConfig.mapChunkSize, globalConfig.mapChunkSize); + + /** + * Stores the contents of the regular layer + * @type {Array>} + */ this.contents = make2DUndefinedArray(globalConfig.mapChunkSize, globalConfig.mapChunkSize); - /** @type {Array>} */ + /** + * Stores the contents of the wires layer + * @type {Array>} + */ this.wireContents = make2DUndefinedArray(globalConfig.mapChunkSize, globalConfig.mapChunkSize); - /** @type {Array>} */ - this.lowerLayer = make2DUndefinedArray(globalConfig.mapChunkSize, globalConfig.mapChunkSize); - /** @type {Array} */ this.containedEntities = []; + /** + * World space rectangle, can be used for culling + */ + this.worldSpaceRectangle = new Rectangle( + this.tileX * globalConfig.tileSize, + this.tileY * globalConfig.tileSize, + globalConfig.mapChunkWorldSize, + globalConfig.mapChunkWorldSize + ); + + /** + * Tile space rectangle, can be used for culling + */ + this.tileSpaceRectangle = new Rectangle( + this.tileX, + this.tileY, + globalConfig.mapChunkSize, + globalConfig.mapChunkSize + ); + /** * Which entities this chunk contains, sorted by layer - * @type {Object>} + * @type {Record>} */ this.containedEntitiesByLayer = { - [enumLayer.regular]: [], - [enumLayer.wires]: [], + regular: [], + wires: [], }; /** @@ -332,7 +362,7 @@ export class MapChunk { * Returns the contents of this chunk from the given world space coordinates * @param {number} worldX * @param {number} worldY - * @param {enumLayer} layer + * @param {Layer} layer * @returns {Entity=} */ getLayerContentFromWorldCoords(worldX, worldY, layer) { @@ -342,7 +372,7 @@ export class MapChunk { assert(localY >= 0, "Local Y is < 0"); assert(localX < globalConfig.mapChunkSize, "Local X is >= chunk size"); assert(localY < globalConfig.mapChunkSize, "Local Y is >= chunk size"); - if (layer === enumLayer.regular) { + if (layer === "regular") { return this.contents[localX][localY] || null; } else { return this.wireContents[localX][localY] || null; @@ -395,7 +425,7 @@ export class MapChunk { * @param {number} tileX * @param {number} tileY * @param {Entity=} contents - * @param {enumLayer} layer + * @param {Layer} layer */ setLayerContentFromWorldCords(tileX, tileY, contents, layer) { const localX = tileX - this.tileX; @@ -406,7 +436,7 @@ export class MapChunk { assert(localY < globalConfig.mapChunkSize, "Local Y is >= chunk size"); let oldContents; - if (layer === enumLayer.regular) { + if (layer === "regular") { oldContents = this.contents[localX][localY]; } else { oldContents = this.wireContents[localX][localY]; @@ -420,7 +450,7 @@ export class MapChunk { fastArrayDeleteValueIfContained(this.containedEntitiesByLayer[layer], oldContents); } - if (layer === enumLayer.regular) { + if (layer === "regular") { this.contents[localX][localY] = contents; } else { this.wireContents[localX][localY] = contents; diff --git a/src/js/game/map_chunk_view.js b/src/js/game/map_chunk_view.js index 17d4d4b8..0918e7af 100644 --- a/src/js/game/map_chunk_view.js +++ b/src/js/game/map_chunk_view.js @@ -1,12 +1,13 @@ -import { MapChunk } from "./map_chunk"; -import { GameRoot, enumLayer } from "./root"; -import { DrawParameters } from "../core/draw_parameters"; -import { smoothenDpi } from "../core/dpi_manager"; import { globalConfig } from "../core/config"; -import { THEME } from "./theme"; +import { DrawParameters } from "../core/draw_parameters"; import { getBuildingDataFromCode } from "./building_codes"; +import { Entity } from "./entity"; +import { MapChunk } from "./map_chunk"; +import { GameRoot } from "./root"; +import { THEME } from "./theme"; +import { drawSpriteClipped } from "../core/draw_utils"; -const CHUNK_OVERLAY_RES = 3; +export const CHUNK_OVERLAY_RES = 3; export class MapChunkView extends MapChunk { /** @@ -41,6 +42,7 @@ export class MapChunkView extends MapChunk { drawBackgroundLayer(parameters) { const systems = this.root.systemMgr.systems; systems.mapResources.drawChunk(parameters, this); + systems.beltUnderlays.drawChunk(parameters, this); systems.belt.drawChunk(parameters, this); } @@ -50,9 +52,16 @@ export class MapChunkView extends MapChunk { */ drawForegroundLayer(parameters) { const systems = this.root.systemMgr.systems; + + systems.itemEjector.drawChunk(parameters, this); + systems.itemAcceptor.drawChunk(parameters, this); + systems.miner.drawChunk(parameters, this); + systems.staticMapEntities.drawChunk(parameters, this); systems.lever.drawChunk(parameters, this); + systems.display.drawChunk(parameters, this); + systems.storage.drawChunk(parameters, this); } /** @@ -60,11 +69,12 @@ export class MapChunkView extends MapChunk { * @param {DrawParameters} parameters */ drawOverlay(parameters) { + const overlaySize = globalConfig.mapChunkSize * CHUNK_OVERLAY_RES; const sprite = this.root.buffers.getForKey({ key: "chunk@" + this.root.currentLayer, subKey: this.renderKey, - w: globalConfig.mapChunkSize * CHUNK_OVERLAY_RES, - h: globalConfig.mapChunkSize * CHUNK_OVERLAY_RES, + w: overlaySize, + h: overlaySize, dpi: 1, redrawMethod: this.generateOverlayBuffer.bind(this), }); @@ -73,20 +83,29 @@ export class MapChunkView extends MapChunk { // Draw chunk "pixel" art parameters.context.imageSmoothingEnabled = false; - parameters.context.drawImage(sprite, this.x * dims, this.y * dims, dims, dims); + drawSpriteClipped({ + parameters, + sprite, + x: this.x * dims, + y: this.y * dims, + w: dims, + h: dims, + originalW: overlaySize, + originalH: overlaySize, + }); + parameters.context.imageSmoothingEnabled = true; // Draw patch items - if (this.root.currentLayer === enumLayer.regular) { + if (this.root.currentLayer === "regular") { for (let i = 0; i < this.patches.length; ++i) { const patch = this.patches[i]; - patch.item.draw( - this.x * dims + patch.pos.x * globalConfig.tileSize, - this.y * dims + patch.pos.y * globalConfig.tileSize, - parameters, - Math.min(80, 30 / parameters.zoomLevel) - ); + const destX = this.x * dims + patch.pos.x * globalConfig.tileSize; + const destY = this.y * dims + patch.pos.y * globalConfig.tileSize; + const diameter = Math.min(80, 30 / parameters.zoomLevel); + + patch.item.drawItemCenteredClipped(destX, destY, parameters, diameter); } } } @@ -178,7 +197,7 @@ export class MapChunkView extends MapChunk { } } - if (this.root.currentLayer === enumLayer.wires) { + if (this.root.currentLayer === "wires") { // Draw wires overlay context.fillStyle = THEME.map.wires.overlayColor; @@ -191,46 +210,54 @@ export class MapChunkView extends MapChunk { if (!content) { continue; } - const staticComp = content.components.StaticMapEntity; - const data = getBuildingDataFromCode(staticComp.code); - const metaBuilding = data.metaInstance; + MapChunkView.drawSingleWiresOverviewTile({ + context, + x: x * CHUNK_OVERLAY_RES, + y: y * CHUNK_OVERLAY_RES, + entity: content, + tileSizePixels: CHUNK_OVERLAY_RES, + }); + } + } + } + } - const overlayMatrix = metaBuilding.getSpecialOverlayRenderMatrix( - staticComp.rotation, - data.rotationVariant, - data.variant, - content - ); - - context.fillStyle = metaBuilding.getSilhouetteColor(); - if (overlayMatrix) { - for (let dx = 0; dx < 3; ++dx) { - for (let dy = 0; dy < 3; ++dy) { - const isFilled = overlayMatrix[dx + dy * 3]; - if (isFilled) { - context.fillRect( - x * CHUNK_OVERLAY_RES + dx, - y * CHUNK_OVERLAY_RES + dy, - 1, - 1 - ); - } - } - } - - continue; - } else { + /** + * @param {object} param0 + * @param {CanvasRenderingContext2D} param0.context + * @param {number} param0.x + * @param {number} param0.y + * @param {Entity} param0.entity + * @param {number} param0.tileSizePixels + * @param {string=} param0.overrideColor Optionally override the color to be rendered + */ + static drawSingleWiresOverviewTile({ context, x, y, entity, tileSizePixels, overrideColor = null }) { + const staticComp = entity.components.StaticMapEntity; + const data = getBuildingDataFromCode(staticComp.code); + const metaBuilding = data.metaInstance; + const overlayMatrix = metaBuilding.getSpecialOverlayRenderMatrix( + staticComp.rotation, + data.rotationVariant, + data.variant, + entity + ); + context.fillStyle = overrideColor || metaBuilding.getSilhouetteColor(); + if (overlayMatrix) { + for (let dx = 0; dx < 3; ++dx) { + for (let dy = 0; dy < 3; ++dy) { + const isFilled = overlayMatrix[dx + dy * 3]; + if (isFilled) { context.fillRect( - x * CHUNK_OVERLAY_RES, - y * CHUNK_OVERLAY_RES, - CHUNK_OVERLAY_RES, - CHUNK_OVERLAY_RES + x + (dx * tileSizePixels) / CHUNK_OVERLAY_RES, + y + (dy * tileSizePixels) / CHUNK_OVERLAY_RES, + tileSizePixels / CHUNK_OVERLAY_RES, + tileSizePixels / CHUNK_OVERLAY_RES ); - - continue; } } } + } else { + context.fillRect(x, y, tileSizePixels, tileSizePixels); } } @@ -242,5 +269,6 @@ export class MapChunkView extends MapChunk { const systems = this.root.systemMgr.systems; systems.wire.drawChunk(parameters, this); systems.staticMapEntities.drawWiresChunk(parameters, this); + systems.wiredPins.drawChunk(parameters, this); } } diff --git a/src/js/game/map_view.js b/src/js/game/map_view.js index c98e7c4b..178344a7 100644 --- a/src/js/game/map_view.js +++ b/src/js/game/map_view.js @@ -140,23 +140,23 @@ export class MapView extends BaseMap { * @param {function} method */ drawVisibleChunks(parameters, method) { - const cullRange = parameters.visibleRect.toTileCullRectangle(); + const cullRange = parameters.visibleRect.allScaled(1 / globalConfig.tileSize); const top = cullRange.top(); const right = cullRange.right(); const bottom = cullRange.bottom(); const left = cullRange.left(); - const border = 1; + const border = 0; const minY = top - border; const maxY = bottom + border; const minX = left - border; - const maxX = right + border - 1; + const maxX = right + border; const chunkStartX = Math.floor(minX / globalConfig.mapChunkSize); const chunkStartY = Math.floor(minY / globalConfig.mapChunkSize); - const chunkEndX = Math.ceil(maxX / globalConfig.mapChunkSize); - const chunkEndY = Math.ceil(maxY / globalConfig.mapChunkSize); + const chunkEndX = Math.floor(maxX / globalConfig.mapChunkSize); + const chunkEndY = Math.floor(maxY / globalConfig.mapChunkSize); // Render y from top down for proper blending for (let chunkX = chunkStartX; chunkX <= chunkEndX; ++chunkX) { @@ -230,7 +230,6 @@ export class MapView extends BaseMap { const chunkEndX = Math.ceil(maxX / globalConfig.mapChunkSize); const chunkEndY = Math.ceil(maxY / globalConfig.mapChunkSize); - // Render y from top down for proper blending for (let chunkX = chunkStartX; chunkX <= chunkEndX; ++chunkX) { for (let chunkY = chunkStartY; chunkY <= chunkEndY; ++chunkY) { parameters.context.fillStyle = "#ffaaaa"; diff --git a/src/js/game/meta_building.js b/src/js/game/meta_building.js index f1b8b05a..b09e901e 100644 --- a/src/js/game/meta_building.js +++ b/src/js/game/meta_building.js @@ -4,7 +4,7 @@ import { Vector } from "../core/vector"; import { SOUNDS } from "../platform/sound"; import { StaticMapEntityComponent } from "./components/static_map_entity"; import { Entity } from "./entity"; -import { enumLayer, GameRoot } from "./root"; +import { GameRoot } from "./root"; import { getCodeFromBuildingData } from "./building_codes"; export const defaultBuildingVariant = "default"; @@ -27,10 +27,10 @@ export class MetaBuilding { /** * Returns the edit layer of the building - * @returns {enumLayer} + * @returns {Layer} */ getLayer() { - return enumLayer.regular; + return "regular"; } /** @@ -91,6 +91,13 @@ export class MetaBuilding { return false; } + /** + * Whether to show a preview of the wires layer when placing the building + */ + getShowWiresLayerPreview() { + return false; + } + /** * Whether to rotate automatically in the dragging direction while placing * @param {string} variant @@ -229,7 +236,7 @@ export class MetaBuilding { * @param {Vector} param0.tile * @param {number} param0.rotation * @param {string} param0.variant - * @param {string} param0.layer + * @param {Layer} param0.layer * @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array }} */ computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) { diff --git a/src/js/game/meta_building_registry.js b/src/js/game/meta_building_registry.js index 15578d18..7bf3b097 100644 --- a/src/js/game/meta_building_registry.js +++ b/src/js/game/meta_building_registry.js @@ -19,7 +19,8 @@ import { MetaConstantSignalBuilding } from "./buildings/constant_signal"; import { MetaLogicGateBuilding, enumLogicGateVariants } from "./buildings/logic_gate"; import { MetaLeverBuilding } from "./buildings/lever"; import { MetaFilterBuilding } from "./buildings/filter"; -import { MetaWireTunnelBuilding } from "./buildings/wire_tunnel"; +import { MetaWireTunnelBuilding, enumWireTunnelVariants } from "./buildings/wire_tunnel"; +import { MetaDisplayBuilding } from "./buildings/display"; const logger = createLogger("building_registry"); @@ -41,6 +42,7 @@ export function initMetaBuildingRegistry() { gMetaBuildingRegistry.register(MetaLeverBuilding); gMetaBuildingRegistry.register(MetaFilterBuilding); gMetaBuildingRegistry.register(MetaWireTunnelBuilding); + gMetaBuildingRegistry.register(MetaDisplayBuilding); // Belt registerBuildingVariant(1, MetaBeltBaseBuilding, defaultBuildingVariant, 0); @@ -114,6 +116,10 @@ export function initMetaBuildingRegistry() { // Wire tunnel registerBuildingVariant(39, MetaWireTunnelBuilding); + registerBuildingVariant(41, MetaWireTunnelBuilding, enumWireTunnelVariants.coating); + + // Display + registerBuildingVariant(40, MetaDisplayBuilding); // Propagate instances for (const key in gBuildingVariants) { diff --git a/src/js/game/production_analytics.js b/src/js/game/production_analytics.js index 41ec31ae..eda79c83 100644 --- a/src/js/game/production_analytics.js +++ b/src/js/game/production_analytics.js @@ -1,7 +1,7 @@ import { GameRoot } from "./root"; import { ShapeDefinition } from "./shape_definition"; import { globalConfig } from "../core/config"; -import { BaseItem, enumItemType } from "./base_item"; +import { BaseItem } from "./base_item"; import { ShapeItem } from "./items/shape_item"; import { BasicSerializableObject } from "../savegame/serialization"; @@ -53,7 +53,7 @@ export class ProductionAnalytics extends BasicSerializableObject { * @param {BaseItem} item */ onItemProduced(item) { - if (item.getItemType() === enumItemType.shape) { + if (item.getItemType() === "shape") { const definition = /** @type {ShapeItem} */ (item).definition; const key = definition.getHash(); const entry = this.history[enumAnalyticsDataSource.produced]; diff --git a/src/js/game/root.js b/src/js/game/root.js index 3279c693..dd224dd8 100644 --- a/src/js/game/root.js +++ b/src/js/game/root.js @@ -1,5 +1,4 @@ /* eslint-disable no-unused-vars */ - import { Signal } from "../core/signal"; import { RandomNumberGenerator } from "../core/rng"; import { createLogger } from "../core/logging"; @@ -32,14 +31,8 @@ import { Vector } from "../core/vector"; const logger = createLogger("game/root"); -/** @enum {string} */ -export const enumLayer = { - regular: "regular", - wires: "wires", -}; - -/** @type {Array} */ -export const arrayLayers = [enumLayer.regular, enumLayer.wires]; +/** @type {Array} */ +export const layers = ["regular", "wires"]; /** * The game root is basically the whole game state at a given point, @@ -134,8 +127,8 @@ export class GameRoot { /** @type {DynamicTickrate} */ this.dynamicTickrate = null; - /** @type {enumLayer} */ - this.currentLayer = enumLayer.regular; + /** @type {Layer} */ + this.currentLayer = "regular"; this.signals = { // Entities @@ -156,6 +149,8 @@ export class GameRoot { gameSaved: /** @type {TypedSignal<[]>} */ (new Signal()), // Game got saved gameRestored: /** @type {TypedSignal<[]>} */ (new Signal()), // Game got restored + gameFrameStarted: /** @type {TypedSignal<[]>} */ (new Signal()), // New frame + storyGoalCompleted: /** @type {TypedSignal<[number, string]>} */ (new Signal()), upgradePurchased: /** @type {TypedSignal<[string]>} */ (new Signal()), @@ -167,7 +162,7 @@ export class GameRoot { bulkOperationFinished: /** @type {TypedSignal<[]>} */ (new Signal()), - editModeChanged: /** @type {TypedSignal<[enumLayer]>} */ (new Signal()), + editModeChanged: /** @type {TypedSignal<[Layer]>} */ (new Signal()), // Called to check if an entity can be placed, second parameter is an additional offset. // Use to introduce additional placement checks diff --git a/src/js/game/shape_definition.js b/src/js/game/shape_definition.js index a0181fbb..d3f6c2d6 100644 --- a/src/js/game/shape_definition.js +++ b/src/js/game/shape_definition.js @@ -2,16 +2,11 @@ import { makeOffscreenBuffer } from "../core/buffer_utils"; import { globalConfig } from "../core/config"; import { smoothenDpi } from "../core/dpi_manager"; import { DrawParameters } from "../core/draw_parameters"; -import { createLogger } from "../core/logging"; import { Vector } from "../core/vector"; import { BasicSerializableObject, types } from "../savegame/serialization"; import { enumColors, enumColorsToHexCode, enumColorToShortcode, enumShortcodeToColor } from "./colors"; import { THEME } from "./theme"; -const rusha = require("rusha"); - -const logger = createLogger("shape_definition"); - /** * @typedef {{ * subShape: enumSubShape, @@ -280,24 +275,25 @@ export class ShapeDefinition extends BasicSerializableObject { * @param {number} x * @param {number} y * @param {DrawParameters} parameters + * @param {number=} diameter */ - draw(x, y, parameters, size = 20) { + drawCentered(x, y, parameters, diameter = 20) { const dpi = smoothenDpi(globalConfig.shapesSharpness * parameters.zoomLevel); if (!this.bufferGenerator) { this.bufferGenerator = this.internalGenerateShapeBuffer.bind(this); } - const key = size + "/" + dpi; + const key = diameter + "/" + dpi + "/" + this.cachedHash; const canvas = parameters.root.buffers.getForKey({ - key, - subKey: this.cachedHash, - w: size, - h: size, + key: "shapedef", + subKey: key, + w: diameter, + h: diameter, dpi, redrawMethod: this.bufferGenerator, }); - parameters.context.drawImage(canvas, x - size / 2, y - size / 2, size, size); + parameters.context.drawImage(canvas, x - diameter / 2, y - diameter / 2, diameter, diameter); } /** diff --git a/src/js/game/systems/belt.js b/src/js/game/systems/belt.js index 042dddfb..4d8151f6 100644 --- a/src/js/game/systems/belt.js +++ b/src/js/game/systems/belt.js @@ -495,17 +495,15 @@ export class BeltSystem extends GameSystemWithFilter { ((this.root.time.realtimeNow() * speedMultiplier * BELT_ANIM_COUNT * 126) / 42) * globalConfig.itemSpacingOnBelts ); - const contents = chunk.contents; - for (let y = 0; y < globalConfig.mapChunkSize; ++y) { - for (let x = 0; x < globalConfig.mapChunkSize; ++x) { - const entity = contents[x][y]; + const contents = chunk.containedEntitiesByLayer.regular; + for (let i = 0; i < contents.length; ++i) { + const entity = contents[i]; + if (entity.components.Belt) { + const direction = entity.components.Belt.direction; + const sprite = this.beltAnimations[direction][animationIndex % BELT_ANIM_COUNT]; - if (entity && entity.components.Belt) { - const direction = entity.components.Belt.direction; - const sprite = this.beltAnimations[direction][animationIndex % BELT_ANIM_COUNT]; - - entity.components.StaticMapEntity.drawSpriteOnFullEntityBounds(parameters, sprite, 0); - } + // Culling happens within the static map entity component + entity.components.StaticMapEntity.drawSpriteOnBoundsClipped(parameters, sprite, 0); } } } diff --git a/src/js/game/systems/belt_underlays.js b/src/js/game/systems/belt_underlays.js index 7964a1f0..5bdf2331 100644 --- a/src/js/game/systems/belt_underlays.js +++ b/src/js/game/systems/belt_underlays.js @@ -3,9 +3,10 @@ import { drawRotatedSprite } from "../../core/draw_utils"; import { Loader } from "../../core/loader"; import { enumDirectionToAngle } from "../../core/vector"; import { BeltUnderlaysComponent } from "../components/belt_underlays"; -import { Entity } from "../entity"; import { GameSystemWithFilter } from "../game_system_with_filter"; import { BELT_ANIM_COUNT } from "./belt"; +import { MapChunkView } from "../map_chunk_view"; +import { DrawParameters } from "../../core/draw_parameters"; export class BeltUnderlaysSystem extends GameSystemWithFilter { constructor(root) { @@ -19,49 +20,65 @@ export class BeltUnderlaysSystem extends GameSystemWithFilter { } /** - * Draws the acceptor underlays - * @param {import("../../core/draw_utils").DrawParameters} parameters + * Draws a given chunk + * @param {DrawParameters} parameters + * @param {MapChunkView} chunk */ - drawUnderlays(parameters) { - this.forEachMatchingEntityOnScreen(parameters, this.drawEntityUnderlays.bind(this)); - } - - /** - * @param {import("../../core/draw_utils").DrawParameters} parameters - * @param {Entity} entity - */ - drawEntityUnderlays(parameters, entity) { - const staticComp = entity.components.StaticMapEntity; - const underlayComp = entity.components.BeltUnderlays; - - if (!staticComp.shouldBeDrawn(parameters)) { - return; - } - + drawChunk(parameters, chunk) { // Limit speed to avoid belts going backwards const speedMultiplier = Math.min(this.root.hubGoals.getBeltBaseSpeed(), 10); - const underlays = underlayComp.underlays; - for (let i = 0; i < underlays.length; ++i) { - const { pos, direction } = underlays[i]; + const contents = chunk.containedEntitiesByLayer.regular; + for (let i = 0; i < contents.length; ++i) { + const entity = contents[i]; + const underlayComp = entity.components.BeltUnderlays; + if (!underlayComp) { + continue; + } - const transformedPos = staticComp.localTileToWorld(pos); - const angle = enumDirectionToAngle[staticComp.localDirectionToWorld(direction)]; + const staticComp = entity.components.StaticMapEntity; + const underlays = underlayComp.underlays; + for (let i = 0; i < underlays.length; ++i) { + const { pos, direction } = underlays[i]; + const transformedPos = staticComp.localTileToWorld(pos); - // SYNC with systems/belt.js:drawSingleEntity! - const animationIndex = Math.floor( - ((this.root.time.realtimeNow() * speedMultiplier * BELT_ANIM_COUNT * 126) / 42) * - globalConfig.itemSpacingOnBelts - ); + // Culling + if (!chunk.tileSpaceRectangle.containsPoint(transformedPos.x, transformedPos.y)) { + continue; + } - drawRotatedSprite({ - parameters, - sprite: this.underlayBeltSprites[animationIndex % this.underlayBeltSprites.length], - x: (transformedPos.x + 0.5) * globalConfig.tileSize, - y: (transformedPos.y + 0.5) * globalConfig.tileSize, - angle: Math.radians(angle), - size: globalConfig.tileSize, - }); + const destX = transformedPos.x * globalConfig.tileSize; + const destY = transformedPos.y * globalConfig.tileSize; + + // Culling, #2 + if ( + !parameters.visibleRect.containsRect4Params( + destX, + destY, + globalConfig.tileSize, + globalConfig.tileSize + ) + ) { + continue; + } + + const angle = enumDirectionToAngle[staticComp.localDirectionToWorld(direction)]; + + // SYNC with systems/belt.js:drawSingleEntity! + const animationIndex = Math.floor( + ((this.root.time.realtimeNow() * speedMultiplier * BELT_ANIM_COUNT * 126) / 42) * + globalConfig.itemSpacingOnBelts + ); + + drawRotatedSprite({ + parameters, + sprite: this.underlayBeltSprites[animationIndex % this.underlayBeltSprites.length], + x: destX + globalConfig.halfTileSize, + y: destY + globalConfig.halfTileSize, + angle: Math.radians(angle), + size: globalConfig.tileSize, + }); + } } } } diff --git a/src/js/game/systems/display.js b/src/js/game/systems/display.js new file mode 100644 index 00000000..b1bd9e69 --- /dev/null +++ b/src/js/game/systems/display.js @@ -0,0 +1,99 @@ +import { globalConfig } from "../../core/config"; +import { Loader } from "../../core/loader"; +import { BaseItem } from "../base_item"; +import { enumColors } from "../colors"; +import { DisplayComponent } from "../components/display"; +import { GameSystemWithFilter } from "../game_system_with_filter"; +import { ColorItem, COLOR_ITEM_SINGLETONS } from "../items/color_item"; +import { MapChunkView } from "../map_chunk_view"; +import { BooleanItem } from "../items/boolean_item"; + +export class DisplaySystem extends GameSystemWithFilter { + constructor(root) { + super(root, [DisplayComponent]); + + /** @type {Object} */ + this.displaySprites = {}; + + for (const colorId in enumColors) { + if (colorId === enumColors.uncolored) { + continue; + } + this.displaySprites[colorId] = Loader.getSprite("sprites/wires/display/" + colorId + ".png"); + } + } + + /** + * Returns the color / value a display should show + * @param {BaseItem} value + * @returns {BaseItem} + */ + getDisplayItem(value) { + if (!value) { + return null; + } + + switch (value.getItemType()) { + case "boolean": { + return /** @type {BooleanItem} */ (value).value + ? COLOR_ITEM_SINGLETONS[enumColors.white] + : null; + } + + case "color": { + const item = /**@type {ColorItem} */ (value); + return item.color === enumColors.uncolored ? null : item; + } + + case "shape": { + return value; + } + + default: + assertAlways(false, "Unknown item type: " + value.getItemType()); + } + } + + /** + * Draws a given chunk + * @param {import("../../core/draw_utils").DrawParameters} parameters + * @param {MapChunkView} chunk + */ + drawChunk(parameters, chunk) { + const contents = chunk.containedEntitiesByLayer.regular; + for (let i = 0; i < contents.length; ++i) { + const entity = contents[i]; + if (entity && entity.components.Display) { + const pinsComp = entity.components.WiredPins; + const network = pinsComp.slots[0].linkedNetwork; + + if (!network || !network.currentValue) { + continue; + } + + const value = this.getDisplayItem(network.currentValue); + + if (!value) { + continue; + } + + const origin = entity.components.StaticMapEntity.origin; + if (value.getItemType() === "color") { + this.displaySprites[/** @type {ColorItem} */ (value).color].drawCachedCentered( + parameters, + (origin.x + 0.5) * globalConfig.tileSize, + (origin.y + 0.5) * globalConfig.tileSize, + globalConfig.tileSize + ); + } else if (value.getItemType() === "shape") { + value.drawItemCenteredClipped( + (origin.x + 0.5) * globalConfig.tileSize, + (origin.y + 0.5) * globalConfig.tileSize, + parameters, + 30 + ); + } + } + } + } +} diff --git a/src/js/game/systems/hub.js b/src/js/game/systems/hub.js index b8968655..2270f941 100644 --- a/src/js/game/systems/hub.js +++ b/src/js/game/systems/hub.js @@ -5,6 +5,14 @@ import { T } from "../../translations"; import { HubComponent } from "../components/hub"; import { Entity } from "../entity"; import { GameSystemWithFilter } from "../game_system_with_filter"; +import { globalConfig } from "../../core/config"; +import { smoothenDpi } from "../../core/dpi_manager"; +import { drawSpriteClipped } from "../../core/draw_utils"; +import { Rectangle } from "../../core/rectangle"; +import { ORIGINAL_SPRITE_SCALE } from "../../core/sprites"; + +const HUB_SIZE_TILES = 4; +const HUB_SIZE_PIXELS = HUB_SIZE_TILES * globalConfig.tileSize; export class HubSystem extends GameSystemWithFilter { constructor(root) { @@ -13,8 +21,13 @@ export class HubSystem extends GameSystemWithFilter { this.hubSprite = Loader.getSprite("sprites/buildings/hub.png"); } + /** + * @param {DrawParameters} parameters + */ draw(parameters) { - this.forEachMatchingEntityOnScreen(parameters, this.drawEntity.bind(this)); + for (let i = 0; i < this.allEntities.length; ++i) { + this.drawEntity(parameters, this.allEntities[i]); + } } update() { @@ -27,35 +40,42 @@ export class HubSystem extends GameSystemWithFilter { ); } } - /** - * @param {DrawParameters} parameters - * @param {Entity} entity + * + * @param {HTMLCanvasElement} canvas + * @param {CanvasRenderingContext2D} context + * @param {number} w + * @param {number} h + * @param {number} dpi */ - drawEntity(parameters, entity) { - const context = parameters.context; - const staticComp = entity.components.StaticMapEntity; + redrawHubBaseTexture(canvas, context, w, h, dpi) { + // This method is quite ugly, please ignore it! - if (!staticComp.shouldBeDrawn(parameters)) { - return; - } + context.scale(dpi, dpi); - const pos = staticComp.getTileSpaceBounds().getCenter().toWorldSpace(); + const parameters = new DrawParameters({ + context, + visibleRect: new Rectangle(0, 0, w, h), + desiredAtlasScale: ORIGINAL_SPRITE_SCALE, + zoomLevel: dpi * 0.75, + root: this.root, + }); - // Background - staticComp.drawSpriteOnFullEntityBounds(parameters, this.hubSprite, 2.2); + context.clearRect(0, 0, w, h); + + this.hubSprite.draw(context, 0, 0, w, h); const definition = this.root.hubGoals.currentGoal.definition; - - definition.draw(pos.x - 25, pos.y - 10, parameters, 40); + definition.drawCentered(45, 58, parameters, 36); const goals = this.root.hubGoals.currentGoal; - const textOffsetX = 2; - const textOffsetY = -6; + const textOffsetX = 70; + const textOffsetY = 61; // Deliver count const delivered = this.root.hubGoals.getCurrentGoalDelivered(); + const deliveredText = "" + formatBigNumber(delivered); if (delivered > 9999) { context.font = "bold 16px GameFont"; @@ -66,52 +86,87 @@ export class HubSystem extends GameSystemWithFilter { } context.fillStyle = "#64666e"; context.textAlign = "left"; - context.fillText("" + formatBigNumber(delivered), pos.x + textOffsetX, pos.y + textOffsetY); + context.fillText(deliveredText, textOffsetX, textOffsetY); // Required context.font = "13px GameFont"; context.fillStyle = "#a4a6b0"; - context.fillText( - "/ " + formatBigNumber(goals.required), - pos.x + textOffsetX, - pos.y + textOffsetY + 13 - ); + context.fillText("/ " + formatBigNumber(goals.required), textOffsetX, textOffsetY + 13); // Reward const rewardText = T.storyRewards[goals.reward].title.toUpperCase(); if (rewardText.length > 12) { - context.font = "bold 9px GameFont"; + context.font = "bold 8px GameFont"; } else { - context.font = "bold 11px GameFont"; + context.font = "bold 10px GameFont"; } context.fillStyle = "#fd0752"; context.textAlign = "center"; - context.fillText(rewardText, pos.x, pos.y + 46); + context.fillText(rewardText, HUB_SIZE_PIXELS / 2, 105); - // Level - context.font = "bold 11px GameFont"; + // Level "8" + context.font = "bold 10px GameFont"; context.fillStyle = "#fff"; - context.fillText("" + this.root.hubGoals.level, pos.x - 42, pos.y - 36); + context.fillText("" + this.root.hubGoals.level, 27, 32); - // Texts + // "LVL" context.textAlign = "center"; context.fillStyle = "#fff"; - context.font = "bold 7px GameFont"; - context.fillText(T.buildings.hub.levelShortcut, pos.x - 42, pos.y - 47); + context.font = "bold 6px GameFont"; + context.fillText(T.buildings.hub.levelShortcut, 27, 22); + // "Deliver" context.fillStyle = "#64666e"; - context.font = "bold 11px GameFont"; - context.fillText(T.buildings.hub.deliver.toUpperCase(), pos.x, pos.y - 40); + context.font = "bold 10px GameFont"; + context.fillText(T.buildings.hub.deliver.toUpperCase(), HUB_SIZE_PIXELS / 2, 30); + // "To unlock" const unlockText = T.buildings.hub.toUnlock.toUpperCase(); if (unlockText.length > 15) { context.font = "bold 8px GameFont"; } else { - context.font = "bold 11px GameFont"; + context.font = "bold 10px GameFont"; } - context.fillText(T.buildings.hub.toUnlock.toUpperCase(), pos.x, pos.y + 30); + context.fillText(T.buildings.hub.toUnlock.toUpperCase(), HUB_SIZE_PIXELS / 2, 92); context.textAlign = "left"; } + + /** + * @param {DrawParameters} parameters + * @param {Entity} entity + */ + drawEntity(parameters, entity) { + const staticComp = entity.components.StaticMapEntity; + if (!staticComp.shouldBeDrawn(parameters)) { + return; + } + + // Deliver count + const delivered = this.root.hubGoals.getCurrentGoalDelivered(); + const deliveredText = "" + formatBigNumber(delivered); + + const dpi = smoothenDpi(globalConfig.shapesSharpness * parameters.zoomLevel); + const canvas = parameters.root.buffers.getForKey({ + key: "hub", + subKey: dpi + "/" + this.root.hubGoals.level + "/" + deliveredText, + w: globalConfig.tileSize * 4, + h: globalConfig.tileSize * 4, + dpi, + redrawMethod: this.redrawHubBaseTexture.bind(this), + }); + + const extrude = 8; + drawSpriteClipped({ + parameters, + sprite: canvas, + x: staticComp.origin.x * globalConfig.tileSize - extrude, + y: staticComp.origin.y * globalConfig.tileSize - extrude, + w: HUB_SIZE_PIXELS + 2 * extrude, + h: HUB_SIZE_PIXELS + 2 * extrude, + originalW: HUB_SIZE_PIXELS * dpi, + originalH: HUB_SIZE_PIXELS * dpi, + }); + } } diff --git a/src/js/game/systems/item_acceptor.js b/src/js/game/systems/item_acceptor.js index 1caf14c6..745da4bf 100644 --- a/src/js/game/systems/item_acceptor.js +++ b/src/js/game/systems/item_acceptor.js @@ -3,9 +3,8 @@ import { DrawParameters } from "../../core/draw_parameters"; import { fastArrayDelete } from "../../core/utils"; import { enumDirectionToVector } from "../../core/vector"; import { ItemAcceptorComponent } from "../components/item_acceptor"; -import { Entity } from "../entity"; import { GameSystemWithFilter } from "../game_system_with_filter"; -import { enumLayer } from "../root"; +import { MapChunkView } from "../map_chunk_view"; export class ItemAcceptorSystem extends GameSystemWithFilter { constructor(root) { @@ -39,43 +38,45 @@ export class ItemAcceptorSystem extends GameSystemWithFilter { } /** - * Draws the acceptor items * @param {DrawParameters} parameters + * @param {MapChunkView} chunk */ - draw(parameters) { - this.forEachMatchingEntityOnScreen(parameters, this.drawEntityRegularLayer.bind(this)); - } + drawChunk(parameters, chunk) { + const contents = chunk.containedEntitiesByLayer.regular; + for (let i = 0; i < contents.length; ++i) { + const entity = contents[i]; + const acceptorComp = entity.components.ItemAcceptor; + if (!acceptorComp) { + continue; + } - /** - * @param {DrawParameters} parameters - * @param {Entity} entity - */ - drawEntityRegularLayer(parameters, entity) { - const staticComp = entity.components.StaticMapEntity; - const acceptorComp = entity.components.ItemAcceptor; + const staticComp = entity.components.StaticMapEntity; + for (let animIndex = 0; animIndex < acceptorComp.itemConsumptionAnimations.length; ++animIndex) { + const { item, slotIndex, animProgress, direction } = acceptorComp.itemConsumptionAnimations[ + animIndex + ]; - if (!staticComp.shouldBeDrawn(parameters)) { - return; - } + const slotData = acceptorComp.slots[slotIndex]; + const realSlotPos = staticComp.localTileToWorld(slotData.pos); - for (let animIndex = 0; animIndex < acceptorComp.itemConsumptionAnimations.length; ++animIndex) { - const { item, slotIndex, animProgress, direction } = acceptorComp.itemConsumptionAnimations[ - animIndex - ]; + if (!chunk.tileSpaceRectangle.containsPoint(realSlotPos.x, realSlotPos.y)) { + // Not within this chunk + continue; + } - const slotData = acceptorComp.slots[slotIndex]; + const fadeOutDirection = enumDirectionToVector[staticComp.localDirectionToWorld(direction)]; + const finalTile = realSlotPos.subScalars( + fadeOutDirection.x * (animProgress / 2 - 0.5), + fadeOutDirection.y * (animProgress / 2 - 0.5) + ); - const slotWorldPos = staticComp.applyRotationToVector(slotData.pos).add(staticComp.origin); - const fadeOutDirection = enumDirectionToVector[staticComp.localDirectionToWorld(direction)]; - const finalTile = slotWorldPos.subScalars( - fadeOutDirection.x * (animProgress / 2 - 0.5), - fadeOutDirection.y * (animProgress / 2 - 0.5) - ); - item.draw( - (finalTile.x + 0.5) * globalConfig.tileSize, - (finalTile.y + 0.5) * globalConfig.tileSize, - parameters - ); + item.drawItemCenteredClipped( + (finalTile.x + 0.5) * globalConfig.tileSize, + (finalTile.y + 0.5) * globalConfig.tileSize, + parameters, + globalConfig.defaultItemDiameter + ); + } } } } diff --git a/src/js/game/systems/item_ejector.js b/src/js/game/systems/item_ejector.js index ed824d46..6804925d 100644 --- a/src/js/game/systems/item_ejector.js +++ b/src/js/game/systems/item_ejector.js @@ -8,6 +8,7 @@ import { ItemEjectorComponent } from "../components/item_ejector"; import { Entity } from "../entity"; import { GameSystemWithFilter } from "../game_system_with_filter"; import { enumItemProcessorTypes } from "../components/item_processor"; +import { MapChunkView } from "../map_chunk_view"; const logger = createLogger("systems/ejector"); @@ -336,50 +337,52 @@ export class ItemEjectorSystem extends GameSystemWithFilter { } /** - * Draws everything * @param {DrawParameters} parameters + * @param {MapChunkView} chunk */ - draw(parameters) { - this.forEachMatchingEntityOnScreen(parameters, this.drawSingleEntity.bind(this)); - } + drawChunk(parameters, chunk) { + const contents = chunk.containedEntitiesByLayer.regular; - /** - * @param {DrawParameters} parameters - * @param {Entity} entity - */ - drawSingleEntity(parameters, entity) { - const ejectorComp = entity.components.ItemEjector; - const staticComp = entity.components.StaticMapEntity; - - if (!staticComp.shouldBeDrawn(parameters)) { - return; - } - - for (let i = 0; i < ejectorComp.slots.length; ++i) { - const slot = ejectorComp.slots[i]; - const ejectedItem = slot.item; - - if (!ejectedItem) { - // No item + for (let i = 0; i < contents.length; ++i) { + const entity = contents[i]; + const ejectorComp = entity.components.ItemEjector; + if (!ejectorComp) { continue; } - const realPosition = slot.pos.rotateFastMultipleOf90(staticComp.rotation); - const realDirection = Vector.transformDirectionFromMultipleOf90( - slot.direction, - staticComp.rotation - ); - const realDirectionVector = enumDirectionToVector[realDirection]; + const staticComp = entity.components.StaticMapEntity; - const tileX = - staticComp.origin.x + realPosition.x + 0.5 + realDirectionVector.x * 0.5 * slot.progress; - const tileY = - staticComp.origin.y + realPosition.y + 0.5 + realDirectionVector.y * 0.5 * slot.progress; + for (let i = 0; i < ejectorComp.slots.length; ++i) { + const slot = ejectorComp.slots[i]; + const ejectedItem = slot.item; - const worldX = tileX * globalConfig.tileSize; - const worldY = tileY * globalConfig.tileSize; + if (!ejectedItem) { + // No item + continue; + } - ejectedItem.draw(worldX, worldY, parameters); + const realPosition = staticComp.localTileToWorld(slot.pos); + if (!chunk.tileSpaceRectangle.containsPoint(realPosition.x, realPosition.y)) { + // Not within this chunk + continue; + } + + const realDirection = staticComp.localDirectionToWorld(slot.direction); + const realDirectionVector = enumDirectionToVector[realDirection]; + + const tileX = realPosition.x + 0.5 + realDirectionVector.x * 0.5 * slot.progress; + const tileY = realPosition.y + 0.5 + realDirectionVector.y * 0.5 * slot.progress; + + const worldX = tileX * globalConfig.tileSize; + const worldY = tileY * globalConfig.tileSize; + + ejectedItem.drawItemCenteredClipped( + worldX, + worldY, + parameters, + globalConfig.defaultItemDiameter + ); + } } } } diff --git a/src/js/game/systems/lever.js b/src/js/game/systems/lever.js index c8ebf1a2..0d538afc 100644 --- a/src/js/game/systems/lever.js +++ b/src/js/game/systems/lever.js @@ -4,7 +4,6 @@ import { BOOL_TRUE_SINGLETON, BOOL_FALSE_SINGLETON } from "../items/boolean_item import { MapChunkView } from "../map_chunk_view"; import { globalConfig } from "../../core/config"; import { Loader } from "../../core/loader"; -import { enumLayer } from "../root"; export class LeverSystem extends GameSystemWithFilter { constructor(root) { @@ -32,11 +31,12 @@ export class LeverSystem extends GameSystemWithFilter { * @param {MapChunkView} chunk */ drawChunk(parameters, chunk) { - const contents = chunk.containedEntitiesByLayer[enumLayer.regular]; + const contents = chunk.containedEntitiesByLayer.regular; for (let i = 0; i < contents.length; ++i) { const entity = contents[i]; - if (entity && entity.components.Lever) { - const sprite = entity.components.Lever.toggled ? this.spriteOn : this.spriteOff; + const leverComp = entity.components.Lever; + if (leverComp) { + const sprite = leverComp.toggled ? this.spriteOn : this.spriteOff; const origin = entity.components.StaticMapEntity.origin; sprite.drawCached( parameters, diff --git a/src/js/game/systems/logic_gate.js b/src/js/game/systems/logic_gate.js index 03cf672f..9fe41250 100644 --- a/src/js/game/systems/logic_gate.js +++ b/src/js/game/systems/logic_gate.js @@ -1,6 +1,6 @@ import { LogicGateComponent, enumLogicGateType } from "../components/logic_gate"; import { GameSystemWithFilter } from "../game_system_with_filter"; -import { BaseItem, enumItemType } from "../base_item"; +import { BaseItem } from "../base_item"; import { enumPinSlotType } from "../components/wired_pins"; import { BOOL_TRUE_SINGLETON, BOOL_FALSE_SINGLETON, BooleanItem } from "../items/boolean_item"; import { enumItemProcessorTypes } from "../components/item_processor"; @@ -62,7 +62,7 @@ export class LogicGateSystem extends GameSystemWithFilter { const param2 = parameters[1]; if (!param1 || !param2) { // Not enough params - return null; + return BOOL_FALSE_SINGLETON; } const itemType = param1.getItemType(); @@ -72,7 +72,7 @@ export class LogicGateSystem extends GameSystemWithFilter { return BOOL_FALSE_SINGLETON; } - if (itemType === enumItemType.boolean) { + if (itemType === "boolean") { return /** @type {BooleanItem} */ (param1).value && /** @type {BooleanItem} */ (param2).value ? BOOL_TRUE_SINGLETON : BOOL_FALSE_SINGLETON; @@ -88,10 +88,10 @@ export class LogicGateSystem extends GameSystemWithFilter { compute_NOT(parameters) { const item = parameters[0]; if (!item) { - return BOOL_FALSE_SINGLETON; + return BOOL_TRUE_SINGLETON; } - if (item.getItemType() !== enumItemType.boolean) { + if (item.getItemType() !== "boolean") { // Not a boolean actually return BOOL_FALSE_SINGLETON; } @@ -109,25 +109,24 @@ export class LogicGateSystem extends GameSystemWithFilter { const param1 = parameters[0]; const param2 = parameters[1]; - if (!param1 || !param2) { + if (!param1 && !param2) { // Not enough params - return null; - } - - const itemType = param1.getItemType(); - - if (itemType !== param2.getItemType()) { - // Differing type return BOOL_FALSE_SINGLETON; } - if (itemType === enumItemType.boolean) { - return /** @type {BooleanItem} */ (param1).value ^ /** @type {BooleanItem} */ (param2).value - ? BOOL_TRUE_SINGLETON - : BOOL_FALSE_SINGLETON; + // Check for the right types + if (param1 && param1.getItemType() !== "boolean") { + return BOOL_FALSE_SINGLETON; } - return BOOL_FALSE_SINGLETON; + if (param2 && param2.getItemType() !== "boolean") { + return BOOL_FALSE_SINGLETON; + } + + const valueParam1 = param1 ? /** @type {BooleanItem} */ (param1).value : 0; + const valueParam2 = param2 ? /** @type {BooleanItem} */ (param2).value : 0; + + return valueParam1 ^ valueParam2 ? BOOL_TRUE_SINGLETON : BOOL_FALSE_SINGLETON; } /** @@ -139,25 +138,17 @@ export class LogicGateSystem extends GameSystemWithFilter { const param1 = parameters[0]; const param2 = parameters[1]; - if (!param1 || !param2) { + if (!param1 && !param2) { // Not enough params - return null; - } - - const itemType = param1.getItemType(); - - if (itemType !== param2.getItemType()) { - // Differing type return BOOL_FALSE_SINGLETON; } - if (itemType === enumItemType.boolean) { - return /** @type {BooleanItem} */ (param1).value || /** @type {BooleanItem} */ (param2).value - ? BOOL_TRUE_SINGLETON - : BOOL_FALSE_SINGLETON; - } + const valueParam1 = + param1 && param1.getItemType() === "boolean" ? /** @type {BooleanItem} */ (param1).value : 0; + const valueParam2 = + param2 && param2.getItemType() === "boolean" ? /** @type {BooleanItem} */ (param2).value : 0; - return BOOL_FALSE_SINGLETON; + return valueParam1 || valueParam2 ? BOOL_TRUE_SINGLETON : BOOL_FALSE_SINGLETON; } /** @@ -174,7 +165,7 @@ export class LogicGateSystem extends GameSystemWithFilter { return null; } - if (flag.getItemType() !== enumItemType.boolean) { + if (flag.getItemType() !== "boolean") { // Flag is not a boolean return null; } diff --git a/src/js/game/systems/map_resources.js b/src/js/game/systems/map_resources.js index ba898813..eb3d0d2f 100644 --- a/src/js/game/systems/map_resources.js +++ b/src/js/game/systems/map_resources.js @@ -3,6 +3,7 @@ import { DrawParameters } from "../../core/draw_parameters"; import { GameSystem } from "../game_system"; import { MapChunkView } from "../map_chunk_view"; import { THEME } from "../theme"; +import { drawSpriteClipped } from "../../core/draw_utils"; export class MapResourcesSystem extends GameSystem { /** @@ -12,7 +13,7 @@ export class MapResourcesSystem extends GameSystem { */ drawChunk(parameters, chunk) { const basicChunkBackground = this.root.buffers.getForKey({ - key: "chunkres", + key: "mapresourcebg", subKey: chunk.renderKey, w: globalConfig.mapChunkSize, h: globalConfig.mapChunkSize, @@ -21,13 +22,16 @@ export class MapResourcesSystem extends GameSystem { }); parameters.context.imageSmoothingEnabled = false; - parameters.context.drawImage( - basicChunkBackground, - chunk.tileX * globalConfig.tileSize, - chunk.tileY * globalConfig.tileSize, - globalConfig.mapChunkWorldSize, - globalConfig.mapChunkWorldSize - ); + drawSpriteClipped({ + parameters, + sprite: basicChunkBackground, + x: chunk.tileX * globalConfig.tileSize, + y: chunk.tileY * globalConfig.tileSize, + w: globalConfig.mapChunkWorldSize, + h: globalConfig.mapChunkWorldSize, + originalW: globalConfig.mapChunkSize, + originalH: globalConfig.mapChunkSize, + }); parameters.context.imageSmoothingEnabled = true; parameters.context.globalAlpha = 0.5; @@ -36,13 +40,11 @@ export class MapResourcesSystem extends GameSystem { // LOW QUALITY: Draw patch items only for (let i = 0; i < chunk.patches.length; ++i) { const patch = chunk.patches[i]; + const destX = chunk.x * globalConfig.mapChunkWorldSize + patch.pos.x * globalConfig.tileSize; + const destY = chunk.y * globalConfig.mapChunkWorldSize + patch.pos.y * globalConfig.tileSize; + const diameter = Math.min(80, 40 / parameters.zoomLevel); - patch.item.draw( - chunk.x * globalConfig.mapChunkWorldSize + patch.pos.x * globalConfig.tileSize, - chunk.y * globalConfig.mapChunkWorldSize + patch.pos.y * globalConfig.tileSize, - parameters, - Math.min(80, 40 / parameters.zoomLevel) - ); + patch.item.drawItemCenteredClipped(destX, destY, parameters, diameter); } } else { // HIGH QUALITY: Draw all items @@ -55,24 +57,14 @@ export class MapResourcesSystem extends GameSystem { if (lowerItem) { const worldY = (chunk.tileY + y) * globalConfig.tileSize; - if ( - !parameters.visibleRect.containsRect4Params( - worldX, - worldY, - globalConfig.tileSize, - globalConfig.tileSize - ) - ) { - // Clipped - continue; - } + const destX = worldX + globalConfig.halfTileSize; + const destY = worldY + globalConfig.halfTileSize; - // parameters.context.fillStyle = lowerItem.getBackgroundColorAsResource(); - // parameters.context.fillRect(worldX, worldY, globalConfig.tileSize, globalConfig.tileSize); - lowerItem.draw( - worldX + globalConfig.halfTileSize, - worldY + globalConfig.halfTileSize, - parameters + lowerItem.drawItemCenteredClipped( + destX, + destY, + parameters, + globalConfig.defaultItemDiameter ); } } @@ -93,7 +85,6 @@ export class MapResourcesSystem extends GameSystem { generateChunkBackground(chunk, canvas, context, w, h, dpi) { if (this.root.app.settings.getAllSettings().disableTileGrid) { // The map doesn't draw a background, so we have to - context.fillStyle = THEME.map.background; context.fillRect(0, 0, w, h); } else { diff --git a/src/js/game/systems/miner.js b/src/js/game/systems/miner.js index f317d0d1..deff1557 100644 --- a/src/js/game/systems/miner.js +++ b/src/js/game/systems/miner.js @@ -6,7 +6,6 @@ import { MinerComponent } from "../components/miner"; import { Entity } from "../entity"; import { GameSystemWithFilter } from "../game_system_with_filter"; import { MapChunkView } from "../map_chunk_view"; -import { enumLayer } from "../root"; export class MinerSystem extends GameSystemWithFilter { constructor(root) { @@ -75,7 +74,7 @@ export class MinerSystem extends GameSystemWithFilter { const ejectingDirection = staticComp.localDirectionToWorld(ejectingSlot.direction); const targetTile = ejectingPos.add(enumDirectionToVector[ejectingDirection]); - const targetContents = this.root.map.getTileContent(targetTile, enumLayer.regular); + const targetContents = this.root.map.getTileContent(targetTile, "regular"); // Check if we are connected to another miner and thus do not eject directly if (targetContents) { @@ -103,41 +102,39 @@ export class MinerSystem extends GameSystemWithFilter { * @param {MapChunkView} chunk */ drawChunk(parameters, chunk) { - const contents = chunk.contents; - for (let y = 0; y < globalConfig.mapChunkSize; ++y) { - for (let x = 0; x < globalConfig.mapChunkSize; ++x) { - const entity = contents[x][y]; + const contents = chunk.containedEntitiesByLayer.regular; - if (entity && entity.components.Miner) { - const staticComp = entity.components.StaticMapEntity; - const minerComp = entity.components.Miner; - if (!staticComp.shouldBeDrawn(parameters)) { - continue; - } - if (!minerComp.cachedMinedItem) { - continue; - } - - if (minerComp.cachedMinedItem) { - const padding = 3; - parameters.context.fillStyle = minerComp.cachedMinedItem.getBackgroundColorAsResource(); - parameters.context.fillRect( - staticComp.origin.x * globalConfig.tileSize + padding, - staticComp.origin.y * globalConfig.tileSize + padding, - globalConfig.tileSize - 2 * padding, - globalConfig.tileSize - 2 * padding - ); - } - - if (minerComp.cachedMinedItem) { - minerComp.cachedMinedItem.draw( - (0.5 + staticComp.origin.x) * globalConfig.tileSize, - (0.5 + staticComp.origin.y) * globalConfig.tileSize, - parameters - ); - } - } + for (let i = 0; i < contents.length; ++i) { + const entity = contents[i]; + const minerComp = entity.components.Miner; + if (!minerComp) { + continue; } + + const staticComp = entity.components.StaticMapEntity; + if (!minerComp.cachedMinedItem) { + continue; + } + + // Draw the item background - this is to hide the ejected item animation from + // the item ejecto + + const padding = 3; + const destX = staticComp.origin.x * globalConfig.tileSize + padding; + const destY = staticComp.origin.y * globalConfig.tileSize + padding; + const dimensions = globalConfig.tileSize - 2 * padding; + + if (parameters.visibleRect.containsRect4Params(destX, destY, dimensions, dimensions)) { + parameters.context.fillStyle = minerComp.cachedMinedItem.getBackgroundColorAsResource(); + parameters.context.fillRect(destX, destY, dimensions, dimensions); + } + + minerComp.cachedMinedItem.drawItemCenteredClipped( + (0.5 + staticComp.origin.x) * globalConfig.tileSize, + (0.5 + staticComp.origin.y) * globalConfig.tileSize, + parameters, + globalConfig.defaultItemDiameter + ); } } } diff --git a/src/js/game/systems/static_map_entity.js b/src/js/game/systems/static_map_entity.js index 00de6b5a..da6575a5 100644 --- a/src/js/game/systems/static_map_entity.js +++ b/src/js/game/systems/static_map_entity.js @@ -6,6 +6,18 @@ import { MapChunkView } from "../map_chunk_view"; export class StaticMapEntitySystem extends GameSystem { constructor(root) { super(root); + + /** @type {Set} */ + this.drawnUids = new Set(); + + this.root.signals.gameFrameStarted.add(this.clearUidList, this); + } + + /** + * Clears the uid list when a new frame started + */ + clearUidList() { + this.drawnUids.clear(); } /** @@ -18,25 +30,21 @@ export class StaticMapEntitySystem extends GameSystem { return; } - const drawnUids = new Set(); + const contents = chunk.containedEntitiesByLayer.regular; + for (let i = 0; i < contents.length; ++i) { + const entity = contents[i]; - const contents = chunk.contents; - for (let y = 0; y < globalConfig.mapChunkSize; ++y) { - for (let x = 0; x < globalConfig.mapChunkSize; ++x) { - const entity = contents[x][y]; - - if (entity) { - if (drawnUids.has(entity.uid)) { - continue; - } - drawnUids.add(entity.uid); - const staticComp = entity.components.StaticMapEntity; - - const sprite = staticComp.getSprite(); - if (sprite) { - staticComp.drawSpriteOnFullEntityBounds(parameters, sprite, 2); - } + const staticComp = entity.components.StaticMapEntity; + const sprite = staticComp.getSprite(); + if (sprite) { + // Avoid drawing an entity twice which has been drawn for + // another chunk already + if (this.drawnUids.has(entity.uid)) { + continue; } + + this.drawnUids.add(entity.uid); + staticComp.drawSpriteOnBoundsClipped(parameters, sprite, 2); } } } @@ -65,7 +73,7 @@ export class StaticMapEntitySystem extends GameSystem { const sprite = staticComp.getSprite(); if (sprite) { - staticComp.drawSpriteOnFullEntityBounds(parameters, sprite, 2); + staticComp.drawSpriteOnBoundsClipped(parameters, sprite, 2); } } } diff --git a/src/js/game/systems/storage.js b/src/js/game/systems/storage.js index 51857348..5a2b57bb 100644 --- a/src/js/game/systems/storage.js +++ b/src/js/game/systems/storage.js @@ -1,16 +1,28 @@ import { GameSystemWithFilter } from "../game_system_with_filter"; import { StorageComponent } from "../components/storage"; -import { Entity } from "../entity"; import { DrawParameters } from "../../core/draw_parameters"; import { formatBigNumber, lerp } from "../../core/utils"; import { Loader } from "../../core/loader"; import { BOOL_TRUE_SINGLETON, BOOL_FALSE_SINGLETON } from "../items/boolean_item"; +import { MapChunkView } from "../map_chunk_view"; export class StorageSystem extends GameSystemWithFilter { constructor(root) { super(root, [StorageComponent]); this.storageOverlaySprite = Loader.getSprite("sprites/misc/storage_overlay.png"); + + /** + * Stores which uids were already drawn to avoid drawing entities twice + * @type {Set} + */ + this.drawnUids = new Set(); + + this.root.signals.gameFrameStarted.add(this.clearDrawnUids, this); + } + + clearDrawnUids() { + this.drawnUids.clear(); } update() { @@ -43,38 +55,46 @@ export class StorageSystem extends GameSystemWithFilter { } } - draw(parameters) { - this.forEachMatchingEntityOnScreen(parameters, this.drawEntity.bind(this)); - } - /** * @param {DrawParameters} parameters - * @param {Entity} entity + * @param {MapChunkView} chunk */ - drawEntity(parameters, entity) { - const context = parameters.context; - const staticComp = entity.components.StaticMapEntity; + drawChunk(parameters, chunk) { + const contents = chunk.containedEntitiesByLayer.regular; + for (let i = 0; i < contents.length; ++i) { + const entity = contents[i]; + const storageComp = entity.components.Storage; + if (!storageComp) { + continue; + } - if (!staticComp.shouldBeDrawn(parameters)) { - return; - } + const storedItem = storageComp.storedItem; + if (!storedItem) { + continue; + } - const storageComp = entity.components.Storage; + if (this.drawnUids.has(entity.uid)) { + continue; + } - const storedItem = storageComp.storedItem; - if (storedItem !== null) { + this.drawnUids.add(entity.uid); + + const staticComp = entity.components.StaticMapEntity; + + const context = parameters.context; context.globalAlpha = storageComp.overlayOpacity; const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace(); - storedItem.draw(center.x, center.y, parameters, 30); + storedItem.drawItemCenteredClipped(center.x, center.y, parameters, 30); this.storageOverlaySprite.drawCached(parameters, center.x - 15, center.y + 15, 30, 15); - context.font = "bold 10px GameFont"; - context.textAlign = "center"; - context.fillStyle = "#64666e"; - context.fillText(formatBigNumber(storageComp.storedCount), center.x, center.y + 25.5); - - context.textAlign = "left"; + if (parameters.visibleRect.containsCircle(center.x, center.y + 25, 20)) { + context.font = "bold 10px GameFont"; + context.textAlign = "center"; + context.fillStyle = "#64666e"; + context.fillText(formatBigNumber(storageComp.storedCount), center.x, center.y + 25.5); + context.textAlign = "left"; + } context.globalAlpha = 1; } } diff --git a/src/js/game/systems/underground_belt.js b/src/js/game/systems/underground_belt.js index 5bbb280c..90d29e50 100644 --- a/src/js/game/systems/underground_belt.js +++ b/src/js/game/systems/underground_belt.js @@ -13,7 +13,6 @@ import { enumUndergroundBeltMode, UndergroundBeltComponent } from "../components import { Entity } from "../entity"; import { GameSystemWithFilter } from "../game_system_with_filter"; import { fastArrayDelete } from "../../core/utils"; -import { enumLayer } from "../root"; const logger = createLogger("tunnels"); @@ -298,7 +297,7 @@ export class UndergroundBeltSystem extends GameSystemWithFilter { ) { currentTile = currentTile.add(searchVector); - const potentialReceiver = this.root.map.getTileContent(currentTile, enumLayer.regular); + const potentialReceiver = this.root.map.getTileContent(currentTile, "regular"); if (!potentialReceiver) { // Empty tile continue; diff --git a/src/js/game/systems/wire.js b/src/js/game/systems/wire.js index bd6a3f17..b8d8031e 100644 --- a/src/js/game/systems/wire.js +++ b/src/js/game/systems/wire.js @@ -1,24 +1,28 @@ -import { GameSystemWithFilter } from "../game_system_with_filter"; -import { WireComponent, enumWireType } from "../components/wire"; -import { MapChunkView } from "../map_chunk_view"; import { globalConfig } from "../../core/config"; -import { Loader } from "../../core/loader"; -import { Entity } from "../entity"; import { gMetaBuildingRegistry } from "../../core/global_registries"; -import { MetaWireBuilding, arrayWireRotationVariantToType } from "../buildings/wire"; +import { Loader } from "../../core/loader"; +import { createLogger } from "../../core/logging"; +import { Rectangle } from "../../core/rectangle"; +import { StaleAreaDetector } from "../../core/stale_area_detector"; +import { fastArrayDeleteValueIfContained } from "../../core/utils"; import { - Vector, + arrayAllDirections, enumDirection, enumDirectionToVector, - arrayAllDirections, enumInvertedDirections, + Vector, } from "../../core/vector"; -import { defaultBuildingVariant } from "../meta_building"; -import { createLogger } from "../../core/logging"; -import { WiredPinsComponent, enumPinSlotType } from "../components/wired_pins"; -import { getCodeFromBuildingData } from "../building_codes"; -import { BaseItem, enumItemType } from "../base_item"; +import { BaseItem } from "../base_item"; import { BooleanItem } from "../items/boolean_item"; +import { arrayWireRotationVariantToType, MetaWireBuilding } from "../buildings/wire"; +import { getCodeFromBuildingData } from "../building_codes"; +import { enumWireType, WireComponent } from "../components/wire"; +import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; +import { WireTunnelComponent } from "../components/wire_tunnel"; +import { Entity } from "../entity"; +import { GameSystemWithFilter } from "../game_system_with_filter"; +import { MapChunkView } from "../map_chunk_view"; +import { defaultBuildingVariant } from "../meta_building"; const logger = createLogger("wires"); @@ -44,6 +48,12 @@ export class WireNetwork { */ this.allSlots = []; + /** + * All connected tunnels + * @type {Array} + */ + this.tunnels = []; + /** * Which wires are in this network * @type {Array} @@ -102,8 +112,8 @@ export class WireSystem extends GameSystemWithFilter { }, }; - this.root.signals.entityDestroyed.add(this.updateSurroundingWirePlacement, this); - this.root.signals.entityAdded.add(this.updateSurroundingWirePlacement, this); + this.root.signals.entityDestroyed.add(this.queuePlacementUpdate, this); + this.root.signals.entityAdded.add(this.queuePlacementUpdate, this); this.root.signals.entityDestroyed.add(this.queueRecomputeIfWire, this); this.root.signals.entityChanged.add(this.queueRecomputeIfWire, this); @@ -111,6 +121,12 @@ export class WireSystem extends GameSystemWithFilter { this.needsRecompute = true; + this.staleArea = new StaleAreaDetector({ + root: this.root, + name: "wires", + recomputeMethod: this.updateSurroundingWirePlacement.bind(this), + }); + /** * @type {Array} */ @@ -125,7 +141,8 @@ export class WireSystem extends GameSystemWithFilter { if (!this.root.gameInitialized) { return; } - if (entity.components.Wire || entity.components.WiredPins) { + + if (this.isEntityRelevantForWires(entity)) { this.needsRecompute = true; this.networks = []; } @@ -146,6 +163,11 @@ export class WireSystem extends GameSystemWithFilter { wireEntities[i].components.Wire.linkedNetwork = null; } + const tunnelEntities = this.root.entityMgr.getAllWithComponent(WireTunnelComponent); + for (let i = 0; i < tunnelEntities.length; ++i) { + tunnelEntities[i].components.WireTunnel.linkedNetworks = []; + } + const pinEntities = this.root.entityMgr.getAllWithComponent(WiredPinsComponent); for (let i = 0; i < pinEntities.length; ++i) { const slots = pinEntities[i].components.WiredPins.slots; @@ -280,7 +302,11 @@ export class WireSystem extends GameSystemWithFilter { if (newSearchTile) { // Find new surrounding wire targets - const newTargets = this.findSurroundingWireTargets(newSearchTile, newSearchDirections); + const newTargets = this.findSurroundingWireTargets( + newSearchTile, + newSearchDirections, + currentNetwork + ); VERBOSE_WIRES && logger.log(" Found", newTargets, "new targets to visit!"); for (let i = 0; i < newTargets.length; ++i) { @@ -291,7 +317,9 @@ export class WireSystem extends GameSystemWithFilter { if ( currentNetwork.providers.length > 0 && - (currentNetwork.wires.length > 0 || currentNetwork.receivers.length > 0) + (currentNetwork.wires.length > 0 || + currentNetwork.receivers.length > 0 || + currentNetwork.tunnels.length > 0) ) { this.networks.push(currentNetwork); VERBOSE_WIRES && logger.log("Attached new network with uid", currentNetwork); @@ -301,6 +329,13 @@ export class WireSystem extends GameSystemWithFilter { currentNetwork.wires[i].components.Wire.linkedNetwork = null; } + for (let i = 0; i < currentNetwork.tunnels.length; ++i) { + fastArrayDeleteValueIfContained( + currentNetwork.tunnels[i].components.WireTunnel.linkedNetworks, + currentNetwork + ); + } + for (let i = 0; i < currentNetwork.allSlots.length; ++i) { currentNetwork.allSlots[i].slot.linkedNetwork = null; } @@ -311,9 +346,10 @@ export class WireSystem extends GameSystemWithFilter { * Finds surrounding entities which are not yet assigned to a network * @param {Vector} initialTile * @param {Array} directions + * @param {WireNetwork} network * @returns {Array} */ - findSurroundingWireTargets(initialTile, directions) { + findSurroundingWireTargets(initialTile, directions, network) { let result = []; VERBOSE_WIRES && @@ -335,6 +371,7 @@ export class WireSystem extends GameSystemWithFilter { ); // Link the initial tile to the initial entities, since it may change + /** @type {Array<{entity: Entity, tile: Vector}>} */ const contents = []; for (let j = 0; j < initialContents.length; ++j) { contents.push({ @@ -384,40 +421,67 @@ export class WireSystem extends GameSystemWithFilter { }); } } + + // Pin slots mean it can be nothing else + continue; } // Check if its a tunnel, if so, go to the forwarded item - if (entity.components.WireTunnel) { - if (!visitedTunnels.has(entity.uid)) { - // Compute where this tunnel connects to - const forwardedTile = entity.components.StaticMapEntity.origin.add(offset); - VERBOSE_WIRES && - logger.log( - " Found tunnel", - entity.uid, - "at", - tile, - "-> forwarding to", - forwardedTile - ); + const tunnelComp = entity.components.WireTunnel; + if (tunnelComp) { + if (visitedTunnels.has(entity.uid)) { + continue; + } - // Figure out which entities are connected - const connectedContents = this.root.map.getLayersContentsMultipleXY( - forwardedTile.x, - forwardedTile.y + const staticComp = entity.components.StaticMapEntity; + + if ( + !tunnelComp.multipleDirections && + !( + direction === staticComp.localDirectionToWorld(enumDirection.top) || + direction === staticComp.localDirectionToWorld(enumDirection.bottom) + ) + ) { + // It's a coating, and it doesn't connect here + continue; + } + + // Compute where this tunnel connects to + const forwardedTile = staticComp.origin.add(offset); + VERBOSE_WIRES && + logger.log( + " Found tunnel", + entity.uid, + "at", + tile, + "-> forwarding to", + forwardedTile ); - // Attach the entities and the tile we search at, because it may change - for (let h = 0; h < connectedContents.length; ++h) { - contents.push({ - entity: connectedContents[h], - tile: forwardedTile, - }); - } + // Figure out which entities are connected + const connectedContents = this.root.map.getLayersContentsMultipleXY( + forwardedTile.x, + forwardedTile.y + ); - // Remember this tunnel - visitedTunnels.add(entity.uid); + // Attach the entities and the tile we search at, because it may change + for (let h = 0; h < connectedContents.length; ++h) { + contents.push({ + entity: connectedContents[h], + tile: forwardedTile, + }); } + + // Add the tunnel to the network + if (tunnelComp.linkedNetworks.indexOf(network) < 0) { + tunnelComp.linkedNetworks.push(network); + } + if (network.tunnels.indexOf(entity) < 0) { + network.tunnels.push(entity); + } + + // Remember this tunnel + visitedTunnels.add(entity.uid); } } } @@ -431,6 +495,8 @@ export class WireSystem extends GameSystemWithFilter { * Updates the wires network */ update() { + this.staleArea.update(); + if (this.needsRecompute) { this.recomputeWiresNetwork(); } @@ -514,17 +580,17 @@ export class WireSystem extends GameSystemWithFilter { } const valueType = value.getItemType(); - if (valueType === enumItemType.shape) { + if (valueType === "shape") { return { spriteSet: this.wireSprites.shape, opacity: 1, }; - } else if (valueType === enumItemType.color) { + } else if (valueType === "color") { return { spriteSet: this.wireSprites.color, opacity: 1, }; - } else if (valueType === enumItemType.boolean) { + } else if (valueType === "boolean") { return { spriteSet: this.wireSprites.regular, opacity: /** @type {BooleanItem} */ (value).value ? 1 : 0.5, @@ -552,39 +618,15 @@ export class WireSystem extends GameSystemWithFilter { if (entity && entity.components.Wire) { const wireComp = entity.components.Wire; const wireType = wireComp.type; - const network = wireComp.linkedNetwork; const { opacity, spriteSet } = this.getSpriteSetAndOpacityForWire(wireComp); - // if (!network) { - // opacity = 0.3; - // } else { - // if (network.valueConflict) { - // opacity = 1; - // // TODO - // } else { - // if (network.currentValue) { - // if ( - // network.currentValue.getItemType() === enumItemType.boolean && - // // @ts-ignore - // network.currentValue.value === 0 - // ) { - // opacity = 0.5; - // } else { - // opacity = 1; - // } - // } else { - // opacity = 0.5; - // } - // } - // } - const sprite = spriteSet[wireType]; assert(sprite, "Unknown wire type: " + wireType); const staticComp = entity.components.StaticMapEntity; parameters.context.globalAlpha = opacity; - staticComp.drawSpriteOnFullEntityBounds(parameters, sprite, 0); + staticComp.drawSpriteOnBoundsClipped(parameters, sprite, 0); parameters.context.globalAlpha = 1; if (G_IS_DEV && globalConfig.debug.renderWireRotations) { @@ -638,32 +680,46 @@ export class WireSystem extends GameSystemWithFilter { } /** - * Updates the wire placement after an entity has been added / deleted + * Returns whether this entity is relevant for the wires network * @param {Entity} entity */ - updateSurroundingWirePlacement(entity) { + isEntityRelevantForWires(entity) { + return entity.components.Wire || entity.components.WiredPins || entity.components.WireTunnel; + } + + /** + * + * @param {Entity} entity + */ + queuePlacementUpdate(entity) { if (!this.root.gameInitialized) { return; } + if (!this.isEntityRelevantForWires(entity)) { + return; + } + const staticComp = entity.components.StaticMapEntity; if (!staticComp) { return; } - const metaWire = gMetaBuildingRegistry.findByClass(MetaWireBuilding); - - // Compute affected area + // Invalidate affected area const originalRect = staticComp.getTileSpaceBounds(); const affectedArea = originalRect.expandedInAllDirections(1); + this.staleArea.invalidate(affectedArea); + } + + /** + * Updates the wire placement after an entity has been added / deleted + * @param {Rectangle} affectedArea + */ + updateSurroundingWirePlacement(affectedArea) { + const metaWire = gMetaBuildingRegistry.findByClass(MetaWireBuilding); for (let x = affectedArea.x; x < affectedArea.right(); ++x) { for (let y = affectedArea.y; y < affectedArea.bottom(); ++y) { - if (originalRect.containsPoint(x, y)) { - // Make sure we don't update the original entity - continue; - } - const targetEntities = this.root.map.getLayersContentsMultipleXY(x, y); for (let i = 0; i < targetEntities.length; ++i) { const targetEntity = targetEntities[i]; diff --git a/src/js/game/systems/wired_pins.js b/src/js/game/systems/wired_pins.js index 8b1d5b5e..43771b49 100644 --- a/src/js/game/systems/wired_pins.js +++ b/src/js/game/systems/wired_pins.js @@ -1,14 +1,13 @@ import { globalConfig } from "../../core/config"; import { DrawParameters } from "../../core/draw_parameters"; +import { drawRotatedSprite } from "../../core/draw_utils"; import { Loader } from "../../core/loader"; -import { Vector, enumDirectionToAngle } from "../../core/vector"; +import { STOP_PROPAGATION } from "../../core/signal"; +import { enumDirectionToAngle, Vector } from "../../core/vector"; import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; import { Entity } from "../entity"; import { GameSystemWithFilter } from "../game_system_with_filter"; -import { enumLayer } from "../root"; -import { STOP_PROPAGATION } from "../../core/signal"; -import { drawRotatedSprite } from "../../core/draw_utils"; -import { GLOBAL_APP } from "../../core/globals"; +import { MapChunkView } from "../map_chunk_view"; export class WiredPinsSystem extends GameSystemWithFilter { constructor(root) { @@ -38,7 +37,7 @@ export class WiredPinsSystem extends GameSystemWithFilter { // If this entity is placed on the wires layer, make sure we don't // place it above a pin - if (entity.layer === enumLayer.wires) { + if (entity.layer === "wires") { for (let x = rect.x; x < rect.x + rect.w; ++x) { for (let y = rect.y; y < rect.y + rect.h; ++y) { // Find which entities are in same tiles of both layers @@ -103,7 +102,7 @@ export class WiredPinsSystem extends GameSystemWithFilter { } // Check if there is any entity on that tile (Wired pins are always on the wires layer) - const collidingEntity = this.root.map.getLayerContentXY(worldPos.x, worldPos.y, enumLayer.wires); + const collidingEntity = this.root.map.getLayerContentXY(worldPos.x, worldPos.y, "wires"); // If there's an entity, and it can't get removed -> That's a collision if (collidingEntity) { @@ -130,7 +129,7 @@ export class WiredPinsSystem extends GameSystemWithFilter { for (let i = 0; i < pinsComp.slots.length; ++i) { const slot = pinsComp.slots[i]; const worldPos = entity.components.StaticMapEntity.localTileToWorld(slot.pos); - const collidingEntity = this.root.map.getLayerContentXY(worldPos.x, worldPos.y, enumLayer.wires); + const collidingEntity = this.root.map.getLayerContentXY(worldPos.x, worldPos.y, "wires"); if (collidingEntity) { assertAlways( collidingEntity.components.StaticMapEntity.getMetaBuilding().getIsReplaceable(), @@ -147,65 +146,84 @@ export class WiredPinsSystem extends GameSystemWithFilter { // TODO } - /** - * Draws the pins - * @param {DrawParameters} parameters - */ - draw(parameters) { - this.forEachMatchingEntityOnScreen(parameters, this.drawSingleEntity.bind(this)); - } - /** * Draws a given entity * @param {DrawParameters} parameters - * @param {Entity} entity + * @param {MapChunkView} chunk */ - drawSingleEntity(parameters, entity) { - const staticComp = entity.components.StaticMapEntity; - const slots = entity.components.WiredPins.slots; + drawChunk(parameters, chunk) { + const contents = chunk.containedEntities; - for (let i = 0; i < slots.length; ++i) { - const slot = slots[i]; - const tile = staticComp.localTileToWorld(slot.pos); - - const worldPos = tile.toWorldSpaceCenterOfTile(); - const effectiveRotation = Math.radians( - staticComp.rotation + enumDirectionToAngle[slot.direction] - ); - - if (staticComp.getMetaBuilding().getRenderPins()) { - drawRotatedSprite({ - parameters, - sprite: this.pinSprites[slot.type], - x: worldPos.x, - y: worldPos.y, - angle: effectiveRotation, - size: globalConfig.tileSize + 2, - offsetX: 0, - offsetY: 0, - }); + for (let i = 0; i < contents.length; ++i) { + const entity = contents[i]; + const pinsComp = entity.components.WiredPins; + if (!pinsComp) { + continue; } - // Draw contained item to visualize whats emitted - const value = slot.value; - if (value) { - const offset = new Vector(0, -9).rotated(effectiveRotation); - value.draw(worldPos.x + offset.x, worldPos.y + offset.y, parameters, 9); - } + const staticComp = entity.components.StaticMapEntity; + const slots = pinsComp.slots; - // Debug view - if (G_IS_DEV && globalConfig.debug.renderWireNetworkInfos) { - const offset = new Vector(0, -10).rotated(effectiveRotation); - const network = slot.linkedNetwork; - parameters.context.fillStyle = "blue"; - parameters.context.font = "5px Tahoma"; - parameters.context.textAlign = "center"; - parameters.context.fillText( - network ? "S" + network.uid : "???", - (tile.x + 0.5) * globalConfig.tileSize + offset.x, - (tile.y + 0.5) * globalConfig.tileSize + offset.y + for (let j = 0; j < slots.length; ++j) { + const slot = slots[j]; + const tile = staticComp.localTileToWorld(slot.pos); + + if (!chunk.tileSpaceRectangle.containsPoint(tile.x, tile.y)) { + // Doesn't belong to this chunk + continue; + } + const worldPos = tile.toWorldSpaceCenterOfTile(); + + // Culling + if ( + !parameters.visibleRect.containsCircle(worldPos.x, worldPos.y, globalConfig.halfTileSize) + ) { + continue; + } + + const effectiveRotation = Math.radians( + staticComp.rotation + enumDirectionToAngle[slot.direction] ); - parameters.context.textAlign = "left"; + + if (staticComp.getMetaBuilding().getRenderPins()) { + drawRotatedSprite({ + parameters, + sprite: this.pinSprites[slot.type], + x: worldPos.x, + y: worldPos.y, + angle: effectiveRotation, + size: globalConfig.tileSize + 2, + offsetX: 0, + offsetY: 0, + }); + } + + // Draw contained item to visualize whats emitted + const value = slot.value; + if (value) { + const offset = new Vector(0, -9).rotated(effectiveRotation); + value.drawItemCenteredClipped( + worldPos.x + offset.x, + worldPos.y + offset.y, + parameters, + 9 + ); + } + + // Debug view + if (G_IS_DEV && globalConfig.debug.renderWireNetworkInfos) { + const offset = new Vector(0, -10).rotated(effectiveRotation); + const network = slot.linkedNetwork; + parameters.context.fillStyle = "blue"; + parameters.context.font = "5px Tahoma"; + parameters.context.textAlign = "center"; + parameters.context.fillText( + network ? "S" + network.uid : "???", + (tile.x + 0.5) * globalConfig.tileSize + offset.x, + (tile.y + 0.5) * globalConfig.tileSize + offset.y + ); + parameters.context.textAlign = "left"; + } } } } diff --git a/src/js/game/themes/dark.json b/src/js/game/themes/dark.json index 85b29617..39debd98 100644 --- a/src/js/game/themes/dark.json +++ b/src/js/game/themes/dark.json @@ -34,7 +34,9 @@ }, "wires": { - "overlayColor": "rgba(97, 161, 152, 0.75)" + "overlayColor": "rgba(97, 161, 152, 0.75)", + "previewColor": "rgb(97, 161, 152, 0.5)", + "highlightColor": "rgba(0, 0, 255, 0.5)" } }, diff --git a/src/js/game/themes/light.json b/src/js/game/themes/light.json index 2bc7050a..28742282 100644 --- a/src/js/game/themes/light.json +++ b/src/js/game/themes/light.json @@ -35,7 +35,9 @@ }, "wires": { - "overlayColor": "rgba(97, 161, 152, 0.75)" + "overlayColor": "rgba(97, 161, 152, 0.75)", + "previewColor": "rgb(97, 161, 152, 0.4)", + "highlightColor": "rgba(72, 137, 255, 0.8)" } }, diff --git a/src/js/globals.d.ts b/src/js/globals.d.ts index 3f842e7e..51e4a2c3 100644 --- a/src/js/globals.d.ts +++ b/src/js/globals.d.ts @@ -193,6 +193,9 @@ declare interface TypedSignal> { removeAll(); } +declare type Layer = "regular" | "wires"; +declare type ItemType = "shape" | "color" | "boolean"; + declare module "worker-loader?inline=true&fallback=false!*" { class WebpackWorker extends Worker { constructor(); diff --git a/src/js/platform/browser/game_analytics.js b/src/js/platform/browser/game_analytics.js index 73da3696..7336ffd9 100644 --- a/src/js/platform/browser/game_analytics.js +++ b/src/js/platform/browser/game_analytics.js @@ -85,10 +85,9 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface { * @returns {Promise} */ sendToApi(endpoint, data) { - return Promise.race([ - new Promise((resolve, reject) => { - setTimeout(() => reject("Request to " + endpoint + " timed out"), 20000); - }), + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => reject("Request to " + endpoint + " timed out"), 20000); + fetch(analyticsUrl + endpoint, { method: "POST", mode: "cors", @@ -103,13 +102,19 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface { body: JSON.stringify(data), }) .then(res => { + clearTimeout(timeout); if (!res.ok || res.status !== 200) { - throw new Error("Fetch error: Bad status " + res.status); + reject("Fetch error: Bad status " + res.status); + } else { + return res.json(); } - return res; }) - .then(res => res.json()), - ]); + .then(resolve) + .catch(reason => { + clearTimeout(timeout); + reject(reason); + }); + }); } /** diff --git a/src/js/savegame/serialization_data_types.js b/src/js/savegame/serialization_data_types.js index 033acda1..9fb53bb8 100644 --- a/src/js/savegame/serialization_data_types.js +++ b/src/js/savegame/serialization_data_types.js @@ -456,7 +456,7 @@ export class TypeEnum extends BaseDataType { */ constructor(enumeration = {}) { super(); - this.availableValues = Object.keys(enumeration); + this.availableValues = Object.values(enumeration); } serialize(value) { diff --git a/translations/base-de.yaml b/translations/base-de.yaml index 62c823da..29713620 100644 --- a/translations/base-de.yaml +++ b/translations/base-de.yaml @@ -78,6 +78,7 @@ steamPage: Die komplette Roadmap gibt es auf dem Trello-Board zum Nachlesen! [b]Links [/b] + [list] [*] [url=https://discord.com/invite/HN7EVzV]Offizieller Discord[/url] [*] [url=https://trello.com/b/ISQncpJP/shapezio]Roadmap[/url] @@ -140,10 +141,15 @@ demoBanners: mainMenu: play: Spielen + continue: Fortsetzen + newGame: Neues Spiel changelog: Änderungshistorie + subreddit: Reddit importSavegame: Importieren openSourceHint: Dieses Spiel ist Open Source! discordLink: Offizieller Discord Server + helpTranslate: Hilf beim Übersetzen! + madeBy: Ein Spiel von # This is shown when using firefox and other browsers which are not supported. browserWarning: >- @@ -152,12 +158,6 @@ mainMenu: savegameLevel: Level savegameLevelUnknown: Unbekanntes Level - helpTranslate: Hilf beim Übersetzen! - continue: Fortsetzen - newGame: Neues Spiel - madeBy: Ein Spiel von - subreddit: Reddit - dialogs: buttons: ok: OK @@ -238,6 +238,16 @@ dialogs: desc: >- Du löscht sehr viele Gebäude ( um genau zu sein)! Bist du dir sicher? + massCutConfirm: + title: Ausschneiden bestätigen + desc: >- + Du schneidest sehr viele Gebäude aus ( um genau zu sein)! Bist du dir sicher? + + massCutInsufficientConfirm: + title: Ausschneiden bestätigen + desc: >- + Du hast aktuell nicht genug Blaupausenformen, um die Auswahl einzufügen! Möchtest du sie trotzdem ausschneiden? + blueprintsNotUnlocked: title: Noch nicht freigeschaltet desc: >- @@ -254,24 +264,15 @@ dialogs: createMarker: title: Neuer Marker - desc: Gib ihm einen griffigen Namen. Du kannst sogar die Abkürzung einer Form eingeben (Diese kann hier generiert werden). titleEdit: Edit Marker + desc: Gib ihm einen griffigen Namen. Du kannst sogar die Abkürzung einer Form eingeben (Diese kann hier generiert werden). markerDemoLimit: desc: Du kannst nur 2 benutzerdefinierte Marker in der Demo benutzen. Hol dir die Standalone, um unendlich viele Marker zu erstellen! - massCutConfirm: - title: Ausschneiden bestätigen - desc: >- - Du schneidest sehr viele Gebäude aus ( um genau zu sein)! Bist du dir sicher? exportScreenshotWarning: title: Bildschirmfoto exportieren - desc: >- - Hier kannst du ein Bildschirmfoto von deiner ganzen Fabrik erstellen. Für extrem große Fabriken kann das jedoch sehr lange dauern und ggf. zum Spielabsturz führen! - - massCutInsufficientConfirm: - title: Ausschneiden bestätigen - desc: Du hast aktuell nicht genug Blaupausenformen, um die Auswahl einzufügen! Möchtest du sie trotzdem ausschneiden? + desc: Hier kannst du ein Bildschirmfoto von deiner ganzen Fabrik erstellen. Für extrem große Fabriken kann das jedoch sehr lange dauern und ggf. zum Spielabsturz führen! ingame: # This is shown in the top left corner and displays useful keybindings in @@ -297,6 +298,18 @@ ingame: pipette: Pipette switchLayers: Ebenen wechseln + # Names of the colors, used for the color blind mode + colors: + red: Rot + green: Grün + blue: Blau + yellow: Gelb + purple: Violett + cyan: Cyan + white: Weiß + black: Schwarz + uncolored: Farblos + # Everything related to placing buildings (I.e. as soon as you selected a building # from the toolbar) buildingPlacement: @@ -391,6 +404,12 @@ ingame: description: Linksklick auf einen Marker, um dort hinzugelangen, Rechtsklick, um ihn zu löschen.

Drücke um einen Marker aus deinem Blickwinkel, oder rechtsklicke, um einen Marker auf der ausgewählten Position zu erschaffen. creationSuccessNotification: Marker wurde erstellt. + # Shape viewer + shapeViewer: + title: Ebenen + empty: Leer + copyKey: Schlüssel kopieren + # Interactive tutorial interactiveTutorial: title: Tutorial @@ -402,21 +421,6 @@ ingame: 1_3_expand: >- Dies ist KEIN Idle-Game! Baue mehr Extrahierer und Förderbänder, um das Ziel schneller zu erreichen.

Tipp: Halte UMSCH, um mehrere Gebäude zu platzieren und nutze R um sie zu rotieren. - colors: - red: Rot - green: Grün - blue: Blau - yellow: Gelb - purple: Violett - cyan: Cyan - white: Weiß - uncolored: Farblos - black: Schwarz - shapeViewer: - title: Ebenen - empty: Leer - copyKey: Schlüssel kopieren - # All shop upgrades shopUpgrades: belt: @@ -434,12 +438,19 @@ shopUpgrades: # Buildings and their name / description buildings: + hub: + deliver: Liefere + toUnlock: >- + Für folgende Belohnung: + levelShortcut: LVL + belt: default: name: &belt Förderband description: Transportiert Items. Halte und ziehe um mehrere zu platzieren. - miner: # Internal name for the Extractor + # Internal name for the Extractor + miner: default: name: &miner Extrahierer description: Platziere ihn auf einer Form oder Farbe um sie zu extrahieren. @@ -448,7 +459,8 @@ buildings: name: Extrahierer (Kette) description: Platziere ihn auf einer Form oder Farbe um sie zu extrahieren. Kann verkettet werden. - underground_belt: # Internal name for the Tunnel + # Internal name for the Tunnel + underground_belt: default: name: &underground_belt Tunnel description: Erlaubt dir, Formen und Farbe unter Gebäuden und Förderbändern durchzuleiten. @@ -457,7 +469,8 @@ buildings: name: Tunnel Stufe II description: Erlaubt dir, Formen und Farbe unter Gebäuden und Förderbändern durchzuleiten. Höhere Reichweite. - splitter: # Internal name for the Balancer + # Internal name for the Balancer + splitter: default: name: &splitter Verteiler description: Multifunktional - Verteilt gleichmäßig vom Eingang auf den Ausgang. @@ -482,7 +495,6 @@ buildings: default: name: &rotater Rotierer description: Rotiert Formen im Uhrzeigersinn um 90 Grad. - ccw: name: Rotierer (CCW) description: Rotiert Formen gegen den Uhrzeigersinn um 90 Grad. @@ -505,6 +517,10 @@ buildings: name: &painter Färber description: &painter_desc Färbt die ganze Form aus dem linken Eingang mit der Farbe aus dem oberen Eingang. + mirrored: + name: *painter + description: *painter_desc + double: name: Färber (2-Fach) description: Färbt beide Formen aus dem linken Eingang mit der Farbe aus dem oberen Eingang. @@ -512,9 +528,6 @@ buildings: quad: name: Färber (4-Fach) description: Erlaubt jedes einzelne Viertel einer Form beliebig einzufärben. - mirrored: - name: *painter - description: *painter_desc trash: default: @@ -525,11 +538,6 @@ buildings: name: Lager description: Lagert den Überschuss bis zu einer gegebenen Kapazität. Kann als Überlauftor agieren. - hub: - deliver: Liefere - toUnlock: >- - Für folgende Belohnung: - levelShortcut: LVL wire: default: name: Energiekabel @@ -660,6 +668,19 @@ settings: large: Groß huge: Riesig + autosaveInterval: + title: Intervall für automatisches Speichern + description: >- + Ändert das Intervall, in dem der Spielstand automatisch gespeichert wird. Die Funktion kann hier auch deaktiviert werden. + + intervals: + one_minute: 1 Minute + two_minutes: 2 Minuten + five_minutes: 5 Minuten + ten_minutes: 10 Minuten + twenty_minutes: 20 Minuten + disabled: Deaktiviert + scrollWheelSensitivity: title: Zoomempfindlichkeit description: >- @@ -671,6 +692,28 @@ settings: fast: Schnell super_fast: Sehr schnell + movementSpeed: + title: Bewegungsgeschwindigkeit + description: >- + Ändert die Geschwindigkeit, mit der der Bildschirm durch die Pfeiltasten bewegt wird. + speeds: + super_slow: Sehr langsam + slow: Langsam + regular: Normal + fast: Schnell + super_fast: Sehr schnell + extremely_fast: Extrem schnell + + language: + title: Sprache + description: >- + Ändere die Sprache. Alle Übersetzungen werden von Nutzern erstellt und sind möglicherweise unvollständig! + + enableColorBlindHelper: + title: Modus für Farbenblinde + description: >- + Aktiviert verschiedene Werkzeuge, die dir das Spielen trotz Farbenblindheit ermöglichen. + fullscreen: title: Vollbild description: >- @@ -690,10 +733,10 @@ settings: title: Farbmodus description: >- Wähle zwischen dunklem und hellem Farbmodus. - themes: dark: Dunkel light: Hell + refreshRate: title: Zielbildwiederholrate description: >- @@ -709,21 +752,6 @@ settings: description: >- Schaltet Hinweise und das Tutorial beim Spielen an und aus. Außerdem werden zu den Levels bestimmte Textfelder versteckt, die den Einstieg erleichtern sollen. - language: - title: Sprache - description: >- - Ändere die Sprache. Alle Übersetzungen werden von Nutzern erstellt und sind möglicherweise unvollständig! - - movementSpeed: - title: Bewegungsgeschwindigkeit - description: Ändert die Geschwindigkeit, mit der der Bildschirm durch die Pfeiltasten bewegt wird. - speeds: - super_slow: Sehr langsam - slow: Langsam - regular: Normal - fast: Schnell - super_fast: Sehr schnell - extremely_fast: Extrem schnell enableTunnelSmartplace: title: Intelligente Tunnel description: >- @@ -732,43 +760,23 @@ settings: vignette: title: Vignette description: >- - Aktiviert den Vignetteneffekt, der den Rand des Bildschirms zunehmend verdunkelt - und das Lesen der Textfelder vereinfacht. + Aktiviert den Vignetteneffekt, der den Rand des Bildschirms zunehmend verdunkelt und das Lesen der Textfelder vereinfacht. - autosaveInterval: - title: Intervall für automatisches Speichern + rotationByBuilding: + title: Rotation pro Gebäudetyp description: >- - Ändert das Intervall, in dem der Spielstand automatisch gespeichert wird. - Die Funktion kann hier auch deaktiviert werden. - intervals: - one_minute: 1 Minute - two_minutes: 2 Minuten - five_minutes: 5 Minuten - ten_minutes: 10 Minuten - twenty_minutes: 20 Minuten - disabled: Deaktiviert + Jeder Gebäudetyp merkt sich einzeln, welche Rotation ausgewählt ist. Das fühlt sich möglicherweise besser an, wenn du häufig zwischen verschiedenen Gebäudetypen wechselst. compactBuildingInfo: title: Kompakte Gebäudeinformationen description: >- - Reduziert die Infoboxen der Gebäude auf ihre Arbeitsgeschwindigkeit. Anderenfalls wird ein - Bild mit Beschreibung angezeigt. + Reduziert die Infoboxen der Gebäude auf ihre Arbeitsgeschwindigkeit. Anderenfalls wird ein Bild mit Beschreibung angezeigt. disableCutDeleteWarnings: title: Deaktiviere Warnungsdialog beim Löschen description: >- Deaktiviert die Warnung, die beim Löschen und Ausschneiden von mehr als 100 Feldern angezeigt wird. - enableColorBlindHelper: - title: Modus für Farbenblinde - description: Aktiviert verschiedene Werkzeuge, die dir das Spielen trotz Farbenblindheit ermöglichen. - rotationByBuilding: - title: Rotation pro Gebäudetyp - description: >- - Jeder Gebäudetyp merkt sich einzeln, welche Rotation ausgewählt ist. - Das fühlt sich möglicherweise besser an, wenn du häufig zwischen verschiedenen - Gebäudetypen wechselst. - keybindings: title: Tastenbelegung hint: >- @@ -792,6 +800,7 @@ keybindings: mapMoveRight: Nach rechts bewegen mapMoveDown: Nach unten bewegen mapMoveLeft: Nach links bewegen + mapMoveFaster: Schneller bewegen centerMap: Karte zentrieren mapZoomIn: Hineinzoomen @@ -800,9 +809,12 @@ keybindings: menuOpenShop: Upgrades menuOpenStats: Statistiken + menuClose: Menü schließen toggleHud: HUD an/aus toggleFPSInfo: FPS und Debug-Info an/aus + switchLayers: Ebenen wechseln + exportScreenshot: Ganze Fabrik als Foto exportieren belt: *belt splitter: *splitter underground_belt: *underground_belt @@ -814,32 +826,28 @@ keybindings: painter: *painter trash: *trash + pipette: Pipette rotateWhilePlacing: Rotieren rotateInverseModifier: >- Modifikator: stattdessen gegen den UZS rotieren cycleBuildingVariants: Variante wählen confirmMassDelete: Massenlöschung bestätigen + pasteLastBlueprint: Letzte Blaupause einfügen cycleBuildings: Gebäude rotieren + lockBeltDirection: Bandplaner aktivieren + switchDirectionLockSide: >- + Planer: Seite wechseln massSelectStart: Halten und ziehen zum Beginnen massSelectSelectMultiple: Mehrere Areale markieren massSelectCopy: Areal kopieren + massSelectCut: Areal ausschneiden placementDisableAutoOrientation: Automatische Orientierung deaktivieren placeMultiple: Im Platziermodus bleiben placeInverse: Automatische Förderbandorientierung invertieren - pasteLastBlueprint: Letzte Blaupause einfügen - massSelectCut: Areal ausschneiden - exportScreenshot: Ganze Fabrik als Foto exportieren - mapMoveFaster: Schneller bewegen - lockBeltDirection: Bandplaner aktivieren - switchDirectionLockSide: "Planer: Seite wechseln" - pipette: Pipette - menuClose: Menü schließen - switchLayers: Ebenen wechseln advanced_processor: Farbnivertierer energy_generator: Energiegenerator - wire: Energiekabel about: title: Über dieses Spiel diff --git a/translations/base-en.yaml b/translations/base-en.yaml index b1a53f21..edc304b9 100644 --- a/translations/base-en.yaml +++ b/translations/base-en.yaml @@ -547,6 +547,10 @@ buildings: name: &wire_tunnel Wire Tunnel description: Allows to cross two wires without connecting them. + coating: + name: Wire Insulation + description: Allows to pass through signals without connecting to other wires on the sides. + constant_signal: default: name: &constant_signal Constant Signal @@ -581,6 +585,12 @@ buildings: # TEMP description: Only leaves through items who match exactly the provided shape / color. If you put in a boolean 1, it leaves everything through, if you put in a 0 it will leave nothing through. + display: + default: + name: &display Display + # TEMP + description: Can be connected on the wires layer to show a color or shape. When inputting a boolean item, the display will be white if the value is 1. + storyRewards: # Those are the rewards gained from completing the store reward_cutter_and_trash: @@ -871,6 +881,7 @@ keybindings: lever: *lever filter: *filter wire_tunnel: *wire_tunnel + display: *display # --- pipette: Pipette diff --git a/translations/base-ind.yaml b/translations/base-ind.yaml index 3f7ee113..80e1238a 100644 --- a/translations/base-ind.yaml +++ b/translations/base-ind.yaml @@ -22,11 +22,10 @@ --- steamPage: # This is the short text appearing on the steam page - shortText: shapez.io is a game about building factories to automate the creation and processing of increasingly complex shapes across an infinitely expanding map. - - # This is the text shown above the Discord link - discordLink: Official Discord - Chat with me! + shortText: shapez.io adalah permainan membangun pabrik-pabrik dengan tujuan untuk mengautomatiskan pembentukan dan pemrosesan bentuk-bentuk yang bertambah semakin kompleks di dalam area permainan yang meluas secara tak terhingga. + # This is the text shown above the discord link + discordLink: Tautan Resmi Discord – Obrol dengan saya! # This is the long description for the steam page - It is contained here so you can help to translate it, and I will regulary update the store page. # NOTICE: # - Do not translate the first line (This is the gif image at the start of the store) @@ -34,50 +33,50 @@ steamPage: longText: >- [img]{STEAM_APP_IMAGE}/extras/store_page_gif.gif[/img] - shapez.io is a game about building factories to automate the creation and processing of increasingly complex shapes across an infinitely expanding map. + shapez.io adalah permainan membangun pabrik-pabrik dengan tujuan untuk mengautomatiskan pembentukan dan pemrosesan bentuk-bentuk yang bertambah semakin kompleks di dalam area permainan yang meluas secara tak terhingga. - Upon delivering the requested shapes you'll progress within the game and unlock upgrades to speed up your factory. + Setelah pengiriman bentuk-bentuk yang diminta, Anda akan maju dalam permainan dan membuka tingkatan versi-versi mesin selanjutnya untuk mempercepat pabrik Anda. - As the demand for shapes increases, you'll have to scale up your factory to meet the demand - Don't forget about resources though, you'll have to expand across the [b]infinite map[/b]! + Seiring meningkatnya kesulitan dari bentuk-bentuk yang diminta, Anda harus meningkatkan pabrik Anda untuk mengatasi kesulitan tersebut – Jangan lupa dengan sumber-sumber daya, Anda harus memperluas ke seluruh [b]area yang tidak terbatas[/b]! - Soon you'll have to mix colors and paint your shapes with them - Combine red, green and blue color resources to produce different colors and paint shapes with them to satisfy the demand. + Kemudian Anda harus mencampurkan warna-warna dan mencat bentuk-bentuk dengannya – Campurkan merah, hijau, dan biru untuk memproduksi warna-warna lain dan mencat bentuk-bentuk dengannya untuk memenuhi permintaan. - This game features 18 progressive levels (Which should already keep you busy for hours!) but I'm constantly adding new content - There's a lot planned! + Permainan ini mempunyai 18 level-level progresif (yang mana akan membuat Anda sibuk berjam-jam!), akan tetapi saya akan terus menambahkan konten-konten baru – Ada banyak yang direncanakan! - Purchasing the game gives you access to the standalone version which has additional features, and you'll also receive access to newly developed features. + Membeli permainan ini akan memberikan Anda akses ke versi penuh yang memiliki fitur-fitur tambahan, dan Anda juga akan menerima akses ke fitur-fitur yang baru dikembangkan. - [b]Standalone Advantages[/b] + [b]Keuntungan Versi Penuh[/b] [list] - [*] Dark Mode - [*] Unlimited Waypoints - [*] Unlimited Savegames - [*] Additional settings - [*] Coming soon: Wires & Energy! Aiming for (roughly) end of July 2020. - [*] Coming soon: More Levels - [*] Allows me to further develop shapez.io ❤️ + [*] Versi Permainan Gelap + [*] Titik Arah Tak Terhingga + [*] Penyimpanan Permainan Tak Terhingga + [*] Pengaturan-pengaturan Tambahan + [*] Akan datang: Kawat & Energi! Akan dicoba untuk dicapai untuk (kira-kira) akhir Juli 2020. + [*] Akan datang: Level-level tambahan + [*] Memperkenankan saya untuk terus mengembangkan shapez.io ❤️ [/list] - [b]Future Updates[/b] + [b]Pembaruan di Masa Depan[/b] - I am updating the game often and trying to push an update at least once every week! + Saya seringkali membarui permainan ini dan terus mencoba untuk menciptakan pembaruan paling sedikit sekali seminggu! [list] - [*] Different maps and challenges (e.g. maps with obstacles) - [*] Puzzles (Deliver the requested shape with a restricted area / set of buildings) - [*] A story mode where buildings have a cost - [*] Configurable map generator (Configure resource/shape size/density, seed and more) - [*] Additional types of shapes - [*] Performance improvements (The game already runs pretty well!) - [*] And much more! + [*] Peta-peta dan tantangan-tantangan berbeda (contohnya, peta-peta dengan berbagai rintangan) + [*] Berbagai teka-teki (Kirim bentuk yang diminta dengan area terbatas atau sekelompok bangunan tertentu) + [*] Modus cerita dimana bangunan-bangunan memiliki biaya. + [*] Generator peta yang dapat dikonfigurasi (Konfigurasikan sumber daya/ukuran bentuk/kepadatan, benih dan lainnya) + [*] Tipe-tipe bentuk tambahan + [*] Peningkatan kinerja (Permainannya sudah bekerja cukup baik!) + [*] Dan masih banyak lagi! [/list] - [b]This game is open source![/b] + [b]Permainan ini bekerja secara open source![/b] - Anybody can contribute, I'm actively involved in the community and attempt to review all suggestions and take feedback into consideration where possible. - Be sure to check out my trello board for the full roadmap! + Siapa saja dapat berkontribusi, saya terlibat aktif di dalam komunitas dan akan mencoba untuk meninjau dan menerima semua saran untuk dipertimbangkan dimana memungkinkan. + Pastikan Anda periksa papan trello saya untuk peta perencanaan yang lengkap! - [b]Links[/b] + [b]Tautan-tautan[/b] [list] [*] [url=https://discord.com/invite/HN7EVzV]Official Discord[/url] @@ -88,8 +87,8 @@ steamPage: [/list] global: - loading: Loading - error: Error + loading: Memuat + error: Terdapat kesalahan # How big numbers are rendered, e.g. "10,000" thousandsDivider: "," @@ -105,25 +104,25 @@ global: trillions: T # Shown for infinitely big numbers - infinite: inf + infinite: tak terhingga time: # Used for formatting past time dates - oneSecondAgo: one second ago - xSecondsAgo: seconds ago - oneMinuteAgo: one minute ago - xMinutesAgo: minutes ago - oneHourAgo: one hour ago - xHoursAgo: hours ago - oneDayAgo: one day ago - xDaysAgo: days ago + oneSecondAgo: satu detik yang lalu + xSecondsAgo: detik yang lalu + oneMinuteAgo: satu menit yang lalu + xMinutesAgo: menit yang lalu + oneHourAgo: satu jam yang lalu + xHoursAgo: jam yang lalu + oneDayAgo: satu hari yang lalu + xDaysAgo: hari yang lalu # Short formats for times, e.g. '5h 23m' - secondsShort: s - minutesAndSecondsShort: m s - hoursAndMinutesShort: h m + secondsShort: det + minutesAndSecondsShort: m det + hoursAndMinutesShort: j m - xMinutes: minutes + xMinutes: menit keys: tab: TAB @@ -135,688 +134,686 @@ global: demoBanners: # This is the "advertisement" shown in the main menu and other various places - title: Demo Version + title: Versi Demo intro: >- - Get the standalone to unlock all features! + Dapatkan versi penuh untuk membuka semua fitur! mainMenu: - play: Play - continue: Continue - newGame: New Game - changelog: Changelog + play: Mulai Permainan + continue: Lanjutkan Permainan + newGame: Permainan Baru + changelog: Ganti Data Log subreddit: Reddit - importSavegame: Import - openSourceHint: This game is open source! - discordLink: Official Discord Server - helpTranslate: Help translate! - madeBy: Made by + importSavegame: Impor Data Simpanan + openSourceHint: Permainan ini bekerja secara open source! + discordLink: Server Discord Resmi + helpTranslate: Bantu Terjemahkan! + madeBy: Dibuat oleh # This is shown when using firefox and other browsers which are not supported. browserWarning: >- - Sorry, but the game is known to run slow on your browser! Get the standalone version or download chrome for the full experience. + Maaf, tetapi permainan ini biasanya lambat pada perambah (browser) Anda! Dapatkan versi penuh atau unduh Chrome untuk pengalaman sepenuhnya. savegameLevel: Level - savegameLevelUnknown: Unknown Level + savegameLevelUnknown: Level tidak diketahui dialogs: buttons: ok: OK - delete: Delete - cancel: Cancel - later: Later - restart: Restart - reset: Reset - getStandalone: Get Standalone - deleteGame: I know what I am doing - viewUpdate: View Update - showUpgrades: Show Upgrades - showKeybindings: Show Keybindings + delete: Hapus + cancel: Batal + later: Nanti + restart: Mulai Ulang + reset: Setel Ulang + getStandalone: Dapatkan Versi Penuh + deleteGame: Saya tahu apa yang saya kerjakan + viewUpdate: Tampilkan Pembaruan + showUpgrades: Tunjukkan Tingkatan + showKeybindings: Tunjukan Tombol Pintas importSavegameError: - title: Import Error + title: Kesalahan pada Impor text: >- - Failed to import your savegame: + Gagal memasukkan data simpanan Anda: importSavegameSuccess: - title: Savegame Imported + title: Impor Berhasil text: >- - Your savegame has been successfully imported. + Data simpanan Anda berhasil dimasukkan. gameLoadFailure: - title: Game is broken + title: Permainan Rusak text: >- - Failed to load your savegame: + Gagal memuat data simpanan Anda: confirmSavegameDelete: - title: Confirm deletion + title: Konfirmasi Penghapusan text: >- - Are you sure you want to delete the game? + Apakah Anda yakin untuk menghapus data permainan? savegameDeletionError: - title: Failed to delete + title: Gagal Menghapus text: >- - Failed to delete the savegame: + Gagal untuk menghapus data simpanan: restartRequired: - title: Restart required + title: Diperlukan untuk Memulai Kembali text: >- - You need to restart the game to apply the settings. + Anda harus memulai kembali permainan untuk menerapkan pengaturan. editKeybinding: - title: Change Keybinding - desc: Press the key or mouse button you want to assign, or escape to cancel. + title: Ganti Tombol Pintas + desc: Tekan tombol pada papan ketik atau tetikus yang ingin anda tetapkan, atau tekan escape untuk membatalkan. resetKeybindingsConfirmation: - title: Reset keybindings - desc: This will reset all keybindings to their default values. Please confirm. + title: Setel Ulang Tombol-tombol Pintas + desc: Ini akan menyetel ulang semua tombol pintas kepada pengaturan awalnya. Harap konfirmasi. keybindingsResetOk: - title: Keybindings reset - desc: The keybindings have been reset to their respective defaults! + title: Setel Ulang + desc: Tombol-tombol pintas sudah disetel ulang ke pengaturan awalnya! featureRestriction: - title: Demo Version - desc: You tried to access a feature () which is not available in the demo. Consider getting the standalone version for the full experience! + title: Versi Demo + desc: Anda mencoba untuk mengakses fitur () yang tidak tersedia pada versi demo. Pertimbangkan untuk mendapatkan versi penuh untuk pengalaman sepenuhnya! oneSavegameLimit: - title: Limited savegames - desc: You can only have one savegame at a time in the demo version. Please remove the existing one or get the standalone version! + title: Penyimpanan Permainan Terbatas + desc: Anda hanya dapat memiliki satu simpanan permainan dalam versi demo. Harap hapus yang telah ada atau dapatkan versi penuh! updateSummary: - title: New update! + title: Pembaruan Baru! desc: >- - Here are the changes since you last played: + Berikut perubahan-perubahan yang telah dibuat sejak Anda main terakhir kali: upgradesIntroduction: - title: Unlock Upgrades + title: Buka Tingkatan-tingkatan desc: >- - All shapes you produce can be used to unlock upgrades - Don't destroy your old factories! - The upgrades tab can be found on the top right corner of the screen. + Semua bentuk yang anda produksi dapat digunakan untuk membuka tingkatan baru - Jangan hancurkan pabrik-pabrik lama Anda! + Tab tingkatan dapat ditemukan di sudut atas kanan layar Anda. massDeleteConfirm: - title: Confirm delete + title: Konfirmasi Penghapusan desc: >- - You are deleting a lot of buildings ( to be exact)! Are you sure you want to do this? + Anda akan menghapus banyak bangunan (tepatnya )! Apakah Anda yakin untuk melakukannya? massCutConfirm: - title: Confirm cut + title: Pastikan Pemindahan desc: >- - You are cutting a lot of buildings ( to be exact)! Are you sure you want to do this? + Anda akan memindahkan banyak bangunan (tepatnya )! Apakah Anda yakin untuk melakukannya? massCutInsufficientConfirm: - title: Confirm cut + title: Tidak Mampu Memindahkan desc: >- - You can not afford to paste this area! Are you sure you want to cut it? + Anda tidak mampu menanggung biaya pemindahan area ini! Apakah Anda yakin untuk memindahkannya? blueprintsNotUnlocked: - title: Not unlocked yet + title: Belum Terbuka desc: >- - Complete level 12 to unlock Blueprints! + Selesaikan level 12 untuk membuka cetak biru! keybindingsIntroduction: - title: Useful keybindings + title: Tombol Pintas Berguna desc: >- - This game has a lot of keybindings which make it easier to build big factories. - Here are a few, but be sure to check out the keybindings!

- CTRL + Drag: Select an area.
- SHIFT: Hold to place multiple of one building.
- ALT: Invert orientation of placed belts.
+ Permainan ini memiliki banyak tombol pintas yang membuatnya lebih mudah untuk membangun pabrik-pabrik besar. + Berikut adalah beberapa, namun pastikan Anda perhatikan tombol-tombol pintasnya!

+ CTRL + Tarik: Pilih sebuah area.
+ SHIFT: Tahan untuk meletakkan beberapa kali bangunan yang.
+ ALT: Ganti orientasi sabuk konveyor yang telah diletakkan.
createMarker: - title: New Marker - titleEdit: Edit Marker - desc: Give it a meaningful name, you can also include a short key of a shape (Which you can generate here) + title: Penanda Baru + titleEdit: Sunting Penanda + desc: Berikan nama yang berarti, Anda juga dapat memasukkan tombol pintas suatu bentuk (yang dapat Anda buat disini) markerDemoLimit: - desc: You can only create two custom markers in the demo. Get the standalone for unlimited markers! + desc: Anda hanya dapat memuat dua penanda pada versi demo. Dapatkan versi penuh untuk penanda-penanda tak terhingga! exportScreenshotWarning: - title: Export screenshot - desc: You requested to export your base as a screenshot. Please note that this can be quite slow for a big base and even crash your game! + title: Ekspor Tangkapan Layar + desc: Anda meminta untuk mengekspor pangkalan pusat Anda sebagai tangkapan layar. Harap ketahui bahwa ini bisa menjadi lambat untuk pangkalan pusat yang besar dan bahkan dapat membuat permainan Anda mogok! ingame: # This is shown in the top left corner and displays useful keybindings in # every situation keybindingsOverlay: - moveMap: Move - selectBuildings: Select area - stopPlacement: Stop placement - rotateBuilding: Rotate building - placeMultiple: Place multiple - reverseOrientation: Reverse orientation - disableAutoOrientation: Disable auto-orientation - toggleHud: Toggle HUD - placeBuilding: Place building - createMarker: Create marker - delete: Delete - pasteLastBlueprint: Paste last blueprint - lockBeltDirection: Enable belt planner - plannerSwitchSide: Flip planner side - cutSelection: Cut - copySelection: Copy - clearSelection: Clear selection - pipette: Pipette - switchLayers: Switch layers + moveMap: Pindahkan + selectBuildings: Pilih area + stopPlacement: Hentikan peletakan + rotateBuilding: Putar bangunan + placeMultiple: Letakkan beberapa + reverseOrientation: Balik orientasi + disableAutoOrientation: Nonaktifkan orientasi otomatis. + toggleHud: Ganti HUD + placeBuilding: Letakan bangunan + createMarker: Ciptakan penanda + delete: Hapus + pasteLastBlueprint: Sisipkan cetak biru terakhir + lockBeltDirection: Aktifkan perencana sabuk konveyor + plannerSwitchSide: Balik sisi perencana + cutSelection: Pindahkan + copySelection: Gandakan + clearSelection: Hapus seleksi + pipette: Pipet + switchLayers: Ganti lapisan # Names of the colors, used for the color blind mode colors: - red: Red - green: Green - blue: Blue - yellow: Yellow - purple: Purple - cyan: Cyan - white: White - black: Black - uncolored: Gray + red: Merah + green: Hijau + blue: Biru + yellow: Kuning + purple: Ungu + cyan: Biru kehijauan + white: Putih + black: Hitam + uncolored: Abu-abu # Everything related to placing buildings (I.e. as soon as you selected a building # from the toolbar) buildingPlacement: # Buildings can have different variants which are unlocked at later levels, # and this is the hint shown when there are multiple variants available. - cycleBuildingVariants: Press to cycle variants. + cycleBuildingVariants: Tekan untuk ganti varian. # Shows the hotkey in the ui, e.g. "Hotkey: Q" hotkeyLabel: >- Hotkey: infoTexts: - speed: Speed - range: Range - storage: Storage - oneItemPerSecond: 1 item / second - itemsPerSecond: items / s + speed: Kecepatan + range: Rentang + storage: Penyimpanan + oneItemPerSecond: satu artikel / detik + itemsPerSecond: artikel / detik itemsPerSecondDouble: (x2) - tiles: tiles + tiles: ubin # The notification when completing a level levelCompleteNotification: # is replaced by the actual level, so this gets 'Level 03' for example. levelTitle: Level - completed: Completed - unlockText: Unlocked ! - buttonNextLevel: Next Level + completed: Selesai + unlockText: Membuka ! + buttonNextLevel: Level Selanjutnya # Notifications on the lower right notifications: - newUpgrade: A new upgrade is available! - gameSaved: Your game has been saved. + newUpgrade: Tingkatan baru tersedia! + gameSaved: Permainan Anda telah disimpan. # The "Upgrades" window shop: - title: Upgrades - buttonUnlock: Upgrade + title: Tingkatan-tingkatan + buttonUnlock: Tingkatkan # Gets replaced to e.g. "Tier IX" - tier: Tier + tier: Tingkat # The roman number for each tier tierLabels: [I, II, III, IV, V, VI, VII, VIII, IX, X] - maximumLevel: MAXIMUM LEVEL (Speed x) + maximumLevel: LEVEL MAKSIMUM (Kecepatan x) # The "Statistics" window statistics: - title: Statistics + title: Statistika dataSources: stored: - title: Stored - description: Displaying amount of stored shapes in your central building. + title: Tersimpan + description: Menunjukan jumlah bentuk-bentuk yang tersimpan pada bangunan pusat Anda. produced: - title: Produced - description: Displaying all shapes your whole factory produces, including intermediate products. + title: Terproduksi + description: Menunjukkan semua bentuk yang diproduksi seluruh pabrik Anda, termasuk produk-produk antara. delivered: - title: Delivered - description: Displaying shapes which are delivered to your central building. - noShapesProduced: No shapes have been produced so far. + title: Terkirim + description: Menunjukkan bentuk-bentuk yang telah terkirim ke bangunan pusat Anda. + noShapesProduced: Sejauh ini belum diproduksi. # Displays the shapes per minute, e.g. '523 / m' shapesPerMinute: / m # Settings menu, when you press "ESC" settingsMenu: - playtime: Playtime + playtime: Waktu bermain - buildingsPlaced: Buildings - beltsPlaced: Belts + buildingsPlaced: Bangunan + beltsPlaced: Sabuk konveyor buttons: - continue: Continue - settings: Settings - menu: Return to menu + continue: Lanjutkan + settings: Pengaturan + menu: Kembali ke menu # Bottom left tutorial hints tutorialHints: - title: Need help? - showHint: Show hint - hideHint: Close + title: Butuh bantuan? + showHint: Tampilkan petunjuk + hideHint: Tutup # When placing a blueprint blueprintPlacer: - cost: Cost + cost: Biaya # Map markers waypoints: - waypoints: Markers - hub: HUB - description: Left-click a marker to jump to it, right-click to delete it.

Press to create a marker from the current view, or right-click to create a marker at the selected location. - creationSuccessNotification: Marker has been created. + waypoints: Penanda + hub: PUSAT + description: Klik tombol kiri tetikus pada penanda untuk melompat kepadanya, klik tombol kanan untuk menghapusnya.

Tekan untuk membuat penanda dari sudut pandang saat ini, atau klik tombol kanan untuk membuat penanda pada lokasi yang dipilih. + creationSuccessNotification: Penanda telah dibuat. # Shape viewer shapeViewer: - title: Layers - empty: Empty - copyKey: Copy Key + title: Lapisan-lapisan + empty: Kosong + copyKey: Gandakan tombol # Interactive tutorial interactiveTutorial: - title: Tutorial + title: Penuntun hints: - 1_1_extractor: Place an extractor on top of a circle shape to extract it! + 1_1_extractor: Letakkan ekstraktor diatas bentuk lingkaran untuk mengekstrak bentuk tersebut! 1_2_conveyor: >- - Connect the extractor with a conveyor belt to your hub!

Tip: Click and drag the belt with your mouse! + Hubungkan ekstraktor dengan sabuk konveyor ke pusat pangkalan Anda!

Kiat: Klik dan seret sabuk konveyor dengan tetikus! 1_3_expand: >- - This is NOT an idle game! Build more extractors and belts to finish the goal quicker.

Tip: Hold SHIFT to place multiple extractors, and use R to rotate them. + Ini BUKAN permainan menganggur! Bangun lebih banyak ekstraktor dan sabuk konveyor untuk menyelesaikan obyektif dengan lebih cepat.

Kiat: Tahan tombolSHIFT untuk meletakkan beberapa ekstraktor, dan gunakan tombol R untuk memutar. # All shop upgrades shopUpgrades: belt: - name: Belts, Distributor & Tunnels - description: Speed x → x + name: Sabuk konveyor, Pembagi Arus & Terowongan + description: Kecepatan x → x miner: - name: Extraction - description: Speed x → x + name: Ekstraksi + description: Kecepatan x → x processors: - name: Cutting, Rotating & Stacking + name: Memotong, Memutar & Menyusun description: Speed x → x painting: - name: Mixing & Painting - description: Speed x → x + name: Mencampur & Mencat + description: Kecepatan x → x # Buildings and their name / description buildings: hub: - deliver: Deliver - toUnlock: to unlock + deliver: Kirim + toUnlock: untuk membuka levelShortcut: LVL belt: default: - name: &belt Conveyor Belt - description: Transports items, hold and drag to place multiple. + name: &belt Sabuk Konveyor + description: Mengangkut artikel-artikel, tahan dan seret untuk meletakkan beberapa. wire: default: - name: &wire Energy Wire - description: Allows you to transport energy. + name: &wire Kawat Energi + description: Memungkinkan anda untuk mengangkut energi. miner: # Internal name for the Extractor default: - name: &miner Extractor - description: Place over a shape or color to extract it. + name: &miner Ekstraktor + description: Letakkan diatas sumber bentuk atau warna untuk mengekstraksi mereka. chainable: - name: Extractor (Chain) - description: Place over a shape or color to extract it. Can be chained. + name: Ekstraktor (Berantai) + description: Letakkan di atas sumber bentuk atau warna untuk mengekstraksi mereka. Dapat dirantai. underground_belt: # Internal name for the Tunnel default: - name: &underground_belt Tunnel - description: Allows you to tunnel resources under buildings and belts. + name: &underground_belt Terowongan + description: Memungkinkan Anda untuk mengangkut sumber-sumber daya dibawah bangunan-bangunan atau sabuk konveyor. tier2: - name: Tunnel Tier II - description: Allows you to tunnel resources under buildings and belts. + name: Terowongan Tingkat II + description: Memungkinkan Anda untuk mengangkut sumber-sumber daya dibawah bangunan-bangunan atau sabuk konveyor. splitter: # Internal name for the Balancer default: - name: &splitter Balancer - description: Multifunctional - Evenly distributes all inputs onto all outputs. + name: &splitter Pengimbang + description: Multifungsi – Mendistribusi secara merata semua masukan ke semua keluaran. compact: - name: Merger (compact) - description: Merges two conveyor belts into one. + name: Penggabung (Padat) + description: Menggabungkan dua sabuk konveyor menjadi satu. compact-inverse: - name: Merger (compact) - description: Merges two conveyor belts into one. + name: Penggabung (Padat) + description: Menggabungkan dua sabuk konveyor menjadi satu. cutter: default: - name: &cutter Cutter - description: Cuts shapes from top to bottom and outputs both halves. If you use only one part, be sure to destroy the other part or it will stall! + name: &cutter Pemotong + description: Memotong bentuk-bentuk secara vertikal dan mengeluarkan kedua bagian. Apabila Anda hanya menggunakan satu bagian, pastikan Anda lenyapkan bagian lain atau mesin akan macet! quad: - name: Cutter (Quad) - description: Cuts shapes into four parts. If you use only one part, be sure to destroy the other parts or it will stall! + name: Pemotong (Empat Bagian) + description: Memotong bentuk-bentuk menjadi empat bagian. Apabila Anda hanya menggunakan satu bagian, pastikan Anda lenyapkan bagian-bagian lain atau mesin akan macet! advanced_processor: default: - name: &advanced_processor Color Inverter - description: Accepts a color or shape and inverts it. + name: &advanced_processor Pembalik Warna + description: Menerima warna atau bentuk dan kemudian membaliknya. rotater: default: - name: &rotater Rotate - description: Rotates shapes clockwise by 90 degrees. + name: &rotater Pemutar + description: Memutar bentuk searah jarum jam sebesar 90 derajat. ccw: - name: Rotate (CCW) - description: Rotates shapes counter-clockwise by 90 degrees. + name: Pemutar (Berlawanan Arah Jarum Jam) + description: Memutar bentuk berlawanan arah jarum jam sebesar 90 derajat. fl: - name: Rotate (180) - description: Rotates shapes by 180 degrees. + name: Pemutar (180) + description: Memutar bentuk sebesar 180 derajat. stacker: default: - name: &stacker Stacker - description: Stacks both items. If they can not be merged, the right item is placed above the left item. + name: &stacker Penyusun + description: Menggabungkan kedua artikel. Apabila mereka tidak dapat digabungkan, artikel kanan akan diletakkan diatas artikel kiri. mixer: default: - name: &mixer Color Mixer - description: Mixes two colors using additive blending. + name: &mixer Pencampur Warna + description: Mencampurkan dua warna menggunakan campuran aditif. painter: default: - name: &painter Painter - description: &painter_desc Colors the whole shape on the left input with the color from the top input. - + name: &painter Pencat + description: &painter_desc Mencat keseluruhan bentuk dari input kiri dengan warna dari input atas. mirrored: name: *painter description: *painter_desc double: - name: Painter (Double) - description: Colors the shapes on the left inputs with the color from the top input. + name: Pencat (Ganda) + description: Mencat bentuk-bentuk dari input kiri dengan warna dari input atas. quad: - name: Painter (Quad) - description: Allows you to color each quadrant of the shape with a different color. + name: Pencat (Empat Bagian) + description: Memungkinkan Anda untuk mencat setiap kuadran bentuk dengan warna-warna berbeda. trash: default: - name: &trash Trash - description: Accepts inputs from all sides and destroys them. Forever. + name: &trash Tong Sampah + description: Menerima input dari semua sisi dan menghancurkannya. Selamanya. storage: - name: Storage - description: Stores excess items, up to a given capacity. Can be used as an overflow gate. - + name: Penyimpanan + description: Menyimpan artikel-artikel berlebih, sampai kapasitas tertentu. Dapat dipergunakan sebagai gerbang luapan. energy_generator: - deliver: Deliver + deliver: Pengiriman # This will be shown before the amount, so for example 'For 123 Energy' - toGenerateEnergy: For + toGenerateEnergy: Untuk default: - name: &energy_generator Energy Generator - description: Generates energy by consuming shapes. + name: &energy_generator Pembangkit Energi + description: Menciptakan energy dengan mengonsumsi bentuk-bentuk. wire_crossings: default: - name: &wire_crossings Wire Splitter - description: Splits a energy wire into two. + name: &wire_crossings Pembelah Kawat + description: Membelah satu kawat energy menjadi dua. merger: - name: Wire Merger - description: Merges two energy wires into one. + name: Penggabung Kawat + description: Menggabungkan dua kawat energy menjadi satu. storyRewards: # Those are the rewards gained from completing the store reward_cutter_and_trash: - title: Cutting Shapes - desc: You just unlocked the cutter - it cuts shapes half from top to bottom regardless of its orientation!

Be sure to get rid of the waste, or otherwise it will stall - For this purpose I gave you a trash, which destroys everything you put into it! + title: Memotong Bentuk + desc: Anda baru saja membuka pemotong - ia memotong bentuk-bentuk separuhnya dari atas ke bawah tanpa memperhatikan orientasinya!

Pastikan Anda membuang yang tidak terpakai, atau mesin akan macet - Untuk tujuan ini saya memberikan Anda tong sampah, yang menghancurkan semua yang anda masukkan kedalamnya! reward_rotater: - title: Rotating - desc: The rotater has been unlocked! It rotates shapes clockwise by 90 degrees. + title: Memutar + desc: Pemutar telah dibuka! Ia memutar bentuk-bentuk searah jarum jam sebesar 90 derajat. reward_painter: - title: Painting + title: Pengecatan desc: >- - The painter has been unlocked - Extract some color veins (just as you do with shapes) and combine it with a shape in the painter to color them!

PS: If you are colorblind, there is a colorblind mode in the settings! + Pencat telah dibuka – Ekstraksi beberapa warna (seperti yang Anda lakukan dengan bentuk) dan kemudian kombinasikan dengan bentuk di dalam pencat untuk mewarnai mereka!

Catatan: Apabila Anda buta warna, terdapat mode buta warna di dalam pengaturan! reward_mixer: - title: Color Mixing - desc: The mixer has been unlocked - Combine two colors using additive blending with this building! + title: Pencampuran Warna + desc: Pencampur telah dibuka – Kombinasikan dua warna menggunakan pencampuran aditif dengan bangunan ini! reward_stacker: - title: Combiner - desc: You can now combine shapes with the combiner! Both inputs are combined, and if they can be put next to each other, they will be fused. If not, the right input is stacked on top of the left input! + title: Penyusun + desc: Anda sekarang dapat mengombinasikan bentuk-bentuk dengan penyusun! Kedua input akan dikombinasikan, dan apabila mereka dapat diletakan disebelah satu sama lain, mereka akan terpadukan. Apabila tidak dapat, input kanan akan diletakkan diatas input kiri! reward_splitter: - title: Splitter/Merger - desc: The multifunctional balancer has been unlocked - It can be used to build bigger factories by splitting and merging items onto multiple belts!

+ title: Pembagi/Penggabung + desc: Pengimbang multifungsi telah dibuka – Ia dapat digunakan untuk membangun pabrik yang lebih besar dengan membagi dan menggabungkan artikel-artikel ke berbagai sabuk konveyor!

reward_tunnel: - title: Tunnel - desc: The tunnel has been unlocked - You can now tunnel items through belts and buildings with it! + title: Terowongan + desc: Terowongan telah dibuka – Sekarang Anda dapat memindahkan artikel-artikel melalui terowongan di bawah sabuk-sabuk konveyor dan bangungan-bangunan dengannya! reward_rotater_ccw: - title: CCW Rotating - desc: You have unlocked a variant of the rotater - It allows you to rotate shapes counter-clockwise! To build it, select the rotater and press 'T' to cycle through its variants! + title: Memutar Berlawanan Arah Jarum Jam + desc: Anda telah membuka varian dari Pemutar - Ia memungkinkan Anda untuk memutar bentuk-bentuk berlawanan arah jarum jam! Untuk membangunnya, pilih pemutar dan tekan 'T' to memilih varian! reward_miner_chainable: - title: Chaining Extractor - desc: You have unlocked the chaining extractor! It can forward its resources to other extractors so you can more efficiently extract resources! + title: Merantai Ekstraktor + desc: Anda telah membuka ekstraktor rantai! Ia dapat meneruskan sumberdaya ekstraksinya ke ekstraktor selanjutnya sehingga Anda dapat mengekstraksi dengan lebih efisien! reward_underground_belt_tier_2: - title: Tunnel Tier II - desc: You have unlocked a new variant of the tunnel - It has a bigger range, and you can also mix-n-match those tunnels now! + title: Terowongan Tingkat II + desc: Anda telah membuka varian baru terowongan - Ia memiliki jangkauan yang lebih panjang, dan sekarang Anda juga dapat memadukan terowongan-terowongan tersebut! reward_splitter_compact: - title: Compact Balancer + title: Pengimbang Padat desc: >- - You have unlocked a compact variant of the balancer - It accepts two inputs and merges them into one belt! + Anda telah membuka varian padat dari pengimbang - Ia menerima dua input dan menggabungkannya ke dalam satu konveyor! reward_cutter_quad: - title: Quad Cutting - desc: You have unlocked a variant of the cutter - It allows you to cut shapes in four parts instead of just two! + title: Pemotongan Empat Bagian + desc: Anda telah membuka varian dari pemotong - Ia memungkinkan Anda memotong bentuk-bentuk ke dalam empat bagian daripada hanya dua bagian! reward_painter_double: - title: Double Painting - desc: You have unlocked a variant of the painter - It works as the regular painter but processes two shapes at once consuming just one color instead of two! + title: Pengecatan Ganda + desc: Anda telah membuka varian dari pencat - Ia bekerja seperti pencat biasa namun dapat memproses dua bentuk sekaligus mengonsumsi hanya satu warna daripada dua! reward_painter_quad: - title: Quad Painting - desc: You have unlocked a variant of the painter - It allows you to paint each part of the shape individually! + title: Pengecatan Empat Bagian + desc: Anda telah membuka varian dari pencat - Ia memungkinkan Anda untuk mencat setiap bagian dari masing-masing bentuk sendiri-sendiri! reward_storage: - title: Storage Buffer - desc: You have unlocked a variant of the trash - It allows you to store items up to a given capacity! + title: Penyangga Penyimpanan + desc: Anda telah membuka varian dari tong sampah - Ia memungkinkan Anda untuk menyimpan artikel-artikel sebanyak kapasitas tertentu! reward_freeplay: - title: Freeplay - desc: You did it! You unlocked the free-play mode! This means that shapes are now randomly generated! (No worries, more content is planned for the standalone!) + title: Permainan Bebas + desc: Anda berhasil! Anda telah membuka mode permainan bebas! Ini berarti sekarang bentuk-bentuk akan dihasilkan secara acak! (Jangan khawatir, lebih banyak konten akan direncanakan untuk versi penuh!) reward_blueprints: - title: Blueprints - desc: You can now copy and paste parts of your factory! Select an area (Hold CTRL, then drag with your mouse), and press 'C' to copy it.

Pasting it is not free, you need to produce blueprint shapes to afford it! (Those you just delivered). + title: Cetak Biru + desc: Anda sekarang dapat menyalin dan meletakkan bagian dari pabrik Anda! Pilih sebuah area (tahan CTRL, lalu seret dengan tetikus), dan tekan 'C' untuk menggandakannya.

Untuk meletakannya tidak gratis, Anda harus memproduksi bentuk cetak biru untuk dapat melakukannya! (Bentuk yang baru saja anda kirim). # Special reward, which is shown when there is no reward actually no_reward: - title: Next level + title: Level Selanjutnya desc: >- - This level gave you no reward, but the next one will!

PS: Better don't destroy your existing factory - You need all those shapes later again to unlock upgrades! + Level ini tidak memiliki hadiah, namun yang selanjutnya akan!

Catatan: Sebaiknya Anda jangan hancurkan pabrik yang telah ada – Anda membutuhkan semua bentuk-bentuk tersebut lagi nanti untuk membuka tingkatan-tingkatan selanjutnya! no_reward_freeplay: - title: Next level + title: Level Selanjutnya desc: >- - Congratulations! By the way, more content is planned for the standalone! + Selamat! Omong-omong, lebih banyak konten telah direncanakan untuk versi penuh! settings: - title: Settings + title: Pengaturan categories: - general: General - userInterface: User Interface - advanced: Advanced + general: Umum + userInterface: Antarmuka Pengguna + advanced: Tingkat Tinggi versionBadges: - dev: Development - staging: Staging - prod: Production - buildDate: Built + dev: Pengembangan + staging: Pementasan + prod: Produksi + buildDate: Dibangun labels: uiScale: - title: Interface scale + title: Skala Antarmuka description: >- - Changes the size of the user interface. The interface will still scale based on your device's resolution, but this setting controls the amount of scaling. + Ganti ukuran antarmuka pengguna. Antarmuka tetap akan berskalakan berdasar resolusi peralatan Anda, tetapi pengaturan ini mengontrol besar skala. scales: - super_small: Super small - small: Small - regular: Regular - large: Large - huge: Huge + super_small: Sangat kecil + small: Kecil + regular: Reguler + large: Besar + huge: Sangat besar autosaveInterval: - title: Autosave Interval + title: Jeda Penyimpanan Otomatis description: >- - Controls how often the game saves automatically. You can also disable it entirely here. + Mengatur seberapa sering permainan menyimpan secara otomatis. Anda juga dapat menonaktifkannya sama sekali disini. intervals: - one_minute: 1 Minute - two_minutes: 2 Minutes - five_minutes: 5 Minutes - ten_minutes: 10 Minutes - twenty_minutes: 20 Minutes - disabled: Disabled + one_minute: 1 Menit + two_minutes: 2 Menit + five_minutes: 5 Menit + ten_minutes: 10 Menit + twenty_minutes: 20 Menit + disabled: Dinonaktifkan scrollWheelSensitivity: - title: Zoom sensitivity + title: Kepekaan perbesaran description: >- - Changes how sensitive the zoom is (Either mouse wheel or trackpad). + Mengubah seberapa peka perbesaran yang dilakukan (baik dengan roda tetikus atau trackpad). sensitivity: - super_slow: Super slow - slow: Slow - regular: Regular - fast: Fast - super_fast: Super fast + super_slow: Sangat lambat + slow: Lambat + regular: Reguler + fast: Cepat + super_fast: Sangat cepat movementSpeed: - title: Movement speed + title: Kecepatan gerakan description: >- - Changes how fast the view moves when using the keyboard. + Mengubah seberapa cepat pandangan bergerak ketika menggunakan papan ketik. speeds: - super_slow: Super slow - slow: Slow - regular: Regular - fast: Fast - super_fast: Super Fast - extremely_fast: Extremely Fast + super_slow: Sangat lambat + slow: Lambat + regular: Reguler + fast: Cepat + super_fast: Sangat cepat + extremely_fast: Luar biasa cepat language: - title: Language + title: Bahasa description: >- - Change the language. All translations are user-contributed and might be incomplete! + Ganti bahasa. Semua terjemahan adalah contribusi pengguna dan mungkin saja tidak lengkap! enableColorBlindHelper: - title: Color Blind Mode + title: Mode buta warna description: >- - Enables various tools which allow you to play the game if you are color blind. + Mengaktifkan berbagai peralatan yang memungkinkan Anda bermain apabila Anda buta warna. fullscreen: - title: Fullscreen + title: Layar penuh description: >- - It is recommended to play the game in fullscreen to get the best experience. Only available in the standalone. + Direkomendasikan untuk bermain dengan layar penuh untuk mendapatkan pengalaman terbaik. Hanya tersedia dalam versi penuh. soundsMuted: - title: Mute Sounds + title: Bisukan suara description: >- - If enabled, mutes all sound effects. + Apabila diaktifkan, membisukan semua efek suara. musicMuted: - title: Mute Music + title: Bisukan musik description: >- - If enabled, mutes all music. + Apabila diaktifkan, membisukan semua musik. theme: - title: Game theme + title: Tema permainan description: >- - Choose the game theme (light / dark). + Pilih tema permainan (terang/gelap). themes: - dark: Dark - light: Light + dark: Gelap + light: Terang refreshRate: - title: Simulation Target + title: Target Simulasi description: >- - If you have a 144hz monitor, change the refresh rate here so the game will properly simulate at higher refresh rates. This might actually decrease the FPS if your computer is too slow. + Apabila Anda memiliki monitor 144hz, ganti laju penyegaran sehingga permainan dapat disimulasikan dengan benar pada laju yang lebih tinggi. Ini mungkin saja mengurangi laju bingkai per detik (frames per second) apabila computer anda terlalu lambat. alwaysMultiplace: - title: Multiplace + title: Peletakkan berganda description: >- - If enabled, all buildings will stay selected after placement until you cancel it. This is equivalent to holding SHIFT permanently. + Apabila diaktifkan, semua bangunan akan tetap terpilih setelah penempatan sampai Anda membatalkannya. Ini sama saja dengan menahan tombol SHIFT secara permanen. offerHints: - title: Hints & Tutorials + title: Petunjuk & penuntun description: >- - Whether to offer hints and tutorials while playing. Also hides certain UI elements up to a given level to make it easier to get into the game. + Apakah akan menawarkan petunjuk dan penuntun ketika bermain. Juga menyembunyikan elemen-elemen antarmuka pengguna (user interface) tertentu sampai level tertentu untuk membuat lebih mudah untuk bermain. enableTunnelSmartplace: - title: Smart Tunnels + title: Terowongan cerdas description: >- - When enabled, placing tunnels will automatically remove unnecessary belts. This also enables you to drag tunnels and excess tunnels will get removed. + Ketika diaktifkan, proses penempatan terowongan akan menghapus konveyor yang tidak berguna secara otomatis. Ini juga memungkinkan Anda untuk menyeret terowongan dan kelebihan terowongan akan dihilangkan. vignette: - title: Vignette + title: Vinyet description: >- - Enables the vignette, which darkens the screen corners and makes text easier to read. + Mengaktifkan vinyet, yang menggelapkan sudut-sudut layar dan membuat teks lebih mudah dibaca. rotationByBuilding: - title: Rotation by building type + title: Pemutaran masing-masing tipe bangunan description: >- - Each building type remembers the rotation you last set it to individually. This may be more comfortable if you frequently switch between placing different building types. + Setiap tipe bangunan mengingat putaran yang Anda tetapkan kepadanya. Ini mungkin lebih nyaman apabila Anda sering berganti untuk menempatkan berbagai tipe bangunan. compactBuildingInfo: - title: Compact Building Infos + title: Pemadatan informasi banguna description: >- - Shortens info boxes for buildings by only showing their ratios. Otherwise a description and image is shown. + Memendekkan kotak-kotak informasi bangunan-bangunan dengan hanya menampilkan rasionya. Jika tidak, deskripsi dan gambar ditampilkan. disableCutDeleteWarnings: - title: Disable Cut/Delete Warnings + title: Nonaktifkan peringatan pemindahan/penghapusan description: >- - Disables the warning dialogs brought up when cutting/deleting more than 100 entities. + Menonaktifkan peringatan yang muncul ketikan pemindahkan/menghapus lebih dari 100 entitas. keybindings: - title: Keybindings + title: Tombol pintas hint: >- - Tip: Be sure to make use of CTRL, SHIFT and ALT! They enable different placement options. + Petunjuk: Pastikan Anda menggunakan CTRL, SHIFT and ALT! Mereka memungkinkan berbagai opsi penempatan. - resetKeybindings: Reset Keybindings + resetKeybindings: Setel Ulang Tombol Pintas categoryLabels: - general: Application - ingame: Game - navigation: Navigating - placement: Placement - massSelect: Mass Select - buildings: Building Shortcuts - placementModifiers: Placement Modifiers + general: Aplikasi + ingame: Permainan + navigation: Navigasi + placement: Penempatan + massSelect: Pemilihan massal + buildings: Tombol pintas bangunan + placementModifiers: Pengubah penempatan mappings: - confirm: Confirm - back: Back - mapMoveUp: Move Up - mapMoveRight: Move Right - mapMoveDown: Move Down - mapMoveLeft: Move Left - mapMoveFaster: Move Faster - centerMap: Center Map + confirm: Konfirmasi + back: Kembali + mapMoveUp: Geser ke atas + mapMoveRight: Geser ke kanan + mapMoveDown: Geser ke bawah + mapMoveLeft: Geser ke kiri + mapMoveFaster: Geser lebih cepat + centerMap: Pusat peta - mapZoomIn: Zoom in - mapZoomOut: Zoom out - createMarker: Create Marker + mapZoomIn: Perbesar + mapZoomOut: Perkecil + createMarker: Buat penanda - menuOpenShop: Upgrades - menuOpenStats: Statistics - menuClose: Close Menu + menuOpenShop: Tingkatan-tingkatan + menuOpenStats: Statistik + menuClose: Tutup menu - toggleHud: Toggle HUD - toggleFPSInfo: Toggle FPS and Debug Info - switchLayers: Switch layers - exportScreenshot: Export whole Base as Image + toggleHud: Alihkan HUD + toggleFPSInfo: Alihkan laju bingkan per detik (frame per second) dan informasi debug + switchLayers: Ganti lapisan + exportScreenshot: Ekspor keseluruhan pangkalan pusat sebagai gambar belt: *belt splitter: *splitter underground_belt: *underground_belt @@ -831,49 +828,49 @@ keybindings: trash: *trash wire: *wire - pipette: Pipette - rotateWhilePlacing: Rotate + pipette: Pipet + rotateWhilePlacing: Putar rotateInverseModifier: >- - Modifier: Rotate CCW instead - cycleBuildingVariants: Cycle Variants - confirmMassDelete: Delete area - pasteLastBlueprint: Paste last blueprint - cycleBuildings: Cycle Buildings - lockBeltDirection: Enable belt planner + Modifier: Putar berlawanan arah jarum jam sebagai gantinya + cycleBuildingVariants: Ganti varian + confirmMassDelete: Hapus area + pasteLastBlueprint: Gandakan cetak biru terakhir + cycleBuildings: Ganti bangunan + lockBeltDirection: Aktifkan perencana konveyor switchDirectionLockSide: >- - Planner: Switch side + Planner: Alih sisi - massSelectStart: Hold and drag to start - massSelectSelectMultiple: Select multiple areas - massSelectCopy: Copy area - massSelectCut: Cut area + massSelectStart: Tahan dan seret untuk memulai + massSelectSelectMultiple: Pilih berbagai area + massSelectCopy: Gandakan area + massSelectCut: Pindahkan area - placementDisableAutoOrientation: Disable automatic orientation - placeMultiple: Stay in placement mode - placeInverse: Invert automatic belt orientation + placementDisableAutoOrientation: Nonaktifkan orientasi otomatis + placeMultiple: Tinggal di mode penempatan + placeInverse: Balikkan orientasi otomatis konveyor about: - title: About this Game + title: Tentang permainan ini body: >- - This game is open source and developed by Tobias Springer (this is me).

+ Permainan ini bekerja secara open source dan dikembangkan oleh Tobias Springer (ini adalah saya).

- If you want to contribute, check out shapez.io on github.

+ Apabila Anda ingin berkontribusi, periksa shapez.io di github.

- This game wouldn't have been possible without the great Discord community around my games - You should really join the Discord server!

+ Permainan ini tidak mungkin ada tanpa komunitas Discord di sekitar permainan saya – Anda hendaknya bergabung server Discord!

- The soundtrack was made by Peppsen - He's awesome.

+ Lagunya dibuat oleh Peppsen - Dia mengagumkan.

- Finally, huge thanks to my best friend Niklas - Without our factorio sessions, this game would never have existed. + Akhirnya, banyak terima kasih kepada teman baik saya Niklas - Tanpa sesi-sesi factorio kami, permainan ini tidak mungkin tercipta. changelog: - title: Changelog + title: Catatan Perubahan demo: features: - restoringGames: Restoring savegames - importingGames: Importing savegames - oneGameLimit: Limited to one savegame - customizeKeybindings: Customizing Keybindings - exportingBase: Exporting whole Base as Image + restoringGames: Mengembalikan simpanan permainan + importingGames: Memasukkan simpanan permainan + oneGameLimit: Terbatas pada satu simpanan permainan + customizeKeybindings: Sesuaikan tombol-tombol pintas + exportingBase: Mengekspor keseluruhan pangkalan pusat sebagai gambar - settingNotAvailable: Not available in the demo. + settingNotAvailable: Tidak tersedia dalam versi demo.